blob: 3e39bdf1e76bac34e7b91b7061bd20c15ddcb24e [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 \"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800108 # Additional base data members declared here
Dan Talaycoac1cb812010-02-06 20:34:18 -0800109
Dan Talaycof75360a2010-02-05 22:22:54 -0800110 # Normally will define pack, unpack, __len__ functions
111
112class template_msg(ofp_template_msg):
113 \"""
114 Sample class wrapper for template_msg
115 This class should live in the of_message name space and provides the
116 high level API for an OpenFlow message object. These objects must
117 implement the functions indicated in this template.
118
119 \"""
120 def __init__(self):
121 \"""
122 Constructor
123 Must set the header type value appropriately for the message
124
125 \"""
Dan Talaycoac1cb812010-02-06 20:34:18 -0800126
127 ##@var header
128 # OpenFlow message header: length, version, xid, type
Dan Talaycof75360a2010-02-05 22:22:54 -0800129 ofp_template_msg.__init__(self)
130 # For a real message, will be set to an integer
Rich Laneb73808c2013-03-11 15:22:23 -0700131 self.type = "TEMPLATE_MSG_VALUE"
Dan Talaycof75360a2010-02-05 22:22:54 -0800132 def pack(self):
133 \"""
134 Pack object into string
135
136 @return The packed string which can go on the wire
137
138 \"""
139 pass
140 def unpack(self, binary_string):
141 \"""
142 Unpack object from a binary string
143
144 @param binary_string The wire protocol byte string holding the object
145 represented as an array of bytes.
146
147 @return Typically returns the remainder of binary_string that
148 was not parsed. May give a warning if that string is non-empty
149
150 \"""
151 pass
152 def __len__(self):
153 \"""
154 Return the length of this object once packed into a string
155
156 @return An integer representing the number bytes in the packed
157 string.
158
159 \"""
160 pass
161 def show(self, prefix=''):
162 \"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800163 Generate a string (with multiple lines) describing the contents
164 of the object in a readable manner
Dan Talaycof75360a2010-02-05 22:22:54 -0800165
Dan Talayco46755fa2010-03-09 21:44:29 -0800166 @param prefix Pre-pended at the beginning of each line.
Dan Talaycof75360a2010-02-05 22:22:54 -0800167
168 \"""
169 pass
170 def __eq__(self, other):
171 \"""
172 Return True if self and other hold the same data
173
174 @param other Other object in comparison
175
176 \"""
177 pass
178 def __ne__(self, other):
179 \"""
180 Return True if self and other do not hold the same data
181
182 @param other Other object in comparison
183
184 \"""
185 pass
186"""
187
188# Dictionary mapping wrapped classes to the auto-generated structure
189# underlieing the class (body only, not header or var-length data)
190message_class_map = {
191 "hello" : "ofp_header",
192 "error" : "ofp_error_msg",
193 "echo_request" : "ofp_header",
194 "echo_reply" : "ofp_header",
195 "vendor" : "ofp_vendor_header",
196 "features_request" : "ofp_header",
197 "features_reply" : "ofp_switch_features",
198 "get_config_request" : "ofp_header",
199 "get_config_reply" : "ofp_switch_config",
200 "set_config" : "ofp_switch_config",
201 "packet_in" : "ofp_packet_in",
202 "flow_removed" : "ofp_flow_removed",
203 "port_status" : "ofp_port_status",
204 "packet_out" : "ofp_packet_out",
205 "flow_mod" : "ofp_flow_mod",
206 "port_mod" : "ofp_port_mod",
207 "stats_request" : "ofp_stats_request",
208 "stats_reply" : "ofp_stats_reply",
209 "barrier_request" : "ofp_header",
210 "barrier_reply" : "ofp_header",
211 "queue_get_config_request" : "ofp_queue_get_config_request",
212 "queue_get_config_reply" : "ofp_queue_get_config_reply"
213}
214
215# These messages have a string member at the end of the data
216string_members = [
217 "hello",
218 "error",
219 "echo_request",
220 "echo_reply",
221 "vendor",
222 "packet_in",
223 "packet_out"
224]
225
226# These messages have a list (with the given name) in the data,
227# after the core members; the type is given for validation
228list_members = {
229 "features_reply" : ('ports', None),
230 "packet_out" : ('actions', 'action_list'),
231 "flow_mod" : ('actions', 'action_list'),
232 "queue_get_config_reply" : ('queues', None)
233}
234
235_ind = " "
236
237def _p1(s): print _ind + s
238def _p2(s): print _ind * 2 + s
239def _p3(s): print _ind * 3 + s
240def _p4(s): print _ind * 4 + s
241
242# Okay, this gets kind of ugly:
243# There are three variables:
244# has_core_members: If parent class is not ofp_header, has inheritance
245# has_list: Whether class has trailing array or class
246# has_string: Whether class has trailing string
247
248def gen_message_wrapper(msg):
249 """
250 Generate a wrapper for the given message based on above info
251 @param msg String identifying the message name for the class
252 """
253
254 msg_name = "OFPT_" + msg.upper()
255 parent = message_class_map[msg]
256
257 has_list = False # Has trailing list
Rich Laneb73808c2013-03-11 15:22:23 -0700258 has_core_members = True
Dan Talaycof75360a2010-02-05 22:22:54 -0800259 has_string = False # Has trailing string
Dan Talaycof75360a2010-02-05 22:22:54 -0800260 if msg in list_members.keys():
261 (list_var, list_type) = list_members[msg]
262 has_list = True
263 if msg in string_members:
264 has_string = True
265
266 if has_core_members:
267 print "class " + msg + "(" + parent + "):"
268 else:
269 print "class " + msg + ":"
270 _p1('"""')
271 _p1("Wrapper class for " + msg)
272 print
Dan Talaycoac1cb812010-02-06 20:34:18 -0800273 _p1("OpenFlow message header: length, version, xid, type")
274 _p1("@arg length: The total length of the message")
275 _p1("@arg version: The OpenFlow version (" + str(OFP_VERSION) + ")")
276 _p1("@arg xid: The transaction ID")
277 _p1("@arg type: The message type (" + msg_name + "=" +
278 str(eval(msg_name)) + ")")
279 print
280 if has_core_members and parent in class_to_members_map.keys():
281 _p1("Data members inherited from " + parent + ":")
282 for var in class_to_members_map[parent]:
283 _p1("@arg " + var)
Dan Talaycof75360a2010-02-05 22:22:54 -0800284 if has_list:
285 if list_type == None:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800286 _p1("@arg " + list_var + ": Variable length array of TBD")
Dan Talaycof75360a2010-02-05 22:22:54 -0800287 else:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800288 _p1("@arg " + list_var + ": Object of type " + list_type);
Dan Talaycof75360a2010-02-05 22:22:54 -0800289 if has_string:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800290 _p1("@arg data: Binary string following message members")
291 print
Dan Talaycof75360a2010-02-05 22:22:54 -0800292 _p1('"""')
293
294 print
Rich Lanec3c2ae12013-01-04 10:13:17 -0800295 _p1("def __init__(self, **kwargs):")
Dan Talaycof75360a2010-02-05 22:22:54 -0800296 if has_core_members:
297 _p2(parent + ".__init__(self)")
Rich Laneb73808c2013-03-11 15:22:23 -0700298 _p2("self.version = OFP_VERSION")
299 _p2("self.type = " + msg_name)
Rich Lane8fbfd662013-03-11 15:30:44 -0700300 _p2("self.xid = None")
Dan Talaycof75360a2010-02-05 22:22:54 -0800301 if has_list:
Rich Lane0f0adc92013-01-04 15:13:02 -0800302 _p2('self.' + list_var + ' = []')
Dan Talaycof75360a2010-02-05 22:22:54 -0800303 if has_string:
304 _p2('self.data = ""')
Rich Lanec3c2ae12013-01-04 10:13:17 -0800305 _p2('for (k, v) in kwargs.items():')
306 _p3('if hasattr(self, k):')
307 _p4('setattr(self, k, v)')
308 _p3('else:')
309 _p4('raise NameError("field %s does not exist in %s" % (k, self.__class__))')
Dan Talaycof75360a2010-02-05 22:22:54 -0800310
Dan Talaycob9cb5482010-02-09 15:23:12 -0800311 print """
312
313 def pack(self):
314 \"""
315 Pack object into string
316
317 @return The packed string which can go on the wire
318
319 \"""
Rich Laneb73808c2013-03-11 15:22:23 -0700320 self.length = len(self)
321 packed = ""
Dan Talaycob9cb5482010-02-09 15:23:12 -0800322"""
323
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800324 # Have to special case the action length calculation for pkt out
325 if msg == 'packet_out':
Rich Lane62e96852013-03-11 12:04:45 -0700326 _p2('self.actions_len = 0')
327 _p2('for obj in self.actions:')
328 _p3('self.actions_len += len(obj)')
Dan Talaycof75360a2010-02-05 22:22:54 -0800329 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800330 _p2("packed += " + parent + ".pack(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800331 if has_list:
332 if list_type == None:
333 _p2('for obj in self.' + list_var + ':')
334 _p3('packed += obj.pack()')
335 else:
Rich Lane62e96852013-03-11 12:04:45 -0700336 _p2('packed += ' + list_type + '(self.' + list_var + ').pack()')
Dan Talaycof75360a2010-02-05 22:22:54 -0800337 if has_string:
338 _p2('packed += self.data')
Dan Talayco6d2470b2010-02-07 22:59:49 -0800339 _p2("return packed")
Dan Talaycof75360a2010-02-05 22:22:54 -0800340
Dan Talaycob9cb5482010-02-09 15:23:12 -0800341 print """
342 def unpack(self, binary_string):
343 \"""
344 Unpack object from a binary string
345
346 @param binary_string The wire protocol byte string holding the object
347 represented as an array of bytes.
Dan Talayco411489d2010-02-12 23:03:46 -0800348 @return The remainder of binary_string that was not parsed.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800349
350 \"""
Dan Talaycob9cb5482010-02-09 15:23:12 -0800351"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800352 if has_core_members:
353 _p2("binary_string = " + parent + ".unpack(self, binary_string)")
354 if has_list:
Dan Talaycoff606492010-05-13 14:22:37 -0700355 if msg == "features_reply": # Special case port parsing
356 # For now, cheat and assume the rest of the message is port list
357 _p2("while len(binary_string) >= OFP_PHY_PORT_BYTES:")
358 _p3("new_port = ofp_phy_port()")
359 _p3("binary_string = new_port.unpack(binary_string)")
360 _p3("self.ports.append(new_port)")
361 elif list_type == None:
Dan Talaycof75360a2010-02-05 22:22:54 -0800362 _p2("for obj in self." + list_var + ":")
363 _p3("binary_string = obj.unpack(binary_string)")
364 elif msg == "packet_out": # Special case this
Rich Lane62e96852013-03-11 12:04:45 -0700365 _p2("obj = action_list()")
366 _p2('binary_string = obj.unpack(' +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800367 'binary_string, bytes=self.actions_len)')
Rich Lane62e96852013-03-11 12:04:45 -0700368 _p2("self.actions = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800369 elif msg == "flow_mod": # Special case this
Rich Laneb73808c2013-03-11 15:22:23 -0700370 _p2("ai_len = self.length - (OFP_FLOW_MOD_BYTES + " +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800371 "OFP_HEADER_BYTES)")
Rich Lane62e96852013-03-11 12:04:45 -0700372 _p2("obj = action_list()")
373 _p2("binary_string = obj.unpack(binary_string, " +
Dan Talayco6d2470b2010-02-07 22:59:49 -0800374 "bytes=ai_len)")
Rich Lane62e96852013-03-11 12:04:45 -0700375 _p2("self.actions = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800376 else:
Rich Lane62e96852013-03-11 12:04:45 -0700377 _p2("obj = " + list_type + "()")
378 _p2("binary_string = obj.unpack(binary_string)")
379 _p2("self." + list_var + " = list(obj)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800380 if has_string:
381 _p2("self.data = binary_string")
382 _p2("binary_string = ''")
383 else:
384 _p2("# Fixme: If no self.data, add check for data remaining")
385 _p2("return binary_string")
386
Dan Talaycob9cb5482010-02-09 15:23:12 -0800387 print """
388 def __len__(self):
389 \"""
390 Return the length of this object once packed into a string
391
392 @return An integer representing the number bytes in the packed
393 string.
394
395 \"""
Rich Laneb73808c2013-03-11 15:22:23 -0700396 length = 0
Dan Talaycob9cb5482010-02-09 15:23:12 -0800397"""
Dan Talayco6d2470b2010-02-07 22:59:49 -0800398 if has_core_members:
399 _p2("length += " + parent + ".__len__(self)")
400 if has_list:
Rich Lane62e96852013-03-11 12:04:45 -0700401 _p2("for obj in self." + list_var + ":")
402 _p3("length += len(obj)")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800403 if has_string:
404 _p2("length += len(self.data)")
405 _p2("return length")
Dan Talaycof75360a2010-02-05 22:22:54 -0800406
Dan Talaycob9cb5482010-02-09 15:23:12 -0800407 print """
Dan Talaycob9cb5482010-02-09 15:23:12 -0800408 def show(self, prefix=''):
409 \"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800410 Generate a string (with multiple lines) describing the contents
411 of the object in a readable manner
Dan Talaycob9cb5482010-02-09 15:23:12 -0800412
Dan Talayco46755fa2010-03-09 21:44:29 -0800413 @param prefix Pre-pended at the beginning of each line.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800414
415 \"""
416"""
Dan Talayco46755fa2010-03-09 21:44:29 -0800417 _p2("outstr = prefix + '" + msg + " (" + msg_name + ")\\n'")
Dan Talaycof75360a2010-02-05 22:22:54 -0800418 _p2("prefix += ' '")
Dan Talayco46755fa2010-03-09 21:44:29 -0800419 _p2("outstr += prefix + 'ofp header\\n'")
Dan Talaycof75360a2010-02-05 22:22:54 -0800420 if has_core_members:
Dan Talayco46755fa2010-03-09 21:44:29 -0800421 _p2("outstr += " + parent + ".show(self, prefix)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800422 if has_list:
423 if list_type == None:
Dan Talayco46755fa2010-03-09 21:44:29 -0800424 _p2('outstr += prefix + "Array ' + list_var + '\\n"')
Dan Talaycof75360a2010-02-05 22:22:54 -0800425 _p2('for obj in self.' + list_var +':')
Dan Talayco46755fa2010-03-09 21:44:29 -0800426 _p3("outstr += obj.show(prefix + ' ')")
Dan Talaycof75360a2010-02-05 22:22:54 -0800427 else:
Dan Talayco46755fa2010-03-09 21:44:29 -0800428 _p2('outstr += prefix + "List ' + list_var + '\\n"')
Rich Lanee6ea3fe2013-03-08 17:54:38 -0800429 _p2('for obj in self.' + list_var + ':')
430 _p3('outstr += obj.show(prefix + " ")')
Dan Talaycof75360a2010-02-05 22:22:54 -0800431 if has_string:
Dan Talayco46755fa2010-03-09 21:44:29 -0800432 _p2("outstr += prefix + 'data is of length ' + str(len(self.data)) + '\\n'")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800433 _p2("##@todo Fix this circular reference")
434 _p2("# if len(self.data) > 0:")
435 _p3("# obj = of_message_parse(self.data)")
436 _p3("# if obj != None:")
Dan Talayco46755fa2010-03-09 21:44:29 -0800437 _p4("# outstr += obj.show(prefix)")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800438 _p3("# else:")
Dan Talayco46755fa2010-03-09 21:44:29 -0800439 _p4('# outstr += prefix + "Unable to parse data\\n"')
440 _p2('return outstr')
Dan Talaycof75360a2010-02-05 22:22:54 -0800441
Dan Talaycob9cb5482010-02-09 15:23:12 -0800442 print """
443 def __eq__(self, other):
444 \"""
445 Return True if self and other hold the same data
446
447 @param other Other object in comparison
448
449 \"""
450 if type(self) != type(other): return False
Dan Talaycob9cb5482010-02-09 15:23:12 -0800451"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800452 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800453 _p2("if not " + parent + ".__eq__(self, other): return False")
Dan Talaycof75360a2010-02-05 22:22:54 -0800454 if has_string:
455 _p2("if self.data != other.data: return False")
456 if has_list:
457 _p2("if self." + list_var + " != other." + list_var + ": return False")
458 _p2("return True")
459
Dan Talaycob9cb5482010-02-09 15:23:12 -0800460 print """
461 def __ne__(self, other):
462 \"""
463 Return True if self and other do not hold the same data
Dan Talaycof75360a2010-02-05 22:22:54 -0800464
Dan Talaycob9cb5482010-02-09 15:23:12 -0800465 @param other Other object in comparison
466
467 \"""
468 return not self.__eq__(other)
469 """
Dan Talaycof75360a2010-02-05 22:22:54 -0800470
471
472################################################################
473#
474# Stats request subclasses
475# description_request, flow, aggregate, table, port, vendor
476#
477################################################################
478
479# table and desc stats requests are special with empty body
480extra_ofp_stats_req_defs = """
481# Stats request bodies for desc and table stats are not defined in the
482# OpenFlow header; We define them here. They are empty classes, really
483
484class ofp_desc_stats_request:
485 \"""
486 Forced definition of ofp_desc_stats_request (empty class)
487 \"""
488 def __init__(self):
489 pass
490 def pack(self, assertstruct=True):
491 return ""
492 def unpack(self, binary_string):
493 return binary_string
494 def __len__(self):
495 return 0
496 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800497 return prefix + "ofp_desc_stats_request (empty)\\n"
Dan Talaycof75360a2010-02-05 22:22:54 -0800498 def __eq__(self, other):
499 return type(self) == type(other)
500 def __ne__(self, other):
501 return type(self) != type(other)
502
503OFP_DESC_STATS_REQUEST_BYTES = 0
504
505class ofp_table_stats_request:
506 \"""
507 Forced definition of ofp_table_stats_request (empty class)
508 \"""
509 def __init__(self):
510 pass
511 def pack(self, assertstruct=True):
512 return ""
513 def unpack(self, binary_string):
514 return binary_string
515 def __len__(self):
516 return 0
517 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800518 return prefix + "ofp_table_stats_request (empty)\\n"
Dan Talaycof75360a2010-02-05 22:22:54 -0800519 def __eq__(self, other):
520 return type(self) == type(other)
521 def __ne__(self, other):
522 return type(self) != type(other)
523
524OFP_TABLE_STATS_REQUEST_BYTES = 0
525
526"""
527
528stats_request_template = """
529class --TYPE--_stats_request(ofp_stats_request, ofp_--TYPE--_stats_request):
530 \"""
531 Wrapper class for --TYPE-- stats request message
532 \"""
Rich Lanec3c2ae12013-01-04 10:13:17 -0800533 def __init__(self, **kwargs):
Dan Talaycof75360a2010-02-05 22:22:54 -0800534 ofp_stats_request.__init__(self)
535 ofp_--TYPE--_stats_request.__init__(self)
Rich Laneb73808c2013-03-11 15:22:23 -0700536 self.version = OFP_VERSION
537 self.type = OFPT_STATS_REQUEST
Rich Lane8fbfd662013-03-11 15:30:44 -0700538 self.xid = None
Rich Lane7c7342a2013-03-11 14:16:58 -0700539 self.stats_type = --STATS_NAME--
Rich Lanec3c2ae12013-01-04 10:13:17 -0800540 for (k, v) in kwargs.items():
541 if hasattr(self, k):
542 setattr(self, k, v)
543 else:
544 raise NameError("field %s does not exist in %s" % (k, self.__class__))
Dan Talaycof75360a2010-02-05 22:22:54 -0800545
546 def pack(self, assertstruct=True):
Rich Laneb73808c2013-03-11 15:22:23 -0700547 self.length = len(self)
548 packed = ""
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800549 packed += ofp_stats_request.pack(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800550 packed += ofp_--TYPE--_stats_request.pack(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800551 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800552
553 def unpack(self, binary_string):
554 binary_string = ofp_stats_request.unpack(self, binary_string)
555 binary_string = ofp_--TYPE--_stats_request.unpack(self, binary_string)
556 if len(binary_string) != 0:
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800557 print "ERROR unpacking --TYPE--: extra data"
Dan Talaycof75360a2010-02-05 22:22:54 -0800558 return binary_string
559
560 def __len__(self):
Rich Laneb73808c2013-03-11 15:22:23 -0700561 return OFP_STATS_REQUEST_BYTES + \\
Dan Talaycof75360a2010-02-05 22:22:54 -0800562 OFP_--TYPE_UPPER--_STATS_REQUEST_BYTES
563
564 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800565 outstr = prefix + "--TYPE--_stats_request\\n"
566 outstr += prefix + "ofp header:\\n"
Dan Talayco46755fa2010-03-09 21:44:29 -0800567 outstr += ofp_stats_request.show(self)
568 outstr += ofp_--TYPE--_stats_request.show(self)
569 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800570
571 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800572 if type(self) != type(other): return False
Rich Laneb73808c2013-03-11 15:22:23 -0700573 return (ofp_stats_request.__eq__(self, other) and
Dan Talaycof75360a2010-02-05 22:22:54 -0800574 ofp_--TYPE--_stats_request.__eq__(self, other))
575
576 def __ne__(self, other): return not self.__eq__(other)
577"""
578
579################################################################
580#
581# Stats replies always have an array at the end.
582# For aggregate and desc, these arrays are always of length 1
583# This array is always called stats
584#
585################################################################
586
587
588# Template for objects stats reply messages
589stats_reply_template = """
590class --TYPE--_stats_reply(ofp_stats_reply):
591 \"""
592 Wrapper class for --TYPE-- stats reply
593 \"""
594 def __init__(self):
Dan Talaycof75360a2010-02-05 22:22:54 -0800595 ofp_stats_reply.__init__(self)
Rich Laneb73808c2013-03-11 15:22:23 -0700596 self.version = OFP_VERSION
597 self.type = OFPT_STATS_REPLY
Rich Lane8fbfd662013-03-11 15:30:44 -0700598 self.xid = None
Rich Laneb73808c2013-03-11 15:22:23 -0700599 self.stats_type = --STATS_NAME--
Dan Talaycof75360a2010-02-05 22:22:54 -0800600 # stats: Array of type --TYPE--_stats_entry
Rich Lane5fd6faf2013-03-11 13:30:20 -0700601 self.entries = []
Dan Talaycof75360a2010-02-05 22:22:54 -0800602
603 def pack(self, assertstruct=True):
Rich Laneb73808c2013-03-11 15:22:23 -0700604 self.length = len(self)
605 packed = ""
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800606 packed += ofp_stats_reply.pack(self)
Rich Lane5fd6faf2013-03-11 13:30:20 -0700607 for obj in self.entries:
Dan Talaycof75360a2010-02-05 22:22:54 -0800608 packed += obj.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800609 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800610
611 def unpack(self, binary_string):
612 binary_string = ofp_stats_reply.unpack(self, binary_string)
613 dummy = --TYPE--_stats_entry()
614 while len(binary_string) >= len(dummy):
615 obj = --TYPE--_stats_entry()
616 binary_string = obj.unpack(binary_string)
Rich Lane5fd6faf2013-03-11 13:30:20 -0700617 self.entries.append(obj)
Dan Talaycof75360a2010-02-05 22:22:54 -0800618 if len(binary_string) != 0:
619 print "ERROR unpacking --TYPE-- stats string: extra bytes"
620 return binary_string
621
622 def __len__(self):
Rich Laneb73808c2013-03-11 15:22:23 -0700623 length = OFP_STATS_REPLY_BYTES
Rich Lane5fd6faf2013-03-11 13:30:20 -0700624 for obj in self.entries:
Dan Talaycof75360a2010-02-05 22:22:54 -0800625 length += len(obj)
626 return length
627
628 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800629 outstr = prefix + "--TYPE--_stats_reply\\n"
630 outstr += prefix + "ofp header:\\n"
Dan Talayco46755fa2010-03-09 21:44:29 -0800631 outstr += ofp_stats_reply.show(self)
Rich Lane5fd6faf2013-03-11 13:30:20 -0700632 outstr += prefix + "Stats array of length " + str(len(self.entries)) + '\\n'
633 for obj in self.entries:
Dan Talayco46755fa2010-03-09 21:44:29 -0800634 outstr += obj.show()
635 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800636
637 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800638 if type(self) != type(other): return False
Rich Laneb73808c2013-03-11 15:22:23 -0700639 return (ofp_stats_reply.__eq__(self, other) and
Rich Lane5fd6faf2013-03-11 13:30:20 -0700640 self.entries == other.entries)
Dan Talaycof75360a2010-02-05 22:22:54 -0800641
642 def __ne__(self, other): return not self.__eq__(other)
643"""
644
645#
646# To address variations in stats reply bodies, the following
647# "_entry" classes are defined for each element in the reply
648#
649
650extra_stats_entry_defs = """
651# Stats entries define the content of one element in a stats
652# reply for the indicated type; define _entry for consistency
653
654aggregate_stats_entry = ofp_aggregate_stats_reply
655desc_stats_entry = ofp_desc_stats
656port_stats_entry = ofp_port_stats
657queue_stats_entry = ofp_queue_stats
658table_stats_entry = ofp_table_stats
659"""
660
661# Special case flow_stats to handle actions_list
662
663flow_stats_entry_def = """
664#
665# Flow stats entry contains an action list of variable length, so
666# it is done by hand
667#
668
669class flow_stats_entry(ofp_flow_stats):
670 \"""
671 Special case flow stats entry to handle action list object
672 \"""
673 def __init__(self):
674 ofp_flow_stats.__init__(self)
Rich Lane62e96852013-03-11 12:04:45 -0700675 self.actions = []
Dan Talaycof75360a2010-02-05 22:22:54 -0800676
677 def pack(self, assertstruct=True):
Dan Talayco6d2470b2010-02-07 22:59:49 -0800678 self.length = len(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800679 packed = ofp_flow_stats.pack(self, assertstruct)
Rich Lane62e96852013-03-11 12:04:45 -0700680 packed += action_list(self.actions).pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800681 if len(packed) != self.length:
682 print("ERROR: flow_stats_entry pack length not equal",
683 self.length, len(packed))
Dan Talaycof75360a2010-02-05 22:22:54 -0800684 return packed
685
686 def unpack(self, binary_string):
687 binary_string = ofp_flow_stats.unpack(self, binary_string)
688 ai_len = self.length - OFP_FLOW_STATS_BYTES
Dan Talayco6d2470b2010-02-07 22:59:49 -0800689 if ai_len < 0:
690 print("ERROR: flow_stats_entry unpack length too small",
691 self.length)
Rich Lane62e96852013-03-11 12:04:45 -0700692 obj = action_list()
693 binary_string = obj.unpack(binary_string, bytes=ai_len)
694 self.actions = list(obj)
Dan Talaycof75360a2010-02-05 22:22:54 -0800695 return binary_string
696
697 def __len__(self):
698 return OFP_FLOW_STATS_BYTES + len(self.actions)
699
700 def show(self, prefix=''):
Dan Talayco46755fa2010-03-09 21:44:29 -0800701 outstr = prefix + "flow_stats_entry\\n"
702 outstr += ofp_flow_stats.show(self, prefix + ' ')
Rich Lanee6ea3fe2013-03-08 17:54:38 -0800703 outstr += prefix + "List actions\\n"
704 for obj in self.actions:
705 outstr += obj.show(prefix + ' ')
Dan Talayco46755fa2010-03-09 21:44:29 -0800706 return outstr
Dan Talaycof75360a2010-02-05 22:22:54 -0800707
708 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800709 if type(self) != type(other): return False
Dan Talaycof75360a2010-02-05 22:22:54 -0800710 return (ofp_flow_stats.__eq__(self, other) and
711 self.actions == other.actions)
712
713 def __ne__(self, other): return not self.__eq__(other)
714"""
715
716stats_types = [
717 'aggregate',
718 'desc',
719 'flow',
720 'port',
721 'queue',
722 'table']
723
724if __name__ == '__main__':
725
726 print message_top_matter
727
728 print """
729################################################################
730#
731# OpenFlow Message Definitions
732#
733################################################################
734"""
735
736 msg_types = message_class_map.keys()
737 msg_types.sort()
738
739 for t in msg_types:
740 gen_message_wrapper(t)
741 print
742
743 print """
744################################################################
745#
746# Stats request and reply subclass definitions
747#
748################################################################
749"""
750
751 print extra_ofp_stats_req_defs
752 print extra_stats_entry_defs
753 print flow_stats_entry_def
754
755 # Generate stats request and reply subclasses
756 for t in stats_types:
757 stats_name = "OFPST_" + t.upper()
758 to_print = re.sub('--TYPE--', t, stats_request_template)
759 to_print = re.sub('--TYPE_UPPER--', t.upper(), to_print)
760 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
761 print to_print
762 to_print = re.sub('--TYPE--', t, stats_reply_template)
763 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
764 print to_print
765
Dan Talayco411489d2010-02-12 23:03:46 -0800766 # Lastly, generate a tuple containing all the message classes
767 print """
768message_type_list = (
769 aggregate_stats_reply,
770 aggregate_stats_request,
771 bad_action_error_msg,
772 bad_request_error_msg,
773 barrier_reply,
774 barrier_request,
775 desc_stats_reply,
776 desc_stats_request,
777 echo_reply,
778 echo_request,
779 features_reply,
780 features_request,
781 flow_mod,
782 flow_mod_failed_error_msg,
783 flow_removed,
784 flow_stats_reply,
785 flow_stats_request,
786 get_config_reply,
787 get_config_request,
788 hello,
789 hello_failed_error_msg,
790 packet_in,
791 packet_out,
792 port_mod,
793 port_mod_failed_error_msg,
794 port_stats_reply,
795 port_stats_request,
796 port_status,
797 queue_get_config_reply,
798 queue_get_config_request,
799 queue_op_failed_error_msg,
800 queue_stats_reply,
801 queue_stats_request,
802 set_config,
803 table_stats_reply,
804 table_stats_request,
805 vendor
806 )
807"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800808
Rich Laneba3f0e22013-03-11 16:43:57 -0700809 print """
810_flow_mod = flow_mod
811flow_mod = None
812
813class flow_add(_flow_mod):
814 def __init__(self, **kwargs):
815 _flow_mod.__init__(self, **kwargs)
816 self.command = OFPFC_ADD
817
818class flow_modify(_flow_mod):
819 def __init__(self, **kwargs):
820 _flow_mod.__init__(self, **kwargs)
821 self.command = OFPFC_MODIFY
822
823class flow_modify_strict(_flow_mod):
824 def __init__(self, **kwargs):
825 _flow_mod.__init__(self, **kwargs)
826 self.command = OFPFC_MODIFY_STRICT
827
828class flow_delete(_flow_mod):
829 def __init__(self, **kwargs):
830 _flow_mod.__init__(self, **kwargs)
831 self.command = OFPFC_DELETE
832
833class flow_delete_strict(_flow_mod):
834 def __init__(self, **kwargs):
835 _flow_mod.__init__(self, **kwargs)
836 self.command = OFPFC_DELETE_STRICT
837"""
838
Dan Talaycof75360a2010-02-05 22:22:54 -0800839#
840# OFP match variants
841# ICMP 0x801 (?) ==> icmp_type/code replace tp_src/dst
842#
843
844