blob: 55cd20f3ab3def92caf9341bbb706c07f60d1e87 [file] [log] [blame]
Rich Lane3f075972013-03-15 22:56:29 -07001#!/usr/bin/env python
2# Copyright 2013, Big Switch Networks, Inc.
3#
4# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
5# the following special exception:
6#
7# LOXI Exception
8#
9# As a special exception to the terms of the EPL, you may distribute libraries
10# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
11# that copyright and licensing notices generated by LoxiGen are not altered or removed
12# from the LoxiGen Libraries and the notice provided below is (i) included in
13# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
14# documentation for the LoxiGen Libraries, if distributed in binary form.
15#
16# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
17#
18# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
19# a copy of the EPL at:
20#
21# http://www.eclipse.org/legal/epl-v10.html
22#
23# Unless required by applicable law or agreed to in writing, software
24# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26# EPL for the specific language governing permissions and limitations
27# under the EPL.
28import unittest
Rich Lane4c764982013-05-01 16:12:22 -070029import difflib
Rich Lane3f075972013-03-15 22:56:29 -070030
31try:
32 import loxi.of13 as ofp
Rich Lane57026dc2013-05-01 10:13:16 -070033 from loxi.generic_util import OFReader
Rich Lane3f075972013-03-15 22:56:29 -070034except ImportError:
35 exit("loxi package not found. Try setting PYTHONPATH.")
36
Rich Lane4c764982013-05-01 16:12:22 -070037# Human-friendly format for binary strings. 8 bytes per line.
38def format_binary(buf):
39 byts = map(ord, buf)
40 lines = [[]]
41 for byt in byts:
42 if len(lines[-1]) == 8:
43 lines.append([])
44 lines[-1].append(byt)
45 return '\n'.join([' '.join(['%02x' % y for y in x]) for x in lines])
46
47def diff(a, b):
48 return '\n'.join(difflib.ndiff(a.splitlines(), b.splitlines()))
49
50# Test serialization / deserialization of a sample object.
51# Depends on the __eq__ method being correct.
52def test_serialization(obj, buf):
53 packed = obj.pack()
54 if packed != buf:
55 a = format_binary(buf)
56 b = format_binary(packed)
57 raise AssertionError("Serialization of %s failed\nExpected:\n%s\nActual:\n%s\nDiff:\n%s" % \
58 (type(obj).__name__, a, b, diff(a, b)))
59 unpacked = type(obj).unpack(buf)
60 if obj != unpacked:
61 a = obj.show()
62 b = unpacked.show()
63 raise AssertionError("Deserialization of %s failed\nExpected:\n%s\nActual:\n%s\nDiff:\n%s" % \
64 (type(obj).__name__, a, b, diff(a, b)))
65
Rich Lane3f075972013-03-15 22:56:29 -070066class TestImports(unittest.TestCase):
67 def test_toplevel(self):
68 import loxi
69 self.assertTrue(hasattr(loxi, "ProtocolError"))
Rich Lane00549ea2013-04-25 13:33:16 -070070 self.assertEquals(loxi.version_names[4], "1.3")
Rich Lane3f075972013-03-15 22:56:29 -070071 ofp = loxi.protocol(4)
72 self.assertEquals(ofp.OFP_VERSION, 4)
73 self.assertTrue(hasattr(ofp, "action"))
74 self.assertTrue(hasattr(ofp, "common"))
75 self.assertTrue(hasattr(ofp, "const"))
76 self.assertTrue(hasattr(ofp, "message"))
Rich Lanea22233e2013-04-25 13:18:41 -070077 self.assertTrue(hasattr(ofp, "oxm"))
Rich Lane3f075972013-03-15 22:56:29 -070078
79 def test_version(self):
80 import loxi
81 self.assertTrue(hasattr(loxi.of13, "ProtocolError"))
82 self.assertTrue(hasattr(loxi.of13, "OFP_VERSION"))
83 self.assertEquals(loxi.of13.OFP_VERSION, 4)
84 self.assertTrue(hasattr(loxi.of13, "action"))
85 self.assertTrue(hasattr(loxi.of13, "common"))
86 self.assertTrue(hasattr(loxi.of13, "const"))
87 self.assertTrue(hasattr(loxi.of13, "message"))
Rich Lanea22233e2013-04-25 13:18:41 -070088 self.assertTrue(hasattr(loxi.of13, "oxm"))
Rich Lane3f075972013-03-15 22:56:29 -070089
Rich Lanee90685c2013-04-05 17:27:41 -070090class TestCommon(unittest.TestCase):
91 sample_hello_elem_buf = ''.join([
92 '\x00\x01', # type
93 '\x00\x0c', # length
94 '\x01\x23\x45\x67', # bitmaps[0]
95 '\x89\xab\xcd\xef', # bitmaps[1]
96 ])
97
98 def test_hello_elem_versionbitmap_pack(self):
99 obj = ofp.hello_elem_versionbitmap(bitmaps=[ofp.uint32(0x01234567),ofp.uint32(0x89abcdef)])
100 self.assertEquals(self.sample_hello_elem_buf, obj.pack())
101
102 def test_hello_elem_versionbitmap_unpack(self):
103 obj = ofp.hello_elem_versionbitmap.unpack(self.sample_hello_elem_buf)
104 self.assertEquals(len(obj.bitmaps), 2)
105 self.assertEquals(obj.bitmaps[0], ofp.uint32(0x01234567))
106 self.assertEquals(obj.bitmaps[1], ofp.uint32(0x89abcdef))
107
108 def test_list_hello_elem_unpack(self):
109 buf = ''.join([
110 '\x00\x01\x00\x04', # versionbitmap
111 '\x00\x00\x00\x04', # unknown type
112 '\x00\x01\x00\x04', # versionbitmap
113 ])
Rich Lane57026dc2013-05-01 10:13:16 -0700114 l = ofp.unpack_list_hello_elem(OFReader(buf))
Rich Lanee90685c2013-04-05 17:27:41 -0700115 self.assertEquals(len(l), 2)
116 self.assertTrue(isinstance(l[0], ofp.hello_elem_versionbitmap))
117 self.assertTrue(isinstance(l[1], ofp.hello_elem_versionbitmap))
118
Rich Lane4c764982013-05-01 16:12:22 -0700119class TestMessages(unittest.TestCase):
120 def test_hello(self):
121 obj = ofp.message.hello(
122 xid=0x12345678,
123 elements=[
124 ofp.hello_elem_versionbitmap(
125 bitmaps=[ofp.uint32(1), ofp.uint32(2)]),
126 ofp.hello_elem_versionbitmap(
127 bitmaps=[ofp.uint32(3), ofp.uint32(4)])])
128 buf = ''.join([
129 '\x04', '\x00', # version, type
130 '\x00\x20', # length
131 '\x12\x34\x56\x78', # xid
132 '\x00\x01', # elements[0].type
133 '\x00\x0c', # elements[0].length
134 '\x00\x00\x00\x01', # elements[0].bitmaps[0]
135 '\x00\x00\x00\x02', # elements[0].bitmaps[1]
136 '\x00\x01', # elements[1].type
137 '\x00\x0c', # elements[1].length
138 '\x00\x00\x00\x03', # elements[1].bitmaps[0]
139 '\x00\x00\x00\x04', # elements[1].bitmaps[1]
140 ])
141 test_serialization(obj, buf)
142
143 def test_error(self):
144 obj = ofp.message.error_msg(
145 xid=0x12345678,
146 err_type=ofp.OFPET_BAD_MATCH,
147 code=ofp.OFPBMC_BAD_MASK,
148 data="abc")
149 buf = ''.join([
150 '\x04', '\x01', # version, type
151 '\x00\x0f', # length
152 '\x12\x34\x56\x78', # xid
153 '\x00\x04', # err_type
154 '\x00\x08', # code
155 'abc', # data
156 ])
157 test_serialization(obj, buf)
158
159 def test_echo_request(self):
160 obj = ofp.message.echo_request(
161 xid=0x12345678,
162 data="abc")
163 buf = ''.join([
164 '\x04', '\x02', # version, type
165 '\x00\x0b', # length
166 '\x12\x34\x56\x78', # xid
167 'abc', # data
168 ])
169 test_serialization(obj, buf)
170
171 def test_echo_reply(self):
172 obj = ofp.message.echo_reply(
173 xid=0x12345678,
174 data="abc")
175 buf = ''.join([
176 '\x04', '\x03', # version, type
177 '\x00\x0b', # length
178 '\x12\x34\x56\x78', # xid
179 'abc', # data
180 ])
181 test_serialization(obj, buf)
182
183 def test_features_request(self):
184 obj = ofp.message.features_request(xid=0x12345678)
185 buf = ''.join([
186 '\x04', '\x05', # version, type
187 '\x00\x08', # length
188 '\x12\x34\x56\x78', # xid
189 ])
190 test_serialization(obj, buf)
191
192 def test_features_reply(self):
193 obj = ofp.message.features_reply(
194 xid=0x12345678,
195 datapath_id=0xFEDCBA9876543210,
196 n_buffers=64,
197 n_tables=200,
198 auxiliary_id=5,
199 capabilities=ofp.OFPC_FLOW_STATS|ofp.OFPC_PORT_BLOCKED,
200 reserved=0)
201 buf = ''.join([
202 '\x04', '\x06', # version, type
203 '\x00\x20', # length
204 '\x12\x34\x56\x78', # xid
205 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # datapath_id
206 '\x00\x00\x00\x40', # n_buffers
207 '\xc8', # n_tables
208 '\x05', # auxiliary_id
209 '\x00\x00', # pad
210 '\x00\x00\x01\x01', # capabilities
211 '\x00\x00\x00\x00', # reserved
212 ])
213 test_serialization(obj, buf)
214
215 def test_get_config_request(self):
216 obj = ofp.message.get_config_request(xid=0x12345678)
217 buf = ''.join([
218 '\x04', '\x07', # version, type
219 '\x00\x08', # length
220 '\x12\x34\x56\x78', # xid
221 ])
222 test_serialization(obj, buf)
223
224 def test_get_config_reply(self):
225 obj = ofp.message.get_config_reply(
226 xid=0x12345678,
227 flags=ofp.OFPC_FRAG_REASM,
228 miss_send_len=0xffff)
229 buf = ''.join([
230 '\x04', '\x08', # version, type
231 '\x00\x0c', # length
232 '\x12\x34\x56\x78', # xid
233 '\x00\x02', # flags
234 '\xff\xff', # miss_send_len
235 ])
236 test_serialization(obj, buf)
237
238 def test_set_config(self):
239 obj = ofp.message.set_config(
240 xid=0x12345678,
241 flags=ofp.OFPC_FRAG_REASM,
242 miss_send_len=0xffff)
243 buf = ''.join([
244 '\x04', '\x09', # version, type
245 '\x00\x0c', # length
246 '\x12\x34\x56\x78', # xid
247 '\x00\x02', # flags
248 '\xff\xff', # miss_send_len
249 ])
250 test_serialization(obj, buf)
251
252 def test_packet_in(self):
253 obj = ofp.message.packet_in(
254 xid=0x12345678,
255 buffer_id=100,
256 total_len=17000,
257 reason=ofp.OFPR_ACTION,
258 table_id=20,
259 cookie=0xFEDCBA9876543210,
260 match=ofp.match(oxm_list=[
261 ofp.oxm.arp_op(value=1),
262 ofp.oxm.in_port_masked(value=4, value_mask=5)]),
263 data="abc")
264 buf = ''.join([
265 '\x04', '\x0a', # version, type
266 '\x00\x35', # length
267 '\x12\x34\x56\x78', # xid
268 '\x00\x00\x00\x64', # buffer_id
269 '\x42\x68', # total_len
270 '\x01', # reason
271 '\x14', # table_id
272 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # cookie
273 '\x00\x01', # match.type
274 '\x00\x16', # match.length
275 '\x80\x00\x2A\x02', # match.oxm_list[0].type_len
276 '\x00\x01', # match.oxm_list[0].value
277 '\x80\x00\x01\x08', # match.oxm_list[1].type_len
278 '\x00\x00\x00\x04', # match.oxm_list[1].value
279 '\x00\x00\x00\x05', # match.oxm_list[1].mask
280 '\x00\x00', # match.pad
281 '\x00\x00', # pad
282 'abc', # data
283 ])
284 test_serialization(obj, buf)
285
286 def test_flow_removed(self):
287 obj = ofp.message.flow_removed(
288 xid=0x12345678,
289 cookie=0xFEDCBA9876543210,
290 priority=17000,
291 reason=ofp.OFPRR_DELETE,
292 table_id=20,
293 duration_sec=10,
294 duration_nsec=1000,
295 idle_timeout=5,
296 hard_timeout=30,
297 packet_count=1,
298 byte_count=2,
299 match=ofp.match(oxm_list=[
300 ofp.oxm.arp_op(value=1),
301 ofp.oxm.in_port_masked(value=4, value_mask=5)]))
302 buf = ''.join([
303 '\x04', '\x0b', # version, type
304 '\x00\x48', # length
305 '\x12\x34\x56\x78', # xid
306 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # cookie
307 '\x42\x68', # priority
308 '\x02', # reason
309 '\x14', # table_id
310 '\x00\x00\x00\x0a', # duration_sec
311 '\x00\x00\x03\xe8', # duration_nsec
312 '\x00\x05', # idle_timeout
313 '\x00\x1e', # hard_timeout
314 '\x00\x00\x00\x00\x00\x00\x00\x01', # packet_count
315 '\x00\x00\x00\x00\x00\x00\x00\x02', # byte_count
316 '\x00\x01', # match.type
317 '\x00\x16', # match.length
318 '\x80\x00\x2A\x02', # match.oxm_list[0].type_len
319 '\x00\x01', # match.oxm_list[0].value
320 '\x80\x00\x01\x08', # match.oxm_list[1].type_len
321 '\x00\x00\x00\x04', # match.oxm_list[1].value
322 '\x00\x00\x00\x05', # match.oxm_list[1].mask
323 '\x00\x00', # match.pad
324 ])
325 test_serialization(obj, buf)
326
327 def test_port_status(self):
328 obj = ofp.message.port_status(
329 xid=0x12345678,
330 reason=ofp.OFPPR_MODIFY,
331 desc=ofp.port_desc(
332 port_no=4,
333 hw_addr=[1,2,3,4,5,6],
334 name="foo",
335 config=ofp.OFPPC_NO_FWD|ofp.OFPPC_NO_RECV,
336 state=ofp.OFPPS_BLOCKED,
337 curr=ofp.OFPPF_10MB_HD,
338 advertised=ofp.OFPPF_10MB_FD,
339 supported=ofp.OFPPF_100MB_HD,
340 peer=ofp.OFPPF_100MB_FD,
341 curr_speed=10,
342 max_speed=20))
343 buf = ''.join([
344 '\x04', '\x0c', # version, type
345 '\x00\x50', # length
346 '\x12\x34\x56\x78', # xid
347 '\x02', # reason
348 '\x00' * 7, # pad
349 '\x00\x00\x00\x04', # port_no
350 '\x00' * 4, # pad
351 '\x01\x02\x03\x04\x05\x06', # hw_addr
352 '\x00' * 2, # pad
353 'foo' + '\x00' * 13, # name
354 '\x00\x00\x00\x24', # config
355 '\x00\x00\x00\x02', # state
356 '\x00\x00\x00\x01', # curr
357 '\x00\x00\x00\x02', # advertised
358 '\x00\x00\x00\x04', # supported
359 '\x00\x00\x00\x08', # peer
360 '\x00\x00\x00\x0a', # curr_speed
361 '\x00\x00\x00\x14', # max_speed
362 ])
363 test_serialization(obj, buf)
364
365 def test_packet_out(self):
366 # TODO
367 pass
368
369
370 ## Flow-mods
371
372 def test_flow_add(self):
373 # TODO
374 pass
375
376 def test_flow_modify(self):
377 # TODO
378 pass
379
380 def test_flow_modify_strict(self):
381 # TODO
382 pass
383
384 def test_flow_delete(self):
385 # TODO
386 pass
387
388 def test_flow_delete_strict(self):
389 # TODO
390 pass
391
392
393 def test_group_mod(self):
394 # TODO
395 pass
396
397 def test_port_mod(self):
398 # TODO
399 pass
400
401 def test_table_mod(self):
402 # TODO
403 pass
404
405
406 ## Multipart messages
407
408 def test_desc_stats_request(self):
409 # TODO
410 pass
411
412 def test_desc_stats_reply(self):
413 # TODO
414 pass
415
416 def test_flow_stats_request(self):
417 # TODO
418 pass
419
420 def test_flow_stats_reply(self):
421 # TODO
422 pass
423
424 def test_aggregate_stats_request(self):
425 # TODO
426 pass
427
428 def test_aggregate_stats_reply(self):
429 # TODO
430 pass
431
432 def test_port_stats_request(self):
433 # TODO
434 pass
435
436 def test_port_stats_reply(self):
437 # TODO
438 pass
439
440 def test_queue_stats_request(self):
441 # TODO
442 pass
443
444 def test_queue_stats_reply(self):
445 # TODO
446 pass
447
448 def test_group_stats_request(self):
449 # TODO
450 pass
451
452 def test_group_stats_reply(self):
453 # TODO
454 pass
455
456 def test_group_desc_stats_request(self):
457 # TODO
458 pass
459
460 def test_group_desc_stats_reply(self):
461 # TODO
462 pass
463
464 def test_group_features_stats_request(self):
465 # TODO
466 pass
467
468 def test_group_features_stats_reply(self):
469 # TODO
470 pass
471
472 def test_meter_stats_request(self):
473 # TODO
474 pass
475
476 def test_meter_stats_reply(self):
477 # TODO
478 pass
479
480 def test_meter_config_stats_request(self):
481 # TODO
482 pass
483
484 def test_meter_config_stats_reply(self):
485 # TODO
486 pass
487
488 def test_meter_features_stats_request(self):
489 # TODO
490 pass
491
492 def test_meter_features_stats_reply(self):
493 # TODO
494 pass
495
496 def test_table_features_stats_request(self):
497 # TODO
498 pass
499
500 def test_table_features_stats_reply(self):
501 # TODO
502 pass
503
504 def test_port_desc_stats_request(self):
505 # TODO
506 pass
507
508 def test_port_desc_stats_reply(self):
509 # TODO
510 pass
511
512
513 def test_barrier_request(self):
514 # TODO
515 pass
516
517 def test_barrier_reply(self):
518 # TODO
519 pass
520
521 def test_queue_get_config_request(self):
522 # TODO
523 pass
524
525 def test_queue_get_config_reply(self):
526 # TODO
527 pass
528
529 def test_role_request(self):
530 # TODO
531 pass
532
533 def test_role_reply(self):
534 # TODO
535 pass
536
537 def test_get_async_request(self):
538 # TODO
539 pass
540
541 def test_get_async_reply(self):
542 # TODO
543 pass
544
545 def test_set_async(self):
546 # TODO
547 pass
548
549 def test_meter_mod(self):
550 # TODO
551 pass
552
553 # TODO test experimenter messages
554
555
Rich Laneea693752013-03-18 11:05:45 -0700556class TestOXM(unittest.TestCase):
557 def test_oxm_in_phy_port_pack(self):
558 import loxi.of13 as ofp
559 obj = ofp.oxm.in_phy_port(value=42)
560 expected = ''.join([
561 '\x80\x00', # class
562 '\x02', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700563 '\x04', # length
Rich Laneea693752013-03-18 11:05:45 -0700564 '\x00\x00\x00\x2a' # value
565 ])
566 self.assertEquals(expected, obj.pack())
567
568 def test_oxm_in_phy_port_masked_pack(self):
569 import loxi.of13 as ofp
570 obj = ofp.oxm.in_phy_port_masked(value=42, value_mask=0xaabbccdd)
571 expected = ''.join([
572 '\x80\x00', # class
573 '\x03', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700574 '\x08', # length
Rich Laneea693752013-03-18 11:05:45 -0700575 '\x00\x00\x00\x2a', # value
576 '\xaa\xbb\xcc\xdd' # mask
577 ])
578 self.assertEquals(expected, obj.pack())
579
Rich Lane41805642013-03-19 15:00:26 -0700580 def test_oxm_ipv6_dst_pack(self):
581 import loxi.of13 as ofp
582 obj = ofp.oxm.ipv6_dst(value='\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0d\x0f')
583 expected = ''.join([
584 '\x80\x00', # class
585 '\x36', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700586 '\x10', # length
Rich Lane41805642013-03-19 15:00:26 -0700587 '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0d\x0f', # value
588 ])
589 self.assertEquals(expected, obj.pack())
590
Rich Lane3f075972013-03-15 22:56:29 -0700591class TestAllOF13(unittest.TestCase):
592 """
593 Round-trips every class through serialization/deserialization.
594 Not a replacement for handcoded tests because it only uses the
595 default member values.
596 """
597
598 def setUp(self):
Rich Laneea693752013-03-18 11:05:45 -0700599 mods = [ofp.action,ofp.message,ofp.common,ofp.oxm]
Rich Lane3f075972013-03-15 22:56:29 -0700600 self.klasses = [klass for mod in mods
601 for klass in mod.__dict__.values()
602 if hasattr(klass, 'show')]
603 self.klasses.sort(key=lambda x: str(x))
604
605 def test_serialization(self):
606 expected_failures = [
Rich Lane3f075972013-03-15 22:56:29 -0700607 ofp.common.group_desc_stats_entry,
Rich Lane3f075972013-03-15 22:56:29 -0700608 ofp.message.group_desc_stats_reply,
609 ofp.message.group_mod,
610 ofp.message.group_stats_reply,
Rich Lane3f075972013-03-15 22:56:29 -0700611 ofp.message.meter_stats_reply,
Rich Lanea0186052013-05-01 14:18:39 -0700612 ofp.message.meter_features_stats_reply,
Rich Lane3f075972013-03-15 22:56:29 -0700613 ofp.message.table_features_stats_reply,
614 ofp.message.table_features_stats_request,
615 ]
616 for klass in self.klasses:
617 def fn():
618 obj = klass()
619 if hasattr(obj, "xid"): obj.xid = 42
620 buf = obj.pack()
621 obj2 = klass.unpack(buf)
622 self.assertEquals(obj, obj2)
623 if klass in expected_failures:
624 self.assertRaises(Exception, fn)
625 else:
626 fn()
627
628 def test_show(self):
Rich Lane8ca3b772013-04-30 13:36:55 -0700629 expected_failures = []
Rich Lane3f075972013-03-15 22:56:29 -0700630 for klass in self.klasses:
631 def fn():
632 obj = klass()
633 if hasattr(obj, "xid"): obj.xid = 42
634 obj.show()
635 if klass in expected_failures:
636 self.assertRaises(Exception, fn)
637 else:
638 fn()
639
640if __name__ == '__main__':
641 unittest.main()