blob: 5d771e3918fe15d5ea493e1d3ab109b64f622c6d [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
Dan Talaycob9cb5482010-02-09 15:23:12 -080090from cstruct import *
Dan Talaycof75360a2010-02-05 22:22:54 -080091from action_list import action_list
Dan Talayco411489d2010-02-12 23:03:46 -080092from error import *
Dan Talaycof75360a2010-02-05 22:22:54 -080093
Dan Talaycof75360a2010-02-05 22:22:54 -080094# Define templates for documentation
95class ofp_template_msg:
96 \"""
97 Sample base class for template_msg; normally auto generated
98 This class should live in the of_header name space and provides the
99 base class for this type of message. It will be wrapped for the
100 high level API.
101
102 \"""
103 def __init__(self):
104 \"""
105 Constructor for base class
106
107 \"""
108 self.header = ofp_header()
109 # 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)
Dan Talaycoac1cb812010-02-06 20:34:18 -0800131 self.header = ofp_header()
Dan Talaycof75360a2010-02-05 22:22:54 -0800132 # For a real message, will be set to an integer
133 self.header.type = "TEMPLATE_MSG_VALUE"
134 def pack(self):
135 \"""
136 Pack object into string
137
138 @return The packed string which can go on the wire
139
140 \"""
141 pass
142 def unpack(self, binary_string):
143 \"""
144 Unpack object from a binary string
145
146 @param binary_string The wire protocol byte string holding the object
147 represented as an array of bytes.
148
149 @return Typically returns the remainder of binary_string that
150 was not parsed. May give a warning if that string is non-empty
151
152 \"""
153 pass
154 def __len__(self):
155 \"""
156 Return the length of this object once packed into a string
157
158 @return An integer representing the number bytes in the packed
159 string.
160
161 \"""
162 pass
163 def show(self, prefix=''):
164 \"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800165 Generate a string (with multiple lines) describing the contents
166 of the object in a readable manner
Dan Talaycof75360a2010-02-05 22:22:54 -0800167
Dan Talayco46755fa2010-03-09 21:44:29 -0800168 @param prefix Pre-pended at the beginning of each line.
Dan Talaycof75360a2010-02-05 22:22:54 -0800169
170 \"""
171 pass
172 def __eq__(self, other):
173 \"""
174 Return True if self and other hold the same data
175
176 @param other Other object in comparison
177
178 \"""
179 pass
180 def __ne__(self, other):
181 \"""
182 Return True if self and other do not hold the same data
183
184 @param other Other object in comparison
185
186 \"""
187 pass
188"""
189
190# Dictionary mapping wrapped classes to the auto-generated structure
191# underlieing the class (body only, not header or var-length data)
192message_class_map = {
193 "hello" : "ofp_header",
194 "error" : "ofp_error_msg",
195 "echo_request" : "ofp_header",
196 "echo_reply" : "ofp_header",
197 "vendor" : "ofp_vendor_header",
198 "features_request" : "ofp_header",
199 "features_reply" : "ofp_switch_features",
200 "get_config_request" : "ofp_header",
201 "get_config_reply" : "ofp_switch_config",
202 "set_config" : "ofp_switch_config",
203 "packet_in" : "ofp_packet_in",
204 "flow_removed" : "ofp_flow_removed",
205 "port_status" : "ofp_port_status",
206 "packet_out" : "ofp_packet_out",
207 "flow_mod" : "ofp_flow_mod",
208 "port_mod" : "ofp_port_mod",
209 "stats_request" : "ofp_stats_request",
210 "stats_reply" : "ofp_stats_reply",
211 "barrier_request" : "ofp_header",
212 "barrier_reply" : "ofp_header",
213 "queue_get_config_request" : "ofp_queue_get_config_request",
214 "queue_get_config_reply" : "ofp_queue_get_config_reply"
215}
216
217# These messages have a string member at the end of the data
218string_members = [
219 "hello",
220 "error",
221 "echo_request",
222 "echo_reply",
223 "vendor",
224 "packet_in",
225 "packet_out"
226]
227
228# These messages have a list (with the given name) in the data,
229# after the core members; the type is given for validation
230list_members = {
231 "features_reply" : ('ports', None),
232 "packet_out" : ('actions', 'action_list'),
233 "flow_mod" : ('actions', 'action_list'),
234 "queue_get_config_reply" : ('queues', None)
235}
236
237_ind = " "
238
239def _p1(s): print _ind + s
240def _p2(s): print _ind * 2 + s
241def _p3(s): print _ind * 3 + s
242def _p4(s): print _ind * 4 + s
243
244# Okay, this gets kind of ugly:
245# There are three variables:
246# has_core_members: If parent class is not ofp_header, has inheritance
247# has_list: Whether class has trailing array or class
248# has_string: Whether class has trailing string
249
250def gen_message_wrapper(msg):
251 """
252 Generate a wrapper for the given message based on above info
253 @param msg String identifying the message name for the class
254 """
255
256 msg_name = "OFPT_" + msg.upper()
257 parent = message_class_map[msg]
258
259 has_list = False # Has trailing list
260 has_core_members = False
261 has_string = False # Has trailing string
262 if parent != 'ofp_header':
263 has_core_members = True
264 if msg in list_members.keys():
265 (list_var, list_type) = list_members[msg]
266 has_list = True
267 if msg in string_members:
268 has_string = True
269
270 if has_core_members:
271 print "class " + msg + "(" + parent + "):"
272 else:
273 print "class " + msg + ":"
274 _p1('"""')
275 _p1("Wrapper class for " + msg)
276 print
Dan Talaycoac1cb812010-02-06 20:34:18 -0800277 _p1("OpenFlow message header: length, version, xid, type")
278 _p1("@arg length: The total length of the message")
279 _p1("@arg version: The OpenFlow version (" + str(OFP_VERSION) + ")")
280 _p1("@arg xid: The transaction ID")
281 _p1("@arg type: The message type (" + msg_name + "=" +
282 str(eval(msg_name)) + ")")
283 print
284 if has_core_members and parent in class_to_members_map.keys():
285 _p1("Data members inherited from " + parent + ":")
286 for var in class_to_members_map[parent]:
287 _p1("@arg " + var)
Dan Talaycof75360a2010-02-05 22:22:54 -0800288 if has_list:
289 if list_type == None:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800290 _p1("@arg " + list_var + ": Variable length array of TBD")
Dan Talaycof75360a2010-02-05 22:22:54 -0800291 else:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800292 _p1("@arg " + list_var + ": Object of type " + list_type);
Dan Talaycof75360a2010-02-05 22:22:54 -0800293 if has_string:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800294 _p1("@arg data: Binary string following message members")
295 print
Dan Talaycof75360a2010-02-05 22:22:54 -0800296 _p1('"""')
297
298 print
Rich Lanec3c2ae12013-01-04 10:13:17 -0800299 _p1("def __init__(self, **kwargs):")
Dan Talaycof75360a2010-02-05 22:22:54 -0800300 if has_core_members:
301 _p2(parent + ".__init__(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800302 _p2("self.header = ofp_header()")
303 _p2("self.header.type = " + msg_name)
304 if has_list:
Rich Lane0f0adc92013-01-04 15:13:02 -0800305 _p2('self.' + list_var + ' = []')
Dan Talaycof75360a2010-02-05 22:22:54 -0800306 if has_string:
307 _p2('self.data = ""')
Rich Lanec3c2ae12013-01-04 10:13:17 -0800308 _p2('for (k, v) in kwargs.items():')
309 _p3('if hasattr(self, k):')
310 _p4('setattr(self, k, v)')
311 _p3('else:')
312 _p4('raise NameError("field %s does not exist in %s" % (k, self.__class__))')
Dan Talaycof75360a2010-02-05 22:22:54 -0800313
Dan Talaycob9cb5482010-02-09 15:23:12 -0800314 print """
315
316 def pack(self):
317 \"""
318 Pack object into string
319
320 @return The packed string which can go on the wire
321
322 \"""
323 self.header.length = len(self)
324 packed = self.header.pack()
325"""
326
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800327 # Have to special case the action length calculation for pkt out
328 if msg == 'packet_out':
Rich Lane62e96852013-03-11 12:04:45 -0700329 _p2('self.actions_len = 0')
330 _p2('for obj in self.actions:')
331 _p3('self.actions_len += len(obj)')
Dan Talaycof75360a2010-02-05 22:22:54 -0800332 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800333 _p2("packed += " + parent + ".pack(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800334 if has_list:
335 if list_type == None:
336 _p2('for obj in self.' + list_var + ':')
337 _p3('packed += obj.pack()')
338 else:
Rich Lane62e96852013-03-11 12:04:45 -0700339 _p2('packed += ' + list_type + '(self.' + list_var + ').pack()')
Dan Talaycof75360a2010-02-05 22:22:54 -0800340 if has_string:
341 _p2('packed += self.data')
Dan Talayco6d2470b2010-02-07 22:59:49 -0800342 _p2("return packed")
Dan Talaycof75360a2010-02-05 22:22:54 -0800343
Dan Talaycob9cb5482010-02-09 15:23:12 -0800344 print """
345 def unpack(self, binary_string):
346 \"""
347 Unpack object from a binary string
348
349 @param binary_string The wire protocol byte string holding the object
350 represented as an array of bytes.
Dan Talayco411489d2010-02-12 23:03:46 -0800351 @return The remainder of binary_string that was not parsed.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800352
353 \"""
354 binary_string = self.header.unpack(binary_string)
355"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800356 if has_core_members:
357 _p2("binary_string = " + parent + ".unpack(self, binary_string)")
358 if has_list:
Dan Talaycoff606492010-05-13 14:22:37 -0700359 if msg == "features_reply": # Special case port parsing
360 # For now, cheat and assume the rest of the message is port list
361 _p2("while len(binary_string) >= OFP_PHY_PORT_BYTES:")
362 _p3("new_port = ofp_phy_port()")
363 _p3("binary_string = new_port.unpack(binary_string)")
364 _p3("self.ports.append(new_port)")
365 elif list_type == None:
Dan Talaycof75360a2010-02-05 22:22:54 -0800366 _p2("for obj in self." + list_var + ":")
367 _p3("binary_string = obj.unpack(binary_string)")
368 elif msg == "packet_out": # Special case this
Rich Lane62e96852013-03-11 12:04:45 -0700369 _p2("obj = action_list()")
370 _p2('binary_string = obj.unpack(' +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800371 'binary_string, bytes=self.actions_len)')
Rich Lane62e96852013-03-11 12:04:45 -0700372 _p2("self.actions = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800373 elif msg == "flow_mod": # Special case this
Dan Talayco6d2470b2010-02-07 22:59:49 -0800374 _p2("ai_len = self.header.length - (OFP_FLOW_MOD_BYTES + " +
375 "OFP_HEADER_BYTES)")
Rich Lane62e96852013-03-11 12:04:45 -0700376 _p2("obj = action_list()")
377 _p2("binary_string = obj.unpack(binary_string, " +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800378 "bytes=ai_len)")
Rich Lane62e96852013-03-11 12:04:45 -0700379 _p2("self.actions = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800380 else:
Rich Lane62e96852013-03-11 12:04:45 -0700381 _p2("obj = " + list_type + "()")
382 _p2("binary_string = obj.unpack(binary_string)")
383 _p2("self." + list_var + " = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800384 if has_string:
385 _p2("self.data = binary_string")
386 _p2("binary_string = ''")
387 else:
388 _p2("# Fixme: If no self.data, add check for data remaining")
389 _p2("return binary_string")
390
Dan Talaycob9cb5482010-02-09 15:23:12 -0800391 print """
392 def __len__(self):
393 \"""
394 Return the length of this object once packed into a string
395
396 @return An integer representing the number bytes in the packed
397 string.
398
399 \"""
400 length = OFP_HEADER_BYTES
401"""
Dan Talayco6d2470b2010-02-07 22:59:49 -0800402 if has_core_members:
403 _p2("length += " + parent + ".__len__(self)")
404 if has_list:
Rich Lane62e96852013-03-11 12:04:45 -0700405 _p2("for obj in self." + list_var + ":")
406 _p3("length += len(obj)")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800407 if has_string:
408 _p2("length += len(self.data)")
409 _p2("return length")
Dan Talaycof75360a2010-02-05 22:22:54 -0800410
Dan Talaycob9cb5482010-02-09 15:23:12 -0800411 print """
Dan Talaycob9cb5482010-02-09 15:23:12 -0800412 def show(self, prefix=''):
413 \"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800414 Generate a string (with multiple lines) describing the contents
415 of the object in a readable manner
Dan Talaycob9cb5482010-02-09 15:23:12 -0800416
Dan Talayco46755fa2010-03-09 21:44:29 -0800417 @param prefix Pre-pended at the beginning of each line.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800418
419 \"""
420"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800421 _p2("outstr = prefix + '" + msg + " (" + msg_name + ")\\n'")
Dan Talaycof75360a2010-02-05 22:22:54 -0800422 _p2("prefix += ' '")
Dan Talayco46755fa2010-03-09 21:44:29 -0800423 _p2("outstr += prefix + 'ofp header\\n'")
424 _p2("outstr += self.header.show(prefix + ' ')")
Dan Talaycof75360a2010-02-05 22:22:54 -0800425 if has_core_members:
Dan Talayco46755fa2010-03-09 21:44:29 -0800426 _p2("outstr += " + parent + ".show(self, prefix)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800427 if has_list:
428 if list_type == None:
Dan Talayco46755fa2010-03-09 21:44:29 -0800429 _p2('outstr += prefix + "Array ' + list_var + '\\n"')
Dan Talaycof75360a2010-02-05 22:22:54 -0800430 _p2('for obj in self.' + list_var +':')
Dan Talayco46755fa2010-03-09 21:44:29 -0800431 _p3("outstr += obj.show(prefix + ' ')")
Dan Talaycof75360a2010-02-05 22:22:54 -0800432 else:
Dan Talayco46755fa2010-03-09 21:44:29 -0800433 _p2('outstr += prefix + "List ' + list_var + '\\n"')
Rich Lanee6ea3fe2013-03-08 17:54:38 -0800434 _p2('for obj in self.' + list_var + ':')
435 _p3('outstr += obj.show(prefix + " ")')
Dan Talaycof75360a2010-02-05 22:22:54 -0800436 if has_string:
Dan Talayco46755fa2010-03-09 21:44:29 -0800437 _p2("outstr += prefix + 'data is of length ' + str(len(self.data)) + '\\n'")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800438 _p2("##@todo Fix this circular reference")
439 _p2("# if len(self.data) > 0:")
440 _p3("# obj = of_message_parse(self.data)")
441 _p3("# if obj != None:")
Dan Talayco46755fa2010-03-09 21:44:29 -0800442 _p4("# outstr += obj.show(prefix)")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800443 _p3("# else:")
Dan Talayco46755fa2010-03-09 21:44:29 -0800444 _p4('# outstr += prefix + "Unable to parse data\\n"')
445 _p2('return outstr')
Dan Talaycof75360a2010-02-05 22:22:54 -0800446
Dan Talaycob9cb5482010-02-09 15:23:12 -0800447 print """
448 def __eq__(self, other):
449 \"""
450 Return True if self and other hold the same data
451
452 @param other Other object in comparison
453
454 \"""
455 if type(self) != type(other): return False
456 if not self.header.__eq__(other.header): return False
457"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800458 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800459 _p2("if not " + parent + ".__eq__(self, other): return False")
Dan Talaycof75360a2010-02-05 22:22:54 -0800460 if has_string:
461 _p2("if self.data != other.data: return False")
462 if has_list:
463 _p2("if self." + list_var + " != other." + list_var + ": return False")
464 _p2("return True")
465
Dan Talaycob9cb5482010-02-09 15:23:12 -0800466 print """
467 def __ne__(self, other):
468 \"""
469 Return True if self and other do not hold the same data
Dan Talaycof75360a2010-02-05 22:22:54 -0800470
Dan Talaycob9cb5482010-02-09 15:23:12 -0800471 @param other Other object in comparison
472
473 \"""
474 return not self.__eq__(other)
475 """
Dan Talaycof75360a2010-02-05 22:22:54 -0800476
477
478################################################################
479#
480# Stats request subclasses
481# description_request, flow, aggregate, table, port, vendor
482#
483################################################################
484
485# table and desc stats requests are special with empty body
486extra_ofp_stats_req_defs = """
487# Stats request bodies for desc and table stats are not defined in the
488# OpenFlow header; We define them here. They are empty classes, really
489
490class ofp_desc_stats_request:
491 \"""
492 Forced definition of ofp_desc_stats_request (empty class)
493 \"""
494 def __init__(self):
495 pass
496 def pack(self, assertstruct=True):
497 return ""
498 def unpack(self, binary_string):
499 return binary_string
500 def __len__(self):
501 return 0
502 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800503 return prefix + "ofp_desc_stats_request (empty)\\n"
Dan Talaycof75360a2010-02-05 22:22:54 -0800504 def __eq__(self, other):
505 return type(self) == type(other)
506 def __ne__(self, other):
507 return type(self) != type(other)
508
509OFP_DESC_STATS_REQUEST_BYTES = 0
510
511class ofp_table_stats_request:
512 \"""
513 Forced definition of ofp_table_stats_request (empty class)
514 \"""
515 def __init__(self):
516 pass
517 def pack(self, assertstruct=True):
518 return ""
519 def unpack(self, binary_string):
520 return binary_string
521 def __len__(self):
522 return 0
523 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800524 return prefix + "ofp_table_stats_request (empty)\\n"
Dan Talaycof75360a2010-02-05 22:22:54 -0800525 def __eq__(self, other):
526 return type(self) == type(other)
527 def __ne__(self, other):
528 return type(self) != type(other)
529
530OFP_TABLE_STATS_REQUEST_BYTES = 0
531
532"""
533
534stats_request_template = """
535class --TYPE--_stats_request(ofp_stats_request, ofp_--TYPE--_stats_request):
536 \"""
537 Wrapper class for --TYPE-- stats request message
538 \"""
Rich Lanec3c2ae12013-01-04 10:13:17 -0800539 def __init__(self, **kwargs):
Dan Talaycof75360a2010-02-05 22:22:54 -0800540 self.header = ofp_header()
541 ofp_stats_request.__init__(self)
542 ofp_--TYPE--_stats_request.__init__(self)
543 self.header.type = OFPT_STATS_REQUEST
544 self.type = --STATS_NAME--
Rich Lanec3c2ae12013-01-04 10:13:17 -0800545 for (k, v) in kwargs.items():
546 if hasattr(self, k):
547 setattr(self, k, v)
548 else:
549 raise NameError("field %s does not exist in %s" % (k, self.__class__))
Dan Talaycof75360a2010-02-05 22:22:54 -0800550
551 def pack(self, assertstruct=True):
Dan Talayco2f820be2010-03-07 11:36:29 -0800552 self.header.length = len(self)
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800553 packed = self.header.pack()
554 packed += ofp_stats_request.pack(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800555 packed += ofp_--TYPE--_stats_request.pack(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800556 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800557
558 def unpack(self, binary_string):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800559 binary_string = self.header.unpack(binary_string)
Dan Talaycof75360a2010-02-05 22:22:54 -0800560 binary_string = ofp_stats_request.unpack(self, binary_string)
561 binary_string = ofp_--TYPE--_stats_request.unpack(self, binary_string)
562 if len(binary_string) != 0:
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800563 print "ERROR unpacking --TYPE--: extra data"
Dan Talaycof75360a2010-02-05 22:22:54 -0800564 return binary_string
565
566 def __len__(self):
567 return len(self.header) + OFP_STATS_REQUEST_BYTES + \\
568 OFP_--TYPE_UPPER--_STATS_REQUEST_BYTES
569
570 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800571 outstr = prefix + "--TYPE--_stats_request\\n"
572 outstr += prefix + "ofp header:\\n"
573 outstr += self.header.show(prefix + ' ')
574 outstr += ofp_stats_request.show(self)
575 outstr += ofp_--TYPE--_stats_request.show(self)
576 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800577
578 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800579 if type(self) != type(other): return False
580 return (self.header == other.header and
581 ofp_stats_request.__eq__(self, other) and
Dan Talaycof75360a2010-02-05 22:22:54 -0800582 ofp_--TYPE--_stats_request.__eq__(self, other))
583
584 def __ne__(self, other): return not self.__eq__(other)
585"""
586
587################################################################
588#
589# Stats replies always have an array at the end.
590# For aggregate and desc, these arrays are always of length 1
591# This array is always called stats
592#
593################################################################
594
595
596# Template for objects stats reply messages
597stats_reply_template = """
598class --TYPE--_stats_reply(ofp_stats_reply):
599 \"""
600 Wrapper class for --TYPE-- stats reply
601 \"""
602 def __init__(self):
603 self.header = ofp_header()
604 ofp_stats_reply.__init__(self)
605 self.header.type = OFPT_STATS_REPLY
606 self.type = --STATS_NAME--
607 # stats: Array of type --TYPE--_stats_entry
608 self.stats = []
609
610 def pack(self, assertstruct=True):
Dan Talayco2f820be2010-03-07 11:36:29 -0800611 self.header.length = len(self)
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800612 packed = self.header.pack()
613 packed += ofp_stats_reply.pack(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800614 for obj in self.stats:
615 packed += obj.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800616 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800617
618 def unpack(self, binary_string):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800619 binary_string = self.header.unpack(binary_string)
Dan Talaycof75360a2010-02-05 22:22:54 -0800620 binary_string = ofp_stats_reply.unpack(self, binary_string)
621 dummy = --TYPE--_stats_entry()
622 while len(binary_string) >= len(dummy):
623 obj = --TYPE--_stats_entry()
624 binary_string = obj.unpack(binary_string)
625 self.stats.append(obj)
626 if len(binary_string) != 0:
627 print "ERROR unpacking --TYPE-- stats string: extra bytes"
628 return binary_string
629
630 def __len__(self):
631 length = len(self.header) + OFP_STATS_REPLY_BYTES
632 for obj in self.stats:
633 length += len(obj)
634 return length
635
636 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800637 outstr = prefix + "--TYPE--_stats_reply\\n"
638 outstr += prefix + "ofp header:\\n"
639 outstr += self.header.show(prefix + ' ')
640 outstr += ofp_stats_reply.show(self)
641 outstr += prefix + "Stats array of length " + str(len(self.stats)) + '\\n'
Dan Talaycof75360a2010-02-05 22:22:54 -0800642 for obj in self.stats:
Dan Talayco46755fa2010-03-09 21:44:29 -0800643 outstr += obj.show()
644 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800645
646 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800647 if type(self) != type(other): return False
648 return (self.header == other.header and
649 ofp_stats_reply.__eq__(self, other) and
650 self.stats == other.stats)
Dan Talaycof75360a2010-02-05 22:22:54 -0800651
652 def __ne__(self, other): return not self.__eq__(other)
653"""
654
655#
656# To address variations in stats reply bodies, the following
657# "_entry" classes are defined for each element in the reply
658#
659
660extra_stats_entry_defs = """
661# Stats entries define the content of one element in a stats
662# reply for the indicated type; define _entry for consistency
663
664aggregate_stats_entry = ofp_aggregate_stats_reply
665desc_stats_entry = ofp_desc_stats
666port_stats_entry = ofp_port_stats
667queue_stats_entry = ofp_queue_stats
668table_stats_entry = ofp_table_stats
669"""
670
671# Special case flow_stats to handle actions_list
672
673flow_stats_entry_def = """
674#
675# Flow stats entry contains an action list of variable length, so
676# it is done by hand
677#
678
679class flow_stats_entry(ofp_flow_stats):
680 \"""
681 Special case flow stats entry to handle action list object
682 \"""
683 def __init__(self):
684 ofp_flow_stats.__init__(self)
Rich Lane62e96852013-03-11 12:04:45 -0700685 self.actions = []
Dan Talaycof75360a2010-02-05 22:22:54 -0800686
687 def pack(self, assertstruct=True):
Dan Talayco6d2470b2010-02-07 22:59:49 -0800688 self.length = len(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800689 packed = ofp_flow_stats.pack(self, assertstruct)
Rich Lane62e96852013-03-11 12:04:45 -0700690 packed += action_list(self.actions).pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800691 if len(packed) != self.length:
692 print("ERROR: flow_stats_entry pack length not equal",
693 self.length, len(packed))
Dan Talaycof75360a2010-02-05 22:22:54 -0800694 return packed
695
696 def unpack(self, binary_string):
697 binary_string = ofp_flow_stats.unpack(self, binary_string)
698 ai_len = self.length - OFP_FLOW_STATS_BYTES
Dan Talayco6d2470b2010-02-07 22:59:49 -0800699 if ai_len < 0:
700 print("ERROR: flow_stats_entry unpack length too small",
701 self.length)
Rich Lane62e96852013-03-11 12:04:45 -0700702 obj = action_list()
703 binary_string = obj.unpack(binary_string, bytes=ai_len)
704 self.actions = list(obj)
Dan Talaycof75360a2010-02-05 22:22:54 -0800705 return binary_string
706
707 def __len__(self):
708 return OFP_FLOW_STATS_BYTES + len(self.actions)
709
710 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800711 outstr = prefix + "flow_stats_entry\\n"
712 outstr += ofp_flow_stats.show(self, prefix + ' ')
Rich Lanee6ea3fe2013-03-08 17:54:38 -0800713 outstr += prefix + "List actions\\n"
714 for obj in self.actions:
715 outstr += obj.show(prefix + ' ')
Dan Talayco46755fa2010-03-09 21:44:29 -0800716 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800717
718 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800719 if type(self) != type(other): return False
Dan Talaycof75360a2010-02-05 22:22:54 -0800720 return (ofp_flow_stats.__eq__(self, other) and
721 self.actions == other.actions)
722
723 def __ne__(self, other): return not self.__eq__(other)
724"""
725
726stats_types = [
727 'aggregate',
728 'desc',
729 'flow',
730 'port',
731 'queue',
732 'table']
733
734if __name__ == '__main__':
735
736 print message_top_matter
737
738 print """
739################################################################
740#
741# OpenFlow Message Definitions
742#
743################################################################
744"""
745
746 msg_types = message_class_map.keys()
747 msg_types.sort()
748
749 for t in msg_types:
750 gen_message_wrapper(t)
751 print
752
753 print """
754################################################################
755#
756# Stats request and reply subclass definitions
757#
758################################################################
759"""
760
761 print extra_ofp_stats_req_defs
762 print extra_stats_entry_defs
763 print flow_stats_entry_def
764
765 # Generate stats request and reply subclasses
766 for t in stats_types:
767 stats_name = "OFPST_" + t.upper()
768 to_print = re.sub('--TYPE--', t, stats_request_template)
769 to_print = re.sub('--TYPE_UPPER--', t.upper(), to_print)
770 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
771 print to_print
772 to_print = re.sub('--TYPE--', t, stats_reply_template)
773 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
774 print to_print
775
Dan Talayco411489d2010-02-12 23:03:46 -0800776 # Lastly, generate a tuple containing all the message classes
777 print """
778message_type_list = (
779 aggregate_stats_reply,
780 aggregate_stats_request,
781 bad_action_error_msg,
782 bad_request_error_msg,
783 barrier_reply,
784 barrier_request,
785 desc_stats_reply,
786 desc_stats_request,
787 echo_reply,
788 echo_request,
789 features_reply,
790 features_request,
791 flow_mod,
792 flow_mod_failed_error_msg,
793 flow_removed,
794 flow_stats_reply,
795 flow_stats_request,
796 get_config_reply,
797 get_config_request,
798 hello,
799 hello_failed_error_msg,
800 packet_in,
801 packet_out,
802 port_mod,
803 port_mod_failed_error_msg,
804 port_stats_reply,
805 port_stats_request,
806 port_status,
807 queue_get_config_reply,
808 queue_get_config_request,
809 queue_op_failed_error_msg,
810 queue_stats_reply,
811 queue_stats_request,
812 set_config,
813 table_stats_reply,
814 table_stats_request,
815 vendor
816 )
817"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800818
819#
820# OFP match variants
821# ICMP 0x801 (?) ==> icmp_type/code replace tp_src/dst
822#
823
824