Adrian Moreno amorenoz@redhat.com writes:
Add sample and psample action support to ovs-dpctl.py.
Refactor common attribute parsing logic into an external function.
Signed-off-by: Adrian Moreno amorenoz@redhat.com
Reviewed-by: Aaron Conole aconole@redhat.com
.../selftests/net/openvswitch/ovs-dpctl.py | 162 +++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py index 182a09975975..dcc400a21a22 100644 --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -8,6 +8,7 @@ import argparse import errno import ipaddress import logging +import math import multiprocessing import re import socket @@ -60,6 +61,7 @@ OVS_FLOW_CMD_DEL = 2 OVS_FLOW_CMD_GET = 3 OVS_FLOW_CMD_SET = 4 +UINT32_MAX = 0xFFFFFFFF def macstr(mac): outstr = ":".join(["%02X" % i for i in mac]) @@ -281,6 +283,75 @@ def parse_extract_field( 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 attributeattr_name: is the name of the attribute as defined in the uAPI.parse_func: is a callable accepting a string and returning eithera single object (the parsed attribute value) or a tuple oftwo 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, actstrdelim = actstr[0]actstr = actstr[1:]if delim == "=":pos = strcspn(actstr, ",)")ret = func(actstr[:pos])else:ret = func(actstr)if isinstance(ret, tuple):(datum, actstr) = retelse:datum = retactstr = 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 = Falsefor i, (key, attr, func) in enumerate(attr_desc):if actstr.startswith(key):datum, actstr = parse_attr(actstr, key, func)attrs.append([attr, datum])found = Truedel 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): # include the OVS version # 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_PUSH_VLAN", "none"), ("OVS_ACTION_ATTR_POP_VLAN", "flag"),
("OVS_ACTION_ATTR_SAMPLE", "none"),
("OVS_ACTION_ATTR_SAMPLE", "sample"), ("OVS_ACTION_ATTR_RECIRC", "uint32"), ("OVS_ACTION_ATTR_HASH", "none"), ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),@@ -318,8 +389,85 @@ class ovsactions(nla): ("OVS_ACTION_ATTR_ADD_MPLS", "none"), ("OVS_ACTION_ATTR_DEC_TTL", "none"), ("OVS_ACTION_ATTR_DROP", "uint32"),
)("OVS_ACTION_ATTR_PSAMPLE", "psample"),
- class psample(nla):
nla_flags = NLA_F_NESTEDnla_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)" % argsdef 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_NESTEDnla_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): nla_flags = NLA_F_NESTED
@@ -683,6 +831,18 @@ class ovsactions(nla): self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact]) 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 = Trueelif parse_starts_block(actstr, "psample(", False):psampleact = self.psample()actstr = psampleact.parse(actstr[len("psample(") : ])self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])parsed = Trueactstr = actstr[strspn(actstr, ", ") :] while parencount > 0: parencount -= 1