blob: faa3a3e039a4eb671a4f7e6839736112a4e55ff2 [file] [log] [blame]
Rich Lanea06d0c32013-03-25 08:52:03 -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
29
30try:
Rich Lanefb7de0e2013-03-29 16:50:29 -070031 import loxi.of10 as ofp
Rich Lanea06d0c32013-03-25 08:52:03 -070032except ImportError:
33 exit("loxi package not found. Try setting PYTHONPATH.")
34
35class TestImports(unittest.TestCase):
36 def test_toplevel(self):
37 import loxi
38 self.assertTrue(hasattr(loxi, "ProtocolError"))
Rich Lane00549ea2013-04-25 13:33:16 -070039 self.assertEquals(loxi.version_names[1], "1.0")
Rich Lanea06d0c32013-03-25 08:52:03 -070040 ofp = loxi.protocol(1)
41 self.assertEquals(ofp.OFP_VERSION, 1)
42 self.assertTrue(hasattr(ofp, "action"))
43 self.assertTrue(hasattr(ofp, "common"))
44 self.assertTrue(hasattr(ofp, "const"))
45 self.assertTrue(hasattr(ofp, "message"))
46
47 def test_version(self):
Rich Lanea94fb082013-04-04 17:04:17 -070048 import loxi
Rich Lanea06d0c32013-03-25 08:52:03 -070049 self.assertTrue(hasattr(loxi.of10, "ProtocolError"))
50 self.assertTrue(hasattr(loxi.of10, "OFP_VERSION"))
51 self.assertEquals(loxi.of10.OFP_VERSION, 1)
52 self.assertTrue(hasattr(loxi.of10, "action"))
53 self.assertTrue(hasattr(loxi.of10, "common"))
54 self.assertTrue(hasattr(loxi.of10, "const"))
55 self.assertTrue(hasattr(loxi.of10, "message"))
56
57class TestActions(unittest.TestCase):
58 def test_output_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070059 expected = "\x00\x00\x00\x08\xff\xf8\xff\xff"
60 action = ofp.action.output(port=ofp.OFPP_IN_PORT, max_len=0xffff)
61 self.assertEquals(expected, action.pack())
62
63 def test_output_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070064 # Normal case
65 buf = "\x00\x00\x00\x08\xff\xf8\xff\xff"
66 action = ofp.action.output.unpack(buf)
67 self.assertEqual(action.port, ofp.OFPP_IN_PORT)
68 self.assertEqual(action.max_len, 0xffff)
69
70 # Invalid length
71 buf = "\x00\x00\x00\x09\xff\xf8\xff\xff\x00"
72 with self.assertRaises(ofp.ProtocolError):
73 ofp.action.output.unpack(buf)
74
75 def test_output_equality(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070076 action = ofp.action.output(port=1, max_len=0x1234)
77 action2 = ofp.action.output(port=1, max_len=0x1234)
78 self.assertEquals(action, action2)
79
80 action2.port = 2
81 self.assertNotEquals(action, action2)
82 action2.port = 1
83
84 action2.max_len = 0xffff
85 self.assertNotEquals(action, action2)
86 action2.max_len = 0x1234
87
88 def test_output_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070089 action = ofp.action.output(port=1, max_len=0x1234)
90 expected = "output { port = 1, max_len = 0x1234 }"
91 self.assertEquals(expected, action.show())
92
93 def test_bsn_set_tunnel_dst_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070094 expected = ''.join([
95 "\xff\xff", "\x00\x10", # type/length
96 "\x00\x5c\x16\xc7", # experimenter
97 "\x00\x00\x00\x02", # subtype
98 "\x12\x34\x56\x78" # dst
99 ])
100 action = ofp.action.bsn_set_tunnel_dst(dst=0x12345678)
101 self.assertEquals(expected, action.pack())
102
103 def test_bsn_set_tunnel_dst_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700104 buf = ''.join([
105 "\xff\xff", "\x00\x10", # type/length
106 "\x00\x5c\x16\xc7", # experimenter
107 "\x00\x00\x00\x02", # subtype
108 "\x12\x34\x56\x78" # dst
109 ])
110 action = ofp.action.bsn_set_tunnel_dst.unpack(buf)
111 self.assertEqual(action.subtype, 2)
112 self.assertEqual(action.dst, 0x12345678)
113
114# Assumes action serialization/deserialization works
115class TestActionList(unittest.TestCase):
116 def test_normal(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700117 expected = []
118 bufs = []
119
120 def add(action):
121 expected.append(action)
122 bufs.append(action.pack())
123
124 add(ofp.action.output(port=1, max_len=0xffff))
125 add(ofp.action.output(port=2, max_len=0xffff))
126 add(ofp.action.output(port=ofp.OFPP_IN_PORT, max_len=0xffff))
127 add(ofp.action.bsn_set_tunnel_dst(dst=0x12345678))
128 add(ofp.action.nicira_dec_ttl())
129
130 actions = ofp.action.unpack_list(''.join(bufs))
131 self.assertEquals(actions, expected)
132
133 def test_empty_list(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700134 self.assertEquals(ofp.action.unpack_list(''), [])
135
136 def test_invalid_list_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700137 buf = '\x00' * 9
138 with self.assertRaisesRegexp(ofp.ProtocolError, 'not a multiple of 8'):
139 ofp.action.unpack_list(buf)
140
141 def test_invalid_action_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700142 buf = '\x00' * 8
Rich Laned4e08692013-02-20 17:40:33 -0800143 with self.assertRaisesRegexp(ofp.ProtocolError, 'is less than the header length'):
Rich Lanea06d0c32013-03-25 08:52:03 -0700144 ofp.action.unpack_list(buf)
145
146 buf = '\x00\x00\x00\x04'
147 with self.assertRaisesRegexp(ofp.ProtocolError, 'not a multiple of 8'):
148 ofp.action.unpack_list(buf)
149
150 buf = '\x00\x00\x00\x10\x00\x00\x00\x00'
151 with self.assertRaisesRegexp(ofp.ProtocolError, 'overrun'):
152 ofp.action.unpack_list(buf)
153
154 def test_invalid_action_type(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700155 buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
156 with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action type'):
157 ofp.action.unpack_list(buf)
158
159class TestConstants(unittest.TestCase):
160 def test_ports(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700161 self.assertEquals(0xffff, ofp.OFPP_NONE)
162
163 def test_wildcards(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700164 self.assertEquals(0xfc000, ofp.OFPFW_NW_DST_MASK)
165
166class TestCommon(unittest.TestCase):
167 def test_port_desc_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700168 obj = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
169 hw_addr=[1,2,3,4,5,6],
170 name="foo",
171 config=ofp.OFPPC_NO_FLOOD,
172 state=ofp.OFPPS_STP_FORWARD,
173 curr=ofp.OFPPF_10MB_HD,
174 advertised=ofp.OFPPF_1GB_FD,
175 supported=ofp.OFPPF_AUTONEG,
176 peer=ofp.OFPPF_PAUSE_ASYM)
177 expected = ''.join([
178 '\xff\xfd', # port_no
179 '\x01\x02\x03\x04\x05\x06', # hw_addr
180 'foo'.ljust(16, '\x00'), # name
181 '\x00\x00\x00\x10', # config
182 '\x00\x00\x02\x00', # state
183 '\x00\x00\x00\x01', # curr
184 '\x00\x00\x00\x20', # advertised
185 '\x00\x00\x02\x00', # supported
186 '\x00\x00\x08\x00', # peer
187 ])
188 self.assertEquals(expected, obj.pack())
189
190 def test_port_desc_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700191 buf = ''.join([
192 '\xff\xfd', # port_no
193 '\x01\x02\x03\x04\x05\x06', # hw_addr
194 'foo'.ljust(16, '\x00'), # name
195 '\x00\x00\x00\x10', # config
196 '\x00\x00\x02\x00', # state
197 '\x00\x00\x00\x01', # curr
198 '\x00\x00\x00\x20', # advertised
199 '\x00\x00\x02\x00', # supported
200 '\x00\x00\x08\x00', # peer
201 ])
202 obj = ofp.port_desc.unpack(buf)
203 self.assertEquals(ofp.OFPP_CONTROLLER, obj.port_no)
204 self.assertEquals('foo', obj.name)
205 self.assertEquals(ofp.OFPPF_PAUSE_ASYM, obj.peer)
206
207 def test_table_stats_entry_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700208 obj = ofp.table_stats_entry(table_id=3,
209 name="foo",
210 wildcards=ofp.OFPFW_ALL,
211 max_entries=5,
212 active_count=2,
213 lookup_count=1099511627775,
214 matched_count=9300233470495232273L)
215 expected = ''.join([
216 '\x03', # table_id
217 '\x00\x00\x00', # pad
218 'foo'.ljust(32, '\x00'), # name
219 '\x00\x3f\xFF\xFF', # wildcards
220 '\x00\x00\x00\x05', # max_entries
221 '\x00\x00\x00\x02', # active_count
222 '\x00\x00\x00\xff\xff\xff\xff\xff', # lookup_count
223 '\x81\x11\x11\x11\x11\x11\x11\x11', # matched_count
224 ])
225 self.assertEquals(expected, obj.pack())
226
227 def test_table_stats_entry_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700228 buf = ''.join([
229 '\x03', # table_id
230 '\x00\x00\x00', # pad
231 'foo'.ljust(32, '\x00'), # name
232 '\x00\x3f\xFF\xFF', # wildcards
233 '\x00\x00\x00\x05', # max_entries
234 '\x00\x00\x00\x02', # active_count
235 '\x00\x00\x00\xff\xff\xff\xff\xff', # lookup_count
236 '\x81\x11\x11\x11\x11\x11\x11\x11', # matched_count
237 ])
238 obj = ofp.table_stats_entry.unpack(buf)
239 self.assertEquals(3, obj.table_id)
240 self.assertEquals('foo', obj.name)
241 self.assertEquals(9300233470495232273L, obj.matched_count)
242
243 def test_flow_stats_entry_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700244 obj = ofp.flow_stats_entry(table_id=3,
245 match=ofp.match(),
246 duration_sec=1,
247 duration_nsec=2,
248 priority=100,
249 idle_timeout=5,
250 hard_timeout=10,
251 cookie=0x0123456789abcdef,
252 packet_count=10,
253 byte_count=1000,
254 actions=[ofp.action.output(port=1),
255 ofp.action.output(port=2)])
256 expected = ''.join([
257 '\x00\x68', # length
258 '\x03', # table_id
259 '\x00', # pad
260 '\x00\x3f\xff\xff', # match.wildcards
261 '\x00' * 36, # remaining match fields
262 '\x00\x00\x00\x01', # duration_sec
263 '\x00\x00\x00\x02', # duration_nsec
264 '\x00\x64', # priority
265 '\x00\x05', # idle_timeout
266 '\x00\x0a', # hard_timeout
267 '\x00' * 6, # pad2
268 '\x01\x23\x45\x67\x89\xab\xcd\xef', # cookie
269 '\x00\x00\x00\x00\x00\x00\x00\x0a', # packet_count
270 '\x00\x00\x00\x00\x00\x00\x03\xe8', # byte_count
271 '\x00\x00', # actions[0].type
272 '\x00\x08', # actions[0].len
273 '\x00\x01', # actions[0].port
274 '\x00\x00', # actions[0].max_len
275 '\x00\x00', # actions[1].type
276 '\x00\x08', # actions[1].len
277 '\x00\x02', # actions[1].port
278 '\x00\x00', # actions[1].max_len
279 ])
280 self.assertEquals(expected, obj.pack())
281
282 def test_flow_stats_entry_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700283 buf = ''.join([
284 '\x00\x68', # length
285 '\x03', # table_id
286 '\x00', # pad
287 '\x00\x3f\xff\xff', # match.wildcards
288 '\x00' * 36, # remaining match fields
289 '\x00\x00\x00\x01', # duration_sec
290 '\x00\x00\x00\x02', # duration_nsec
291 '\x00\x64', # priority
292 '\x00\x05', # idle_timeout
293 '\x00\x0a', # hard_timeout
294 '\x00' * 6, # pad2
295 '\x01\x23\x45\x67\x89\xab\xcd\xef', # cookie
296 '\x00\x00\x00\x00\x00\x00\x00\x0a', # packet_count
297 '\x00\x00\x00\x00\x00\x00\x03\xe8', # byte_count
298 '\x00\x00', # actions[0].type
299 '\x00\x08', # actions[0].len
300 '\x00\x01', # actions[0].port
301 '\x00\x00', # actions[0].max_len
302 '\x00\x00', # actions[1].type
303 '\x00\x08', # actions[1].len
304 '\x00\x02', # actions[1].port
305 '\x00\x00', # actions[1].max_len
306 ])
307 obj = ofp.flow_stats_entry.unpack(buf)
308 self.assertEquals(3, obj.table_id)
309 self.assertEquals(ofp.OFPFW_ALL, obj.match.wildcards)
310 self.assertEquals(2, len(obj.actions))
311 self.assertEquals(1, obj.actions[0].port)
312 self.assertEquals(2, obj.actions[1].port)
313
314 def test_match(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700315 match = ofp.match()
316 self.assertEquals(match.wildcards, ofp.OFPFW_ALL)
317 self.assertEquals(match.tcp_src, 0)
318 buf = match.pack()
319 match2 = ofp.match.unpack(buf)
320 self.assertEquals(match, match2)
321
322class TestMessages(unittest.TestCase):
323 def test_hello_construction(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700324 msg = ofp.message.hello()
325 self.assertEquals(msg.version, ofp.OFP_VERSION)
326 self.assertEquals(msg.type, ofp.OFPT_HELLO)
327 self.assertEquals(msg.xid, None)
328
329 msg = ofp.message.hello(xid=123)
330 self.assertEquals(msg.xid, 123)
331
332 # 0 is a valid xid distinct from None
333 msg = ofp.message.hello(xid=0)
334 self.assertEquals(msg.xid, 0)
335
336 def test_hello_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700337 # Normal case
338 buf = "\x01\x00\x00\x08\x12\x34\x56\x78"
339 msg = ofp.message.hello(xid=0x12345678)
340 self.assertEquals(buf, msg.pack())
341
342 # Invalid length
343 buf = "\x01\x00\x00\x09\x12\x34\x56\x78\x9a"
344 with self.assertRaisesRegexp(ofp.ProtocolError, "should be 8"):
345 ofp.message.hello.unpack(buf)
346
347 def test_echo_request_construction(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700348 msg = ofp.message.echo_request(data="abc")
349 self.assertEquals(msg.data, "abc")
350
351 def test_echo_request_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700352 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
353 buf = msg.pack()
354 self.assertEquals(buf, "\x01\x02\x00\x0b\x12\x34\x56\x78\x61\x62\x63")
355
356 msg2 = ofp.message.echo_request.unpack(buf)
357 self.assertEquals(msg, msg2)
358
359 def test_echo_request_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700360 # Normal case
361 buf = "\x01\x02\x00\x0b\x12\x34\x56\x78\x61\x62\x63"
362 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
363 self.assertEquals(buf, msg.pack())
364
365 # Invalid length
366 buf = "\x01\x02\x00\x07\x12\x34\x56"
367 with self.assertRaisesRegexp(ofp.ProtocolError, "buffer too short"):
368 ofp.message.echo_request.unpack(buf)
369
370 def test_echo_request_equality(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700371 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
372 msg2 = ofp.message.echo_request(xid=0x12345678, data="abc")
373 #msg2 = ofp.message.echo_request.unpack(msg.pack())
374 self.assertEquals(msg, msg2)
375
376 msg2.xid = 1
377 self.assertNotEquals(msg, msg2)
378 msg2.xid = msg.xid
379
380 msg2.data = "a"
381 self.assertNotEquals(msg, msg2)
382 msg2.data = msg.data
383
384 def test_echo_request_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700385 expected = "echo_request { xid = 0x12345678, data = 'ab\\x01' }"
386 msg = ofp.message.echo_request(xid=0x12345678, data="ab\x01")
387 self.assertEquals(msg.show(), expected)
388
389 def test_flow_add(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700390 match = ofp.match()
391 msg = ofp.message.flow_add(xid=1,
392 match=match,
393 cookie=1,
394 idle_timeout=5,
395 flags=ofp.OFPFF_CHECK_OVERLAP,
396 actions=[
397 ofp.action.output(port=1),
398 ofp.action.output(port=2),
399 ofp.action.output(port=ofp.OFPP_CONTROLLER,
400 max_len=1024)])
401 buf = msg.pack()
402 msg2 = ofp.message.flow_add.unpack(buf)
403 self.assertEquals(msg, msg2)
404
405 def test_port_mod_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700406 msg = ofp.message.port_mod(xid=2,
407 port_no=ofp.OFPP_CONTROLLER,
408 hw_addr=[1,2,3,4,5,6],
409 config=0x90ABCDEF,
410 mask=0xFF11FF11,
411 advertise=0xCAFE6789)
412 expected = "\x01\x0f\x00\x20\x00\x00\x00\x02\xff\xfd\x01\x02\x03\x04\x05\x06\x90\xab\xcd\xef\xff\x11\xff\x11\xca\xfe\x67\x89\x00\x00\x00\x00"
413 self.assertEquals(expected, msg.pack())
414
415 def test_desc_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700416 msg = ofp.message.desc_stats_reply(xid=3,
417 flags=ofp.OFPSF_REPLY_MORE,
418 mfr_desc="The Indigo-2 Community",
419 hw_desc="Unknown server",
420 sw_desc="Indigo-2 LRI pre-release",
421 serial_num="11235813213455",
422 dp_desc="Indigo-2 LRI forwarding module")
423 expected = ''.join([
424 '\x01', '\x11', # version/type
425 '\x04\x2c', # length
426 '\x00\x00\x00\x03', # xid
427 '\x00\x00', # stats_type
428 '\x00\x01', # flags
429 'The Indigo-2 Community'.ljust(256, '\x00'), # mfr_desc
430 'Unknown server'.ljust(256, '\x00'), # hw_desc
431 'Indigo-2 LRI pre-release'.ljust(256, '\x00'), # sw_desc
432 '11235813213455'.ljust(32, '\x00'), # serial_num
433 'Indigo-2 LRI forwarding module'.ljust(256, '\x00'), # dp_desc
434 ])
435 self.assertEquals(expected, msg.pack())
436
437 def test_desc_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700438 buf = ''.join([
439 '\x01', '\x11', # version/type
440 '\x04\x2c', # length
441 '\x00\x00\x00\x03', # xid
442 '\x00\x00', # stats_type
443 '\x00\x01', # flags
444 'The Indigo-2 Community'.ljust(256, '\x00'), # mfr_desc
445 'Unknown server'.ljust(256, '\x00'), # hw_desc
446 'Indigo-2 LRI pre-release'.ljust(256, '\x00'), # sw_desc
447 '11235813213455'.ljust(32, '\x00'), # serial_num
448 'Indigo-2 LRI forwarding module'.ljust(256, '\x00'), # dp_desc
449 ])
450 msg = ofp.message.desc_stats_reply.unpack(buf)
451 self.assertEquals('Indigo-2 LRI forwarding module', msg.dp_desc)
452 self.assertEquals('11235813213455', msg.serial_num)
453 self.assertEquals(ofp.OFPST_DESC, msg.stats_type)
454 self.assertEquals(ofp.OFPSF_REPLY_MORE, msg.flags)
455
456 def test_port_status_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700457 desc = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
458 hw_addr=[1,2,3,4,5,6],
459 name="foo",
460 config=ofp.OFPPC_NO_FLOOD,
461 state=ofp.OFPPS_STP_FORWARD,
462 curr=ofp.OFPPF_10MB_HD,
463 advertised=ofp.OFPPF_1GB_FD,
464 supported=ofp.OFPPF_AUTONEG,
465 peer=ofp.OFPPF_PAUSE_ASYM)
466
467 msg = ofp.message.port_status(xid=4,
468 reason=ofp.OFPPR_DELETE,
469 desc=desc)
470 expected = ''.join([
471 '\x01', '\x0c', # version/type
472 '\x00\x40', # length
473 '\x00\x00\x00\x04', # xid
474 '\x01', # reason
475 '\x00\x00\x00\x00\x00\x00\x00' # pad
476 '\xff\xfd', # desc.port_no
477 '\x01\x02\x03\x04\x05\x06', # desc.hw_addr
478 'foo'.ljust(16, '\x00'), # desc.name
479 '\x00\x00\x00\x10', # desc.config
480 '\x00\x00\x02\x00', # desc.state
481 '\x00\x00\x00\x01', # desc.curr
482 '\x00\x00\x00\x20', # desc.advertised
483 '\x00\x00\x02\x00', # desc.supported
484 '\x00\x00\x08\x00', # desc.peer
485 ])
486 self.assertEquals(expected, msg.pack())
487
488 def test_port_status_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700489 buf = ''.join([
490 '\x01', '\x0c', # version/type
491 '\x00\x40', # length
492 '\x00\x00\x00\x04', # xid
493 '\x01', # reason
494 '\x00\x00\x00\x00\x00\x00\x00' # pad
495 '\xff\xfd', # desc.port_no
496 '\x01\x02\x03\x04\x05\x06', # desc.hw_addr
497 'foo'.ljust(16, '\x00'), # desc.name
498 '\x00\x00\x00\x10', # desc.config
499 '\x00\x00\x02\x00', # desc.state
500 '\x00\x00\x00\x01', # desc.curr
501 '\x00\x00\x00\x20', # desc.advertised
502 '\x00\x00\x02\x00', # desc.supported
503 '\x00\x00\x08\x00', # desc.peer
504 ])
505 msg = ofp.message.port_status.unpack(buf)
506 self.assertEquals('foo', msg.desc.name)
507 self.assertEquals(ofp.OFPPF_PAUSE_ASYM, msg.desc.peer)
508
509 def test_port_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700510 msg = ofp.message.port_stats_reply(xid=5, flags=0, entries=[
511 ofp.port_stats_entry(port_no=1, rx_packets=56, collisions=5),
512 ofp.port_stats_entry(port_no=ofp.OFPP_LOCAL, rx_packets=1, collisions=1)])
513 expected = ''.join([
514 '\x01', '\x11', # version/type
515 '\x00\xdc', # length
516 '\x00\x00\x00\x05', # xid
517 '\x00\x04', # stats_type
518 '\x00\x00', # flags
519 '\x00\x01', # entries[0].port_no
520 '\x00\x00\x00\x00\x00\x00' # entries[0].pad
521 '\x00\x00\x00\x00\x00\x00\x00\x38', # entries[0].rx_packets
522 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_packets
523 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_bytes
524 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_bytes
525 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_dropped
526 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_dropped
527 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_errors
528 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_errors
529 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_frame_err
530 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_over_err
531 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_crc_err
532 '\x00\x00\x00\x00\x00\x00\x00\x05', # entries[0].collisions
533 '\xff\xfe', # entries[1].port_no
534 '\x00\x00\x00\x00\x00\x00' # entries[1].pad
535 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].rx_packets
536 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_packets
537 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_bytes
538 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_bytes
539 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_dropped
540 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_dropped
541 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_errors
542 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_errors
543 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_frame_err
544 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_over_err
545 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_crc_err
546 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].collisions
547 ])
548 self.assertEquals(expected, msg.pack())
549
550 def test_port_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700551 buf = ''.join([
552 '\x01', '\x11', # version/type
553 '\x00\xdc', # length
554 '\x00\x00\x00\x05', # xid
555 '\x00\x04', # stats_type
556 '\x00\x00', # flags
557 '\x00\x01', # entries[0].port_no
558 '\x00\x00\x00\x00\x00\x00' # entries[0].pad
559 '\x00\x00\x00\x00\x00\x00\x00\x38', # entries[0].rx_packets
560 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_packets
561 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_bytes
562 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_bytes
563 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_dropped
564 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_dropped
565 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_errors
566 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_errors
567 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_frame_err
568 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_over_err
569 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_crc_err
570 '\x00\x00\x00\x00\x00\x00\x00\x05', # entries[0].collisions
571 '\xff\xfe', # entries[1].port_no
572 '\x00\x00\x00\x00\x00\x00' # entries[1].pad
573 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].rx_packets
574 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_packets
575 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_bytes
576 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_bytes
577 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_dropped
578 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_dropped
579 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_errors
580 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_errors
581 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_frame_err
582 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_over_err
583 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_crc_err
584 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].collisions
585 ])
586 msg = ofp.message.port_stats_reply.unpack(buf)
587 self.assertEquals(ofp.OFPST_PORT, msg.stats_type)
588 self.assertEquals(2, len(msg.entries))
589
590 sample_flow_stats_reply_buf = ''.join([
591 '\x01', '\x11', # version/type
592 '\x00\xe4', # length
593 '\x00\x00\x00\x06', # xid
594 '\x00\x01', # stats_type
595 '\x00\x00', # flags
596 '\x00\x68', # entries[0].length
597 '\x03', # entries[0].table_id
598 '\x00', # entries[0].pad
599 '\x00\x3f\xff\xff', # entries[0].match.wildcards
600 '\x00' * 36, # remaining match fields
601 '\x00\x00\x00\x01', # entries[0].duration_sec
602 '\x00\x00\x00\x02', # entries[0].duration_nsec
603 '\x00\x64', # entries[0].priority
604 '\x00\x05', # entries[0].idle_timeout
605 '\x00\x0a', # entries[0].hard_timeout
606 '\x00' * 6, # entries[0].pad2
607 '\x01\x23\x45\x67\x89\xab\xcd\xef', # entries[0].cookie
608 '\x00\x00\x00\x00\x00\x00\x00\x0a', # entries[0].packet_count
609 '\x00\x00\x00\x00\x00\x00\x03\xe8', # entries[0].byte_count
610 '\x00\x00', # entries[0].actions[0].type
611 '\x00\x08', # entries[0].actions[0].len
612 '\x00\x01', # entries[0].actions[0].port
613 '\x00\x00', # entries[0].actions[0].max_len
614 '\x00\x00', # entries[0].actions[1].type
615 '\x00\x08', # entries[0].actions[1].len
616 '\x00\x02', # entries[0].actions[1].port
617 '\x00\x00', # entries[0].actions[1].max_len
618 '\x00\x70', # entries[1].length
619 '\x04', # entries[1].table_id
620 '\x00', # entries[1].pad
621 '\x00\x3f\xff\xff', # entries[1].match.wildcards
622 '\x00' * 36, # remaining match fields
623 '\x00\x00\x00\x01', # entries[1].duration_sec
624 '\x00\x00\x00\x02', # entries[1].duration_nsec
625 '\x00\x64', # entries[1].priority
626 '\x00\x05', # entries[1].idle_timeout
627 '\x00\x0a', # entries[1].hard_timeout
628 '\x00' * 6, # entries[1].pad2
629 '\x01\x23\x45\x67\x89\xab\xcd\xef', # entries[1].cookie
630 '\x00\x00\x00\x00\x00\x00\x00\x0a', # entries[1].packet_count
631 '\x00\x00\x00\x00\x00\x00\x03\xe8', # entries[1].byte_count
632 '\x00\x00', # entries[1].actions[0].type
633 '\x00\x08', # entries[1].actions[0].len
634 '\x00\x01', # entries[1].actions[0].port
635 '\x00\x00', # entries[1].actions[0].max_len
636 '\x00\x00', # entries[1].actions[1].type
637 '\x00\x08', # entries[1].actions[1].len
638 '\x00\x02', # entries[1].actions[1].port
639 '\x00\x00', # entries[1].actions[1].max_len
640 '\x00\x00', # entries[1].actions[2].type
641 '\x00\x08', # entries[1].actions[2].len
642 '\x00\x03', # entries[1].actions[2].port
643 '\x00\x00', # entries[1].actions[2].max_len
644 ])
645
646 def test_flow_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700647 msg = ofp.message.flow_stats_reply(xid=6, flags=0, entries=[
648 ofp.flow_stats_entry(table_id=3,
649 match=ofp.match(),
650 duration_sec=1,
651 duration_nsec=2,
652 priority=100,
653 idle_timeout=5,
654 hard_timeout=10,
655 cookie=0x0123456789abcdef,
656 packet_count=10,
657 byte_count=1000,
658 actions=[ofp.action.output(port=1),
659 ofp.action.output(port=2)]),
660 ofp.flow_stats_entry(table_id=4,
661 match=ofp.match(),
662 duration_sec=1,
663 duration_nsec=2,
664 priority=100,
665 idle_timeout=5,
666 hard_timeout=10,
667 cookie=0x0123456789abcdef,
668 packet_count=10,
669 byte_count=1000,
670 actions=[ofp.action.output(port=1),
671 ofp.action.output(port=2),
672 ofp.action.output(port=3)])])
673 self.assertEquals(self.sample_flow_stats_reply_buf, msg.pack())
674
675 def test_flow_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700676 msg = ofp.message.flow_stats_reply.unpack(self.sample_flow_stats_reply_buf)
677 self.assertEquals(ofp.OFPST_FLOW, msg.stats_type)
678 self.assertEquals(2, len(msg.entries))
679 self.assertEquals(2, len(msg.entries[0].actions))
680 self.assertEquals(3, len(msg.entries[1].actions))
681
682 def test_flow_add_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700683 expected = """\
684flow_add {
685 xid = None,
686 match = match_v1 {
687 wildcards = OFPFW_DL_SRC|OFPFW_DL_DST,
688 in_port = 3,
689 eth_src = 01:23:45:67:89:ab,
690 eth_dst = cd:ef:01:23:45:67,
691 vlan_vid = 0x0,
692 vlan_pcp = 0x0,
Rich Lanec58a2322013-03-15 23:28:52 -0700693 pad = 0x0,
Rich Lanea06d0c32013-03-25 08:52:03 -0700694 eth_type = 0x0,
695 ip_dscp = 0x0,
696 ip_proto = 0x0,
Rich Lanec58a2322013-03-15 23:28:52 -0700697 pad1 = [ 0, 0 ],
Rich Lanea06d0c32013-03-25 08:52:03 -0700698 ipv4_src = 192.168.3.127,
699 ipv4_dst = 255.255.255.255,
700 tcp_src = 0x0,
701 tcp_dst = 0x0
702 },
703 cookie = 0x0,
704 idle_timeout = 0x0,
705 hard_timeout = 0x0,
706 priority = 0x0,
707 buffer_id = 0x0,
708 out_port = 0,
709 flags = 0x0,
710 actions = [
711 output { port = OFPP_FLOOD, max_len = 0x0 },
Rich Lanec58a2322013-03-15 23:28:52 -0700712 nicira_dec_ttl { pad = 0x0, pad1 = 0x0 },
Rich Lanea06d0c32013-03-25 08:52:03 -0700713 bsn_set_tunnel_dst { dst = 0x0 }
714 ]
715}"""
716 msg = ofp.message.flow_add(
717 match=ofp.match(
718 wildcards=ofp.OFPFW_DL_SRC|ofp.OFPFW_DL_DST,
719 in_port=3,
720 ipv4_src=0xc0a8037f,
721 ipv4_dst=0xffffffff,
722 eth_src=[0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
723 eth_dst=[0xcd, 0xef, 0x01, 0x23, 0x45, 0x67]),
724 actions=[
725 ofp.action.output(port=ofp.OFPP_FLOOD),
726 ofp.action.nicira_dec_ttl(),
727 ofp.action.bsn_set_tunnel_dst()])
728 self.assertEquals(msg.show(), expected)
729
730 sample_packet_out_buf = ''.join([
731 '\x01', '\x0d', # version/type
732 '\x00\x23', # length
733 '\x12\x34\x56\x78', # xid
734 '\xab\xcd\xef\x01', # buffer_id
735 '\xff\xfe', # in_port
736 '\x00\x10', # actions_len
737 '\x00\x00', # actions[0].type
738 '\x00\x08', # actions[0].len
739 '\x00\x01', # actions[0].port
740 '\x00\x00', # actions[0].max_len
741 '\x00\x00', # actions[1].type
742 '\x00\x08', # actions[1].len
743 '\x00\x02', # actions[1].port
744 '\x00\x00', # actions[1].max_len
745 'abc' # data
746 ])
747
748 def test_packet_out_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700749 msg = ofp.message.packet_out(
750 xid=0x12345678,
751 buffer_id=0xabcdef01,
752 in_port=ofp.OFPP_LOCAL,
753 actions=[
754 ofp.action.output(port=1),
755 ofp.action.output(port=2)],
756 data='abc')
757 self.assertEquals(self.sample_packet_out_buf, msg.pack())
758
759 def test_packet_out_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700760 msg = ofp.message.packet_out.unpack(self.sample_packet_out_buf)
761 self.assertEquals(0x12345678, msg.xid)
762 self.assertEquals(0xabcdef01, msg.buffer_id)
763 self.assertEquals(ofp.OFPP_LOCAL, msg.in_port)
764 self.assertEquals(2, len(msg.actions))
765 self.assertEquals(1, msg.actions[0].port)
766 self.assertEquals(2, msg.actions[1].port)
767 self.assertEquals('abc', msg.data)
768
769 sample_packet_in_buf = ''.join([
770 '\x01', '\x0a', # version/type
771 '\x00\x15', # length
772 '\x12\x34\x56\x78', # xid
773 '\xab\xcd\xef\x01', # buffer_id
774 '\x00\x09', # total_len
775 '\xff\xfe', # in_port
776 '\x01', # reason
777 '\x00', # pad
778 'abc', # data
779 ])
780
781 def test_packet_in_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700782 msg = ofp.message.packet_in(
783 xid=0x12345678,
784 buffer_id=0xabcdef01,
785 total_len=9,
786 in_port=ofp.OFPP_LOCAL,
787 reason=ofp.OFPR_ACTION,
788 data='abc')
789 self.assertEquals(self.sample_packet_in_buf, msg.pack())
790
791 def test_packet_in_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700792 msg = ofp.message.packet_in.unpack(self.sample_packet_in_buf)
793 self.assertEquals(0x12345678, msg.xid)
794 self.assertEquals(0xabcdef01, msg.buffer_id)
795 self.assertEquals(9, msg.total_len)
796 self.assertEquals(ofp.OFPP_LOCAL, msg.in_port)
797 self.assertEquals(ofp.OFPR_ACTION, msg.reason)
798 self.assertEquals('abc', msg.data)
799
800 sample_queue_get_config_reply_buf = ''.join([
801 '\x01', '\x15', # version/type
802 '\x00\x50', # length
803 '\x12\x34\x56\x78', # xid
804 '\xff\xfe', # port
805 '\x00\x00\x00\x00\x00\x00', # pad
806 '\x00\x00\x00\x01', # queues[0].queue_id
807 '\x00\x18', # queues[0].len
808 '\x00\x00', # queues[0].pad
809 '\x00\x01', # queues[0].properties[0].type
810 '\x00\x10', # queues[0].properties[0].length
811 '\x00\x00\x00\x00', # queues[0].properties[0].pad
812 '\x00\x05', # queues[0].properties[0].rate
813 '\x00\x00\x00\x00\x00\x00', # queues[0].properties[0].pad2
814 '\x00\x00\x00\x02', # queues[1].queue_id
815 '\x00\x28', # queues[1].len
816 '\x00\x00', # queues[1].pad
817 '\x00\x01', # queues[1].properties[0].type
818 '\x00\x10', # queues[1].properties[0].length
819 '\x00\x00\x00\x00', # queues[1].properties[0].pad
820 '\x00\x06', # queues[1].properties[0].rate
821 '\x00\x00\x00\x00\x00\x00', # queues[1].properties[0].pad2
822 '\x00\x01', # queues[1].properties[1].type
823 '\x00\x10', # queues[1].properties[1].length
824 '\x00\x00\x00\x00', # queues[1].properties[1].pad
825 '\x00\x07', # queues[1].properties[1].rate
826 '\x00\x00\x00\x00\x00\x00', # queues[1].properties[1].pad2
827 ])
828
829 def test_queue_get_config_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700830 msg = ofp.message.queue_get_config_reply(
831 xid=0x12345678,
832 port=ofp.OFPP_LOCAL,
833 queues=[
834 ofp.packet_queue(queue_id=1, properties=[
835 ofp.queue_prop_min_rate(rate=5)]),
836 ofp.packet_queue(queue_id=2, properties=[
837 ofp.queue_prop_min_rate(rate=6),
838 ofp.queue_prop_min_rate(rate=7)])])
839 self.assertEquals(self.sample_queue_get_config_reply_buf, msg.pack())
840
841 def test_queue_get_config_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700842 msg = ofp.message.queue_get_config_reply.unpack(self.sample_queue_get_config_reply_buf)
843 self.assertEquals(ofp.OFPP_LOCAL, msg.port)
844 self.assertEquals(msg.queues[0].queue_id, 1)
845 self.assertEquals(msg.queues[0].properties[0].rate, 5)
846 self.assertEquals(msg.queues[1].queue_id, 2)
847 self.assertEquals(msg.queues[1].properties[0].rate, 6)
848 self.assertEquals(msg.queues[1].properties[1].rate, 7)
849
850class TestParse(unittest.TestCase):
851 def test_parse_header(self):
852 import loxi
Rich Lanea06d0c32013-03-25 08:52:03 -0700853
854 msg_ver, msg_type, msg_len, msg_xid = ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56\x78")
855 self.assertEquals(1, msg_ver)
856 self.assertEquals(4, msg_type)
857 self.assertEquals(45032, msg_len)
858 self.assertEquals(0x12345678, msg_xid)
859
860 with self.assertRaisesRegexp(loxi.ProtocolError, "too short"):
861 ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56")
862
863 def test_parse_message(self):
864 import loxi
865 import loxi.of10 as ofp
866
867 buf = "\x01\x00\x00\x08\x12\x34\x56\x78"
868 msg = ofp.message.parse_message(buf)
869 assert(msg.xid == 0x12345678)
870
871 # Get a list of all message classes
872 test_klasses = [x for x in ofp.message.__dict__.values()
873 if type(x) == type
874 and issubclass(x, ofp.message.Message)
875 and x != ofp.message.Message]
876
877 for klass in test_klasses:
878 self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
879
880class TestUtils(unittest.TestCase):
881 def test_unpack_array(self):
882 import loxi
883 import loxi.of10.util as util
884
885 a = util.unpack_array(str, 3, "abcdefghi")
886 self.assertEquals(['abc', 'def', 'ghi'], a)
887
888 with self.assertRaisesRegexp(loxi.ProtocolError, "invalid array length"):
889 util.unpack_array(str, 3, "abcdefgh")
890
891 def test_pretty_wildcards(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700892 self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
893 self.assertEquals("0", ofp.util.pretty_wildcards(0))
894 self.assertEquals("OFPFW_DL_SRC|OFPFW_DL_DST",
895 ofp.util.pretty_wildcards(ofp.OFPFW_DL_SRC|ofp.OFPFW_DL_DST))
896 self.assertEquals("OFPFW_NW_SRC_MASK&0x2000",
897 ofp.util.pretty_wildcards(ofp.OFPFW_NW_SRC_ALL))
898 self.assertEquals("OFPFW_NW_SRC_MASK&0x1a00",
899 ofp.util.pretty_wildcards(0x00001a00))
900 self.assertEquals("OFPFW_IN_PORT|0x80000000",
901 ofp.util.pretty_wildcards(ofp.OFPFW_IN_PORT|0x80000000))
902
903class TestAll(unittest.TestCase):
904 """
905 Round-trips every class through serialization/deserialization.
906 Not a replacement for handcoded tests because it only uses the
907 default member values.
908 """
909
910 def setUp(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700911 mods = [ofp.action,ofp.message,ofp.common]
912 self.klasses = [klass for mod in mods
913 for klass in mod.__dict__.values()
914 if hasattr(klass, 'show')]
915 self.klasses.sort(key=lambda x: str(x))
916
917 def test_serialization(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700918 expected_failures = []
919 for klass in self.klasses:
920 def fn():
921 obj = klass()
922 if hasattr(obj, "xid"): obj.xid = 42
923 buf = obj.pack()
924 obj2 = klass.unpack(buf)
925 self.assertEquals(obj, obj2)
926 if klass in expected_failures:
927 self.assertRaises(Exception, fn)
928 else:
929 fn()
930
931 def test_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700932 expected_failures = []
933 for klass in self.klasses:
934 def fn():
935 obj = klass()
936 if hasattr(obj, "xid"): obj.xid = 42
937 obj.show()
938 if klass in expected_failures:
939 self.assertRaises(Exception, fn)
940 else:
941 fn()
942
943if __name__ == '__main__':
944 unittest.main()