blob: 68814cb095d79bbf5cfae4c0bbb5a92d0eb0fcd4 [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 Lane1d5b0102013-05-10 16:28:17 -070029from testutil import test_serialization
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
37class TestImports(unittest.TestCase):
38 def test_toplevel(self):
39 import loxi
40 self.assertTrue(hasattr(loxi, "ProtocolError"))
Rich Lane00549ea2013-04-25 13:33:16 -070041 self.assertEquals(loxi.version_names[4], "1.3")
Rich Lane3f075972013-03-15 22:56:29 -070042 ofp = loxi.protocol(4)
43 self.assertEquals(ofp.OFP_VERSION, 4)
44 self.assertTrue(hasattr(ofp, "action"))
45 self.assertTrue(hasattr(ofp, "common"))
46 self.assertTrue(hasattr(ofp, "const"))
47 self.assertTrue(hasattr(ofp, "message"))
Rich Lanea22233e2013-04-25 13:18:41 -070048 self.assertTrue(hasattr(ofp, "oxm"))
Rich Lane3f075972013-03-15 22:56:29 -070049
50 def test_version(self):
51 import loxi
52 self.assertTrue(hasattr(loxi.of13, "ProtocolError"))
53 self.assertTrue(hasattr(loxi.of13, "OFP_VERSION"))
54 self.assertEquals(loxi.of13.OFP_VERSION, 4)
55 self.assertTrue(hasattr(loxi.of13, "action"))
56 self.assertTrue(hasattr(loxi.of13, "common"))
57 self.assertTrue(hasattr(loxi.of13, "const"))
58 self.assertTrue(hasattr(loxi.of13, "message"))
Rich Lanea22233e2013-04-25 13:18:41 -070059 self.assertTrue(hasattr(loxi.of13, "oxm"))
Rich Lane3f075972013-03-15 22:56:29 -070060
Rich Lanee90685c2013-04-05 17:27:41 -070061class TestCommon(unittest.TestCase):
62 sample_hello_elem_buf = ''.join([
63 '\x00\x01', # type
64 '\x00\x0c', # length
65 '\x01\x23\x45\x67', # bitmaps[0]
66 '\x89\xab\xcd\xef', # bitmaps[1]
67 ])
68
69 def test_hello_elem_versionbitmap_pack(self):
70 obj = ofp.hello_elem_versionbitmap(bitmaps=[ofp.uint32(0x01234567),ofp.uint32(0x89abcdef)])
71 self.assertEquals(self.sample_hello_elem_buf, obj.pack())
72
73 def test_hello_elem_versionbitmap_unpack(self):
74 obj = ofp.hello_elem_versionbitmap.unpack(self.sample_hello_elem_buf)
75 self.assertEquals(len(obj.bitmaps), 2)
76 self.assertEquals(obj.bitmaps[0], ofp.uint32(0x01234567))
77 self.assertEquals(obj.bitmaps[1], ofp.uint32(0x89abcdef))
78
79 def test_list_hello_elem_unpack(self):
80 buf = ''.join([
81 '\x00\x01\x00\x04', # versionbitmap
82 '\x00\x00\x00\x04', # unknown type
83 '\x00\x01\x00\x04', # versionbitmap
84 ])
Rich Lane57026dc2013-05-01 10:13:16 -070085 l = ofp.unpack_list_hello_elem(OFReader(buf))
Rich Lanee90685c2013-04-05 17:27:41 -070086 self.assertEquals(len(l), 2)
87 self.assertTrue(isinstance(l[0], ofp.hello_elem_versionbitmap))
88 self.assertTrue(isinstance(l[1], ofp.hello_elem_versionbitmap))
89
Rich Lane4c764982013-05-01 16:12:22 -070090class TestMessages(unittest.TestCase):
91 def test_hello(self):
92 obj = ofp.message.hello(
93 xid=0x12345678,
94 elements=[
95 ofp.hello_elem_versionbitmap(
96 bitmaps=[ofp.uint32(1), ofp.uint32(2)]),
97 ofp.hello_elem_versionbitmap(
98 bitmaps=[ofp.uint32(3), ofp.uint32(4)])])
99 buf = ''.join([
100 '\x04', '\x00', # version, type
101 '\x00\x20', # length
102 '\x12\x34\x56\x78', # xid
103 '\x00\x01', # elements[0].type
104 '\x00\x0c', # elements[0].length
105 '\x00\x00\x00\x01', # elements[0].bitmaps[0]
106 '\x00\x00\x00\x02', # elements[0].bitmaps[1]
107 '\x00\x01', # elements[1].type
108 '\x00\x0c', # elements[1].length
109 '\x00\x00\x00\x03', # elements[1].bitmaps[0]
110 '\x00\x00\x00\x04', # elements[1].bitmaps[1]
111 ])
112 test_serialization(obj, buf)
113
114 def test_error(self):
115 obj = ofp.message.error_msg(
116 xid=0x12345678,
117 err_type=ofp.OFPET_BAD_MATCH,
118 code=ofp.OFPBMC_BAD_MASK,
119 data="abc")
120 buf = ''.join([
121 '\x04', '\x01', # version, type
122 '\x00\x0f', # length
123 '\x12\x34\x56\x78', # xid
124 '\x00\x04', # err_type
125 '\x00\x08', # code
126 'abc', # data
127 ])
128 test_serialization(obj, buf)
129
130 def test_echo_request(self):
131 obj = ofp.message.echo_request(
132 xid=0x12345678,
133 data="abc")
134 buf = ''.join([
135 '\x04', '\x02', # version, type
136 '\x00\x0b', # length
137 '\x12\x34\x56\x78', # xid
138 'abc', # data
139 ])
140 test_serialization(obj, buf)
141
142 def test_echo_reply(self):
143 obj = ofp.message.echo_reply(
144 xid=0x12345678,
145 data="abc")
146 buf = ''.join([
147 '\x04', '\x03', # version, type
148 '\x00\x0b', # length
149 '\x12\x34\x56\x78', # xid
150 'abc', # data
151 ])
152 test_serialization(obj, buf)
153
154 def test_features_request(self):
155 obj = ofp.message.features_request(xid=0x12345678)
156 buf = ''.join([
157 '\x04', '\x05', # version, type
158 '\x00\x08', # length
159 '\x12\x34\x56\x78', # xid
160 ])
161 test_serialization(obj, buf)
162
163 def test_features_reply(self):
164 obj = ofp.message.features_reply(
165 xid=0x12345678,
166 datapath_id=0xFEDCBA9876543210,
167 n_buffers=64,
168 n_tables=200,
169 auxiliary_id=5,
170 capabilities=ofp.OFPC_FLOW_STATS|ofp.OFPC_PORT_BLOCKED,
171 reserved=0)
172 buf = ''.join([
173 '\x04', '\x06', # version, type
174 '\x00\x20', # length
175 '\x12\x34\x56\x78', # xid
176 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # datapath_id
177 '\x00\x00\x00\x40', # n_buffers
178 '\xc8', # n_tables
179 '\x05', # auxiliary_id
180 '\x00\x00', # pad
181 '\x00\x00\x01\x01', # capabilities
182 '\x00\x00\x00\x00', # reserved
183 ])
184 test_serialization(obj, buf)
185
186 def test_get_config_request(self):
187 obj = ofp.message.get_config_request(xid=0x12345678)
188 buf = ''.join([
189 '\x04', '\x07', # version, type
190 '\x00\x08', # length
191 '\x12\x34\x56\x78', # xid
192 ])
193 test_serialization(obj, buf)
194
195 def test_get_config_reply(self):
196 obj = ofp.message.get_config_reply(
197 xid=0x12345678,
198 flags=ofp.OFPC_FRAG_REASM,
199 miss_send_len=0xffff)
200 buf = ''.join([
201 '\x04', '\x08', # version, type
202 '\x00\x0c', # length
203 '\x12\x34\x56\x78', # xid
204 '\x00\x02', # flags
205 '\xff\xff', # miss_send_len
206 ])
207 test_serialization(obj, buf)
208
209 def test_set_config(self):
210 obj = ofp.message.set_config(
211 xid=0x12345678,
212 flags=ofp.OFPC_FRAG_REASM,
213 miss_send_len=0xffff)
214 buf = ''.join([
215 '\x04', '\x09', # version, type
216 '\x00\x0c', # length
217 '\x12\x34\x56\x78', # xid
218 '\x00\x02', # flags
219 '\xff\xff', # miss_send_len
220 ])
221 test_serialization(obj, buf)
222
223 def test_packet_in(self):
224 obj = ofp.message.packet_in(
225 xid=0x12345678,
226 buffer_id=100,
227 total_len=17000,
228 reason=ofp.OFPR_ACTION,
229 table_id=20,
230 cookie=0xFEDCBA9876543210,
231 match=ofp.match(oxm_list=[
232 ofp.oxm.arp_op(value=1),
233 ofp.oxm.in_port_masked(value=4, value_mask=5)]),
234 data="abc")
235 buf = ''.join([
236 '\x04', '\x0a', # version, type
237 '\x00\x35', # length
238 '\x12\x34\x56\x78', # xid
239 '\x00\x00\x00\x64', # buffer_id
240 '\x42\x68', # total_len
241 '\x01', # reason
242 '\x14', # table_id
243 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # cookie
244 '\x00\x01', # match.type
245 '\x00\x16', # match.length
246 '\x80\x00\x2A\x02', # match.oxm_list[0].type_len
247 '\x00\x01', # match.oxm_list[0].value
248 '\x80\x00\x01\x08', # match.oxm_list[1].type_len
249 '\x00\x00\x00\x04', # match.oxm_list[1].value
250 '\x00\x00\x00\x05', # match.oxm_list[1].mask
251 '\x00\x00', # match.pad
252 '\x00\x00', # pad
253 'abc', # data
254 ])
255 test_serialization(obj, buf)
256
257 def test_flow_removed(self):
258 obj = ofp.message.flow_removed(
259 xid=0x12345678,
260 cookie=0xFEDCBA9876543210,
261 priority=17000,
262 reason=ofp.OFPRR_DELETE,
263 table_id=20,
264 duration_sec=10,
265 duration_nsec=1000,
266 idle_timeout=5,
267 hard_timeout=30,
268 packet_count=1,
269 byte_count=2,
270 match=ofp.match(oxm_list=[
271 ofp.oxm.arp_op(value=1),
272 ofp.oxm.in_port_masked(value=4, value_mask=5)]))
273 buf = ''.join([
274 '\x04', '\x0b', # version, type
275 '\x00\x48', # length
276 '\x12\x34\x56\x78', # xid
277 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # cookie
278 '\x42\x68', # priority
279 '\x02', # reason
280 '\x14', # table_id
281 '\x00\x00\x00\x0a', # duration_sec
282 '\x00\x00\x03\xe8', # duration_nsec
283 '\x00\x05', # idle_timeout
284 '\x00\x1e', # hard_timeout
285 '\x00\x00\x00\x00\x00\x00\x00\x01', # packet_count
286 '\x00\x00\x00\x00\x00\x00\x00\x02', # byte_count
287 '\x00\x01', # match.type
288 '\x00\x16', # match.length
289 '\x80\x00\x2A\x02', # match.oxm_list[0].type_len
290 '\x00\x01', # match.oxm_list[0].value
291 '\x80\x00\x01\x08', # match.oxm_list[1].type_len
292 '\x00\x00\x00\x04', # match.oxm_list[1].value
293 '\x00\x00\x00\x05', # match.oxm_list[1].mask
294 '\x00\x00', # match.pad
295 ])
296 test_serialization(obj, buf)
297
298 def test_port_status(self):
299 obj = ofp.message.port_status(
300 xid=0x12345678,
301 reason=ofp.OFPPR_MODIFY,
302 desc=ofp.port_desc(
303 port_no=4,
304 hw_addr=[1,2,3,4,5,6],
305 name="foo",
306 config=ofp.OFPPC_NO_FWD|ofp.OFPPC_NO_RECV,
307 state=ofp.OFPPS_BLOCKED,
308 curr=ofp.OFPPF_10MB_HD,
309 advertised=ofp.OFPPF_10MB_FD,
310 supported=ofp.OFPPF_100MB_HD,
311 peer=ofp.OFPPF_100MB_FD,
312 curr_speed=10,
313 max_speed=20))
314 buf = ''.join([
315 '\x04', '\x0c', # version, type
316 '\x00\x50', # length
317 '\x12\x34\x56\x78', # xid
318 '\x02', # reason
319 '\x00' * 7, # pad
320 '\x00\x00\x00\x04', # port_no
321 '\x00' * 4, # pad
322 '\x01\x02\x03\x04\x05\x06', # hw_addr
323 '\x00' * 2, # pad
324 'foo' + '\x00' * 13, # name
325 '\x00\x00\x00\x24', # config
326 '\x00\x00\x00\x02', # state
327 '\x00\x00\x00\x01', # curr
328 '\x00\x00\x00\x02', # advertised
329 '\x00\x00\x00\x04', # supported
330 '\x00\x00\x00\x08', # peer
331 '\x00\x00\x00\x0a', # curr_speed
332 '\x00\x00\x00\x14', # max_speed
333 ])
334 test_serialization(obj, buf)
335
336 def test_packet_out(self):
Rich Lanea4d3d2d2013-05-01 16:57:00 -0700337 obj = ofp.message.packet_out(
338 xid=0x12345678,
339 buffer_id=100,
340 in_port=4,
341 actions=[
342 ofp.action.output(port=2, max_len=0xffff),
343 ofp.action.dec_nw_ttl()],
344 data="abc")
345 buf = ''.join([
346 '\x04', '\x0d', # version, type
347 '\x00\x33', # length
348 '\x12\x34\x56\x78', # xid
349 '\x00\x00\x00\x64', # buffer_id
350 '\x00\x00\x00\x04', # in_port
351 '\x00\x18', # actions_len
352 '\x00' * 6, # pad
353 '\x00\x00', # actions[0].type
354 '\x00\x10', # actions[0].length
355 '\x00\x00\x00\x02', # actions[0].port
356 '\xff\xff', # actions[0].max_len
357 '\x00' * 6, # pad
358 '\x00\x18', # actions[1].type
359 '\x00\x08', # actions[1].length
360 '\x00' * 4, # pad
361 'abc', # data
362 ])
363 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700364
365
366 ## Flow-mods
367
368 def test_flow_add(self):
Rich Laneed4f9062013-05-02 17:05:03 -0700369 obj = ofp.message.flow_add(
370 xid=0x12345678,
371 cookie=0xFEDCBA9876543210,
372 cookie_mask=0xFF00FF00FF00FF00,
373 table_id=3,
374 idle_timeout=5,
375 hard_timeout=10,
376 priority=6000,
377 buffer_id=50,
378 out_port=6,
379 out_group=8,
380 flags=0,
381 match=ofp.match(oxm_list=[]),
382 instructions=[
383 ofp.instruction.goto_table(table_id=4),
384 ofp.instruction.goto_table(table_id=7)])
385 buf = ''.join([
386 '\x04', '\x0e', # version, type
387 '\x00\x48', # length
388 '\x12\x34\x56\x78', # xid
389
390 '\xfe\xdc\xba\x98\x76\x54\x32\x10', # cookie
391
392 '\xff\x00\xff\x00\xff\x00\xff\x00', # cookie_mask
393
394 '\x03', # table_id
395 '\x00', # _command
396 '\x00\x05', # idle_timeout
397 '\x00\x0a', # hard_timeout
398 '\x17\x70', # priority
399
400 '\x00\x00\x00\x32', # buffer_id
401 '\x00\x00\x00\x06', # out_port
402
403 '\x00\x00\x00\x08', # out_group
404 '\x00\x00', # flags
405 '\x00' * 2, # pad
406
407 '\x00\x01', # match.type
408 '\x00\x04', # match.length
409 '\x00' * 4, # pad
410
411 '\x00\x01', # instructions[0].type
412 '\x00\x08', # instructions[0].length
413 '\x04', # instructions[0].table_id
414 '\x00' * 3, # pad
415
416 '\x00\x01', # instructions[1].type
417 '\x00\x08', # instructions[1].length
418 '\x07', # instructions[1].table_id
419 '\x00' * 3, # pad
420 ])
421 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700422
423 def test_flow_modify(self):
424 # TODO
425 pass
426
427 def test_flow_modify_strict(self):
428 # TODO
429 pass
430
431 def test_flow_delete(self):
432 # TODO
433 pass
434
435 def test_flow_delete_strict(self):
436 # TODO
437 pass
438
439
440 def test_group_mod(self):
Rich Lane8e27ec72013-05-02 11:04:31 -0700441 obj = ofp.message.group_mod(
442 xid=0x12345678,
443 command=ofp.OFPGC_MODIFY,
444 group_type=ofp.OFPGT_FF,
445 group_id=5,
446 buckets=[
447 ofp.bucket(
448 weight=1,
449 watch_port=5,
450 watch_group=0xffffffff,
451 actions=[
452 ofp.action.output(port=5, max_len=0),
453 ofp.action.output(port=6, max_len=0)]),
454 ofp.bucket(
455 weight=1,
456 watch_port=6,
457 watch_group=0xffffffff,
458 actions=[
459 ofp.action.output(port=5, max_len=0),
460 ofp.action.output(port=6, max_len=0)])])
461 buf = ''.join([
462 '\x04', '\x0f', # version, type
463 '\x00\x70', # length
464 '\x12\x34\x56\x78', # xid
465 '\x00\x01', # command
466 '\x03', # group_type
467 '\x00', # pad
468 '\x00\x00\x00\x05', # group_id
469 '\x00\x30', # buckets[0].len
470 '\x00\x01', # buckets[0].weight
471 '\x00\x00\x00\x05', # buckets[0].watch_port
472 '\xff\xff\xff\xff', # buckets[0].watch_group
473 '\x00' * 4, # pad
474 '\x00\x00', # buckets[0].actions[0].type
475 '\x00\x10', # buckets[0].actions[0].len
476 '\x00\x00\x00\x05', # buckets[0].actions[0].port
477 '\x00\x00', # buckets[0].actions[0].max_len
478 '\x00' * 6, # pad
479 '\x00\x00', # buckets[0].actions[1].type
480 '\x00\x10', # buckets[0].actions[1].len
481 '\x00\x00\x00\x06', # buckets[0].actions[1].port
482 '\x00\x00', # buckets[0].actions[1].max_len
483 '\x00' * 6, # pad
484 '\x00\x30', # buckets[1].len
485 '\x00\x01', # buckets[1].weight
486 '\x00\x00\x00\x06', # buckets[1].watch_port
487 '\xff\xff\xff\xff', # buckets[1].watch_group
488 '\x00' * 4, # pad
489 '\x00\x00', # buckets[1].actions[0].type
490 '\x00\x10', # buckets[1].actions[0].len
491 '\x00\x00\x00\x05', # buckets[1].actions[0].port
492 '\x00\x00', # buckets[1].actions[0].max_len
493 '\x00' * 6, # pad
494 '\x00\x00', # buckets[1].actions[1].type
495 '\x00\x10', # buckets[1].actions[1].len
496 '\x00\x00\x00\x06', # buckets[1].actions[1].port
497 '\x00\x00', # buckets[1].actions[1].max_len
498 '\x00' * 6, # pad
499 ])
500 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700501
502 def test_port_mod(self):
503 # TODO
504 pass
505
506 def test_table_mod(self):
507 # TODO
508 pass
509
510
511 ## Multipart messages
512
513 def test_desc_stats_request(self):
514 # TODO
515 pass
516
517 def test_desc_stats_reply(self):
518 # TODO
519 pass
520
521 def test_flow_stats_request(self):
522 # TODO
523 pass
524
525 def test_flow_stats_reply(self):
526 # TODO
527 pass
528
529 def test_aggregate_stats_request(self):
530 # TODO
531 pass
532
533 def test_aggregate_stats_reply(self):
534 # TODO
535 pass
536
537 def test_port_stats_request(self):
538 # TODO
539 pass
540
541 def test_port_stats_reply(self):
542 # TODO
543 pass
544
545 def test_queue_stats_request(self):
546 # TODO
547 pass
548
549 def test_queue_stats_reply(self):
550 # TODO
551 pass
552
553 def test_group_stats_request(self):
554 # TODO
555 pass
556
557 def test_group_stats_reply(self):
Rich Lane42bf98c2013-05-02 14:48:32 -0700558 obj = ofp.message.group_stats_reply(
559 xid=0x12345678,
560 flags=0,
561 entries=[
562 ofp.group_stats_entry(
563 group_id=1,
564 ref_count=8,
565 packet_count=16,
566 byte_count=32,
567 duration_sec=20,
568 duration_nsec=100,
569 bucket_stats=[
570 ofp.bucket_counter(packet_count=1, byte_count=2),
571 ofp.bucket_counter(packet_count=3, byte_count=4)]),
572 ofp.group_stats_entry(
573 group_id=1,
574 ref_count=8,
575 packet_count=16,
576 byte_count=32,
577 duration_sec=20,
578 duration_nsec=100,
579 bucket_stats=[])])
580 buf = ''.join([
581 '\x04', '\x13', # version, type
582 '\x00\x80', # length
583 '\x12\x34\x56\x78', # xid
584 '\x00\x06', # stats_type
585 '\x00\x00', # flags
586 '\x00' * 4, # pad
587 '\x00\x48', # entries[0].length
588 '\x00' * 2, # pad
589 '\x00\x00\x00\x01', # entries[0].group_id
590 '\x00\x00\x00\x08', # entries[0].ref_count
591 '\x00' * 4, # pad
592 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[0].packet_count
593 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[0].byte_count
594 '\x00\x00\x00\x14', # entries[0].duration_sec
595 '\x00\x00\x00\x64', # entries[0].duration_nsec
596 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[0].bucket_stats[0].packet_count
597 '\x00\x00\x00\x00\x00\x00\x00\x02', # entries[0].bucket_stats[0].byte_count
598 '\x00\x00\x00\x00\x00\x00\x00\x03', # entries[0].bucket_stats[1].packet_count
599 '\x00\x00\x00\x00\x00\x00\x00\x04', # entries[0].bucket_stats[1].byte_count
600 '\x00\x28', # entries[0].length
601 '\x00' * 2, # pad
602 '\x00\x00\x00\x01', # entries[0].group_id
603 '\x00\x00\x00\x08', # entries[0].ref_count
604 '\x00' * 4, # pad
605 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[0].packet_count
606 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[0].byte_count
607 '\x00\x00\x00\x14', # entries[0].duration_sec
608 '\x00\x00\x00\x64', # entries[0].duration_nsec
609 ])
610 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700611
612 def test_group_desc_stats_request(self):
613 # TODO
614 pass
615
616 def test_group_desc_stats_reply(self):
Rich Lane9b38d112013-05-02 14:35:40 -0700617 obj = ofp.message.group_desc_stats_reply(
618 xid=0x12345678,
619 flags=0,
620 entries=[
621 ofp.group_desc_stats_entry(
622 type=ofp.OFPGT_FF,
623 group_id=1,
624 buckets=[
625 ofp.bucket(
626 weight=1,
627 watch_port=5,
628 watch_group=0xffffffff,
629 actions=[
630 ofp.action.output(port=5, max_len=0),
631 ofp.action.output(port=6, max_len=0)]),
632 ofp.bucket(
633 weight=1,
634 watch_port=6,
635 watch_group=0xffffffff,
636 actions=[
637 ofp.action.output(port=5, max_len=0),
638 ofp.action.output(port=6, max_len=0)])]),
639 ofp.group_desc_stats_entry(type=ofp.OFPGT_FF, group_id=2, buckets=[])])
640 buf = ''.join([
641 '\x04', '\x13', # version, type
642 '\x00\x80', # length
643 '\x12\x34\x56\x78', # xid
644 '\x00\x07', # stats_type
645 '\x00\x00', # flags
646 '\x00' * 4, # pad
647 '\x00\x68', # entries[0].length
648 '\x03', # entries[0].group_type
649 '\x00', # entries[0].pad
650 '\x00\x00\x00\x01', # entries[0].group_id
651 '\x00\x30', # entries[0].buckets[0].len
652 '\x00\x01', # entries[0].buckets[0].weight
653 '\x00\x00\x00\x05', # entries[0].buckets[0].watch_port
654 '\xff\xff\xff\xff', # entries[0].buckets[0].watch_group
655 '\x00' * 4, # entries[0].pad
656 '\x00\x00', # entries[0].buckets[0].actions[0].type
657 '\x00\x10', # entries[0].buckets[0].actions[0].len
658 '\x00\x00\x00\x05', # entries[0].buckets[0].actions[0].port
659 '\x00\x00', # entries[0].buckets[0].actions[0].max_len
660 '\x00' * 6, # entries[0].pad
661 '\x00\x00', # entries[0].buckets[0].actions[1].type
662 '\x00\x10', # entries[0].buckets[0].actions[1].len
663 '\x00\x00\x00\x06', # entries[0].buckets[0].actions[1].port
664 '\x00\x00', # entries[0].buckets[0].actions[1].max_len
665 '\x00' * 6, # entries[0].pad
666 '\x00\x30', # entries[0].buckets[1].len
667 '\x00\x01', # entries[0].buckets[1].weight
668 '\x00\x00\x00\x06', # entries[0].buckets[1].watch_port
669 '\xff\xff\xff\xff', # entries[0].buckets[1].watch_group
670 '\x00' * 4, # entries[0].pad
671 '\x00\x00', # entries[0].buckets[1].actions[0].type
672 '\x00\x10', # entries[0].buckets[1].actions[0].len
673 '\x00\x00\x00\x05', # entries[0].buckets[1].actions[0].port
674 '\x00\x00', # entries[0].buckets[1].actions[0].max_len
675 '\x00' * 6, # entries[0].pad
676 '\x00\x00', # entries[0].buckets[1].actions[1].type
677 '\x00\x10', # entries[0].buckets[1].actions[1].len
678 '\x00\x00\x00\x06', # entries[0].buckets[1].actions[1].port
679 '\x00\x00', # entries[0].buckets[1].actions[1].max_len
680 '\x00' * 6, # entries[0].pad
681 '\x00\x08', # entries[1].length
682 '\x03', # entries[1].group_type
683 '\x00', # entries[1].pad
684 '\x00\x00\x00\x02', # entries[1].group_id
685 ])
686 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700687
688 def test_group_features_stats_request(self):
689 # TODO
690 pass
691
692 def test_group_features_stats_reply(self):
693 # TODO
694 pass
695
696 def test_meter_stats_request(self):
697 # TODO
698 pass
699
700 def test_meter_stats_reply(self):
Rich Lane6c3acb22013-05-02 15:59:05 -0700701 obj = ofp.message.meter_stats_reply(
702 xid=0x12345678,
703 flags=0,
704 entries=[
705 ofp.meter_stats(
706 meter_id=1,
707 flow_count=8,
708 packet_in_count=16,
709 byte_in_count=32,
710 duration_sec=20,
711 duration_nsec=100,
712 band_stats=[
713 ofp.meter_band_stats(packet_band_count=1, byte_band_count=2),
714 ofp.meter_band_stats(packet_band_count=3, byte_band_count=4)]),
715 ofp.meter_stats(
716 meter_id=2,
717 flow_count=8,
718 packet_in_count=16,
719 byte_in_count=32,
720 duration_sec=20,
721 duration_nsec=100,
722 band_stats=[])])
723 buf = ''.join([
724 '\x04', '\x13', # version, type
725 '\x00\x80', # length
726 '\x12\x34\x56\x78', # xid
727 '\x00\x09', # stats_type
728 '\x00\x00', # flags
729 '\x00' * 4, # pad
730 '\x00\x00\x00\x01', # entries[0].meter_id
731 '\x00\x48', # entries[0].len
732 '\x00' * 6, # pad
733 '\x00\x00\x00\x08', # entries[0].flow_count
734 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[0].packet_in_count
735 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[0].byte_in_count
736 '\x00\x00\x00\x14', # entries[0].duration_sec
737 '\x00\x00\x00\x64', # entries[0].duration_nsec
738 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[0].band_stats[0].packet_band_count
739 '\x00\x00\x00\x00\x00\x00\x00\x02', # entries[0].band_stats[0].byte_band_count
740 '\x00\x00\x00\x00\x00\x00\x00\x03', # entries[0].band_stats[1].packet_band_count
741 '\x00\x00\x00\x00\x00\x00\x00\x04', # entries[0].band_stats[1].byte_band_count
742 '\x00\x00\x00\x02', # entries[1].meter_id
743 '\x00\x28', # entries[1].len
744 '\x00' * 6, # pad
745 '\x00\x00\x00\x08', # entries[1].flow_count
746 '\x00\x00\x00\x00\x00\x00\x00\x10', # entries[1].packet_in_count
747 '\x00\x00\x00\x00\x00\x00\x00\x20', # entries[1].byte_in_count
748 '\x00\x00\x00\x14', # entries[1].duration_sec
749 '\x00\x00\x00\x64', # entries[1].duration_nsec
750 ])
751 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700752
753 def test_meter_config_stats_request(self):
754 # TODO
755 pass
756
757 def test_meter_config_stats_reply(self):
Rich Laned82c0a62013-05-02 15:40:35 -0700758 obj = ofp.message.meter_config_stats_reply(
759 xid=0x12345678,
760 flags=0,
761 entries=[
762 ofp.meter_band.drop(rate=1, burst_size=2),
763 ofp.meter_band.dscp_remark(rate=3, burst_size=4, prec_level=5)])
764 buf = ''.join([
765 '\x04', '\x13', # version, type
766 '\x00\x30', # length
767 '\x12\x34\x56\x78', # xid
768 '\x00\x0a', # stats_type
769 '\x00\x00', # flags
770 '\x00' * 4, # pad
771 '\x00\x01', # entries[0].type
772 '\x00\x10', # entries[0].length
773 '\x00\x00\x00\x01', # entries[0].rate
774 '\x00\x00\x00\x02', # entries[0].burst_size
775 '\x00' * 4, # pad
776 '\x00\x02', # entries[1].type
777 '\x00\x10', # entries[1].length
778 '\x00\x00\x00\x03', # entries[1].rate
779 '\x00\x00\x00\x04', # entries[1].burst_size
780 '\x05', # entries[1].prec_level
781 '\x00' * 3, # pad
782 ])
783 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700784
785 def test_meter_features_stats_request(self):
786 # TODO
787 pass
788
789 def test_meter_features_stats_reply(self):
Rich Laned367a242013-05-02 16:14:23 -0700790 obj = ofp.message.meter_features_stats_reply(
791 xid=0x12345678,
792 flags=0,
793 features=ofp.meter_features(
794 max_meter=5,
795 band_types=ofp.OFPMBT_DROP|ofp.OFPMBT_DSCP_REMARK,
796 capabilities=ofp.OFPMF_KBPS|ofp.OFPMF_STATS,
797 max_bands=10,
798 max_color=7))
799 buf = ''.join([
800 '\x04', '\x13', # version, type
801 '\x00\x20', # length
802 '\x12\x34\x56\x78', # xid
803 '\x00\x0b', # stats_type
804 '\x00\x00', # flags
805 '\x00' * 4, # pad
806 '\x00\x00\x00\x05', # max_meter
807 '\x00\x00\x00\x03', # band_types
808 '\x00\x00\x00\x09', # capabilities
809 '\x0a', # max_bands
810 '\x07', # max_color
811 '\x00' * 2, # pad
812 ])
813 test_serialization(obj, buf)
Rich Lane4c764982013-05-01 16:12:22 -0700814
815 def test_table_features_stats_request(self):
816 # TODO
817 pass
818
819 def test_table_features_stats_reply(self):
820 # TODO
821 pass
822
823 def test_port_desc_stats_request(self):
824 # TODO
825 pass
826
827 def test_port_desc_stats_reply(self):
828 # TODO
829 pass
830
831
832 def test_barrier_request(self):
833 # TODO
834 pass
835
836 def test_barrier_reply(self):
837 # TODO
838 pass
839
840 def test_queue_get_config_request(self):
841 # TODO
842 pass
843
844 def test_queue_get_config_reply(self):
845 # TODO
846 pass
847
848 def test_role_request(self):
849 # TODO
850 pass
851
852 def test_role_reply(self):
853 # TODO
854 pass
855
856 def test_get_async_request(self):
857 # TODO
858 pass
859
860 def test_get_async_reply(self):
861 # TODO
862 pass
863
864 def test_set_async(self):
865 # TODO
866 pass
867
868 def test_meter_mod(self):
869 # TODO
870 pass
871
872 # TODO test experimenter messages
873
874
Rich Laneea693752013-03-18 11:05:45 -0700875class TestOXM(unittest.TestCase):
876 def test_oxm_in_phy_port_pack(self):
877 import loxi.of13 as ofp
878 obj = ofp.oxm.in_phy_port(value=42)
879 expected = ''.join([
880 '\x80\x00', # class
881 '\x02', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700882 '\x04', # length
Rich Laneea693752013-03-18 11:05:45 -0700883 '\x00\x00\x00\x2a' # value
884 ])
885 self.assertEquals(expected, obj.pack())
886
887 def test_oxm_in_phy_port_masked_pack(self):
888 import loxi.of13 as ofp
889 obj = ofp.oxm.in_phy_port_masked(value=42, value_mask=0xaabbccdd)
890 expected = ''.join([
891 '\x80\x00', # class
892 '\x03', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700893 '\x08', # length
Rich Laneea693752013-03-18 11:05:45 -0700894 '\x00\x00\x00\x2a', # value
895 '\xaa\xbb\xcc\xdd' # mask
896 ])
897 self.assertEquals(expected, obj.pack())
898
Rich Lane41805642013-03-19 15:00:26 -0700899 def test_oxm_ipv6_dst_pack(self):
900 import loxi.of13 as ofp
901 obj = ofp.oxm.ipv6_dst(value='\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0d\x0f')
902 expected = ''.join([
903 '\x80\x00', # class
904 '\x36', # type/masked
Rich Lane82e9f6e2013-04-25 17:32:22 -0700905 '\x10', # length
Rich Lane41805642013-03-19 15:00:26 -0700906 '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0d\x0f', # value
907 ])
908 self.assertEquals(expected, obj.pack())
909
Rich Lanee02314c2013-05-02 16:42:04 -0700910class TestInstructions(unittest.TestCase):
911 def test_goto_table(self):
912 obj = ofp.instruction.goto_table(table_id=5)
913 buf = ''.join([
914 '\x00\x01', # type
915 '\x00\x08', # length
916 '\x05', # table_id
917 '\x00' * 3, # pad
918 ])
919 test_serialization(obj, buf)
920
921 def test_write_metadata(self):
922 # TODO
923 pass
924
925 def test_write_actions(self):
926 # TODO
927 pass
928
929 def test_apply_actions(self):
930 # TODO
931 pass
932
933 def test_clear_actions(self):
934 # TODO
935 pass
936
937 def test_meter(self):
938 # TODO
939 pass
940
941 # TODO test experimenter instructions
942
Rich Lane3f075972013-03-15 22:56:29 -0700943class TestAllOF13(unittest.TestCase):
944 """
945 Round-trips every class through serialization/deserialization.
946 Not a replacement for handcoded tests because it only uses the
947 default member values.
948 """
949
950 def setUp(self):
Rich Laneea693752013-03-18 11:05:45 -0700951 mods = [ofp.action,ofp.message,ofp.common,ofp.oxm]
Rich Lane3f075972013-03-15 22:56:29 -0700952 self.klasses = [klass for mod in mods
953 for klass in mod.__dict__.values()
954 if hasattr(klass, 'show')]
955 self.klasses.sort(key=lambda x: str(x))
956
957 def test_serialization(self):
958 expected_failures = [
Rich Lane8692ecd2013-05-02 11:33:53 -0700959 ofp.common.table_feature_prop_apply_actions,
960 ofp.common.table_feature_prop_apply_actions_miss,
Rich Lane8692ecd2013-05-02 11:33:53 -0700961 ofp.common.table_feature_prop_write_actions,
962 ofp.common.table_feature_prop_write_actions_miss,
963 ofp.common.table_features,
Rich Lane3f075972013-03-15 22:56:29 -0700964 ofp.message.table_features_stats_reply,
965 ofp.message.table_features_stats_request,
966 ]
967 for klass in self.klasses:
968 def fn():
969 obj = klass()
970 if hasattr(obj, "xid"): obj.xid = 42
971 buf = obj.pack()
972 obj2 = klass.unpack(buf)
973 self.assertEquals(obj, obj2)
974 if klass in expected_failures:
975 self.assertRaises(Exception, fn)
976 else:
977 fn()
978
979 def test_show(self):
Rich Lane8ca3b772013-04-30 13:36:55 -0700980 expected_failures = []
Rich Lane3f075972013-03-15 22:56:29 -0700981 for klass in self.klasses:
982 def fn():
983 obj = klass()
984 if hasattr(obj, "xid"): obj.xid = 42
985 obj.show()
986 if klass in expected_failures:
987 self.assertRaises(Exception, fn)
988 else:
989 fn()
990
991if __name__ == '__main__':
992 unittest.main()