blob: 3c9772f5867f0eac6318c6e4567ae4b17b8c9276 [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):
Rich Lanea4d3d2d2013-05-01 16:57:00 -0700366 obj = ofp.message.packet_out(
367 xid=0x12345678,
368 buffer_id=100,
369 in_port=4,
370 actions=[
371 ofp.action.output(port=2, max_len=0xffff),
372 ofp.action.dec_nw_ttl()],
373 data="abc")
374 buf = ''.join([
375 '\x04', '\x0d', # version, type
376 '\x00\x33', # length
377 '\x12\x34\x56\x78', # xid
378 '\x00\x00\x00\x64', # buffer_id
379 '\x00\x00\x00\x04', # in_port
380 '\x00\x18', # actions_len
381 '\x00' * 6, # pad
382 '\x00\x00', # actions[0].type
383 '\x00\x10', # actions[0].length
384 '\x00\x00\x00\x02', # actions[0].port
385 '\xff\xff', # actions[0].max_len
386 '\x00' * 6, # pad
387 '\x00\x18', # actions[1].type
388 '\x00\x08', # actions[1].length
389 '\x00' * 4, # pad
390 'abc', # data
391 ])
392 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700393
394
395 ## Flow-mods
396
397 def test_flow_add(self):
Rich Laneed4f9062013-05-02 17:05:03 -0700398 obj = ofp.message.flow_add(
399 xid=0x12345678,
400 cookie=0xFEDCBA9876543210,
401 cookie_mask=0xFF00FF00FF00FF00,
402 table_id=3,
403 idle_timeout=5,
404 hard_timeout=10,
405 priority=6000,
406 buffer_id=50,
407 out_port=6,
408 out_group=8,
409 flags=0,
410 match=ofp.match(oxm_list=[]),
411 instructions=[
412 ofp.instruction.goto_table(table_id=4),
413 ofp.instruction.goto_table(table_id=7)])
414 buf = ''.join([
415 '\x04', '\x0e', # version, type
416 '\x00\x48', # length
417 '\x12\x34\x56\x78', # xid
418
419 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # cookie
420
421 '\xff\x00\xff\x00\xff\x00\xff\x00', # cookie_mask
422
423 '\x03', # table_id
424 '\x00', # _command
425 '\x00\x05', # idle_timeout
426 '\x00\x0a', # hard_timeout
427 '\x17\x70', # priority
428
429 '\x00\x00\x00\x32', # buffer_id
430 '\x00\x00\x00\x06', # out_port
431
432 '\x00\x00\x00\x08', # out_group
433 '\x00\x00', # flags
434 '\x00' * 2, # pad
435
436 '\x00\x01', # match.type
437 '\x00\x04', # match.length
438 '\x00' * 4, # pad
439
440 '\x00\x01', # instructions[0].type
441 '\x00\x08', # instructions[0].length
442 '\x04', # instructions[0].table_id
443 '\x00' * 3, # pad
444
445 '\x00\x01', # instructions[1].type
446 '\x00\x08', # instructions[1].length
447 '\x07', # instructions[1].table_id
448 '\x00' * 3, # pad
449 ])
450 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700451
452 def test_flow_modify(self):
453 # TODO
454 pass
455
456 def test_flow_modify_strict(self):
457 # TODO
458 pass
459
460 def test_flow_delete(self):
461 # TODO
462 pass
463
464 def test_flow_delete_strict(self):
465 # TODO
466 pass
467
468
469 def test_group_mod(self):
Rich Lane8e27ec72013-05-02 11:04:31 -0700470 obj = ofp.message.group_mod(
471 xid=0x12345678,
472 command=ofp.OFPGC_MODIFY,
473 group_type=ofp.OFPGT_FF,
474 group_id=5,
475 buckets=[
476 ofp.bucket(
477 weight=1,
478 watch_port=5,
479 watch_group=0xffffffff,
480 actions=[
481 ofp.action.output(port=5, max_len=0),
482 ofp.action.output(port=6, max_len=0)]),
483 ofp.bucket(
484 weight=1,
485 watch_port=6,
486 watch_group=0xffffffff,
487 actions=[
488 ofp.action.output(port=5, max_len=0),
489 ofp.action.output(port=6, max_len=0)])])
490 buf = ''.join([
491 '\x04', '\x0f', # version, type
492 '\x00\x70', # length
493 '\x12\x34\x56\x78', # xid
494 '\x00\x01', # command
495 '\x03', # group_type
496 '\x00', # pad
497 '\x00\x00\x00\x05', # group_id
498 '\x00\x30', # buckets[0].len
499 '\x00\x01', # buckets[0].weight
500 '\x00\x00\x00\x05', # buckets[0].watch_port
501 '\xff\xff\xff\xff', # buckets[0].watch_group
502 '\x00' * 4, # pad
503 '\x00\x00', # buckets[0].actions[0].type
504 '\x00\x10', # buckets[0].actions[0].len
505 '\x00\x00\x00\x05', # buckets[0].actions[0].port
506 '\x00\x00', # buckets[0].actions[0].max_len
507 '\x00' * 6, # pad
508 '\x00\x00', # buckets[0].actions[1].type
509 '\x00\x10', # buckets[0].actions[1].len
510 '\x00\x00\x00\x06', # buckets[0].actions[1].port
511 '\x00\x00', # buckets[0].actions[1].max_len
512 '\x00' * 6, # pad
513 '\x00\x30', # buckets[1].len
514 '\x00\x01', # buckets[1].weight
515 '\x00\x00\x00\x06', # buckets[1].watch_port
516 '\xff\xff\xff\xff', # buckets[1].watch_group
517 '\x00' * 4, # pad
518 '\x00\x00', # buckets[1].actions[0].type
519 '\x00\x10', # buckets[1].actions[0].len
520 '\x00\x00\x00\x05', # buckets[1].actions[0].port
521 '\x00\x00', # buckets[1].actions[0].max_len
522 '\x00' * 6, # pad
523 '\x00\x00', # buckets[1].actions[1].type
524 '\x00\x10', # buckets[1].actions[1].len
525 '\x00\x00\x00\x06', # buckets[1].actions[1].port
526 '\x00\x00', # buckets[1].actions[1].max_len
527 '\x00' * 6, # pad
528 ])
529 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700530
531 def test_port_mod(self):
532 # TODO
533 pass
534
535 def test_table_mod(self):
536 # TODO
537 pass
538
539
540 ## Multipart messages
541
542 def test_desc_stats_request(self):
543 # TODO
544 pass
545
546 def test_desc_stats_reply(self):
547 # TODO
548 pass
549
550 def test_flow_stats_request(self):
551 # TODO
552 pass
553
554 def test_flow_stats_reply(self):
555 # TODO
556 pass
557
558 def test_aggregate_stats_request(self):
559 # TODO
560 pass
561
562 def test_aggregate_stats_reply(self):
563 # TODO
564 pass
565
566 def test_port_stats_request(self):
567 # TODO
568 pass
569
570 def test_port_stats_reply(self):
571 # TODO
572 pass
573
574 def test_queue_stats_request(self):
575 # TODO
576 pass
577
578 def test_queue_stats_reply(self):
579 # TODO
580 pass
581
582 def test_group_stats_request(self):
583 # TODO
584 pass
585
586 def test_group_stats_reply(self):
Rich Lane42bf98c2013-05-02 14:48:32 -0700587 obj = ofp.message.group_stats_reply(
588 xid=0x12345678,
589 flags=0,
590 entries=[
591 ofp.group_stats_entry(
592 group_id=1,
593 ref_count=8,
594 packet_count=16,
595 byte_count=32,
596 duration_sec=20,
597 duration_nsec=100,
598 bucket_stats=[
599 ofp.bucket_counter(packet_count=1, byte_count=2),
600 ofp.bucket_counter(packet_count=3, byte_count=4)]),
601 ofp.group_stats_entry(
602 group_id=1,
603 ref_count=8,
604 packet_count=16,
605 byte_count=32,
606 duration_sec=20,
607 duration_nsec=100,
608 bucket_stats=[])])
609 buf = ''.join([
610 '\x04', '\x13', # version, type
611 '\x00\x80', # length
612 '\x12\x34\x56\x78', # xid
613 '\x00\x06', # stats_type
614 '\x00\x00', # flags
615 '\x00' * 4, # pad
616 '\x00\x48', # entries[0].length
617 '\x00' * 2, # pad
618 '\x00\x00\x00\x01', # entries[0].group_id
619 '\x00\x00\x00\x08', # entries[0].ref_count
620 '\x00' * 4, # pad
621 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[0].packet_count
622 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[0].byte_count
623 '\x00\x00\x00\x14', # entries[0].duration_sec
624 '\x00\x00\x00\x64', # entries[0].duration_nsec
625 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[0].bucket_stats[0].packet_count
626 '\x00\x00\x00\x00\x00\x00\x00\x02', # entries[0].bucket_stats[0].byte_count
627 '\x00\x00\x00\x00\x00\x00\x00\x03', # entries[0].bucket_stats[1].packet_count
628 '\x00\x00\x00\x00\x00\x00\x00\x04', # entries[0].bucket_stats[1].byte_count
629 '\x00\x28', # entries[0].length
630 '\x00' * 2, # pad
631 '\x00\x00\x00\x01', # entries[0].group_id
632 '\x00\x00\x00\x08', # entries[0].ref_count
633 '\x00' * 4, # pad
634 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[0].packet_count
635 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[0].byte_count
636 '\x00\x00\x00\x14', # entries[0].duration_sec
637 '\x00\x00\x00\x64', # entries[0].duration_nsec
638 ])
639 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700640
641 def test_group_desc_stats_request(self):
642 # TODO
643 pass
644
645 def test_group_desc_stats_reply(self):
Rich Lane9b38d112013-05-02 14:35:40 -0700646 obj = ofp.message.group_desc_stats_reply(
647 xid=0x12345678,
648 flags=0,
649 entries=[
650 ofp.group_desc_stats_entry(
651 type=ofp.OFPGT_FF,
652 group_id=1,
653 buckets=[
654 ofp.bucket(
655 weight=1,
656 watch_port=5,
657 watch_group=0xffffffff,
658 actions=[
659 ofp.action.output(port=5, max_len=0),
660 ofp.action.output(port=6, max_len=0)]),
661 ofp.bucket(
662 weight=1,
663 watch_port=6,
664 watch_group=0xffffffff,
665 actions=[
666 ofp.action.output(port=5, max_len=0),
667 ofp.action.output(port=6, max_len=0)])]),
668 ofp.group_desc_stats_entry(type=ofp.OFPGT_FF, group_id=2, buckets=[])])
669 buf = ''.join([
670 '\x04', '\x13', # version, type
671 '\x00\x80', # length
672 '\x12\x34\x56\x78', # xid
673 '\x00\x07', # stats_type
674 '\x00\x00', # flags
675 '\x00' * 4, # pad
676 '\x00\x68', # entries[0].length
677 '\x03', # entries[0].group_type
678 '\x00', # entries[0].pad
679 '\x00\x00\x00\x01', # entries[0].group_id
680 '\x00\x30', # entries[0].buckets[0].len
681 '\x00\x01', # entries[0].buckets[0].weight
682 '\x00\x00\x00\x05', # entries[0].buckets[0].watch_port
683 '\xff\xff\xff\xff', # entries[0].buckets[0].watch_group
684 '\x00' * 4, # entries[0].pad
685 '\x00\x00', # entries[0].buckets[0].actions[0].type
686 '\x00\x10', # entries[0].buckets[0].actions[0].len
687 '\x00\x00\x00\x05', # entries[0].buckets[0].actions[0].port
688 '\x00\x00', # entries[0].buckets[0].actions[0].max_len
689 '\x00' * 6, # entries[0].pad
690 '\x00\x00', # entries[0].buckets[0].actions[1].type
691 '\x00\x10', # entries[0].buckets[0].actions[1].len
692 '\x00\x00\x00\x06', # entries[0].buckets[0].actions[1].port
693 '\x00\x00', # entries[0].buckets[0].actions[1].max_len
694 '\x00' * 6, # entries[0].pad
695 '\x00\x30', # entries[0].buckets[1].len
696 '\x00\x01', # entries[0].buckets[1].weight
697 '\x00\x00\x00\x06', # entries[0].buckets[1].watch_port
698 '\xff\xff\xff\xff', # entries[0].buckets[1].watch_group
699 '\x00' * 4, # entries[0].pad
700 '\x00\x00', # entries[0].buckets[1].actions[0].type
701 '\x00\x10', # entries[0].buckets[1].actions[0].len
702 '\x00\x00\x00\x05', # entries[0].buckets[1].actions[0].port
703 '\x00\x00', # entries[0].buckets[1].actions[0].max_len
704 '\x00' * 6, # entries[0].pad
705 '\x00\x00', # entries[0].buckets[1].actions[1].type
706 '\x00\x10', # entries[0].buckets[1].actions[1].len
707 '\x00\x00\x00\x06', # entries[0].buckets[1].actions[1].port
708 '\x00\x00', # entries[0].buckets[1].actions[1].max_len
709 '\x00' * 6, # entries[0].pad
710 '\x00\x08', # entries[1].length
711 '\x03', # entries[1].group_type
712 '\x00', # entries[1].pad
713 '\x00\x00\x00\x02', # entries[1].group_id
714 ])
715 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700716
717 def test_group_features_stats_request(self):
718 # TODO
719 pass
720
721 def test_group_features_stats_reply(self):
722 # TODO
723 pass
724
725 def test_meter_stats_request(self):
726 # TODO
727 pass
728
729 def test_meter_stats_reply(self):
Rich Lane6c3acb22013-05-02 15:59:05 -0700730 obj = ofp.message.meter_stats_reply(
731 xid=0x12345678,
732 flags=0,
733 entries=[
734 ofp.meter_stats(
735 meter_id=1,
736 flow_count=8,
737 packet_in_count=16,
738 byte_in_count=32,
739 duration_sec=20,
740 duration_nsec=100,
741 band_stats=[
742 ofp.meter_band_stats(packet_band_count=1, byte_band_count=2),
743 ofp.meter_band_stats(packet_band_count=3, byte_band_count=4)]),
744 ofp.meter_stats(
745 meter_id=2,
746 flow_count=8,
747 packet_in_count=16,
748 byte_in_count=32,
749 duration_sec=20,
750 duration_nsec=100,
751 band_stats=[])])
752 buf = ''.join([
753 '\x04', '\x13', # version, type
754 '\x00\x80', # length
755 '\x12\x34\x56\x78', # xid
756 '\x00\x09', # stats_type
757 '\x00\x00', # flags
758 '\x00' * 4, # pad
759 '\x00\x00\x00\x01', # entries[0].meter_id
760 '\x00\x48', # entries[0].len
761 '\x00' * 6, # pad
762 '\x00\x00\x00\x08', # entries[0].flow_count
763 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[0].packet_in_count
764 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[0].byte_in_count
765 '\x00\x00\x00\x14', # entries[0].duration_sec
766 '\x00\x00\x00\x64', # entries[0].duration_nsec
767 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[0].band_stats[0].packet_band_count
768 '\x00\x00\x00\x00\x00\x00\x00\x02', # entries[0].band_stats[0].byte_band_count
769 '\x00\x00\x00\x00\x00\x00\x00\x03', # entries[0].band_stats[1].packet_band_count
770 '\x00\x00\x00\x00\x00\x00\x00\x04', # entries[0].band_stats[1].byte_band_count
771 '\x00\x00\x00\x02', # entries[1].meter_id
772 '\x00\x28', # entries[1].len
773 '\x00' * 6, # pad
774 '\x00\x00\x00\x08', # entries[1].flow_count
775 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[1].packet_in_count
776 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[1].byte_in_count
777 '\x00\x00\x00\x14', # entries[1].duration_sec
778 '\x00\x00\x00\x64', # entries[1].duration_nsec
779 ])
780 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700781
782 def test_meter_config_stats_request(self):
783 # TODO
784 pass
785
786 def test_meter_config_stats_reply(self):
Rich Laned82c0a62013-05-02 15:40:35 -0700787 obj = ofp.message.meter_config_stats_reply(
788 xid=0x12345678,
789 flags=0,
790 entries=[
791 ofp.meter_band.drop(rate=1, burst_size=2),
792 ofp.meter_band.dscp_remark(rate=3, burst_size=4, prec_level=5)])
793 buf = ''.join([
794 '\x04', '\x13', # version, type
795 '\x00\x30', # length
796 '\x12\x34\x56\x78', # xid
797 '\x00\x0a', # stats_type
798 '\x00\x00', # flags
799 '\x00' * 4, # pad
800 '\x00\x01', # entries[0].type
801 '\x00\x10', # entries[0].length
802 '\x00\x00\x00\x01', # entries[0].rate
803 '\x00\x00\x00\x02', # entries[0].burst_size
804 '\x00' * 4, # pad
805 '\x00\x02', # entries[1].type
806 '\x00\x10', # entries[1].length
807 '\x00\x00\x00\x03', # entries[1].rate
808 '\x00\x00\x00\x04', # entries[1].burst_size
809 '\x05', # entries[1].prec_level
810 '\x00' * 3, # pad
811 ])
812 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700813
814 def test_meter_features_stats_request(self):
815 # TODO
816 pass
817
818 def test_meter_features_stats_reply(self):
Rich Laned367a242013-05-02 16:14:23 -0700819 obj = ofp.message.meter_features_stats_reply(
820 xid=0x12345678,
821 flags=0,
822 features=ofp.meter_features(
823 max_meter=5,
824 band_types=ofp.OFPMBT_DROP|ofp.OFPMBT_DSCP_REMARK,
825 capabilities=ofp.OFPMF_KBPS|ofp.OFPMF_STATS,
826 max_bands=10,
827 max_color=7))
828 buf = ''.join([
829 '\x04', '\x13', # version, type
830 '\x00\x20', # length
831 '\x12\x34\x56\x78', # xid
832 '\x00\x0b', # stats_type
833 '\x00\x00', # flags
834 '\x00' * 4, # pad
835 '\x00\x00\x00\x05', # max_meter
836 '\x00\x00\x00\x03', # band_types
837 '\x00\x00\x00\x09', # capabilities
838 '\x0a', # max_bands
839 '\x07', # max_color
840 '\x00' * 2, # pad
841 ])
842 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700843
844 def test_table_features_stats_request(self):
845 # TODO
846 pass
847
848 def test_table_features_stats_reply(self):
849 # TODO
850 pass
851
852 def test_port_desc_stats_request(self):
853 # TODO
854 pass
855
856 def test_port_desc_stats_reply(self):
857 # TODO
858 pass
859
860
861 def test_barrier_request(self):
862 # TODO
863 pass
864
865 def test_barrier_reply(self):
866 # TODO
867 pass
868
869 def test_queue_get_config_request(self):
870 # TODO
871 pass
872
873 def test_queue_get_config_reply(self):
874 # TODO
875 pass
876
877 def test_role_request(self):
878 # TODO
879 pass
880
881 def test_role_reply(self):
882 # TODO
883 pass
884
885 def test_get_async_request(self):
886 # TODO
887 pass
888
889 def test_get_async_reply(self):
890 # TODO
891 pass
892
893 def test_set_async(self):
894 # TODO
895 pass
896
897 def test_meter_mod(self):
898 # TODO
899 pass
900
901 # TODO test experimenter messages
902
903
Rich Laneea693752013-03-18 11:05:45 -0700904class TestOXM(unittest.TestCase):
905 def test_oxm_in_phy_port_pack(self):
906 import loxi.of13 as ofp
907 obj = ofp.oxm.in_phy_port(value=42)
908 expected = ''.join([
909 '\x80\x00', # class
910 '\x02', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700911 '\x04', # length
Rich Laneea693752013-03-18 11:05:45 -0700912 '\x00\x00\x00\x2a' # value
913 ])
914 self.assertEquals(expected, obj.pack())
915
916 def test_oxm_in_phy_port_masked_pack(self):
917 import loxi.of13 as ofp
918 obj = ofp.oxm.in_phy_port_masked(value=42, value_mask=0xaabbccdd)
919 expected = ''.join([
920 '\x80\x00', # class
921 '\x03', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700922 '\x08', # length
Rich Laneea693752013-03-18 11:05:45 -0700923 '\x00\x00\x00\x2a', # value
924 '\xaa\xbb\xcc\xdd' # mask
925 ])
926 self.assertEquals(expected, obj.pack())
927
Rich Lane41805642013-03-19 15:00:26 -0700928 def test_oxm_ipv6_dst_pack(self):
929 import loxi.of13 as ofp
930 obj = ofp.oxm.ipv6_dst(value='\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0d\x0f')
931 expected = ''.join([
932 '\x80\x00', # class
933 '\x36', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700934 '\x10', # length
Rich Lane41805642013-03-19 15:00:26 -0700935 '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0d\x0f', # value
936 ])
937 self.assertEquals(expected, obj.pack())
938
Rich Lanee02314c2013-05-02 16:42:04 -0700939class TestInstructions(unittest.TestCase):
940 def test_goto_table(self):
941 obj = ofp.instruction.goto_table(table_id=5)
942 buf = ''.join([
943 '\x00\x01', # type
944 '\x00\x08', # length
945 '\x05', # table_id
946 '\x00' * 3, # pad
947 ])
948 test_serialization(obj, buf)
949
950 def test_write_metadata(self):
951 # TODO
952 pass
953
954 def test_write_actions(self):
955 # TODO
956 pass
957
958 def test_apply_actions(self):
959 # TODO
960 pass
961
962 def test_clear_actions(self):
963 # TODO
964 pass
965
966 def test_meter(self):
967 # TODO
968 pass
969
970 # TODO test experimenter instructions
971
Rich Lane3f075972013-03-15 22:56:29 -0700972class TestAllOF13(unittest.TestCase):
973 """
974 Round-trips every class through serialization/deserialization.
975 Not a replacement for handcoded tests because it only uses the
976 default member values.
977 """
978
979 def setUp(self):
Rich Laneea693752013-03-18 11:05:45 -0700980 mods = [ofp.action,ofp.message,ofp.common,ofp.oxm]
Rich Lane3f075972013-03-15 22:56:29 -0700981 self.klasses = [klass for mod in mods
982 for klass in mod.__dict__.values()
983 if hasattr(klass, 'show')]
984 self.klasses.sort(key=lambda x: str(x))
985
986 def test_serialization(self):
987 expected_failures = [
Rich Lane8692ecd2013-05-02 11:33:53 -0700988 ofp.common.table_feature_prop_apply_actions,
989 ofp.common.table_feature_prop_apply_actions_miss,
Rich Lane8692ecd2013-05-02 11:33:53 -0700990 ofp.common.table_feature_prop_write_actions,
991 ofp.common.table_feature_prop_write_actions_miss,
992 ofp.common.table_features,
Rich Lane3f075972013-03-15 22:56:29 -0700993 ofp.message.table_features_stats_reply,
994 ofp.message.table_features_stats_request,
995 ]
996 for klass in self.klasses:
997 def fn():
998 obj = klass()
999 if hasattr(obj, "xid"): obj.xid = 42
1000 buf = obj.pack()
1001 obj2 = klass.unpack(buf)
1002 self.assertEquals(obj, obj2)
1003 if klass in expected_failures:
1004 self.assertRaises(Exception, fn)
1005 else:
1006 fn()
1007
1008 def test_show(self):
Rich Lane8ca3b772013-04-30 13:36:55 -07001009 expected_failures = []
Rich Lane3f075972013-03-15 22:56:29 -07001010 for klass in self.klasses:
1011 def fn():
1012 obj = klass()
1013 if hasattr(obj, "xid"): obj.xid = 42
1014 obj.show()
1015 if klass in expected_failures:
1016 self.assertRaises(Exception, fn)
1017 else:
1018 fn()
1019
1020if __name__ == '__main__':
1021 unittest.main()