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 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 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):
|
||||
# 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_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):
|
||||
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 = 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, ", ") :]
|
||||
while parencount > 0:
|
||||
parencount -= 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user