1 # Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
19 from nose.tools import eq_
24 from ryu.lib import ofctl_v1_0
25 from ryu.lib import ofctl_v1_2
26 from ryu.lib import ofctl_v1_3
27 from ryu.lib import ofctl_v1_4
28 from ryu.lib import ofctl_v1_5
29 from ryu.ofproto import ofproto_parser
30 from ryu.ofproto.ofproto_protocol import ProtocolDesc
31 from ryu.tests import test_lib
33 LOG = logging.getLogger(__name__)
36 class DummyDatapath(ProtocolDesc):
38 def __init__(self, version):
39 super(DummyDatapath, self).__init__(version)
41 self.request_msg = None
50 def send_msg(self, msg):
52 self.request_msg = msg
55 lock, msgs = self.waiters[self.id][msg.xid]
56 msgs.append(self.reply_msg)
57 del self.waiters[self.id][msg.xid]
60 def set_reply(self, msg, waiters):
62 self.waiters = waiters
65 class Test_ofctl(unittest.TestCase):
67 def _test(self, name, dp, method, args, request, reply, expected):
68 print('processing %s ...' % name)
70 dp.set_reply(reply, waiters)
72 output = method(dp=dp, waiters=waiters, **args)
74 output = method(dp=dp, **args)
76 # expected message <--> sent message
79 eq_(json.dumps(request.to_jsondict(), sort_keys=True),
80 json.dumps(dp.request_msg.to_jsondict(), sort_keys=True))
81 except AssertionError as e:
83 json.dump(dp.request_msg.to_jsondict(),
84 open('/tmp/' + name + '_request.json', 'w'),
85 indent=3, sort_keys=True)
88 # expected output <--> return of ofctl
89 def _remove(d, names):
91 return _remove(x, names)
93 if isinstance(d, list):
94 return list(map(f, d))
95 if isinstance(d, dict):
97 for k, v in d.items():
104 expected = _remove(expected, ['len', 'length'])
105 output = _remove(output, ['len', 'length'])
107 eq_(json.dumps(expected, sort_keys=True),
108 json.dumps(output, sort_keys=True))
109 except AssertionError as e:
111 json.dump(output, open('/tmp/' + name + '_reply.json', 'w'),
128 'method': ofctl_v1_0.mod_flow_entry,
129 'request': '1-2-ofp_flow_mod.packet.json',
135 'method': ofctl_v1_2.get_desc_stats,
136 'request': '3-24-ofp_desc_stats_request.packet.json',
137 'reply': '3-0-ofp_desc_stats_reply.packet.json'
140 'method': ofctl_v1_2.get_queue_stats,
141 'request': '3-37-ofp_queue_stats_request.packet.json',
142 'reply': '3-38-ofp_queue_stats_reply.packet.json'
145 'method': ofctl_v1_2.get_queue_stats,
146 'request': 'lib-ofctl-ofp_queue_stats_request.packet1.json',
147 'reply': '3-38-ofp_queue_stats_reply.packet.json'
150 'method': ofctl_v1_2.get_queue_stats,
151 'request': 'lib-ofctl-ofp_queue_stats_request.packet2.json',
152 'reply': '3-38-ofp_queue_stats_reply.packet.json'
155 'method': ofctl_v1_2.get_queue_stats,
156 'request': 'lib-ofctl-ofp_queue_stats_request.packet3.json',
157 'reply': '3-38-ofp_queue_stats_reply.packet.json'
160 'method': ofctl_v1_2.get_queue_config,
161 'request': '3-35-ofp_queue_get_config_request.packet.json',
162 'reply': '3-36-ofp_queue_get_config_reply.packet.json'
165 'method': ofctl_v1_2.get_queue_config,
166 'request': 'lib-ofctl-ofp_queue_get_config_request.packet.json',
167 'reply': '3-36-ofp_queue_get_config_reply.packet.json'
170 'method': ofctl_v1_2.get_flow_stats,
171 'request': '3-11-ofp_flow_stats_request.packet.json',
172 'reply': '3-12-ofp_flow_stats_reply.packet.json'
175 'method': ofctl_v1_2.get_aggregate_flow_stats,
176 'request': '3-25-ofp_aggregate_stats_request.packet.json',
177 'reply': '3-26-ofp_aggregate_stats_reply.packet.json'
180 'method': ofctl_v1_2.get_table_stats,
181 'request': '3-27-ofp_table_stats_request.packet.json',
182 'reply': '3-28-ofp_table_stats_reply.packet.json'
185 'method': ofctl_v1_2.get_port_stats,
186 'request': '3-29-ofp_port_stats_request.packet.json',
187 'reply': '3-30-ofp_port_stats_reply.packet.json'
190 'method': ofctl_v1_2.get_port_stats,
191 'request': 'lib-ofctl-ofp_port_stats_request.packet.json',
192 'reply': '3-30-ofp_port_stats_reply.packet.json'
195 'method': ofctl_v1_2.get_group_stats,
196 'request': '3-61-ofp_group_stats_request.packet.json',
197 'reply': '3-62-ofp_group_stats_reply.packet.json'
200 'method': ofctl_v1_2.get_group_stats,
201 'request': 'lib-ofctl-ofp_group_stats_request.packet.json',
202 'reply': '3-62-ofp_group_stats_reply.packet.json'
205 'method': ofctl_v1_2.get_group_features,
206 'request': '3-31-ofp_group_features_stats_request.packet.json',
207 'reply': '3-32-ofp_group_features_stats_reply.packet.json'
210 'method': ofctl_v1_2.get_group_desc,
211 'request': '3-33-ofp_group_desc_stats_request.packet.json',
212 'reply': '3-34-ofp_group_desc_stats_reply.packet.json'
214 # In OpenFlow 1.2, ofp_port_desc is not defined.
215 # We use ofp_features_request to get ports description instead.
217 'method': ofctl_v1_2.get_port_desc,
218 'request': '3-5-ofp_features_request.packet.json',
219 'reply': '3-6-ofp_features_reply.packet.json'
222 'method': ofctl_v1_2.mod_flow_entry,
223 'request': '3-2-ofp_flow_mod.packet.json',
227 'method': ofctl_v1_2.mod_group_entry,
228 'request': '3-21-ofp_group_mod.packet.json',
232 'method': ofctl_v1_2.mod_port_behavior,
233 'request': '3-22-ofp_port_mod.packet.json',
237 'method': ofctl_v1_2.send_experimenter,
238 'request': '3-16-ofp_experimenter.packet.json',
244 'method': ofctl_v1_3.get_desc_stats,
245 'request': '4-24-ofp_desc_request.packet.json',
246 'reply': '4-0-ofp_desc_reply.packet.json'
249 'method': ofctl_v1_3.get_queue_stats,
250 'request': '4-37-ofp_queue_stats_request.packet.json',
251 'reply': '4-38-ofp_queue_stats_reply.packet.json'
254 'method': ofctl_v1_3.get_queue_stats,
255 'request': 'lib-ofctl-ofp_queue_stats_request.packet1.json',
256 'reply': '4-38-ofp_queue_stats_reply.packet.json'
259 'method': ofctl_v1_3.get_queue_stats,
260 'request': 'lib-ofctl-ofp_queue_stats_request.packet2.json',
261 'reply': '4-38-ofp_queue_stats_reply.packet.json'
264 'method': ofctl_v1_3.get_queue_stats,
265 'request': 'lib-ofctl-ofp_queue_stats_request.packet3.json',
266 'reply': '4-38-ofp_queue_stats_reply.packet.json'
269 'method': ofctl_v1_3.get_queue_config,
270 'request': '4-35-ofp_queue_get_config_request.packet.json',
271 'reply': '4-36-ofp_queue_get_config_reply.packet.json'
274 'method': ofctl_v1_3.get_queue_config,
275 'request': 'lib-ofctl-ofp_queue_get_config_request.packet.json',
276 'reply': '4-36-ofp_queue_get_config_reply.packet.json'
279 'method': ofctl_v1_3.get_flow_stats,
280 'request': '4-11-ofp_flow_stats_request.packet.json',
281 'reply': '4-12-ofp_flow_stats_reply.packet.json'
284 'method': ofctl_v1_3.get_aggregate_flow_stats,
285 'request': '4-25-ofp_aggregate_stats_request.packet.json',
286 'reply': '4-26-ofp_aggregate_stats_reply.packet.json'
289 'method': ofctl_v1_3.get_table_stats,
290 'request': '4-27-ofp_table_stats_request.packet.json',
291 'reply': '4-28-ofp_table_stats_reply.packet.json'
294 'method': ofctl_v1_3.get_table_features,
295 'request': 'lib-ofctl-ofp_table_features_request.packet.json',
296 'reply': '4-56-ofp_table_features_reply.packet.json'
299 'method': ofctl_v1_3.get_port_stats,
300 'request': '4-29-ofp_port_stats_request.packet.json',
301 'reply': '4-30-ofp_port_stats_reply.packet.json'
304 'method': ofctl_v1_3.get_port_stats,
305 'request': 'lib-ofctl-ofp_port_stats_request.packet.json',
306 'reply': '4-30-ofp_port_stats_reply.packet.json'
309 'method': ofctl_v1_3.get_meter_stats,
310 'request': '4-49-ofp_meter_stats_request.packet.json',
311 'reply': '4-50-ofp_meter_stats_reply.packet.json'
314 'method': ofctl_v1_3.get_meter_stats,
315 'request': 'lib-ofctl-ofp_meter_stats_request.packet.json',
316 'reply': '4-50-ofp_meter_stats_reply.packet.json'
319 'method': ofctl_v1_3.get_meter_features,
320 'request': '4-51-ofp_meter_features_request.packet.json',
321 'reply': '4-52-ofp_meter_features_reply.packet.json'
324 'method': ofctl_v1_3.get_meter_config,
325 'request': '4-47-ofp_meter_config_request.packet.json',
326 'reply': '4-48-ofp_meter_config_reply.packet.json'
329 'method': ofctl_v1_3.get_meter_config,
330 'request': 'lib-ofctl-ofp_meter_config_request.packet.json',
331 'reply': '4-48-ofp_meter_config_reply.packet.json'
334 'method': ofctl_v1_3.get_group_stats,
335 'request': '4-57-ofp_group_stats_request.packet.json',
336 'reply': '4-58-ofp_group_stats_reply.packet.json'
339 'method': ofctl_v1_3.get_group_stats,
340 'request': 'lib-ofctl-ofp_group_stats_request.packet.json',
341 'reply': '4-58-ofp_group_stats_reply.packet.json'
344 'method': ofctl_v1_3.get_group_features,
345 'request': '4-31-ofp_group_features_request.packet.json',
346 'reply': '4-32-ofp_group_features_reply.packet.json'
349 'method': ofctl_v1_3.get_group_desc,
350 'request': '4-33-ofp_group_desc_request.packet.json',
351 'reply': '4-34-ofp_group_desc_reply.packet.json'
354 'method': ofctl_v1_3.get_port_desc,
355 'request': '4-53-ofp_port_desc_request.packet.json',
356 'reply': '4-54-ofp_port_desc_reply.packet.json'
359 'method': ofctl_v1_3.mod_flow_entry,
360 'request': '4-2-ofp_flow_mod.packet.json',
364 'method': ofctl_v1_3.mod_meter_entry,
365 'request': '4-45-ofp_meter_mod.packet.json',
369 'method': ofctl_v1_3.mod_group_entry,
370 'request': '4-21-ofp_group_mod.packet.json',
374 'method': ofctl_v1_3.mod_port_behavior,
375 'request': '4-22-ofp_port_mod.packet.json',
379 'method': ofctl_v1_3.send_experimenter,
380 'request': '4-16-ofp_experimenter.packet.json',
386 'method': ofctl_v1_4.get_desc_stats,
387 'request': '5-24-ofp_desc_request.packet.json',
388 'reply': '5-0-ofp_desc_reply.packet.json'
391 'method': ofctl_v1_4.get_queue_stats,
392 'request': '5-35-ofp_queue_stats_request.packet.json',
393 'reply': '5-36-ofp_queue_stats_reply.packet.json'
396 'method': ofctl_v1_4.get_queue_desc,
397 'request': '5-63-ofp_queue_desc_request.packet.json',
398 'reply': '5-64-ofp_queue_desc_reply.packet.json'
401 'method': ofctl_v1_4.get_flow_stats,
402 'request': '5-11-ofp_flow_stats_request.packet.json',
403 'reply': '5-12-ofp_flow_stats_reply.packet.json'
406 'method': ofctl_v1_4.get_aggregate_flow_stats,
407 'request': '5-25-ofp_aggregate_stats_request.packet.json',
408 'reply': '5-26-ofp_aggregate_stats_reply.packet.json'
411 'method': ofctl_v1_4.get_table_stats,
412 'request': '5-27-ofp_table_stats_request.packet.json',
413 'reply': '5-28-ofp_table_stats_reply.packet.json'
416 'method': ofctl_v1_4.get_table_features,
417 'request': 'lib-ofctl-ofp_table_features_request.packet.json',
418 'reply': '5-54-ofp_table_features_reply.packet.json'
421 'method': ofctl_v1_4.get_port_stats,
422 'request': '5-29-ofp_port_stats_request.packet.json',
423 'reply': '5-30-ofp_port_stats_reply.packet.json'
426 'method': ofctl_v1_4.get_meter_stats,
427 'request': '5-47-ofp_meter_stats_request.packet.json',
428 'reply': '5-48-ofp_meter_stats_reply.packet.json'
431 'method': ofctl_v1_4.get_meter_features,
432 'request': '5-49-ofp_meter_features_request.packet.json',
433 'reply': '5-50-ofp_meter_features_reply.packet.json'
436 'method': ofctl_v1_4.get_meter_config,
437 'request': '5-45-ofp_meter_config_request.packet.json',
438 'reply': '5-46-ofp_meter_config_reply.packet.json'
441 'method': ofctl_v1_4.get_group_stats,
442 'request': '5-55-ofp_group_stats_request.packet.json',
443 'reply': '5-56-ofp_group_stats_reply.packet.json'
446 'method': ofctl_v1_4.get_group_features,
447 'request': '5-31-ofp_group_features_request.packet.json',
448 'reply': '5-32-ofp_group_features_reply.packet.json'
451 'method': ofctl_v1_4.get_group_desc,
452 'request': '5-33-ofp_group_desc_request.packet.json',
453 'reply': '5-34-ofp_group_desc_reply.packet.json'
456 'method': ofctl_v1_4.get_port_desc,
457 'request': '5-51-ofp_port_desc_request.packet.json',
458 'reply': '5-52-ofp_port_desc_reply.packet.json'
461 'method': ofctl_v1_4.mod_flow_entry,
462 'request': '5-2-ofp_flow_mod.packet.json',
466 'method': ofctl_v1_4.mod_meter_entry,
467 'request': '5-43-ofp_meter_mod.packet.json',
471 'method': ofctl_v1_4.mod_group_entry,
472 'request': '5-21-ofp_group_mod.packet.json',
476 'method': ofctl_v1_4.mod_port_behavior,
477 'request': '5-22-ofp_port_mod.packet.json',
481 'method': ofctl_v1_4.send_experimenter,
482 'request': '5-16-ofp_experimenter.packet.json',
488 'method': ofctl_v1_5.get_desc_stats,
489 'request': 'libofproto-OFP15-desc_request.packet.json',
490 'reply': 'libofproto-OFP15-desc_reply.packet.json'
493 'method': ofctl_v1_5.get_queue_stats,
494 'request': 'lib-ofctl-ofp_queue_stats_request.packet.json',
495 'reply': 'libofproto-OFP15-queue_stats_reply.packet.json'
498 'method': ofctl_v1_5.get_queue_desc,
499 'request': 'libofproto-OFP15-queue_desc_request.packet.json',
500 'reply': 'libofproto-OFP15-queue_desc_reply.packet.json'
503 'method': ofctl_v1_5.get_flow_stats,
504 'request': 'libofproto-OFP15-flow_stats_request.packet.json',
505 'reply': 'libofproto-OFP15-flow_stats_reply.packet.json'
508 'method': ofctl_v1_5.get_flow_desc_stats,
509 'request': 'libofproto-OFP15-flow_desc_request.packet.json',
510 'reply': 'libofproto-OFP15-flow_desc_reply.packet.json'
513 'method': ofctl_v1_5.get_flow_desc_stats,
514 'request': 'lib-ofctl-OFP15-flow_desc_request.packet.json',
515 'reply': 'lib-ofctl-OFP15-flow_desc_reply.packet.json'
518 'method': ofctl_v1_5.get_aggregate_flow_stats,
519 'request': 'libofproto-OFP15-aggregate_stats_request.packet.json',
520 'reply': 'libofproto-OFP15-aggregate_stats_reply.packet.json'
523 'method': ofctl_v1_5.get_table_stats,
524 'request': 'libofproto-OFP15-table_stats_request.packet.json',
525 'reply': 'libofproto-OFP15-table_stats_reply.packet.json'
528 'method': ofctl_v1_5.get_table_features,
529 'request': 'lib-ofctl-ofp_table_features_request.packet.json',
530 'reply': 'libofproto-OFP15-table_features_reply.packet.json'
533 'method': ofctl_v1_5.get_port_stats,
534 'request': 'libofproto-OFP15-port_stats_request.packet.json',
535 'reply': 'libofproto-OFP15-port_stats_reply.packet.json'
538 'method': ofctl_v1_5.get_meter_stats,
539 'request': 'libofproto-OFP15-meter_stats_request.packet.json',
540 'reply': 'libofproto-OFP15-meter_stats_reply.packet.json'
543 'method': ofctl_v1_5.get_meter_features,
544 'request': 'libofproto-OFP15-meter_features_request.packet.json',
545 'reply': 'libofproto-OFP15-meter_features_reply.packet.json'
548 'method': ofctl_v1_5.get_meter_desc,
549 'request': 'libofproto-OFP15-meter_desc_request.packet.json',
550 'reply': 'libofproto-OFP15-meter_desc_reply.packet.json'
553 'method': ofctl_v1_5.get_group_stats,
554 'request': 'libofproto-OFP15-group_stats_request.packet.json',
555 'reply': 'libofproto-OFP15-group_stats_reply.packet.json'
558 'method': ofctl_v1_5.get_group_features,
559 'request': 'libofproto-OFP15-group_features_request.packet.json',
560 'reply': 'libofproto-OFP15-group_features_reply.packet.json'
563 'method': ofctl_v1_5.get_group_desc,
564 'request': 'libofproto-OFP15-group_desc_request.packet.json',
565 'reply': 'libofproto-OFP15-group_desc_reply.packet.json'
568 'method': ofctl_v1_5.get_port_desc,
569 'request': 'libofproto-OFP15-port_desc_request.packet.json',
570 'reply': 'libofproto-OFP15-port_desc_reply.packet.json'
573 'method': ofctl_v1_5.mod_flow_entry,
574 'request': 'libofproto-OFP15-flow_mod_no_nx.packet.json',
578 'method': ofctl_v1_5.mod_flow_entry,
579 'request': 'lib-ofctl-OFP15-flow_mod.packet.json',
583 'method': ofctl_v1_5.mod_meter_entry,
584 'request': 'libofproto-OFP15-meter_mod.packet.json',
588 'method': ofctl_v1_5.mod_group_entry,
589 'request': 'libofproto-OFP15-group_mod.packet.json',
593 'method': ofctl_v1_5.mod_port_behavior,
594 'request': 'libofproto-OFP15-port_mod.packet.json',
598 'method': ofctl_v1_5.send_experimenter,
599 'request': 'libofproto-OFP15-experimenter.packet.json',
605 def _jsonfile_to_msg(datapath, jsonfile):
606 return ofproto_parser.ofp_msg_from_jsondict(
607 datapath, json.load(open(jsonfile)))
609 this_dir = os.path.dirname(sys.modules[__name__].__file__)
610 parser_json_root = os.path.join(this_dir, '../ofproto/json/')
611 ofctl_json_root = os.path.join(this_dir, 'ofctl_json/')
613 for ofp_ver, tests in _test_cases.items():
614 dp = DummyDatapath(_ofp_vers[ofp_ver])
615 parser_json_dir = os.path.join(parser_json_root, ofp_ver)
616 ofctl_json_dir = os.path.join(ofctl_json_root, ofp_ver)
618 name = 'test_ofctl_' + ofp_ver + '_' + test['request']
619 print('adding %s ...' % name)
621 args_json_path = os.path.join(ofctl_json_dir, test['request'])
622 if os.path.exists(args_json_path):
623 args = json.load(open(args_json_path))
624 request = _jsonfile_to_msg(
625 dp, os.path.join(parser_json_dir, test['request']))
629 reply = _jsonfile_to_msg(
630 dp, os.path.join(parser_json_dir, test['reply']))
631 expected = json.load(
632 open(os.path.join(ofctl_json_dir, test['reply'])))
633 f = functools.partial(
634 Test_ofctl._test, name=name, dp=dp, method=test['method'],
635 args=args, request=request, reply=reply, expected=expected)
636 test_lib.add_method(Test_ofctl, name, f)
641 if __name__ == "__main__":