8
8
import errno
9
9
import ipaddress
10
10
import logging
11
+ import math
11
12
import multiprocessing
12
13
import re
13
14
import socket
60
61
OVS_FLOW_CMD_GET = 3
61
62
OVS_FLOW_CMD_SET = 4
62
63
64
+ UINT32_MAX = 0xFFFFFFFF
63
65
64
66
def macstr (mac ):
65
67
outstr = ":" .join (["%02X" % i for i in mac ])
@@ -281,6 +283,75 @@ def parse_extract_field(
281
283
return str_skipped , data
282
284
283
285
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
+
284
355
class ovs_dp_msg (genlmsg ):
285
356
# include the OVS version
286
357
# We need a custom header rather than just being able to rely on
@@ -299,7 +370,7 @@ class ovsactions(nla):
299
370
("OVS_ACTION_ATTR_SET" , "ovskey" ),
300
371
("OVS_ACTION_ATTR_PUSH_VLAN" , "none" ),
301
372
("OVS_ACTION_ATTR_POP_VLAN" , "flag" ),
302
- ("OVS_ACTION_ATTR_SAMPLE" , "none " ),
373
+ ("OVS_ACTION_ATTR_SAMPLE" , "sample " ),
303
374
("OVS_ACTION_ATTR_RECIRC" , "uint32" ),
304
375
("OVS_ACTION_ATTR_HASH" , "none" ),
305
376
("OVS_ACTION_ATTR_PUSH_MPLS" , "none" ),
@@ -318,8 +389,85 @@ class ovsactions(nla):
318
389
("OVS_ACTION_ATTR_ADD_MPLS" , "none" ),
319
390
("OVS_ACTION_ATTR_DEC_TTL" , "none" ),
320
391
("OVS_ACTION_ATTR_DROP" , "uint32" ),
392
+ ("OVS_ACTION_ATTR_PSAMPLE" , "psample" ),
321
393
)
322
394
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
+
323
471
class ctact (nla ):
324
472
nla_flags = NLA_F_NESTED
325
473
@@ -683,6 +831,18 @@ def parse(self, actstr):
683
831
self ["attrs" ].append (["OVS_ACTION_ATTR_CT" , ctact ])
684
832
parsed = True
685
833
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
+
686
846
actstr = actstr [strspn (actstr , ", " ) :]
687
847
while parencount > 0 :
688
848
parencount -= 1
0 commit comments