Skip to content

Commit 60ccf62

Browse files
amorenozkuba-moo
authored andcommitted
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 <[email protected]> Signed-off-by: Adrian Moreno <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 71763d8 commit 60ccf62

File tree

1 file changed

+161
-1
lines changed

1 file changed

+161
-1
lines changed

tools/testing/selftests/net/openvswitch/ovs-dpctl.py

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import errno
99
import ipaddress
1010
import logging
11+
import math
1112
import multiprocessing
1213
import re
1314
import socket
@@ -60,6 +61,7 @@
6061
OVS_FLOW_CMD_GET = 3
6162
OVS_FLOW_CMD_SET = 4
6263

64+
UINT32_MAX = 0xFFFFFFFF
6365

6466
def macstr(mac):
6567
outstr = ":".join(["%02X" % i for i in mac])
@@ -281,6 +283,75 @@ def parse_extract_field(
281283
return str_skipped, data
282284

283285

286+
def parse_attrs(actstr, attr_desc):
287+
"""Parses the given action string and returns a list of netlink
288+
attributes based on a list of attribute descriptions.
289+
290+
Each element in the attribute description list is a tuple such as:
291+
(name, attr_name, parse_func)
292+
where:
293+
name: is the string representing the attribute
294+
attr_name: is the name of the attribute as defined in the uAPI.
295+
parse_func: is a callable accepting a string and returning either
296+
a single object (the parsed attribute value) or a tuple of
297+
two values (the parsed attribute value and the remaining string)
298+
299+
Returns a list of attributes and the remaining string.
300+
"""
301+
def parse_attr(actstr, key, func):
302+
actstr = actstr[len(key) :]
303+
304+
if not func:
305+
return None, actstr
306+
307+
delim = actstr[0]
308+
actstr = actstr[1:]
309+
310+
if delim == "=":
311+
pos = strcspn(actstr, ",)")
312+
ret = func(actstr[:pos])
313+
else:
314+
ret = func(actstr)
315+
316+
if isinstance(ret, tuple):
317+
(datum, actstr) = ret
318+
else:
319+
datum = ret
320+
actstr = actstr[strcspn(actstr, ",)"):]
321+
322+
if delim == "(":
323+
if not actstr or actstr[0] != ")":
324+
raise ValueError("Action contains unbalanced parentheses")
325+
326+
actstr = actstr[1:]
327+
328+
actstr = actstr[strspn(actstr, ", ") :]
329+
330+
return datum, actstr
331+
332+
attrs = []
333+
attr_desc = list(attr_desc)
334+
while actstr and actstr[0] != ")" and attr_desc:
335+
found = False
336+
for i, (key, attr, func) in enumerate(attr_desc):
337+
if actstr.startswith(key):
338+
datum, actstr = parse_attr(actstr, key, func)
339+
attrs.append([attr, datum])
340+
found = True
341+
del attr_desc[i]
342+
343+
if not found:
344+
raise ValueError("Unknown attribute: '%s'" % actstr)
345+
346+
actstr = actstr[strspn(actstr, ", ") :]
347+
348+
if actstr[0] != ")":
349+
raise ValueError("Action string contains extra garbage or has "
350+
"unbalanced parenthesis: '%s'" % actstr)
351+
352+
return attrs, actstr[1:]
353+
354+
284355
class ovs_dp_msg(genlmsg):
285356
# include the OVS version
286357
# We need a custom header rather than just being able to rely on
@@ -299,7 +370,7 @@ class ovsactions(nla):
299370
("OVS_ACTION_ATTR_SET", "ovskey"),
300371
("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
301372
("OVS_ACTION_ATTR_POP_VLAN", "flag"),
302-
("OVS_ACTION_ATTR_SAMPLE", "none"),
373+
("OVS_ACTION_ATTR_SAMPLE", "sample"),
303374
("OVS_ACTION_ATTR_RECIRC", "uint32"),
304375
("OVS_ACTION_ATTR_HASH", "none"),
305376
("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
@@ -318,8 +389,85 @@ class ovsactions(nla):
318389
("OVS_ACTION_ATTR_ADD_MPLS", "none"),
319390
("OVS_ACTION_ATTR_DEC_TTL", "none"),
320391
("OVS_ACTION_ATTR_DROP", "uint32"),
392+
("OVS_ACTION_ATTR_PSAMPLE", "psample"),
321393
)
322394

395+
class psample(nla):
396+
nla_flags = NLA_F_NESTED
397+
398+
nla_map = (
399+
("OVS_PSAMPLE_ATTR_UNSPEC", "none"),
400+
("OVS_PSAMPLE_ATTR_GROUP", "uint32"),
401+
("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"),
402+
)
403+
404+
def dpstr(self, more=False):
405+
args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP")
406+
407+
cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")
408+
if cookie:
409+
args += ",cookie(%s)" % \
410+
"".join(format(x, "02x") for x in cookie)
411+
412+
return "psample(%s)" % args
413+
414+
def parse(self, actstr):
415+
desc = (
416+
("group", "OVS_PSAMPLE_ATTR_GROUP", int),
417+
("cookie", "OVS_PSAMPLE_ATTR_COOKIE",
418+
lambda x: list(bytearray.fromhex(x)))
419+
)
420+
421+
attrs, actstr = parse_attrs(actstr, desc)
422+
423+
for attr in attrs:
424+
self["attrs"].append(attr)
425+
426+
return actstr
427+
428+
class sample(nla):
429+
nla_flags = NLA_F_NESTED
430+
431+
nla_map = (
432+
("OVS_SAMPLE_ATTR_UNSPEC", "none"),
433+
("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"),
434+
("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"),
435+
)
436+
437+
def dpstr(self, more=False):
438+
args = []
439+
440+
args.append("sample={:.2f}%".format(
441+
100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") /
442+
UINT32_MAX))
443+
444+
actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS")
445+
if actions:
446+
args.append("actions(%s)" % actions.dpstr(more))
447+
448+
return "sample(%s)" % ",".join(args)
449+
450+
def parse(self, actstr):
451+
def parse_nested_actions(actstr):
452+
subacts = ovsactions()
453+
parsed_len = subacts.parse(actstr)
454+
return subacts, actstr[parsed_len :]
455+
456+
def percent_to_rate(percent):
457+
percent = float(percent.strip('%'))
458+
return int(math.floor(UINT32_MAX * (percent / 100.0) + .5))
459+
460+
desc = (
461+
("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate),
462+
("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions),
463+
)
464+
attrs, actstr = parse_attrs(actstr, desc)
465+
466+
for attr in attrs:
467+
self["attrs"].append(attr)
468+
469+
return actstr
470+
323471
class ctact(nla):
324472
nla_flags = NLA_F_NESTED
325473

@@ -683,6 +831,18 @@ def parse(self, actstr):
683831
self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])
684832
parsed = True
685833

834+
elif parse_starts_block(actstr, "sample(", False):
835+
sampleact = self.sample()
836+
actstr = sampleact.parse(actstr[len("sample(") : ])
837+
self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact])
838+
parsed = True
839+
840+
elif parse_starts_block(actstr, "psample(", False):
841+
psampleact = self.psample()
842+
actstr = psampleact.parse(actstr[len("psample(") : ])
843+
self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])
844+
parsed = True
845+
686846
actstr = actstr[strspn(actstr, ", ") :]
687847
while parencount > 0:
688848
parencount -= 1

0 commit comments

Comments
 (0)