blob: c91c7b38e8d360bd49c3aab084680b72ab24c112 [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
Rich Lane9a3f1fd2013-05-10 16:29:17 -070029import test_data
Rich Lane5c4446e2013-05-16 13:53:51 -070030from testutil import add_datafiles_tests
Rich Lanea06d0c32013-03-25 08:52:03 -070031
32try:
Rich Lanefb7de0e2013-03-29 16:50:29 -070033 import loxi.of10 as ofp
Rich Lane57026dc2013-05-01 10:13:16 -070034 from loxi.generic_util import OFReader
Rich Lanea06d0c32013-03-25 08:52:03 -070035except ImportError:
36 exit("loxi package not found. Try setting PYTHONPATH.")
37
38class TestImports(unittest.TestCase):
39 def test_toplevel(self):
40 import loxi
41 self.assertTrue(hasattr(loxi, "ProtocolError"))
Rich Lane00549ea2013-04-25 13:33:16 -070042 self.assertEquals(loxi.version_names[1], "1.0")
Rich Lanea06d0c32013-03-25 08:52:03 -070043 ofp = loxi.protocol(1)
44 self.assertEquals(ofp.OFP_VERSION, 1)
45 self.assertTrue(hasattr(ofp, "action"))
46 self.assertTrue(hasattr(ofp, "common"))
47 self.assertTrue(hasattr(ofp, "const"))
48 self.assertTrue(hasattr(ofp, "message"))
49
50 def test_version(self):
Rich Lanea94fb082013-04-04 17:04:17 -070051 import loxi
Rich Lanea06d0c32013-03-25 08:52:03 -070052 self.assertTrue(hasattr(loxi.of10, "ProtocolError"))
53 self.assertTrue(hasattr(loxi.of10, "OFP_VERSION"))
54 self.assertEquals(loxi.of10.OFP_VERSION, 1)
55 self.assertTrue(hasattr(loxi.of10, "action"))
56 self.assertTrue(hasattr(loxi.of10, "common"))
57 self.assertTrue(hasattr(loxi.of10, "const"))
58 self.assertTrue(hasattr(loxi.of10, "message"))
59
60class TestActions(unittest.TestCase):
Rich Lanea06d0c32013-03-25 08:52:03 -070061 def test_output_equality(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070062 action = ofp.action.output(port=1, max_len=0x1234)
63 action2 = ofp.action.output(port=1, max_len=0x1234)
64 self.assertEquals(action, action2)
65
66 action2.port = 2
67 self.assertNotEquals(action, action2)
68 action2.port = 1
69
70 action2.max_len = 0xffff
71 self.assertNotEquals(action, action2)
72 action2.max_len = 0x1234
73
Rich Lanea06d0c32013-03-25 08:52:03 -070074# Assumes action serialization/deserialization works
75class TestActionList(unittest.TestCase):
76 def test_normal(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070077 expected = []
78 bufs = []
79
80 def add(action):
81 expected.append(action)
82 bufs.append(action.pack())
83
84 add(ofp.action.output(port=1, max_len=0xffff))
85 add(ofp.action.output(port=2, max_len=0xffff))
86 add(ofp.action.output(port=ofp.OFPP_IN_PORT, max_len=0xffff))
87 add(ofp.action.bsn_set_tunnel_dst(dst=0x12345678))
88 add(ofp.action.nicira_dec_ttl())
89
Rich Lane57026dc2013-05-01 10:13:16 -070090 actions = ofp.action.unpack_list(OFReader(''.join(bufs)))
Rich Lanea06d0c32013-03-25 08:52:03 -070091 self.assertEquals(actions, expected)
92
93 def test_empty_list(self):
Rich Lane57026dc2013-05-01 10:13:16 -070094 self.assertEquals(ofp.action.unpack_list(OFReader('')), [])
Rich Lanea06d0c32013-03-25 08:52:03 -070095
96 def test_invalid_list_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070097 buf = '\x00' * 9
Rich Lane57026dc2013-05-01 10:13:16 -070098 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
99 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700100
101 def test_invalid_action_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700102 buf = '\x00' * 8
Rich Lane57026dc2013-05-01 10:13:16 -0700103 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
104 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700105
106 buf = '\x00\x00\x00\x04'
Rich Lane57026dc2013-05-01 10:13:16 -0700107 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
108 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700109
110 buf = '\x00\x00\x00\x10\x00\x00\x00\x00'
Rich Lane57026dc2013-05-01 10:13:16 -0700111 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
112 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700113
114 def test_invalid_action_type(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700115 buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
116 with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action type'):
Rich Lane57026dc2013-05-01 10:13:16 -0700117 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700118
119class TestConstants(unittest.TestCase):
120 def test_ports(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700121 self.assertEquals(0xffff, ofp.OFPP_NONE)
122
123 def test_wildcards(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700124 self.assertEquals(0xfc000, ofp.OFPFW_NW_DST_MASK)
125
126class TestCommon(unittest.TestCase):
Rich Lanea06d0c32013-03-25 08:52:03 -0700127 def test_match(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700128 match = ofp.match()
129 self.assertEquals(match.wildcards, ofp.OFPFW_ALL)
130 self.assertEquals(match.tcp_src, 0)
131 buf = match.pack()
132 match2 = ofp.match.unpack(buf)
133 self.assertEquals(match, match2)
134
135class TestMessages(unittest.TestCase):
136 def test_hello_construction(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700137 msg = ofp.message.hello()
138 self.assertEquals(msg.version, ofp.OFP_VERSION)
139 self.assertEquals(msg.type, ofp.OFPT_HELLO)
140 self.assertEquals(msg.xid, None)
141
142 msg = ofp.message.hello(xid=123)
143 self.assertEquals(msg.xid, 123)
144
145 # 0 is a valid xid distinct from None
146 msg = ofp.message.hello(xid=0)
147 self.assertEquals(msg.xid, 0)
148
Rich Lanea06d0c32013-03-25 08:52:03 -0700149 def test_echo_request_construction(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700150 msg = ofp.message.echo_request(data="abc")
151 self.assertEquals(msg.data, "abc")
152
Rich Lane5c4446e2013-05-16 13:53:51 -0700153 def test_echo_request_invalid_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700154 buf = "\x01\x02\x00\x07\x12\x34\x56"
155 with self.assertRaisesRegexp(ofp.ProtocolError, "buffer too short"):
156 ofp.message.echo_request.unpack(buf)
157
158 def test_echo_request_equality(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700159 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
160 msg2 = ofp.message.echo_request(xid=0x12345678, data="abc")
161 #msg2 = ofp.message.echo_request.unpack(msg.pack())
162 self.assertEquals(msg, msg2)
163
164 msg2.xid = 1
165 self.assertNotEquals(msg, msg2)
166 msg2.xid = msg.xid
167
168 msg2.data = "a"
169 self.assertNotEquals(msg, msg2)
170 msg2.data = msg.data
171
Rich Lanea06d0c32013-03-25 08:52:03 -0700172 def test_flow_add(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700173 match = ofp.match()
174 msg = ofp.message.flow_add(xid=1,
175 match=match,
176 cookie=1,
177 idle_timeout=5,
178 flags=ofp.OFPFF_CHECK_OVERLAP,
179 actions=[
180 ofp.action.output(port=1),
181 ofp.action.output(port=2),
182 ofp.action.output(port=ofp.OFPP_CONTROLLER,
183 max_len=1024)])
184 buf = msg.pack()
185 msg2 = ofp.message.flow_add.unpack(buf)
186 self.assertEquals(msg, msg2)
187
188 def test_port_mod_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700189 msg = ofp.message.port_mod(xid=2,
190 port_no=ofp.OFPP_CONTROLLER,
191 hw_addr=[1,2,3,4,5,6],
192 config=0x90ABCDEF,
193 mask=0xFF11FF11,
194 advertise=0xCAFE6789)
195 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"
196 self.assertEquals(expected, msg.pack())
197
198 def test_desc_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700199 msg = ofp.message.desc_stats_reply(xid=3,
200 flags=ofp.OFPSF_REPLY_MORE,
201 mfr_desc="The Indigo-2 Community",
202 hw_desc="Unknown server",
203 sw_desc="Indigo-2 LRI pre-release",
204 serial_num="11235813213455",
205 dp_desc="Indigo-2 LRI forwarding module")
206 expected = ''.join([
207 '\x01', '\x11', # version/type
208 '\x04\x2c', # length
209 '\x00\x00\x00\x03', # xid
210 '\x00\x00', # stats_type
211 '\x00\x01', # flags
212 'The Indigo-2 Community'.ljust(256, '\x00'), # mfr_desc
213 'Unknown server'.ljust(256, '\x00'), # hw_desc
214 'Indigo-2 LRI pre-release'.ljust(256, '\x00'), # sw_desc
215 '11235813213455'.ljust(32, '\x00'), # serial_num
216 'Indigo-2 LRI forwarding module'.ljust(256, '\x00'), # dp_desc
217 ])
218 self.assertEquals(expected, msg.pack())
219
220 def test_desc_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700221 buf = ''.join([
222 '\x01', '\x11', # version/type
223 '\x04\x2c', # length
224 '\x00\x00\x00\x03', # xid
225 '\x00\x00', # stats_type
226 '\x00\x01', # flags
227 'The Indigo-2 Community'.ljust(256, '\x00'), # mfr_desc
228 'Unknown server'.ljust(256, '\x00'), # hw_desc
229 'Indigo-2 LRI pre-release'.ljust(256, '\x00'), # sw_desc
230 '11235813213455'.ljust(32, '\x00'), # serial_num
231 'Indigo-2 LRI forwarding module'.ljust(256, '\x00'), # dp_desc
232 ])
233 msg = ofp.message.desc_stats_reply.unpack(buf)
234 self.assertEquals('Indigo-2 LRI forwarding module', msg.dp_desc)
235 self.assertEquals('11235813213455', msg.serial_num)
236 self.assertEquals(ofp.OFPST_DESC, msg.stats_type)
237 self.assertEquals(ofp.OFPSF_REPLY_MORE, msg.flags)
238
239 def test_port_status_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700240 desc = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
241 hw_addr=[1,2,3,4,5,6],
242 name="foo",
243 config=ofp.OFPPC_NO_FLOOD,
244 state=ofp.OFPPS_STP_FORWARD,
245 curr=ofp.OFPPF_10MB_HD,
246 advertised=ofp.OFPPF_1GB_FD,
247 supported=ofp.OFPPF_AUTONEG,
248 peer=ofp.OFPPF_PAUSE_ASYM)
249
250 msg = ofp.message.port_status(xid=4,
251 reason=ofp.OFPPR_DELETE,
252 desc=desc)
253 expected = ''.join([
254 '\x01', '\x0c', # version/type
255 '\x00\x40', # length
256 '\x00\x00\x00\x04', # xid
257 '\x01', # reason
258 '\x00\x00\x00\x00\x00\x00\x00' # pad
259 '\xff\xfd', # desc.port_no
260 '\x01\x02\x03\x04\x05\x06', # desc.hw_addr
261 'foo'.ljust(16, '\x00'), # desc.name
262 '\x00\x00\x00\x10', # desc.config
263 '\x00\x00\x02\x00', # desc.state
264 '\x00\x00\x00\x01', # desc.curr
265 '\x00\x00\x00\x20', # desc.advertised
266 '\x00\x00\x02\x00', # desc.supported
267 '\x00\x00\x08\x00', # desc.peer
268 ])
269 self.assertEquals(expected, msg.pack())
270
271 def test_port_status_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700272 buf = ''.join([
273 '\x01', '\x0c', # version/type
274 '\x00\x40', # length
275 '\x00\x00\x00\x04', # xid
276 '\x01', # reason
277 '\x00\x00\x00\x00\x00\x00\x00' # pad
278 '\xff\xfd', # desc.port_no
279 '\x01\x02\x03\x04\x05\x06', # desc.hw_addr
280 'foo'.ljust(16, '\x00'), # desc.name
281 '\x00\x00\x00\x10', # desc.config
282 '\x00\x00\x02\x00', # desc.state
283 '\x00\x00\x00\x01', # desc.curr
284 '\x00\x00\x00\x20', # desc.advertised
285 '\x00\x00\x02\x00', # desc.supported
286 '\x00\x00\x08\x00', # desc.peer
287 ])
288 msg = ofp.message.port_status.unpack(buf)
289 self.assertEquals('foo', msg.desc.name)
290 self.assertEquals(ofp.OFPPF_PAUSE_ASYM, msg.desc.peer)
291
292 def test_port_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700293 msg = ofp.message.port_stats_reply(xid=5, flags=0, entries=[
294 ofp.port_stats_entry(port_no=1, rx_packets=56, collisions=5),
295 ofp.port_stats_entry(port_no=ofp.OFPP_LOCAL, rx_packets=1, collisions=1)])
296 expected = ''.join([
297 '\x01', '\x11', # version/type
298 '\x00\xdc', # length
299 '\x00\x00\x00\x05', # xid
300 '\x00\x04', # stats_type
301 '\x00\x00', # flags
302 '\x00\x01', # entries[0].port_no
303 '\x00\x00\x00\x00\x00\x00' # entries[0].pad
304 '\x00\x00\x00\x00\x00\x00\x00\x38', # entries[0].rx_packets
305 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_packets
306 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_bytes
307 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_bytes
308 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_dropped
309 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_dropped
310 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_errors
311 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_errors
312 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_frame_err
313 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_over_err
314 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_crc_err
315 '\x00\x00\x00\x00\x00\x00\x00\x05', # entries[0].collisions
316 '\xff\xfe', # entries[1].port_no
317 '\x00\x00\x00\x00\x00\x00' # entries[1].pad
318 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].rx_packets
319 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_packets
320 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_bytes
321 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_bytes
322 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_dropped
323 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_dropped
324 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_errors
325 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_errors
326 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_frame_err
327 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_over_err
328 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_crc_err
329 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].collisions
330 ])
331 self.assertEquals(expected, msg.pack())
332
333 def test_port_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700334 buf = ''.join([
335 '\x01', '\x11', # version/type
336 '\x00\xdc', # length
337 '\x00\x00\x00\x05', # xid
338 '\x00\x04', # stats_type
339 '\x00\x00', # flags
340 '\x00\x01', # entries[0].port_no
341 '\x00\x00\x00\x00\x00\x00' # entries[0].pad
342 '\x00\x00\x00\x00\x00\x00\x00\x38', # entries[0].rx_packets
343 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_packets
344 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_bytes
345 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_bytes
346 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_dropped
347 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_dropped
348 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_errors
349 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_errors
350 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_frame_err
351 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_over_err
352 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_crc_err
353 '\x00\x00\x00\x00\x00\x00\x00\x05', # entries[0].collisions
354 '\xff\xfe', # entries[1].port_no
355 '\x00\x00\x00\x00\x00\x00' # entries[1].pad
356 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].rx_packets
357 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_packets
358 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_bytes
359 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_bytes
360 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_dropped
361 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_dropped
362 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_errors
363 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_errors
364 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_frame_err
365 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_over_err
366 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_crc_err
367 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].collisions
368 ])
369 msg = ofp.message.port_stats_reply.unpack(buf)
370 self.assertEquals(ofp.OFPST_PORT, msg.stats_type)
371 self.assertEquals(2, len(msg.entries))
372
373 sample_flow_stats_reply_buf = ''.join([
374 '\x01', '\x11', # version/type
375 '\x00\xe4', # length
376 '\x00\x00\x00\x06', # xid
377 '\x00\x01', # stats_type
378 '\x00\x00', # flags
379 '\x00\x68', # entries[0].length
380 '\x03', # entries[0].table_id
381 '\x00', # entries[0].pad
382 '\x00\x3f\xff\xff', # entries[0].match.wildcards
383 '\x00' * 36, # remaining match fields
384 '\x00\x00\x00\x01', # entries[0].duration_sec
385 '\x00\x00\x00\x02', # entries[0].duration_nsec
386 '\x00\x64', # entries[0].priority
387 '\x00\x05', # entries[0].idle_timeout
388 '\x00\x0a', # entries[0].hard_timeout
389 '\x00' * 6, # entries[0].pad2
390 '\x01\x23\x45\x67\x89\xab\xcd\xef', # entries[0].cookie
391 '\x00\x00\x00\x00\x00\x00\x00\x0a', # entries[0].packet_count
392 '\x00\x00\x00\x00\x00\x00\x03\xe8', # entries[0].byte_count
393 '\x00\x00', # entries[0].actions[0].type
394 '\x00\x08', # entries[0].actions[0].len
395 '\x00\x01', # entries[0].actions[0].port
396 '\x00\x00', # entries[0].actions[0].max_len
397 '\x00\x00', # entries[0].actions[1].type
398 '\x00\x08', # entries[0].actions[1].len
399 '\x00\x02', # entries[0].actions[1].port
400 '\x00\x00', # entries[0].actions[1].max_len
401 '\x00\x70', # entries[1].length
402 '\x04', # entries[1].table_id
403 '\x00', # entries[1].pad
404 '\x00\x3f\xff\xff', # entries[1].match.wildcards
405 '\x00' * 36, # remaining match fields
406 '\x00\x00\x00\x01', # entries[1].duration_sec
407 '\x00\x00\x00\x02', # entries[1].duration_nsec
408 '\x00\x64', # entries[1].priority
409 '\x00\x05', # entries[1].idle_timeout
410 '\x00\x0a', # entries[1].hard_timeout
411 '\x00' * 6, # entries[1].pad2
412 '\x01\x23\x45\x67\x89\xab\xcd\xef', # entries[1].cookie
413 '\x00\x00\x00\x00\x00\x00\x00\x0a', # entries[1].packet_count
414 '\x00\x00\x00\x00\x00\x00\x03\xe8', # entries[1].byte_count
415 '\x00\x00', # entries[1].actions[0].type
416 '\x00\x08', # entries[1].actions[0].len
417 '\x00\x01', # entries[1].actions[0].port
418 '\x00\x00', # entries[1].actions[0].max_len
419 '\x00\x00', # entries[1].actions[1].type
420 '\x00\x08', # entries[1].actions[1].len
421 '\x00\x02', # entries[1].actions[1].port
422 '\x00\x00', # entries[1].actions[1].max_len
423 '\x00\x00', # entries[1].actions[2].type
424 '\x00\x08', # entries[1].actions[2].len
425 '\x00\x03', # entries[1].actions[2].port
426 '\x00\x00', # entries[1].actions[2].max_len
427 ])
428
429 def test_flow_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700430 msg = ofp.message.flow_stats_reply(xid=6, flags=0, entries=[
431 ofp.flow_stats_entry(table_id=3,
432 match=ofp.match(),
433 duration_sec=1,
434 duration_nsec=2,
435 priority=100,
436 idle_timeout=5,
437 hard_timeout=10,
438 cookie=0x0123456789abcdef,
439 packet_count=10,
440 byte_count=1000,
441 actions=[ofp.action.output(port=1),
442 ofp.action.output(port=2)]),
443 ofp.flow_stats_entry(table_id=4,
444 match=ofp.match(),
445 duration_sec=1,
446 duration_nsec=2,
447 priority=100,
448 idle_timeout=5,
449 hard_timeout=10,
450 cookie=0x0123456789abcdef,
451 packet_count=10,
452 byte_count=1000,
453 actions=[ofp.action.output(port=1),
454 ofp.action.output(port=2),
455 ofp.action.output(port=3)])])
456 self.assertEquals(self.sample_flow_stats_reply_buf, msg.pack())
457
458 def test_flow_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700459 msg = ofp.message.flow_stats_reply.unpack(self.sample_flow_stats_reply_buf)
460 self.assertEquals(ofp.OFPST_FLOW, msg.stats_type)
461 self.assertEquals(2, len(msg.entries))
462 self.assertEquals(2, len(msg.entries[0].actions))
463 self.assertEquals(3, len(msg.entries[1].actions))
464
465 def test_flow_add_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700466 expected = """\
467flow_add {
468 xid = None,
469 match = match_v1 {
470 wildcards = OFPFW_DL_SRC|OFPFW_DL_DST,
471 in_port = 3,
472 eth_src = 01:23:45:67:89:ab,
473 eth_dst = cd:ef:01:23:45:67,
474 vlan_vid = 0x0,
475 vlan_pcp = 0x0,
Rich Lanea06d0c32013-03-25 08:52:03 -0700476 eth_type = 0x0,
477 ip_dscp = 0x0,
478 ip_proto = 0x0,
Rich Lanea06d0c32013-03-25 08:52:03 -0700479 ipv4_src = 192.168.3.127,
480 ipv4_dst = 255.255.255.255,
481 tcp_src = 0x0,
482 tcp_dst = 0x0
483 },
484 cookie = 0x0,
485 idle_timeout = 0x0,
486 hard_timeout = 0x0,
487 priority = 0x0,
488 buffer_id = 0x0,
489 out_port = 0,
490 flags = 0x0,
491 actions = [
492 output { port = OFPP_FLOOD, max_len = 0x0 },
Rich Lanec2685792013-04-30 14:08:33 -0700493 nicira_dec_ttl { },
Rich Lanea06d0c32013-03-25 08:52:03 -0700494 bsn_set_tunnel_dst { dst = 0x0 }
495 ]
496}"""
497 msg = ofp.message.flow_add(
498 match=ofp.match(
499 wildcards=ofp.OFPFW_DL_SRC|ofp.OFPFW_DL_DST,
500 in_port=3,
501 ipv4_src=0xc0a8037f,
502 ipv4_dst=0xffffffff,
503 eth_src=[0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
504 eth_dst=[0xcd, 0xef, 0x01, 0x23, 0x45, 0x67]),
505 actions=[
506 ofp.action.output(port=ofp.OFPP_FLOOD),
507 ofp.action.nicira_dec_ttl(),
508 ofp.action.bsn_set_tunnel_dst()])
509 self.assertEquals(msg.show(), expected)
510
511 sample_packet_out_buf = ''.join([
512 '\x01', '\x0d', # version/type
513 '\x00\x23', # length
514 '\x12\x34\x56\x78', # xid
515 '\xab\xcd\xef\x01', # buffer_id
516 '\xff\xfe', # in_port
517 '\x00\x10', # actions_len
518 '\x00\x00', # actions[0].type
519 '\x00\x08', # actions[0].len
520 '\x00\x01', # actions[0].port
521 '\x00\x00', # actions[0].max_len
522 '\x00\x00', # actions[1].type
523 '\x00\x08', # actions[1].len
524 '\x00\x02', # actions[1].port
525 '\x00\x00', # actions[1].max_len
526 'abc' # data
527 ])
528
529 def test_packet_out_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700530 msg = ofp.message.packet_out(
531 xid=0x12345678,
532 buffer_id=0xabcdef01,
533 in_port=ofp.OFPP_LOCAL,
534 actions=[
535 ofp.action.output(port=1),
536 ofp.action.output(port=2)],
537 data='abc')
538 self.assertEquals(self.sample_packet_out_buf, msg.pack())
539
540 def test_packet_out_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700541 msg = ofp.message.packet_out.unpack(self.sample_packet_out_buf)
542 self.assertEquals(0x12345678, msg.xid)
543 self.assertEquals(0xabcdef01, msg.buffer_id)
544 self.assertEquals(ofp.OFPP_LOCAL, msg.in_port)
545 self.assertEquals(2, len(msg.actions))
546 self.assertEquals(1, msg.actions[0].port)
547 self.assertEquals(2, msg.actions[1].port)
548 self.assertEquals('abc', msg.data)
549
550 sample_packet_in_buf = ''.join([
551 '\x01', '\x0a', # version/type
552 '\x00\x15', # length
553 '\x12\x34\x56\x78', # xid
554 '\xab\xcd\xef\x01', # buffer_id
555 '\x00\x09', # total_len
556 '\xff\xfe', # in_port
557 '\x01', # reason
558 '\x00', # pad
559 'abc', # data
560 ])
561
562 def test_packet_in_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700563 msg = ofp.message.packet_in(
564 xid=0x12345678,
565 buffer_id=0xabcdef01,
566 total_len=9,
567 in_port=ofp.OFPP_LOCAL,
568 reason=ofp.OFPR_ACTION,
569 data='abc')
570 self.assertEquals(self.sample_packet_in_buf, msg.pack())
571
572 def test_packet_in_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700573 msg = ofp.message.packet_in.unpack(self.sample_packet_in_buf)
574 self.assertEquals(0x12345678, msg.xid)
575 self.assertEquals(0xabcdef01, msg.buffer_id)
576 self.assertEquals(9, msg.total_len)
577 self.assertEquals(ofp.OFPP_LOCAL, msg.in_port)
578 self.assertEquals(ofp.OFPR_ACTION, msg.reason)
579 self.assertEquals('abc', msg.data)
580
581 sample_queue_get_config_reply_buf = ''.join([
582 '\x01', '\x15', # version/type
583 '\x00\x50', # length
584 '\x12\x34\x56\x78', # xid
585 '\xff\xfe', # port
586 '\x00\x00\x00\x00\x00\x00', # pad
587 '\x00\x00\x00\x01', # queues[0].queue_id
588 '\x00\x18', # queues[0].len
589 '\x00\x00', # queues[0].pad
590 '\x00\x01', # queues[0].properties[0].type
591 '\x00\x10', # queues[0].properties[0].length
592 '\x00\x00\x00\x00', # queues[0].properties[0].pad
593 '\x00\x05', # queues[0].properties[0].rate
594 '\x00\x00\x00\x00\x00\x00', # queues[0].properties[0].pad2
595 '\x00\x00\x00\x02', # queues[1].queue_id
596 '\x00\x28', # queues[1].len
597 '\x00\x00', # queues[1].pad
598 '\x00\x01', # queues[1].properties[0].type
599 '\x00\x10', # queues[1].properties[0].length
600 '\x00\x00\x00\x00', # queues[1].properties[0].pad
601 '\x00\x06', # queues[1].properties[0].rate
602 '\x00\x00\x00\x00\x00\x00', # queues[1].properties[0].pad2
603 '\x00\x01', # queues[1].properties[1].type
604 '\x00\x10', # queues[1].properties[1].length
605 '\x00\x00\x00\x00', # queues[1].properties[1].pad
606 '\x00\x07', # queues[1].properties[1].rate
607 '\x00\x00\x00\x00\x00\x00', # queues[1].properties[1].pad2
608 ])
609
610 def test_queue_get_config_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700611 msg = ofp.message.queue_get_config_reply(
612 xid=0x12345678,
613 port=ofp.OFPP_LOCAL,
614 queues=[
615 ofp.packet_queue(queue_id=1, properties=[
616 ofp.queue_prop_min_rate(rate=5)]),
617 ofp.packet_queue(queue_id=2, properties=[
618 ofp.queue_prop_min_rate(rate=6),
619 ofp.queue_prop_min_rate(rate=7)])])
620 self.assertEquals(self.sample_queue_get_config_reply_buf, msg.pack())
621
622 def test_queue_get_config_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700623 msg = ofp.message.queue_get_config_reply.unpack(self.sample_queue_get_config_reply_buf)
624 self.assertEquals(ofp.OFPP_LOCAL, msg.port)
625 self.assertEquals(msg.queues[0].queue_id, 1)
626 self.assertEquals(msg.queues[0].properties[0].rate, 5)
627 self.assertEquals(msg.queues[1].queue_id, 2)
628 self.assertEquals(msg.queues[1].properties[0].rate, 6)
629 self.assertEquals(msg.queues[1].properties[1].rate, 7)
630
Rich Lane5c4446e2013-05-16 13:53:51 -0700631class TestDataFiles(unittest.TestCase):
632 pass
633
634add_datafiles_tests(TestDataFiles, 'of10/', ofp)
635
Rich Lanea06d0c32013-03-25 08:52:03 -0700636class TestParse(unittest.TestCase):
637 def test_parse_header(self):
638 import loxi
Rich Lanea06d0c32013-03-25 08:52:03 -0700639
640 msg_ver, msg_type, msg_len, msg_xid = ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56\x78")
641 self.assertEquals(1, msg_ver)
642 self.assertEquals(4, msg_type)
643 self.assertEquals(45032, msg_len)
644 self.assertEquals(0x12345678, msg_xid)
645
646 with self.assertRaisesRegexp(loxi.ProtocolError, "too short"):
647 ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56")
648
649 def test_parse_message(self):
650 import loxi
651 import loxi.of10 as ofp
652
653 buf = "\x01\x00\x00\x08\x12\x34\x56\x78"
654 msg = ofp.message.parse_message(buf)
655 assert(msg.xid == 0x12345678)
656
657 # Get a list of all message classes
658 test_klasses = [x for x in ofp.message.__dict__.values()
659 if type(x) == type
660 and issubclass(x, ofp.message.Message)
661 and x != ofp.message.Message]
662
663 for klass in test_klasses:
664 self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
665
666class TestUtils(unittest.TestCase):
Rich Lanea06d0c32013-03-25 08:52:03 -0700667 def test_pretty_wildcards(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700668 self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
669 self.assertEquals("0", ofp.util.pretty_wildcards(0))
670 self.assertEquals("OFPFW_DL_SRC|OFPFW_DL_DST",
671 ofp.util.pretty_wildcards(ofp.OFPFW_DL_SRC|ofp.OFPFW_DL_DST))
672 self.assertEquals("OFPFW_NW_SRC_MASK&0x2000",
673 ofp.util.pretty_wildcards(ofp.OFPFW_NW_SRC_ALL))
674 self.assertEquals("OFPFW_NW_SRC_MASK&0x1a00",
675 ofp.util.pretty_wildcards(0x00001a00))
676 self.assertEquals("OFPFW_IN_PORT|0x80000000",
677 ofp.util.pretty_wildcards(ofp.OFPFW_IN_PORT|0x80000000))
678
679class TestAll(unittest.TestCase):
680 """
681 Round-trips every class through serialization/deserialization.
682 Not a replacement for handcoded tests because it only uses the
683 default member values.
684 """
685
686 def setUp(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700687 mods = [ofp.action,ofp.message,ofp.common]
688 self.klasses = [klass for mod in mods
689 for klass in mod.__dict__.values()
690 if hasattr(klass, 'show')]
691 self.klasses.sort(key=lambda x: str(x))
692
693 def test_serialization(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700694 expected_failures = []
695 for klass in self.klasses:
696 def fn():
697 obj = klass()
698 if hasattr(obj, "xid"): obj.xid = 42
699 buf = obj.pack()
700 obj2 = klass.unpack(buf)
701 self.assertEquals(obj, obj2)
702 if klass in expected_failures:
703 self.assertRaises(Exception, fn)
704 else:
705 fn()
706
707 def test_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700708 expected_failures = []
709 for klass in self.klasses:
710 def fn():
711 obj = klass()
712 if hasattr(obj, "xid"): obj.xid = 42
713 obj.show()
714 if klass in expected_failures:
715 self.assertRaises(Exception, fn)
716 else:
717 fn()
718
719if __name__ == '__main__':
720 unittest.main()