blob: 6565bb8f08bf235808e910d237bc37154d5d49bd [file] [log] [blame]
Dan Talaycof75360a2010-02-05 22:22:54 -08001#!/usr/bin/python
2#
3# This python script generates wrapper functions for OpenFlow messages
4#
5# See the doc string below for more info
6#
7
8# To do:
9# Default type values for messages
10# Generate all message objects
11# Action list objects?
12# Autogen lengths when possible
13# Dictionaries for enum strings
14# Resolve sub struct initializers (see ofp_flow_mod)
15
16
17"""
18Generate wrapper classes for OpenFlow messages
19
20(C) Copyright Stanford University
21Date February 2010
22Created by dtalayco
23
24Attempting to follow http://www.python.org/dev/peps/pep-0008/
25The main exception is that our class names do not use CamelCase
26so as to more closely match the original C code names.
27
28This file is meant to generate a file of_wrapper.py which imports
29the base classes generated form automatic processing of openflow.h
30and produces wrapper classes for each OpenFlow message type.
31
32This file will normally be included in of_message.py which provides
33additional hand-generated work.
34
35There are two types of structures/classes here: base components and
36message classes.
37
38Base components are the base data classes which are fixed
39length structures including:
40 ofp_header
41 Each ofp_action structure
42 ofp_phy_port
43 The array elements of all the stats reply messages
44The base components are to be imported from a file of_header.py.
45
46Message classes define a complete message on the wire. These are
47comprised of possibly variable length lists of possibly variably
48typed objects from the base component list above.
49
50Each OpenFlow message has a header and zero or more fixed length
51members (the "core members" of the class) followed by zero or more
52variable length lists.
53
54The wrapper classes should live in their own name space, probably
55of_message. Automatically generated base component and skeletons for
56the message classes are assumed generated and the wrapper classes
57will inherit from those.
58
59Every message class must implement pack and unpack functions to
60convert between the class and a string representing what goes on the
61wire.
62
63For unpacking, the low level (base-component) classes must implement
64their own unpack functions. A single top level unpack function
65will do the parsing and call the lower layer unpack functions as
66appropriate.
67
68Every base and message class should implement a show function to
69(recursively) display the contents of the object.
70
71Certain OpenFlow message types are further subclassed. These include
72stats_request, stats_reply and error.
73
74"""
75
76# Don't generate header object in messages
77# Map each message to a body that doesn't include the header
78# The body has does not include variable length info at the end
79
80import re
81import string
82import sys
Rich Lane6242d9f2013-01-06 17:35:39 -080083sys.path.append("../../src/python/of10")
Dan Talaycob9cb5482010-02-09 15:23:12 -080084from cstruct import *
85from class_maps import class_to_members_map
Dan Talaycof75360a2010-02-05 22:22:54 -080086
87message_top_matter = """
88# Python OpenFlow message wrapper classes
89
Rich Lanef6883512013-03-11 17:00:09 -070090import logging
Dan Talaycob9cb5482010-02-09 15:23:12 -080091from cstruct import *
Dan Talaycof75360a2010-02-05 22:22:54 -080092from action_list import action_list
Dan Talayco411489d2010-02-12 23:03:46 -080093from error import *
Dan Talaycof75360a2010-02-05 22:22:54 -080094
Dan Talaycof75360a2010-02-05 22:22:54 -080095# Define templates for documentation
96class ofp_template_msg:
97 \"""
98 Sample base class for template_msg; normally auto generated
99 This class should live in the of_header name space and provides the
100 base class for this type of message. It will be wrapped for the
101 high level API.
102
103 \"""
104 def __init__(self):
105 \"""
106 Constructor for base class
107
108 \"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800109 # Additional base data members declared here
Dan Talaycoac1cb812010-02-06 20:34:18 -0800110
Dan Talaycof75360a2010-02-05 22:22:54 -0800111 # Normally will define pack, unpack, __len__ functions
112
113class template_msg(ofp_template_msg):
114 \"""
115 Sample class wrapper for template_msg
116 This class should live in the of_message name space and provides the
117 high level API for an OpenFlow message object. These objects must
118 implement the functions indicated in this template.
119
120 \"""
121 def __init__(self):
122 \"""
123 Constructor
124 Must set the header type value appropriately for the message
125
126 \"""
Dan Talaycoac1cb812010-02-06 20:34:18 -0800127
128 ##@var header
129 # OpenFlow message header: length, version, xid, type
Dan Talaycof75360a2010-02-05 22:22:54 -0800130 ofp_template_msg.__init__(self)
131 # For a real message, will be set to an integer
Rich Laneb73808c2013-03-11 15:22:23 -0700132 self.type = "TEMPLATE_MSG_VALUE"
Dan Talaycof75360a2010-02-05 22:22:54 -0800133 def pack(self):
134 \"""
135 Pack object into string
136
137 @return The packed string which can go on the wire
138
139 \"""
140 pass
141 def unpack(self, binary_string):
142 \"""
143 Unpack object from a binary string
144
145 @param binary_string The wire protocol byte string holding the object
146 represented as an array of bytes.
147
148 @return Typically returns the remainder of binary_string that
149 was not parsed. May give a warning if that string is non-empty
150
151 \"""
152 pass
153 def __len__(self):
154 \"""
155 Return the length of this object once packed into a string
156
157 @return An integer representing the number bytes in the packed
158 string.
159
160 \"""
161 pass
162 def show(self, prefix=''):
163 \"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800164 Generate a string (with multiple lines) describing the contents
165 of the object in a readable manner
Dan Talaycof75360a2010-02-05 22:22:54 -0800166
Dan Talayco46755fa2010-03-09 21:44:29 -0800167 @param prefix Pre-pended at the beginning of each line.
Dan Talaycof75360a2010-02-05 22:22:54 -0800168
169 \"""
170 pass
171 def __eq__(self, other):
172 \"""
173 Return True if self and other hold the same data
174
175 @param other Other object in comparison
176
177 \"""
178 pass
179 def __ne__(self, other):
180 \"""
181 Return True if self and other do not hold the same data
182
183 @param other Other object in comparison
184
185 \"""
186 pass
187"""
188
189# Dictionary mapping wrapped classes to the auto-generated structure
190# underlieing the class (body only, not header or var-length data)
191message_class_map = {
192 "hello" : "ofp_header",
193 "error" : "ofp_error_msg",
194 "echo_request" : "ofp_header",
195 "echo_reply" : "ofp_header",
196 "vendor" : "ofp_vendor_header",
197 "features_request" : "ofp_header",
198 "features_reply" : "ofp_switch_features",
199 "get_config_request" : "ofp_header",
200 "get_config_reply" : "ofp_switch_config",
201 "set_config" : "ofp_switch_config",
202 "packet_in" : "ofp_packet_in",
203 "flow_removed" : "ofp_flow_removed",
204 "port_status" : "ofp_port_status",
205 "packet_out" : "ofp_packet_out",
206 "flow_mod" : "ofp_flow_mod",
207 "port_mod" : "ofp_port_mod",
208 "stats_request" : "ofp_stats_request",
209 "stats_reply" : "ofp_stats_reply",
210 "barrier_request" : "ofp_header",
211 "barrier_reply" : "ofp_header",
212 "queue_get_config_request" : "ofp_queue_get_config_request",
213 "queue_get_config_reply" : "ofp_queue_get_config_reply"
214}
215
216# These messages have a string member at the end of the data
217string_members = [
218 "hello",
219 "error",
220 "echo_request",
221 "echo_reply",
222 "vendor",
223 "packet_in",
224 "packet_out"
225]
226
227# These messages have a list (with the given name) in the data,
228# after the core members; the type is given for validation
229list_members = {
230 "features_reply" : ('ports', None),
231 "packet_out" : ('actions', 'action_list'),
232 "flow_mod" : ('actions', 'action_list'),
233 "queue_get_config_reply" : ('queues', None)
234}
235
236_ind = " "
237
238def _p1(s): print _ind + s
239def _p2(s): print _ind * 2 + s
240def _p3(s): print _ind * 3 + s
241def _p4(s): print _ind * 4 + s
242
243# Okay, this gets kind of ugly:
244# There are three variables:
245# has_core_members: If parent class is not ofp_header, has inheritance
246# has_list: Whether class has trailing array or class
247# has_string: Whether class has trailing string
248
249def gen_message_wrapper(msg):
250 """
251 Generate a wrapper for the given message based on above info
252 @param msg String identifying the message name for the class
253 """
254
255 msg_name = "OFPT_" + msg.upper()
256 parent = message_class_map[msg]
257
258 has_list = False # Has trailing list
Rich Laneb73808c2013-03-11 15:22:23 -0700259 has_core_members = True
Dan Talaycof75360a2010-02-05 22:22:54 -0800260 has_string = False # Has trailing string
Dan Talaycof75360a2010-02-05 22:22:54 -0800261 if msg in list_members.keys():
262 (list_var, list_type) = list_members[msg]
263 has_list = True
264 if msg in string_members:
265 has_string = True
266
267 if has_core_members:
268 print "class " + msg + "(" + parent + "):"
269 else:
270 print "class " + msg + ":"
271 _p1('"""')
272 _p1("Wrapper class for " + msg)
273 print
Dan Talaycoac1cb812010-02-06 20:34:18 -0800274 _p1("OpenFlow message header: length, version, xid, type")
275 _p1("@arg length: The total length of the message")
276 _p1("@arg version: The OpenFlow version (" + str(OFP_VERSION) + ")")
277 _p1("@arg xid: The transaction ID")
278 _p1("@arg type: The message type (" + msg_name + "=" +
279 str(eval(msg_name)) + ")")
280 print
281 if has_core_members and parent in class_to_members_map.keys():
282 _p1("Data members inherited from " + parent + ":")
283 for var in class_to_members_map[parent]:
284 _p1("@arg " + var)
Dan Talaycof75360a2010-02-05 22:22:54 -0800285 if has_list:
286 if list_type == None:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800287 _p1("@arg " + list_var + ": Variable length array of TBD")
Dan Talaycof75360a2010-02-05 22:22:54 -0800288 else:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800289 _p1("@arg " + list_var + ": Object of type " + list_type);
Dan Talaycof75360a2010-02-05 22:22:54 -0800290 if has_string:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800291 _p1("@arg data: Binary string following message members")
292 print
Dan Talaycof75360a2010-02-05 22:22:54 -0800293 _p1('"""')
294
295 print
Rich Lanec3c2ae12013-01-04 10:13:17 -0800296 _p1("def __init__(self, **kwargs):")
Dan Talaycof75360a2010-02-05 22:22:54 -0800297 if has_core_members:
298 _p2(parent + ".__init__(self)")
Rich Laneb73808c2013-03-11 15:22:23 -0700299 _p2("self.version = OFP_VERSION")
300 _p2("self.type = " + msg_name)
Rich Lane8fbfd662013-03-11 15:30:44 -0700301 _p2("self.xid = None")
Dan Talaycof75360a2010-02-05 22:22:54 -0800302 if has_list:
Rich Lane0f0adc92013-01-04 15:13:02 -0800303 _p2('self.' + list_var + ' = []')
Dan Talaycof75360a2010-02-05 22:22:54 -0800304 if has_string:
305 _p2('self.data = ""')
Rich Lanec3c2ae12013-01-04 10:13:17 -0800306 _p2('for (k, v) in kwargs.items():')
307 _p3('if hasattr(self, k):')
308 _p4('setattr(self, k, v)')
309 _p3('else:')
310 _p4('raise NameError("field %s does not exist in %s" % (k, self.__class__))')
Dan Talaycof75360a2010-02-05 22:22:54 -0800311
Dan Talaycob9cb5482010-02-09 15:23:12 -0800312 print """
313
314 def pack(self):
315 \"""
316 Pack object into string
317
318 @return The packed string which can go on the wire
319
320 \"""
Rich Laneb73808c2013-03-11 15:22:23 -0700321 self.length = len(self)
322 packed = ""
Dan Talaycob9cb5482010-02-09 15:23:12 -0800323"""
324
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800325 # Have to special case the action length calculation for pkt out
326 if msg == 'packet_out':
Rich Lane62e96852013-03-11 12:04:45 -0700327 _p2('self.actions_len = 0')
328 _p2('for obj in self.actions:')
329 _p3('self.actions_len += len(obj)')
Dan Talaycof75360a2010-02-05 22:22:54 -0800330 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800331 _p2("packed += " + parent + ".pack(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800332 if has_list:
333 if list_type == None:
334 _p2('for obj in self.' + list_var + ':')
335 _p3('packed += obj.pack()')
336 else:
Rich Lane62e96852013-03-11 12:04:45 -0700337 _p2('packed += ' + list_type + '(self.' + list_var + ').pack()')
Dan Talaycof75360a2010-02-05 22:22:54 -0800338 if has_string:
339 _p2('packed += self.data')
Dan Talayco6d2470b2010-02-07 22:59:49 -0800340 _p2("return packed")
Dan Talaycof75360a2010-02-05 22:22:54 -0800341
Dan Talaycob9cb5482010-02-09 15:23:12 -0800342 print """
343 def unpack(self, binary_string):
344 \"""
345 Unpack object from a binary string
346
347 @param binary_string The wire protocol byte string holding the object
348 represented as an array of bytes.
Dan Talayco411489d2010-02-12 23:03:46 -0800349 @return The remainder of binary_string that was not parsed.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800350
351 \"""
Dan Talaycob9cb5482010-02-09 15:23:12 -0800352"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800353 if has_core_members:
354 _p2("binary_string = " + parent + ".unpack(self, binary_string)")
355 if has_list:
Dan Talaycoff606492010-05-13 14:22:37 -0700356 if msg == "features_reply": # Special case port parsing
357 # For now, cheat and assume the rest of the message is port list
358 _p2("while len(binary_string) >= OFP_PHY_PORT_BYTES:")
359 _p3("new_port = ofp_phy_port()")
360 _p3("binary_string = new_port.unpack(binary_string)")
361 _p3("self.ports.append(new_port)")
362 elif list_type == None:
Dan Talaycof75360a2010-02-05 22:22:54 -0800363 _p2("for obj in self." + list_var + ":")
364 _p3("binary_string = obj.unpack(binary_string)")
365 elif msg == "packet_out": # Special case this
Rich Lane62e96852013-03-11 12:04:45 -0700366 _p2("obj = action_list()")
367 _p2('binary_string = obj.unpack(' +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800368 'binary_string, bytes=self.actions_len)')
Rich Lane62e96852013-03-11 12:04:45 -0700369 _p2("self.actions = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800370 elif msg == "flow_mod": # Special case this
Rich Laneb73808c2013-03-11 15:22:23 -0700371 _p2("ai_len = self.length - (OFP_FLOW_MOD_BYTES + " +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800372 "OFP_HEADER_BYTES)")
Rich Lane62e96852013-03-11 12:04:45 -0700373 _p2("obj = action_list()")
374 _p2("binary_string = obj.unpack(binary_string, " +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800375 "bytes=ai_len)")
Rich Lane62e96852013-03-11 12:04:45 -0700376 _p2("self.actions = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800377 else:
Rich Lane62e96852013-03-11 12:04:45 -0700378 _p2("obj = " + list_type + "()")
379 _p2("binary_string = obj.unpack(binary_string)")
380 _p2("self." + list_var + " = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800381 if has_string:
382 _p2("self.data = binary_string")
383 _p2("binary_string = ''")
384 else:
385 _p2("# Fixme: If no self.data, add check for data remaining")
386 _p2("return binary_string")
387
Dan Talaycob9cb5482010-02-09 15:23:12 -0800388 print """
389 def __len__(self):
390 \"""
391 Return the length of this object once packed into a string
392
393 @return An integer representing the number bytes in the packed
394 string.
395
396 \"""
Rich Laneb73808c2013-03-11 15:22:23 -0700397 length = 0
Dan Talaycob9cb5482010-02-09 15:23:12 -0800398"""
Dan Talayco6d2470b2010-02-07 22:59:49 -0800399 if has_core_members:
400 _p2("length += " + parent + ".__len__(self)")
401 if has_list:
Rich Lane62e96852013-03-11 12:04:45 -0700402 _p2("for obj in self." + list_var + ":")
403 _p3("length += len(obj)")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800404 if has_string:
405 _p2("length += len(self.data)")
406 _p2("return length")
Dan Talaycof75360a2010-02-05 22:22:54 -0800407
Dan Talaycob9cb5482010-02-09 15:23:12 -0800408 print """
Dan Talaycob9cb5482010-02-09 15:23:12 -0800409 def show(self, prefix=''):
410 \"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800411 Generate a string (with multiple lines) describing the contents
412 of the object in a readable manner
Dan Talaycob9cb5482010-02-09 15:23:12 -0800413
Dan Talayco46755fa2010-03-09 21:44:29 -0800414 @param prefix Pre-pended at the beginning of each line.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800415
416 \"""
417"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800418 _p2("outstr = prefix + '" + msg + " (" + msg_name + ")\\n'")
Dan Talaycof75360a2010-02-05 22:22:54 -0800419 _p2("prefix += ' '")
Dan Talayco46755fa2010-03-09 21:44:29 -0800420 _p2("outstr += prefix + 'ofp header\\n'")
Dan Talaycof75360a2010-02-05 22:22:54 -0800421 if has_core_members:
Dan Talayco46755fa2010-03-09 21:44:29 -0800422 _p2("outstr += " + parent + ".show(self, prefix)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800423 if has_list:
424 if list_type == None:
Dan Talayco46755fa2010-03-09 21:44:29 -0800425 _p2('outstr += prefix + "Array ' + list_var + '\\n"')
Dan Talaycof75360a2010-02-05 22:22:54 -0800426 _p2('for obj in self.' + list_var +':')
Dan Talayco46755fa2010-03-09 21:44:29 -0800427 _p3("outstr += obj.show(prefix + ' ')")
Dan Talaycof75360a2010-02-05 22:22:54 -0800428 else:
Dan Talayco46755fa2010-03-09 21:44:29 -0800429 _p2('outstr += prefix + "List ' + list_var + '\\n"')
Rich Lanee6ea3fe2013-03-08 17:54:38 -0800430 _p2('for obj in self.' + list_var + ':')
431 _p3('outstr += obj.show(prefix + " ")')
Dan Talaycof75360a2010-02-05 22:22:54 -0800432 if has_string:
Dan Talayco46755fa2010-03-09 21:44:29 -0800433 _p2("outstr += prefix + 'data is of length ' + str(len(self.data)) + '\\n'")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800434 _p2("##@todo Fix this circular reference")
435 _p2("# if len(self.data) > 0:")
436 _p3("# obj = of_message_parse(self.data)")
437 _p3("# if obj != None:")
Dan Talayco46755fa2010-03-09 21:44:29 -0800438 _p4("# outstr += obj.show(prefix)")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800439 _p3("# else:")
Dan Talayco46755fa2010-03-09 21:44:29 -0800440 _p4('# outstr += prefix + "Unable to parse data\\n"')
441 _p2('return outstr')
Dan Talaycof75360a2010-02-05 22:22:54 -0800442
Dan Talaycob9cb5482010-02-09 15:23:12 -0800443 print """
444 def __eq__(self, other):
445 \"""
446 Return True if self and other hold the same data
447
448 @param other Other object in comparison
449
450 \"""
451 if type(self) != type(other): return False
Dan Talaycob9cb5482010-02-09 15:23:12 -0800452"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800453 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800454 _p2("if not " + parent + ".__eq__(self, other): return False")
Dan Talaycof75360a2010-02-05 22:22:54 -0800455 if has_string:
456 _p2("if self.data != other.data: return False")
457 if has_list:
458 _p2("if self." + list_var + " != other." + list_var + ": return False")
459 _p2("return True")
460
Dan Talaycob9cb5482010-02-09 15:23:12 -0800461 print """
462 def __ne__(self, other):
463 \"""
464 Return True if self and other do not hold the same data
Dan Talaycof75360a2010-02-05 22:22:54 -0800465
Dan Talaycob9cb5482010-02-09 15:23:12 -0800466 @param other Other object in comparison
467
468 \"""
469 return not self.__eq__(other)
470 """
Dan Talaycof75360a2010-02-05 22:22:54 -0800471
472
473################################################################
474#
475# Stats request subclasses
476# description_request, flow, aggregate, table, port, vendor
477#
478################################################################
479
480# table and desc stats requests are special with empty body
481extra_ofp_stats_req_defs = """
482# Stats request bodies for desc and table stats are not defined in the
483# OpenFlow header; We define them here. They are empty classes, really
484
485class ofp_desc_stats_request:
486 \"""
487 Forced definition of ofp_desc_stats_request (empty class)
488 \"""
489 def __init__(self):
490 pass
491 def pack(self, assertstruct=True):
492 return ""
493 def unpack(self, binary_string):
494 return binary_string
495 def __len__(self):
496 return 0
497 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800498 return prefix + "ofp_desc_stats_request (empty)\\n"
Dan Talaycof75360a2010-02-05 22:22:54 -0800499 def __eq__(self, other):
500 return type(self) == type(other)
501 def __ne__(self, other):
502 return type(self) != type(other)
503
504OFP_DESC_STATS_REQUEST_BYTES = 0
505
506class ofp_table_stats_request:
507 \"""
508 Forced definition of ofp_table_stats_request (empty class)
509 \"""
510 def __init__(self):
511 pass
512 def pack(self, assertstruct=True):
513 return ""
514 def unpack(self, binary_string):
515 return binary_string
516 def __len__(self):
517 return 0
518 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800519 return prefix + "ofp_table_stats_request (empty)\\n"
Dan Talaycof75360a2010-02-05 22:22:54 -0800520 def __eq__(self, other):
521 return type(self) == type(other)
522 def __ne__(self, other):
523 return type(self) != type(other)
524
525OFP_TABLE_STATS_REQUEST_BYTES = 0
526
527"""
528
529stats_request_template = """
530class --TYPE--_stats_request(ofp_stats_request, ofp_--TYPE--_stats_request):
531 \"""
532 Wrapper class for --TYPE-- stats request message
533 \"""
Rich Lanec3c2ae12013-01-04 10:13:17 -0800534 def __init__(self, **kwargs):
Dan Talaycof75360a2010-02-05 22:22:54 -0800535 ofp_stats_request.__init__(self)
536 ofp_--TYPE--_stats_request.__init__(self)
Rich Laneb73808c2013-03-11 15:22:23 -0700537 self.version = OFP_VERSION
538 self.type = OFPT_STATS_REQUEST
Rich Lane8fbfd662013-03-11 15:30:44 -0700539 self.xid = None
Rich Lane7c7342a2013-03-11 14:16:58 -0700540 self.stats_type = --STATS_NAME--
Rich Lanec3c2ae12013-01-04 10:13:17 -0800541 for (k, v) in kwargs.items():
542 if hasattr(self, k):
543 setattr(self, k, v)
544 else:
545 raise NameError("field %s does not exist in %s" % (k, self.__class__))
Dan Talaycof75360a2010-02-05 22:22:54 -0800546
547 def pack(self, assertstruct=True):
Rich Laneb73808c2013-03-11 15:22:23 -0700548 self.length = len(self)
549 packed = ""
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800550 packed += ofp_stats_request.pack(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800551 packed += ofp_--TYPE--_stats_request.pack(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800552 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800553
554 def unpack(self, binary_string):
555 binary_string = ofp_stats_request.unpack(self, binary_string)
556 binary_string = ofp_--TYPE--_stats_request.unpack(self, binary_string)
557 if len(binary_string) != 0:
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800558 print "ERROR unpacking --TYPE--: extra data"
Dan Talaycof75360a2010-02-05 22:22:54 -0800559 return binary_string
560
561 def __len__(self):
Rich Laneb73808c2013-03-11 15:22:23 -0700562 return OFP_STATS_REQUEST_BYTES + \\
Dan Talaycof75360a2010-02-05 22:22:54 -0800563 OFP_--TYPE_UPPER--_STATS_REQUEST_BYTES
564
565 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800566 outstr = prefix + "--TYPE--_stats_request\\n"
567 outstr += prefix + "ofp header:\\n"
Dan Talayco46755fa2010-03-09 21:44:29 -0800568 outstr += ofp_stats_request.show(self)
569 outstr += ofp_--TYPE--_stats_request.show(self)
570 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800571
572 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800573 if type(self) != type(other): return False
Rich Laneb73808c2013-03-11 15:22:23 -0700574 return (ofp_stats_request.__eq__(self, other) and
Dan Talaycof75360a2010-02-05 22:22:54 -0800575 ofp_--TYPE--_stats_request.__eq__(self, other))
576
577 def __ne__(self, other): return not self.__eq__(other)
578"""
579
580################################################################
581#
582# Stats replies always have an array at the end.
583# For aggregate and desc, these arrays are always of length 1
584# This array is always called stats
585#
586################################################################
587
588
589# Template for objects stats reply messages
590stats_reply_template = """
591class --TYPE--_stats_reply(ofp_stats_reply):
592 \"""
593 Wrapper class for --TYPE-- stats reply
594 \"""
595 def __init__(self):
Dan Talaycof75360a2010-02-05 22:22:54 -0800596 ofp_stats_reply.__init__(self)
Rich Laneb73808c2013-03-11 15:22:23 -0700597 self.version = OFP_VERSION
598 self.type = OFPT_STATS_REPLY
Rich Lane8fbfd662013-03-11 15:30:44 -0700599 self.xid = None
Rich Laneb73808c2013-03-11 15:22:23 -0700600 self.stats_type = --STATS_NAME--
Dan Talaycof75360a2010-02-05 22:22:54 -0800601 # stats: Array of type --TYPE--_stats_entry
Rich Lane5fd6faf2013-03-11 13:30:20 -0700602 self.entries = []
Dan Talaycof75360a2010-02-05 22:22:54 -0800603
604 def pack(self, assertstruct=True):
Rich Laneb73808c2013-03-11 15:22:23 -0700605 self.length = len(self)
606 packed = ""
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800607 packed += ofp_stats_reply.pack(self)
Rich Lane5fd6faf2013-03-11 13:30:20 -0700608 for obj in self.entries:
Dan Talaycof75360a2010-02-05 22:22:54 -0800609 packed += obj.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800610 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800611
612 def unpack(self, binary_string):
613 binary_string = ofp_stats_reply.unpack(self, binary_string)
614 dummy = --TYPE--_stats_entry()
615 while len(binary_string) >= len(dummy):
616 obj = --TYPE--_stats_entry()
617 binary_string = obj.unpack(binary_string)
Rich Lane5fd6faf2013-03-11 13:30:20 -0700618 self.entries.append(obj)
Dan Talaycof75360a2010-02-05 22:22:54 -0800619 if len(binary_string) != 0:
620 print "ERROR unpacking --TYPE-- stats string: extra bytes"
621 return binary_string
622
623 def __len__(self):
Rich Laneb73808c2013-03-11 15:22:23 -0700624 length = OFP_STATS_REPLY_BYTES
Rich Lane5fd6faf2013-03-11 13:30:20 -0700625 for obj in self.entries:
Dan Talaycof75360a2010-02-05 22:22:54 -0800626 length += len(obj)
627 return length
628
629 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800630 outstr = prefix + "--TYPE--_stats_reply\\n"
631 outstr += prefix + "ofp header:\\n"
Dan Talayco46755fa2010-03-09 21:44:29 -0800632 outstr += ofp_stats_reply.show(self)
Rich Lane5fd6faf2013-03-11 13:30:20 -0700633 outstr += prefix + "Stats array of length " + str(len(self.entries)) + '\\n'
634 for obj in self.entries:
Dan Talayco46755fa2010-03-09 21:44:29 -0800635 outstr += obj.show()
636 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800637
638 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800639 if type(self) != type(other): return False
Rich Laneb73808c2013-03-11 15:22:23 -0700640 return (ofp_stats_reply.__eq__(self, other) and
Rich Lane5fd6faf2013-03-11 13:30:20 -0700641 self.entries == other.entries)
Dan Talaycof75360a2010-02-05 22:22:54 -0800642
643 def __ne__(self, other): return not self.__eq__(other)
644"""
645
646#
647# To address variations in stats reply bodies, the following
648# "_entry" classes are defined for each element in the reply
649#
650
651extra_stats_entry_defs = """
652# Stats entries define the content of one element in a stats
653# reply for the indicated type; define _entry for consistency
654
655aggregate_stats_entry = ofp_aggregate_stats_reply
656desc_stats_entry = ofp_desc_stats
657port_stats_entry = ofp_port_stats
658queue_stats_entry = ofp_queue_stats
659table_stats_entry = ofp_table_stats
660"""
661
662# Special case flow_stats to handle actions_list
663
664flow_stats_entry_def = """
665#
666# Flow stats entry contains an action list of variable length, so
667# it is done by hand
668#
669
670class flow_stats_entry(ofp_flow_stats):
671 \"""
672 Special case flow stats entry to handle action list object
673 \"""
674 def __init__(self):
675 ofp_flow_stats.__init__(self)
Rich Lane62e96852013-03-11 12:04:45 -0700676 self.actions = []
Dan Talaycof75360a2010-02-05 22:22:54 -0800677
678 def pack(self, assertstruct=True):
Dan Talayco6d2470b2010-02-07 22:59:49 -0800679 self.length = len(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800680 packed = ofp_flow_stats.pack(self, assertstruct)
Rich Lane62e96852013-03-11 12:04:45 -0700681 packed += action_list(self.actions).pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800682 if len(packed) != self.length:
683 print("ERROR: flow_stats_entry pack length not equal",
684 self.length, len(packed))
Dan Talaycof75360a2010-02-05 22:22:54 -0800685 return packed
686
687 def unpack(self, binary_string):
688 binary_string = ofp_flow_stats.unpack(self, binary_string)
689 ai_len = self.length - OFP_FLOW_STATS_BYTES
Dan Talayco6d2470b2010-02-07 22:59:49 -0800690 if ai_len < 0:
691 print("ERROR: flow_stats_entry unpack length too small",
692 self.length)
Rich Lane62e96852013-03-11 12:04:45 -0700693 obj = action_list()
694 binary_string = obj.unpack(binary_string, bytes=ai_len)
695 self.actions = list(obj)
Dan Talaycof75360a2010-02-05 22:22:54 -0800696 return binary_string
697
698 def __len__(self):
699 return OFP_FLOW_STATS_BYTES + len(self.actions)
700
701 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800702 outstr = prefix + "flow_stats_entry\\n"
703 outstr += ofp_flow_stats.show(self, prefix + ' ')
Rich Lanee6ea3fe2013-03-08 17:54:38 -0800704 outstr += prefix + "List actions\\n"
705 for obj in self.actions:
706 outstr += obj.show(prefix + ' ')
Dan Talayco46755fa2010-03-09 21:44:29 -0800707 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800708
709 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800710 if type(self) != type(other): return False
Dan Talaycof75360a2010-02-05 22:22:54 -0800711 return (ofp_flow_stats.__eq__(self, other) and
712 self.actions == other.actions)
713
714 def __ne__(self, other): return not self.__eq__(other)
715"""
716
717stats_types = [
718 'aggregate',
719 'desc',
720 'flow',
721 'port',
722 'queue',
723 'table']
724
725if __name__ == '__main__':
726
727 print message_top_matter
728
729 print """
730################################################################
731#
732# OpenFlow Message Definitions
733#
734################################################################
735"""
736
737 msg_types = message_class_map.keys()
738 msg_types.sort()
739
740 for t in msg_types:
741 gen_message_wrapper(t)
742 print
743
744 print """
745################################################################
746#
747# Stats request and reply subclass definitions
748#
749################################################################
750"""
751
752 print extra_ofp_stats_req_defs
753 print extra_stats_entry_defs
754 print flow_stats_entry_def
755
756 # Generate stats request and reply subclasses
757 for t in stats_types:
758 stats_name = "OFPST_" + t.upper()
759 to_print = re.sub('--TYPE--', t, stats_request_template)
760 to_print = re.sub('--TYPE_UPPER--', t.upper(), to_print)
761 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
762 print to_print
763 to_print = re.sub('--TYPE--', t, stats_reply_template)
764 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
765 print to_print
766
Dan Talayco411489d2010-02-12 23:03:46 -0800767 # Lastly, generate a tuple containing all the message classes
768 print """
769message_type_list = (
770 aggregate_stats_reply,
771 aggregate_stats_request,
772 bad_action_error_msg,
773 bad_request_error_msg,
774 barrier_reply,
775 barrier_request,
776 desc_stats_reply,
777 desc_stats_request,
778 echo_reply,
779 echo_request,
780 features_reply,
781 features_request,
782 flow_mod,
783 flow_mod_failed_error_msg,
784 flow_removed,
785 flow_stats_reply,
786 flow_stats_request,
787 get_config_reply,
788 get_config_request,
789 hello,
790 hello_failed_error_msg,
791 packet_in,
792 packet_out,
793 port_mod,
794 port_mod_failed_error_msg,
795 port_stats_reply,
796 port_stats_request,
797 port_status,
798 queue_get_config_reply,
799 queue_get_config_request,
800 queue_op_failed_error_msg,
801 queue_stats_reply,
802 queue_stats_request,
803 set_config,
804 table_stats_reply,
805 table_stats_request,
806 vendor
807 )
808"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800809
Rich Laneba3f0e22013-03-11 16:43:57 -0700810 print """
811_flow_mod = flow_mod
812flow_mod = None
813
814class flow_add(_flow_mod):
815 def __init__(self, **kwargs):
816 _flow_mod.__init__(self, **kwargs)
817 self.command = OFPFC_ADD
818
819class flow_modify(_flow_mod):
820 def __init__(self, **kwargs):
821 _flow_mod.__init__(self, **kwargs)
822 self.command = OFPFC_MODIFY
823
824class flow_modify_strict(_flow_mod):
825 def __init__(self, **kwargs):
826 _flow_mod.__init__(self, **kwargs)
827 self.command = OFPFC_MODIFY_STRICT
828
829class flow_delete(_flow_mod):
830 def __init__(self, **kwargs):
831 _flow_mod.__init__(self, **kwargs)
832 self.command = OFPFC_DELETE
833
834class flow_delete_strict(_flow_mod):
835 def __init__(self, **kwargs):
836 _flow_mod.__init__(self, **kwargs)
837 self.command = OFPFC_DELETE_STRICT
Rich Lanef6883512013-03-11 17:00:09 -0700838
839# These message types are subclassed
840msg_type_subclassed = [
841 OFPT_STATS_REQUEST,
842 OFPT_STATS_REPLY,
843 OFPT_ERROR
844]
845
846# Maps from sub-types to classes
847stats_reply_to_class_map = {
848 OFPST_DESC : desc_stats_reply,
849 OFPST_AGGREGATE : aggregate_stats_reply,
850 OFPST_FLOW : flow_stats_reply,
851 OFPST_TABLE : table_stats_reply,
852 OFPST_PORT : port_stats_reply,
853 OFPST_QUEUE : queue_stats_reply
854}
855
856stats_request_to_class_map = {
857 OFPST_DESC : desc_stats_request,
858 OFPST_AGGREGATE : aggregate_stats_request,
859 OFPST_FLOW : flow_stats_request,
860 OFPST_TABLE : table_stats_request,
861 OFPST_PORT : port_stats_request,
862 OFPST_QUEUE : queue_stats_request
863}
864
865error_to_class_map = {
866 OFPET_HELLO_FAILED : hello_failed_error_msg,
867 OFPET_BAD_REQUEST : bad_request_error_msg,
868 OFPET_BAD_ACTION : bad_action_error_msg,
869 OFPET_FLOW_MOD_FAILED : flow_mod_failed_error_msg,
870 OFPET_PORT_MOD_FAILED : port_mod_failed_error_msg,
871 OFPET_QUEUE_OP_FAILED : queue_op_failed_error_msg
872}
873
874# Map from header type value to the underlieing message class
875msg_type_to_class_map = {
876 OFPT_HELLO : hello,
877 OFPT_ERROR : error,
878 OFPT_ECHO_REQUEST : echo_request,
879 OFPT_ECHO_REPLY : echo_reply,
880 OFPT_VENDOR : vendor,
881 OFPT_FEATURES_REQUEST : features_request,
882 OFPT_FEATURES_REPLY : features_reply,
883 OFPT_GET_CONFIG_REQUEST : get_config_request,
884 OFPT_GET_CONFIG_REPLY : get_config_reply,
885 OFPT_SET_CONFIG : set_config,
886 OFPT_PACKET_IN : packet_in,
887 OFPT_FLOW_REMOVED : flow_removed,
888 OFPT_PORT_STATUS : port_status,
889 OFPT_PACKET_OUT : packet_out,
890 OFPT_FLOW_MOD : flow_mod,
891 OFPT_PORT_MOD : port_mod,
892 OFPT_STATS_REQUEST : stats_request,
893 OFPT_STATS_REPLY : stats_reply,
894 OFPT_BARRIER_REQUEST : barrier_request,
895 OFPT_BARRIER_REPLY : barrier_reply,
896 OFPT_QUEUE_GET_CONFIG_REQUEST : queue_get_config_request,
897 OFPT_QUEUE_GET_CONFIG_REPLY : queue_get_config_reply
898}
899
900def _of_message_to_object(binary_string):
901 \"""
902 Map a binary string to the corresponding class.
903
904 Appropriately resolves subclasses
905 \"""
906 hdr = ofp_header()
907 hdr.unpack(binary_string)
908 logging.info(hdr.show())
909 # FIXME: Add error detection
910 if not hdr.type in msg_type_subclassed:
911 return msg_type_to_class_map[hdr.type]()
912 if hdr.type == OFPT_STATS_REQUEST:
913 sub_hdr = ofp_stats_request()
914 sub_hdr.unpack(binary_string)
915 try:
916 obj = stats_request_to_class_map[sub_hdr.stats_type]()
917 except KeyError:
918 obj = None
919 return obj
920 elif hdr.type == OFPT_STATS_REPLY:
921 sub_hdr = ofp_stats_reply()
922 sub_hdr.unpack(binary_string)
923 try:
924 obj = stats_reply_to_class_map[sub_hdr.stats_type]()
925 except KeyError:
926 obj = None
927 return obj
928 elif hdr.type == OFPT_ERROR:
929 sub_hdr = ofp_error_msg()
930 sub_hdr.unpack(binary_string)
931 return error_to_class_map[sub_hdr.err_type]()
932 else:
933 logging.error("Cannot parse pkt to message")
934 return None
935
936def parse_message(binary_string):
937 \"""
938 Parse an OpenFlow packet
939
940 Parses a raw OpenFlow packet into a Python class, with class
941 members fully populated.
942
943 @param binary_string The packet (string) to be parsed
944 @param raw If true, interpret the packet as an L2 packet. Not
945 yet supported.
946 @return An object of some message class or None if fails
947 Note that any data beyond that parsed is not returned
948
949 \"""
950 obj = _of_message_to_object(binary_string)
951 if obj:
952 obj.unpack(binary_string)
953 return obj
954
955
956def parse_header(binary_string):
957 \"""
958 Parse only the header from an OpenFlow packet
959
960 Parses the header from a raw OpenFlow packet into a
961 an ofp_header Python class.
962
963 @param binary_string The packet (string) to be parsed
Rich Lane1622bbb2013-03-11 17:11:53 -0700964 @return Tuple of (verison, type, length, xid)
Rich Lanef6883512013-03-11 17:00:09 -0700965
966 \"""
Rich Lane1622bbb2013-03-11 17:11:53 -0700967 return struct.unpack_from("!BBHL", binary_string)
Rich Lanef6883512013-03-11 17:00:09 -0700968
Rich Laneba3f0e22013-03-11 16:43:57 -0700969"""
970
Dan Talaycof75360a2010-02-05 22:22:54 -0800971#
972# OFP match variants
973# ICMP 0x801 (?) ==> icmp_type/code replace tp_src/dst
974#
975
976