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