blob: a839e77a2d9f5b21e38c4104b3366e0fd60bab60 [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
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080083sys.path.append("../../src/python/oftest")
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 \"""
165 Display the contents of the object in a readable manner
166
167 @param prefix Printed at the beginning of each line.
168
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
259 has_core_members = False
260 has_string = False # Has trailing string
261 if parent != 'ofp_header':
262 has_core_members = True
263 if msg in list_members.keys():
264 (list_var, list_type) = list_members[msg]
265 has_list = True
266 if msg in string_members:
267 has_string = True
268
269 if has_core_members:
270 print "class " + msg + "(" + parent + "):"
271 else:
272 print "class " + msg + ":"
273 _p1('"""')
274 _p1("Wrapper class for " + msg)
275 print
Dan Talaycoac1cb812010-02-06 20:34:18 -0800276 _p1("OpenFlow message header: length, version, xid, type")
277 _p1("@arg length: The total length of the message")
278 _p1("@arg version: The OpenFlow version (" + str(OFP_VERSION) + ")")
279 _p1("@arg xid: The transaction ID")
280 _p1("@arg type: The message type (" + msg_name + "=" +
281 str(eval(msg_name)) + ")")
282 print
283 if has_core_members and parent in class_to_members_map.keys():
284 _p1("Data members inherited from " + parent + ":")
285 for var in class_to_members_map[parent]:
286 _p1("@arg " + var)
Dan Talaycof75360a2010-02-05 22:22:54 -0800287 if has_list:
288 if list_type == None:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800289 _p1("@arg " + list_var + ": Variable length array of TBD")
Dan Talaycof75360a2010-02-05 22:22:54 -0800290 else:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800291 _p1("@arg " + list_var + ": Object of type " + list_type);
Dan Talaycof75360a2010-02-05 22:22:54 -0800292 if has_string:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800293 _p1("@arg data: Binary string following message members")
294 print
Dan Talaycof75360a2010-02-05 22:22:54 -0800295 _p1('"""')
296
297 print
298 _p1("def __init__(self):")
299 if has_core_members:
300 _p2(parent + ".__init__(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800301 _p2("self.header = ofp_header()")
302 _p2("self.header.type = " + msg_name)
303 if has_list:
304 if list_type == None:
305 _p2('self.' + list_var + ' = []')
306 else:
307 _p2('self.' + list_var + ' = ' + list_type + '()')
308 if has_string:
309 _p2('self.data = ""')
310
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 \"""
320 self.header.length = len(self)
321 packed = self.header.pack()
322"""
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':
326 _p2('self.actions_len = len(self.actions)')
Dan Talaycof75360a2010-02-05 22:22:54 -0800327 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800328 _p2("packed += " + parent + ".pack(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800329 if has_list:
330 if list_type == None:
331 _p2('for obj in self.' + list_var + ':')
332 _p3('packed += obj.pack()')
333 else:
334 _p2('packed += self.' + list_var + '.pack()')
335 if has_string:
336 _p2('packed += self.data')
Dan Talayco6d2470b2010-02-07 22:59:49 -0800337 _p2("return packed")
Dan Talaycof75360a2010-02-05 22:22:54 -0800338
Dan Talaycob9cb5482010-02-09 15:23:12 -0800339 print """
340 def unpack(self, binary_string):
341 \"""
342 Unpack object from a binary string
343
344 @param binary_string The wire protocol byte string holding the object
345 represented as an array of bytes.
Dan Talayco411489d2010-02-12 23:03:46 -0800346 @return The remainder of binary_string that was not parsed.
Dan Talaycob9cb5482010-02-09 15:23:12 -0800347
348 \"""
349 binary_string = self.header.unpack(binary_string)
350"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800351 if has_core_members:
352 _p2("binary_string = " + parent + ".unpack(self, binary_string)")
353 if has_list:
354 if list_type == None:
355 _p2("for obj in self." + list_var + ":")
356 _p3("binary_string = obj.unpack(binary_string)")
357 elif msg == "packet_out": # Special case this
Dan Talayco6d2470b2010-02-07 22:59:49 -0800358 _p2('binary_string = self.actions.unpack(' +
359 'binary_string, bytes=self.actions_len)')
Dan Talaycof75360a2010-02-05 22:22:54 -0800360 elif msg == "flow_mod": # Special case this
Dan Talayco6d2470b2010-02-07 22:59:49 -0800361 _p2("ai_len = self.header.length - (OFP_FLOW_MOD_BYTES + " +
362 "OFP_HEADER_BYTES)")
363 _p2("binary_string = self.actions.unpack(binary_string, " +
364 "bytes=ai_len)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800365 else:
366 _p2("binary_string = self." + list_var + ".unpack(binary_string)")
367 if has_string:
368 _p2("self.data = binary_string")
369 _p2("binary_string = ''")
370 else:
371 _p2("# Fixme: If no self.data, add check for data remaining")
372 _p2("return binary_string")
373
Dan Talaycob9cb5482010-02-09 15:23:12 -0800374 print """
375 def __len__(self):
376 \"""
377 Return the length of this object once packed into a string
378
379 @return An integer representing the number bytes in the packed
380 string.
381
382 \"""
383 length = OFP_HEADER_BYTES
384"""
Dan Talayco6d2470b2010-02-07 22:59:49 -0800385 if has_core_members:
386 _p2("length += " + parent + ".__len__(self)")
387 if has_list:
388 if list_type == None:
389 _p2("for obj in self." + list_var + ":")
390 _p3("length += len(obj)")
391 else:
392 _p2("length += len(self." + list_var + ")")
393 if has_string:
394 _p2("length += len(self.data)")
395 _p2("return length")
Dan Talaycof75360a2010-02-05 22:22:54 -0800396
Dan Talaycob9cb5482010-02-09 15:23:12 -0800397 print """
398 ##@todo Convert this to __str__
399 def show(self, prefix=''):
400 \"""
401 Display the contents of the object in a readable manner
402
403 @param prefix Printed at the beginning of each line.
404
405 \"""
406"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800407 _p2("print prefix + '" + msg + " (" + msg_name + ")'")
408 _p2("prefix += ' '")
Dan Talayco4557d782010-02-18 15:18:01 -0800409 _p2("print prefix + 'ofp header'")
410 _p2("self.header.show(prefix + ' ')")
Dan Talaycof75360a2010-02-05 22:22:54 -0800411 if has_core_members:
412 _p2(parent + ".show(self, prefix)")
413 if has_list:
414 if list_type == None:
415 _p2('print prefix + "Array ' + list_var + '"')
416 _p2('for obj in self.' + list_var +':')
417 _p3("obj.show(prefix + ' ')")
418 else:
419 _p2('print prefix + "List ' + list_var + '"')
420 _p2('self.' + list_var + ".show(prefix + ' ')")
421 if has_string:
422 _p2("print prefix + 'data is of length ' + str(len(self.data))")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800423 _p2("##@todo Fix this circular reference")
424 _p2("# if len(self.data) > 0:")
425 _p3("# obj = of_message_parse(self.data)")
426 _p3("# if obj != None:")
427 _p4("# obj.show(prefix)")
428 _p3("# else:")
429 _p4('# print prefix + "Unable to parse data"')
Dan Talaycof75360a2010-02-05 22:22:54 -0800430
Dan Talaycob9cb5482010-02-09 15:23:12 -0800431 print """
432 def __eq__(self, other):
433 \"""
434 Return True if self and other hold the same data
435
436 @param other Other object in comparison
437
438 \"""
439 if type(self) != type(other): return False
440 if not self.header.__eq__(other.header): return False
441"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800442 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800443 _p2("if not " + parent + ".__eq__(self, other): return False")
Dan Talaycof75360a2010-02-05 22:22:54 -0800444 if has_string:
445 _p2("if self.data != other.data: return False")
446 if has_list:
447 _p2("if self." + list_var + " != other." + list_var + ": return False")
448 _p2("return True")
449
Dan Talaycob9cb5482010-02-09 15:23:12 -0800450 print """
451 def __ne__(self, other):
452 \"""
453 Return True if self and other do not hold the same data
Dan Talaycof75360a2010-02-05 22:22:54 -0800454
Dan Talaycob9cb5482010-02-09 15:23:12 -0800455 @param other Other object in comparison
456
457 \"""
458 return not self.__eq__(other)
459 """
Dan Talaycof75360a2010-02-05 22:22:54 -0800460
461
462################################################################
463#
464# Stats request subclasses
465# description_request, flow, aggregate, table, port, vendor
466#
467################################################################
468
469# table and desc stats requests are special with empty body
470extra_ofp_stats_req_defs = """
471# Stats request bodies for desc and table stats are not defined in the
472# OpenFlow header; We define them here. They are empty classes, really
473
474class ofp_desc_stats_request:
475 \"""
476 Forced definition of ofp_desc_stats_request (empty class)
477 \"""
478 def __init__(self):
479 pass
480 def pack(self, assertstruct=True):
481 return ""
482 def unpack(self, binary_string):
483 return binary_string
484 def __len__(self):
485 return 0
486 def show(self, prefix=''):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800487 print prefix + "ofp_desc_stats_request (empty)"
Dan Talaycof75360a2010-02-05 22:22:54 -0800488 def __eq__(self, other):
489 return type(self) == type(other)
490 def __ne__(self, other):
491 return type(self) != type(other)
492
493OFP_DESC_STATS_REQUEST_BYTES = 0
494
495class ofp_table_stats_request:
496 \"""
497 Forced definition of ofp_table_stats_request (empty class)
498 \"""
499 def __init__(self):
500 pass
501 def pack(self, assertstruct=True):
502 return ""
503 def unpack(self, binary_string):
504 return binary_string
505 def __len__(self):
506 return 0
507 def show(self, prefix=''):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800508 print prefix + "ofp_table_stats_request (empty)"
Dan Talaycof75360a2010-02-05 22:22:54 -0800509 def __eq__(self, other):
510 return type(self) == type(other)
511 def __ne__(self, other):
512 return type(self) != type(other)
513
514OFP_TABLE_STATS_REQUEST_BYTES = 0
515
516"""
517
518stats_request_template = """
519class --TYPE--_stats_request(ofp_stats_request, ofp_--TYPE--_stats_request):
520 \"""
521 Wrapper class for --TYPE-- stats request message
522 \"""
523 def __init__(self):
524 self.header = ofp_header()
525 ofp_stats_request.__init__(self)
526 ofp_--TYPE--_stats_request.__init__(self)
527 self.header.type = OFPT_STATS_REQUEST
528 self.type = --STATS_NAME--
529
530 def pack(self, assertstruct=True):
Dan Talayco2f820be2010-03-07 11:36:29 -0800531 self.header.length = len(self)
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800532 packed = self.header.pack()
533 packed += ofp_stats_request.pack(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800534 packed += ofp_--TYPE--_stats_request.pack(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800535 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800536
537 def unpack(self, binary_string):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800538 binary_string = self.header.unpack(binary_string)
Dan Talaycof75360a2010-02-05 22:22:54 -0800539 binary_string = ofp_stats_request.unpack(self, binary_string)
540 binary_string = ofp_--TYPE--_stats_request.unpack(self, binary_string)
541 if len(binary_string) != 0:
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800542 print "ERROR unpacking --TYPE--: extra data"
Dan Talaycof75360a2010-02-05 22:22:54 -0800543 return binary_string
544
545 def __len__(self):
546 return len(self.header) + OFP_STATS_REQUEST_BYTES + \\
547 OFP_--TYPE_UPPER--_STATS_REQUEST_BYTES
548
549 def show(self, prefix=''):
550 print prefix + "--TYPE--_stats_request"
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800551 print prefix + "ofp header:"
552 self.header.show(prefix + ' ')
Dan Talaycof75360a2010-02-05 22:22:54 -0800553 ofp_stats_request.show(self)
554 ofp_--TYPE--_stats_request.show(self)
555
556 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800557 if type(self) != type(other): return False
558 return (self.header == other.header and
559 ofp_stats_request.__eq__(self, other) and
Dan Talaycof75360a2010-02-05 22:22:54 -0800560 ofp_--TYPE--_stats_request.__eq__(self, other))
561
562 def __ne__(self, other): return not self.__eq__(other)
563"""
564
565################################################################
566#
567# Stats replies always have an array at the end.
568# For aggregate and desc, these arrays are always of length 1
569# This array is always called stats
570#
571################################################################
572
573
574# Template for objects stats reply messages
575stats_reply_template = """
576class --TYPE--_stats_reply(ofp_stats_reply):
577 \"""
578 Wrapper class for --TYPE-- stats reply
579 \"""
580 def __init__(self):
581 self.header = ofp_header()
582 ofp_stats_reply.__init__(self)
583 self.header.type = OFPT_STATS_REPLY
584 self.type = --STATS_NAME--
585 # stats: Array of type --TYPE--_stats_entry
586 self.stats = []
587
588 def pack(self, assertstruct=True):
Dan Talayco2f820be2010-03-07 11:36:29 -0800589 self.header.length = len(self)
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800590 packed = self.header.pack()
591 packed += ofp_stats_reply.pack(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800592 for obj in self.stats:
593 packed += obj.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800594 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800595
596 def unpack(self, binary_string):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800597 binary_string = self.header.unpack(binary_string)
Dan Talaycof75360a2010-02-05 22:22:54 -0800598 binary_string = ofp_stats_reply.unpack(self, binary_string)
599 dummy = --TYPE--_stats_entry()
600 while len(binary_string) >= len(dummy):
601 obj = --TYPE--_stats_entry()
602 binary_string = obj.unpack(binary_string)
603 self.stats.append(obj)
604 if len(binary_string) != 0:
605 print "ERROR unpacking --TYPE-- stats string: extra bytes"
606 return binary_string
607
608 def __len__(self):
609 length = len(self.header) + OFP_STATS_REPLY_BYTES
610 for obj in self.stats:
611 length += len(obj)
612 return length
613
614 def show(self, prefix=''):
615 print prefix + "--TYPE--_stats_reply"
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800616 print prefix + "ofp header:"
617 self.header.show(prefix + ' ')
Dan Talaycof75360a2010-02-05 22:22:54 -0800618 ofp_stats_reply.show(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800619 print prefix + "Stats array of length " + str(len(self.stats))
Dan Talaycof75360a2010-02-05 22:22:54 -0800620 for obj in self.stats:
621 obj.show()
622
623 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800624 if type(self) != type(other): return False
625 return (self.header == other.header and
626 ofp_stats_reply.__eq__(self, other) and
627 self.stats == other.stats)
Dan Talaycof75360a2010-02-05 22:22:54 -0800628
629 def __ne__(self, other): return not self.__eq__(other)
630"""
631
632#
633# To address variations in stats reply bodies, the following
634# "_entry" classes are defined for each element in the reply
635#
636
637extra_stats_entry_defs = """
638# Stats entries define the content of one element in a stats
639# reply for the indicated type; define _entry for consistency
640
641aggregate_stats_entry = ofp_aggregate_stats_reply
642desc_stats_entry = ofp_desc_stats
643port_stats_entry = ofp_port_stats
644queue_stats_entry = ofp_queue_stats
645table_stats_entry = ofp_table_stats
646"""
647
648# Special case flow_stats to handle actions_list
649
650flow_stats_entry_def = """
651#
652# Flow stats entry contains an action list of variable length, so
653# it is done by hand
654#
655
656class flow_stats_entry(ofp_flow_stats):
657 \"""
658 Special case flow stats entry to handle action list object
659 \"""
660 def __init__(self):
661 ofp_flow_stats.__init__(self)
662 self.actions = action_list()
663
664 def pack(self, assertstruct=True):
Dan Talayco6d2470b2010-02-07 22:59:49 -0800665 self.length = len(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800666 packed = ofp_flow_stats.pack(self, assertstruct)
667 packed += self.actions.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800668 if len(packed) != self.length:
669 print("ERROR: flow_stats_entry pack length not equal",
670 self.length, len(packed))
Dan Talaycof75360a2010-02-05 22:22:54 -0800671 return packed
672
673 def unpack(self, binary_string):
674 binary_string = ofp_flow_stats.unpack(self, binary_string)
675 ai_len = self.length - OFP_FLOW_STATS_BYTES
Dan Talayco6d2470b2010-02-07 22:59:49 -0800676 if ai_len < 0:
677 print("ERROR: flow_stats_entry unpack length too small",
678 self.length)
Dan Talaycof75360a2010-02-05 22:22:54 -0800679 binary_string = self.actions.unpack(binary_string, bytes=ai_len)
680 return binary_string
681
682 def __len__(self):
683 return OFP_FLOW_STATS_BYTES + len(self.actions)
684
685 def show(self, prefix=''):
686 print prefix + "flow_stats_entry"
687 ofp_flow_stats.show(self, prefix + ' ')
688 self.actions.show(prefix + ' ')
689
690 def __eq__(self, other):
Dan Talayco36f2f1f2010-02-10 22:40:26 -0800691 if type(self) != type(other): return False
Dan Talaycof75360a2010-02-05 22:22:54 -0800692 return (ofp_flow_stats.__eq__(self, other) and
693 self.actions == other.actions)
694
695 def __ne__(self, other): return not self.__eq__(other)
696"""
697
698stats_types = [
699 'aggregate',
700 'desc',
701 'flow',
702 'port',
703 'queue',
704 'table']
705
706if __name__ == '__main__':
707
708 print message_top_matter
709
710 print """
711################################################################
712#
713# OpenFlow Message Definitions
714#
715################################################################
716"""
717
718 msg_types = message_class_map.keys()
719 msg_types.sort()
720
721 for t in msg_types:
722 gen_message_wrapper(t)
723 print
724
725 print """
726################################################################
727#
728# Stats request and reply subclass definitions
729#
730################################################################
731"""
732
733 print extra_ofp_stats_req_defs
734 print extra_stats_entry_defs
735 print flow_stats_entry_def
736
737 # Generate stats request and reply subclasses
738 for t in stats_types:
739 stats_name = "OFPST_" + t.upper()
740 to_print = re.sub('--TYPE--', t, stats_request_template)
741 to_print = re.sub('--TYPE_UPPER--', t.upper(), to_print)
742 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
743 print to_print
744 to_print = re.sub('--TYPE--', t, stats_reply_template)
745 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
746 print to_print
747
Dan Talayco411489d2010-02-12 23:03:46 -0800748 # Lastly, generate a tuple containing all the message classes
749 print """
750message_type_list = (
751 aggregate_stats_reply,
752 aggregate_stats_request,
753 bad_action_error_msg,
754 bad_request_error_msg,
755 barrier_reply,
756 barrier_request,
757 desc_stats_reply,
758 desc_stats_request,
759 echo_reply,
760 echo_request,
761 features_reply,
762 features_request,
763 flow_mod,
764 flow_mod_failed_error_msg,
765 flow_removed,
766 flow_stats_reply,
767 flow_stats_request,
768 get_config_reply,
769 get_config_request,
770 hello,
771 hello_failed_error_msg,
772 packet_in,
773 packet_out,
774 port_mod,
775 port_mod_failed_error_msg,
776 port_stats_reply,
777 port_stats_request,
778 port_status,
779 queue_get_config_reply,
780 queue_get_config_request,
781 queue_op_failed_error_msg,
782 queue_stats_reply,
783 queue_stats_request,
784 set_config,
785 table_stats_reply,
786 table_stats_request,
787 vendor
788 )
789"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800790
791#
792# OFP match variants
793# ICMP 0x801 (?) ==> icmp_type/code replace tp_src/dst
794#
795
796