selftests: openvswitch: add psample action
Add sample and psample action support to ovs-dpctl.py. Refactor common attribute parsing logic into an external function. Reviewed-by: Aaron Conole <aconole@redhat.com> Signed-off-by: Adrian Moreno <amorenoz@redhat.com> Link: https://patch.msgid.link/20240704085710.353845-8-amorenoz@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
71763d8a82
commit
60ccf62d3c
@ -8,6 +8,7 @@ import argparse
|
|||||||
import errno
|
import errno
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
@ -60,6 +61,7 @@ OVS_FLOW_CMD_DEL = 2
|
|||||||
OVS_FLOW_CMD_GET = 3
|
OVS_FLOW_CMD_GET = 3
|
||||||
OVS_FLOW_CMD_SET = 4
|
OVS_FLOW_CMD_SET = 4
|
||||||
|
|
||||||
|
UINT32_MAX = 0xFFFFFFFF
|
||||||
|
|
||||||
def macstr(mac):
|
def macstr(mac):
|
||||||
outstr = ":".join(["%02X" % i for i in mac])
|
outstr = ":".join(["%02X" % i for i in mac])
|
||||||
@ -281,6 +283,75 @@ def parse_extract_field(
|
|||||||
return str_skipped, data
|
return str_skipped, data
|
||||||
|
|
||||||
|
|
||||||
|
def parse_attrs(actstr, attr_desc):
|
||||||
|
"""Parses the given action string and returns a list of netlink
|
||||||
|
attributes based on a list of attribute descriptions.
|
||||||
|
|
||||||
|
Each element in the attribute description list is a tuple such as:
|
||||||
|
(name, attr_name, parse_func)
|
||||||
|
where:
|
||||||
|
name: is the string representing the attribute
|
||||||
|
attr_name: is the name of the attribute as defined in the uAPI.
|
||||||
|
parse_func: is a callable accepting a string and returning either
|
||||||
|
a single object (the parsed attribute value) or a tuple of
|
||||||
|
two values (the parsed attribute value and the remaining string)
|
||||||
|
|
||||||
|
Returns a list of attributes and the remaining string.
|
||||||
|
"""
|
||||||
|
def parse_attr(actstr, key, func):
|
||||||
|
actstr = actstr[len(key) :]
|
||||||
|
|
||||||
|
if not func:
|
||||||
|
return None, actstr
|
||||||
|
|
||||||
|
delim = actstr[0]
|
||||||
|
actstr = actstr[1:]
|
||||||
|
|
||||||
|
if delim == "=":
|
||||||
|
pos = strcspn(actstr, ",)")
|
||||||
|
ret = func(actstr[:pos])
|
||||||
|
else:
|
||||||
|
ret = func(actstr)
|
||||||
|
|
||||||
|
if isinstance(ret, tuple):
|
||||||
|
(datum, actstr) = ret
|
||||||
|
else:
|
||||||
|
datum = ret
|
||||||
|
actstr = actstr[strcspn(actstr, ",)"):]
|
||||||
|
|
||||||
|
if delim == "(":
|
||||||
|
if not actstr or actstr[0] != ")":
|
||||||
|
raise ValueError("Action contains unbalanced parentheses")
|
||||||
|
|
||||||
|
actstr = actstr[1:]
|
||||||
|
|
||||||
|
actstr = actstr[strspn(actstr, ", ") :]
|
||||||
|
|
||||||
|
return datum, actstr
|
||||||
|
|
||||||
|
attrs = []
|
||||||
|
attr_desc = list(attr_desc)
|
||||||
|
while actstr and actstr[0] != ")" and attr_desc:
|
||||||
|
found = False
|
||||||
|
for i, (key, attr, func) in enumerate(attr_desc):
|
||||||
|
if actstr.startswith(key):
|
||||||
|
datum, actstr = parse_attr(actstr, key, func)
|
||||||
|
attrs.append([attr, datum])
|
||||||
|
found = True
|
||||||
|
del attr_desc[i]
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
raise ValueError("Unknown attribute: '%s'" % actstr)
|
||||||
|
|
||||||
|
actstr = actstr[strspn(actstr, ", ") :]
|
||||||
|
|
||||||
|
if actstr[0] != ")":
|
||||||
|
raise ValueError("Action string contains extra garbage or has "
|
||||||
|
"unbalanced parenthesis: '%s'" % actstr)
|
||||||
|
|
||||||
|
return attrs, actstr[1:]
|
||||||
|
|
||||||
|
|
||||||
class ovs_dp_msg(genlmsg):
|
class ovs_dp_msg(genlmsg):
|
||||||
# include the OVS version
|
# include the OVS version
|
||||||
# We need a custom header rather than just being able to rely on
|
# We need a custom header rather than just being able to rely on
|
||||||
@ -299,7 +370,7 @@ class ovsactions(nla):
|
|||||||
("OVS_ACTION_ATTR_SET", "ovskey"),
|
("OVS_ACTION_ATTR_SET", "ovskey"),
|
||||||
("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
|
("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
|
||||||
("OVS_ACTION_ATTR_POP_VLAN", "flag"),
|
("OVS_ACTION_ATTR_POP_VLAN", "flag"),
|
||||||
("OVS_ACTION_ATTR_SAMPLE", "none"),
|
("OVS_ACTION_ATTR_SAMPLE", "sample"),
|
||||||
("OVS_ACTION_ATTR_RECIRC", "uint32"),
|
("OVS_ACTION_ATTR_RECIRC", "uint32"),
|
||||||
("OVS_ACTION_ATTR_HASH", "none"),
|
("OVS_ACTION_ATTR_HASH", "none"),
|
||||||
("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
|
("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
|
||||||
@ -318,8 +389,85 @@ class ovsactions(nla):
|
|||||||
("OVS_ACTION_ATTR_ADD_MPLS", "none"),
|
("OVS_ACTION_ATTR_ADD_MPLS", "none"),
|
||||||
("OVS_ACTION_ATTR_DEC_TTL", "none"),
|
("OVS_ACTION_ATTR_DEC_TTL", "none"),
|
||||||
("OVS_ACTION_ATTR_DROP", "uint32"),
|
("OVS_ACTION_ATTR_DROP", "uint32"),
|
||||||
|
("OVS_ACTION_ATTR_PSAMPLE", "psample"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class psample(nla):
|
||||||
|
nla_flags = NLA_F_NESTED
|
||||||
|
|
||||||
|
nla_map = (
|
||||||
|
("OVS_PSAMPLE_ATTR_UNSPEC", "none"),
|
||||||
|
("OVS_PSAMPLE_ATTR_GROUP", "uint32"),
|
||||||
|
("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def dpstr(self, more=False):
|
||||||
|
args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP")
|
||||||
|
|
||||||
|
cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")
|
||||||
|
if cookie:
|
||||||
|
args += ",cookie(%s)" % \
|
||||||
|
"".join(format(x, "02x") for x in cookie)
|
||||||
|
|
||||||
|
return "psample(%s)" % args
|
||||||
|
|
||||||
|
def parse(self, actstr):
|
||||||
|
desc = (
|
||||||
|
("group", "OVS_PSAMPLE_ATTR_GROUP", int),
|
||||||
|
("cookie", "OVS_PSAMPLE_ATTR_COOKIE",
|
||||||
|
lambda x: list(bytearray.fromhex(x)))
|
||||||
|
)
|
||||||
|
|
||||||
|
attrs, actstr = parse_attrs(actstr, desc)
|
||||||
|
|
||||||
|
for attr in attrs:
|
||||||
|
self["attrs"].append(attr)
|
||||||
|
|
||||||
|
return actstr
|
||||||
|
|
||||||
|
class sample(nla):
|
||||||
|
nla_flags = NLA_F_NESTED
|
||||||
|
|
||||||
|
nla_map = (
|
||||||
|
("OVS_SAMPLE_ATTR_UNSPEC", "none"),
|
||||||
|
("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"),
|
||||||
|
("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def dpstr(self, more=False):
|
||||||
|
args = []
|
||||||
|
|
||||||
|
args.append("sample={:.2f}%".format(
|
||||||
|
100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") /
|
||||||
|
UINT32_MAX))
|
||||||
|
|
||||||
|
actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS")
|
||||||
|
if actions:
|
||||||
|
args.append("actions(%s)" % actions.dpstr(more))
|
||||||
|
|
||||||
|
return "sample(%s)" % ",".join(args)
|
||||||
|
|
||||||
|
def parse(self, actstr):
|
||||||
|
def parse_nested_actions(actstr):
|
||||||
|
subacts = ovsactions()
|
||||||
|
parsed_len = subacts.parse(actstr)
|
||||||
|
return subacts, actstr[parsed_len :]
|
||||||
|
|
||||||
|
def percent_to_rate(percent):
|
||||||
|
percent = float(percent.strip('%'))
|
||||||
|
return int(math.floor(UINT32_MAX * (percent / 100.0) + .5))
|
||||||
|
|
||||||
|
desc = (
|
||||||
|
("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate),
|
||||||
|
("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions),
|
||||||
|
)
|
||||||
|
attrs, actstr = parse_attrs(actstr, desc)
|
||||||
|
|
||||||
|
for attr in attrs:
|
||||||
|
self["attrs"].append(attr)
|
||||||
|
|
||||||
|
return actstr
|
||||||
|
|
||||||
class ctact(nla):
|
class ctact(nla):
|
||||||
nla_flags = NLA_F_NESTED
|
nla_flags = NLA_F_NESTED
|
||||||
|
|
||||||
@ -683,6 +831,18 @@ class ovsactions(nla):
|
|||||||
self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])
|
self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])
|
||||||
parsed = True
|
parsed = True
|
||||||
|
|
||||||
|
elif parse_starts_block(actstr, "sample(", False):
|
||||||
|
sampleact = self.sample()
|
||||||
|
actstr = sampleact.parse(actstr[len("sample(") : ])
|
||||||
|
self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact])
|
||||||
|
parsed = True
|
||||||
|
|
||||||
|
elif parse_starts_block(actstr, "psample(", False):
|
||||||
|
psampleact = self.psample()
|
||||||
|
actstr = psampleact.parse(actstr[len("psample(") : ])
|
||||||
|
self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])
|
||||||
|
parsed = True
|
||||||
|
|
||||||
actstr = actstr[strspn(actstr, ", ") :]
|
actstr = actstr[strspn(actstr, ", ") :]
|
||||||
while parencount > 0:
|
while parencount > 0:
|
||||||
parencount -= 1
|
parencount -= 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user