Skip to content

Commit c464e66

Browse files
committed
sdn-controller: add optional cookie argument support
if used, set cookie on OF rule to help deleting group of rules
1 parent 572dd4d commit c464e66

File tree

4 files changed

+85
-12
lines changed

4 files changed

+85
-12
lines changed

SOURCES/etc/xapi.d/plugins/sdncontroller.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,22 @@ def parse_priority(self):
161161
E_PARSER, "'{}' is not a valid priority".format(priority)
162162
)
163163

164+
def parse_cookie(self):
165+
COOKIE_REGEX = re.compile(
166+
r"^0x[0-9a-fA-F]{1,16}$"
167+
)
168+
169+
cookie = self.args.get("cookie")
170+
if cookie is None:
171+
return "0x0"
172+
173+
if not COOKIE_REGEX.match(cookie):
174+
log_and_raise_error(
175+
E_PARSER, "'{}' is not a valid cookie".format(cookie)
176+
)
177+
178+
return hex(int(cookie, 16))
179+
164180
def read(self, key, parse_fn, dests=None):
165181
# parse_fn can return a single value or a tuple of values.
166182
# In this case we are expecting dests to match the expected
@@ -303,6 +319,7 @@ def build_rule_string(direction, ofport, args, uplink=False):
303319
rule_parts = {
304320
"priority": ("priority", "priority"),
305321
"protocol": (None, None),
322+
"cookie": ("cookie", "cookie"),
306323
"ofport": ("in_port", "in_port"),
307324
"mac": ("dl_src", "dl_dst"),
308325
"iprange": ("nw_dst", "nw_src"),
@@ -318,6 +335,8 @@ def build_rule_string(direction, ofport, args, uplink=False):
318335
if args.get("priority"):
319336
rule += "priority={}".format(args["priority"]) + ","
320337
rule += args["protocol"]
338+
if args.get("cookie"):
339+
rule += ",cookie={}".format(args["cookie"])
321340
if uplink:
322341
rule += ",dl_vlan={}".format(vlanid)
323342
if ofport:
@@ -342,6 +361,7 @@ def run_ofctl_cmd(cmd, bridge, rule):
342361
% (format(ofctl_cmd), cmd["stderr"]),
343362
)
344363
_LOGGER.info("Applied rule: {}".format(ofctl_cmd))
364+
return cmd
345365

346366

347367
@error_wrapped
@@ -358,6 +378,7 @@ def add_rule(_session, args):
358378
parser.read("port", parser.parse_port)
359379
parser.read("allow", parser.parse_allow)
360380
parser.read("priority", parser.parse_priority)
381+
parser.read("cookie", parser.parse_cookie)
361382
except XenAPIPlugin.Failure as e:
362383
log_and_raise_error(
363384
E_PARSER, "add_rule: Failed to get parameters: {}".format(e.params[1])
@@ -383,6 +404,14 @@ def add_rule(_session, args):
383404
E_PORTS, "No ports found for bridge: {}".format(rule_args["bridge"])
384405
)
385406

407+
# validate cookie isn't already used
408+
if rule_args["cookie"] != "0x0":
409+
cmd = run_ofctl_cmd("dump-flows", "", "cookie={}/-1".format(rule_args["cookie"]))
410+
if cmd["stdout"] != "\n":
411+
log_and_raise_error(
412+
E_PARAMS, "add_rule: this cookie is already used"
413+
)
414+
386415
# We can now build the open flow rule
387416
rules = build_rules_strings(rule_args)
388417
_LOGGER.info("Built rules: {}".format(rules))
@@ -408,6 +437,7 @@ def del_rule(_session, args):
408437
parser.read("protocol", parser.parse_protocol)
409438
parser.read("iprange", parser.parse_iprange)
410439
parser.read("port", parser.parse_port)
440+
parser.read("cookie", parser.parse_cookie)
411441
except XenAPIPlugin.Failure as e:
412442
log_and_raise_error(
413443
E_PARSER, "del_rule: Failed to get parameters: {}".format(e.params[1])
@@ -427,11 +457,21 @@ def del_rule(_session, args):
427457
E_PARAMS, "del_rule: No port provided, tcp and udp requires one"
428458
)
429459

430-
update_args_from_ovs(rule_args)
460+
# to match on a cookie, need to specify a mask
461+
rule_args["cookie"] = "{}/-1".format(rule_args["cookie"])
431462

432-
# We can now build the open flow rule
433-
rules = build_rules_strings(rule_args)
434-
_LOGGER.info("Built rules: {}".format(rules))
463+
if rule_args["cookie"] == "0x0/-1":
464+
update_args_from_ovs(rule_args)
465+
466+
# We can now build the open flow rule
467+
rules = build_rules_strings(rule_args)
468+
_LOGGER.info("Built rules: {}".format(rules))
469+
470+
else:
471+
# if cookie value is meanful, use it to remove all related rules
472+
rules = [
473+
"cookie={}".format(rule_args["cookie"]),
474+
]
435475

436476
for rule in rules:
437477
run_ofctl_cmd("del-flows", rule_args["parent-bridge"], rule)

tests/sdncontroller_test_cases/functions.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
}, # list-interfaces
193193
],
194194
"calls": [
195-
call("add-flow", "xenbr0", "ip,in_port=5,nw_dst=1.1.1.1,actions=drop"),
195+
call("add-flow", "xenbr0", "ip,cookie=0x0,in_port=5,nw_dst=1.1.1.1,actions=drop"),
196196
],
197197
},
198198
{ # subnet tcp 4242 allow
@@ -221,7 +221,7 @@
221221
call(
222222
"add-flow",
223223
"xenbr0",
224-
"tcp,in_port=5,nw_dst=1.1.1.1/24,tp_dst=4242,actions=normal",
224+
"tcp,cookie=0x0,in_port=5,nw_dst=1.1.1.1/24,tp_dst=4242,actions=normal",
225225
)
226226
],
227227
},
@@ -252,7 +252,7 @@
252252
call(
253253
"add-flow",
254254
"xenbr0",
255-
"udp,dl_dst=DE:AD:BE:EF:CA:FE,nw_src=4.4.4.4,tp_src=2121,actions=drop",
255+
"udp,cookie=0x0,dl_dst=DE:AD:BE:EF:CA:FE,nw_src=4.4.4.4,tp_src=2121,actions=drop",
256256
),
257257
],
258258
},
@@ -283,7 +283,7 @@
283283
"type": XenAPIPlugin.Failure,
284284
"code": "3",
285285
"text": "Error running ovs-ofctl command: ['ovs-ofctl', '-O', 'OpenFlow11', 'add-flow', 'xenbr0', "
286-
"'ip,in_port=5,nw_dst=1.1.1.1/24,actions=drop']: fake error",
286+
"'ip,cookie=0x0,in_port=5,nw_dst=1.1.1.1/24,actions=drop']: fake error",
287287
},
288288
"cmd": [
289289
{"returncode": 0, "stdout": "xenbr0", "stderr": ""}, # br-to-parent
@@ -341,7 +341,7 @@
341341
}, # list-interfaces
342342
],
343343
"calls": [
344-
call("del-flows", "xenbr0", "ip,in_port=5,nw_dst=1.1.1.1"),
344+
call("del-flows", "xenbr0", "ip,cookie=0x0,in_port=5,nw_dst=1.1.1.1"),
345345
],
346346
},
347347
{ # subnet tcp 4242 allow
@@ -370,7 +370,7 @@
370370
call(
371371
"del-flows",
372372
"xenbr0",
373-
"tcp,in_port=5,nw_dst=1.1.1.1/24,tp_dst=4242",
373+
"tcp,cookie=0x0,in_port=5,nw_dst=1.1.1.1/24,tp_dst=4242",
374374
)
375375
],
376376
},
@@ -401,7 +401,7 @@
401401
call(
402402
"del-flows",
403403
"xenbr0",
404-
"udp,dl_dst=DE:AD:BE:EF:CA:FE,nw_src=4.4.4.4,tp_src=2121",
404+
"udp,cookie=0x0,dl_dst=DE:AD:BE:EF:CA:FE,nw_src=4.4.4.4,tp_src=2121",
405405
),
406406
],
407407
},
@@ -432,7 +432,7 @@
432432
"type": XenAPIPlugin.Failure,
433433
"code": "3",
434434
"text": "Error running ovs-ofctl command: ['ovs-ofctl', '-O', 'OpenFlow11', 'del-flows', 'xenbr0', "
435-
"'ip,in_port=5,nw_dst=1.1.1.1/24']: fake error",
435+
"'ip,cookie=0x0,in_port=5,nw_dst=1.1.1.1/24']: fake error",
436436
},
437437
"cmd": [
438438
{"returncode": 0, "stdout": "xenbr0", "stderr": ""}, # br-to-parent

tests/sdncontroller_test_cases/parser.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,27 @@
370370
},
371371
]
372372
PRIORITY_IDS = ["100", "0", "65535", "65536", "aoeui", "empty string", "no parameters"]
373+
374+
375+
COOKIE_PARAMS = [
376+
{"input": {"cookie": "0x0"}, "result": "0x0", "exception": None},
377+
{"input": {"cookie": "0x01234"}, "result": "0x1234", "exception": None},
378+
{"input": {"cookie": "0xFFFFffffFFFFffff"}, "result": "0xffffffffffffffff", "exception": None},
379+
{"input": {}, "result": "0x0", "exception": None},
380+
{
381+
"input": {"cookie": "0x"},
382+
"result": None,
383+
"exception": {
384+
"type": XenAPIPlugin.Failure,
385+
"code": "1",
386+
"text": "'0x' is not a valid cookie",
387+
},
388+
},
389+
]
390+
COOKIE_IDS = [
391+
"0x0",
392+
"0x1234",
393+
"0xFFFFffffFFFFffff",
394+
"no parameter",
395+
"0x",
396+
]

tests/test_sdn-controller.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
ALLOW_IDS,
2222
PRIORITY_PARAMS,
2323
PRIORITY_IDS,
24+
COOKIE_PARAMS,
25+
COOKIE_IDS,
2426
)
2527

2628
from sdncontroller_test_cases.functions import (
@@ -112,6 +114,13 @@ def test_parse_priority(self, priority):
112114
p = Parser(priority["input"])
113115
parser_test(p.parse_priority, priority)
114116

117+
@pytest.fixture(params=COOKIE_PARAMS, ids=COOKIE_IDS)
118+
def cookie(self, request):
119+
return request.param
120+
121+
def test_parse_cookie(self, cookie):
122+
p = Parser(cookie["input"])
123+
parser_test(p.parse_cookie, cookie)
115124

116125
@mock.patch("sdncontroller.run_command", autospec=True)
117126
class TestSdnControllerFunctions:

0 commit comments

Comments
 (0)