blob: d56f20b399084d866f88b4a2778bdc3b8e36dec8 [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 Talaycob9cb5482010-02-09 15:23:12 -080083sys.path.append("../../src/python/oftest/protocol")
84from 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
92
Dan Talaycof75360a2010-02-05 22:22:54 -080093# Define templates for documentation
94class ofp_template_msg:
95 \"""
96 Sample base class for template_msg; normally auto generated
97 This class should live in the of_header name space and provides the
98 base class for this type of message. It will be wrapped for the
99 high level API.
100
101 \"""
102 def __init__(self):
103 \"""
104 Constructor for base class
105
106 \"""
107 self.header = ofp_header()
108 # 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)
Dan Talaycoac1cb812010-02-06 20:34:18 -0800130 self.header = ofp_header()
Dan Talaycof75360a2010-02-05 22:22:54 -0800131 # For a real message, will be set to an integer
132 self.header.type = "TEMPLATE_MSG_VALUE"
133 def pack(self):
134 \"""
135 Pack object into string
136
137 @return The packed string which can go on the wire
138
139 \"""
140 pass
141 def unpack(self, binary_string):
142 \"""
143 Unpack object from a binary string
144
145 @param binary_string The wire protocol byte string holding the object
146 represented as an array of bytes.
147
148 @return Typically returns the remainder of binary_string that
149 was not parsed. May give a warning if that string is non-empty
150
151 \"""
152 pass
153 def __len__(self):
154 \"""
155 Return the length of this object once packed into a string
156
157 @return An integer representing the number bytes in the packed
158 string.
159
160 \"""
161 pass
162 def show(self, prefix=''):
163 \"""
164 Display the contents of the object in a readable manner
165
166 @param prefix Printed at the beginning of each line.
167
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
258 has_core_members = False
259 has_string = False # Has trailing string
260 if parent != 'ofp_header':
261 has_core_members = True
262 if msg in list_members.keys():
263 (list_var, list_type) = list_members[msg]
264 has_list = True
265 if msg in string_members:
266 has_string = True
267
268 if has_core_members:
269 print "class " + msg + "(" + parent + "):"
270 else:
271 print "class " + msg + ":"
272 _p1('"""')
273 _p1("Wrapper class for " + msg)
274 print
Dan Talaycoac1cb812010-02-06 20:34:18 -0800275 _p1("OpenFlow message header: length, version, xid, type")
276 _p1("@arg length: The total length of the message")
277 _p1("@arg version: The OpenFlow version (" + str(OFP_VERSION) + ")")
278 _p1("@arg xid: The transaction ID")
279 _p1("@arg type: The message type (" + msg_name + "=" +
280 str(eval(msg_name)) + ")")
281 print
282 if has_core_members and parent in class_to_members_map.keys():
283 _p1("Data members inherited from " + parent + ":")
284 for var in class_to_members_map[parent]:
285 _p1("@arg " + var)
Dan Talaycof75360a2010-02-05 22:22:54 -0800286 if has_list:
287 if list_type == None:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800288 _p1("@arg " + list_var + ": Variable length array of TBD")
Dan Talaycof75360a2010-02-05 22:22:54 -0800289 else:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800290 _p1("@arg " + list_var + ": Object of type " + list_type);
Dan Talaycof75360a2010-02-05 22:22:54 -0800291 if has_string:
Dan Talaycoac1cb812010-02-06 20:34:18 -0800292 _p1("@arg data: Binary string following message members")
293 print
Dan Talaycof75360a2010-02-05 22:22:54 -0800294 _p1('"""')
295
296 print
297 _p1("def __init__(self):")
298 if has_core_members:
299 _p2(parent + ".__init__(self)")
Dan Talaycoac1cb812010-02-06 20:34:18 -0800300 _p2("##@var header")
301 _p2("# OpenFlow message header: length, version, xid, type")
302 _p2("# @arg length: The total length of the message")
303 _p2("# @arg version: The OpenFlow version (" + str(OFP_VERSION) + ")")
304 _p2("# @arg xid: The transaction ID")
305 _p2("# @arg type: The message type (" + msg_name + "=" +
306 str(eval(msg_name)) + ")")
307 print
308 if has_list:
309 _p2("##@var " + list_var)
310 if list_type == None:
311 _p2("# Array of objects of type TBD")
312 else:
313 _p2("# Object of type " + list_type)
314 print
315 if has_string:
316 _p2("##@var data")
317 _p2("# Binary string following message members")
318 print
319
Dan Talaycof75360a2010-02-05 22:22:54 -0800320 _p2("self.header = ofp_header()")
321 _p2("self.header.type = " + msg_name)
322 if has_list:
323 if list_type == None:
324 _p2('self.' + list_var + ' = []')
325 else:
326 _p2('self.' + list_var + ' = ' + list_type + '()')
327 if has_string:
328 _p2('self.data = ""')
329
Dan Talaycob9cb5482010-02-09 15:23:12 -0800330 print """
331
332 def pack(self):
333 \"""
334 Pack object into string
335
336 @return The packed string which can go on the wire
337
338 \"""
339 self.header.length = len(self)
340 packed = self.header.pack()
341"""
342
Dan Talaycof75360a2010-02-05 22:22:54 -0800343 if has_core_members:
Dan Talayco6d2470b2010-02-07 22:59:49 -0800344 _p2("packed += " + parent + ".pack(self)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800345 if has_list:
346 if list_type == None:
347 _p2('for obj in self.' + list_var + ':')
348 _p3('packed += obj.pack()')
349 else:
350 _p2('packed += self.' + list_var + '.pack()')
351 if has_string:
352 _p2('packed += self.data')
Dan Talayco6d2470b2010-02-07 22:59:49 -0800353 _p2("return packed")
Dan Talaycof75360a2010-02-05 22:22:54 -0800354
Dan Talaycob9cb5482010-02-09 15:23:12 -0800355 print """
356 def unpack(self, binary_string):
357 \"""
358 Unpack object from a binary string
359
360 @param binary_string The wire protocol byte string holding the object
361 represented as an array of bytes.
362 @return Typically returns the remainder of binary_string that
363 was not parsed. May give a warning if that string is non-empty
364
365 \"""
366 binary_string = self.header.unpack(binary_string)
367"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800368 if has_core_members:
369 _p2("binary_string = " + parent + ".unpack(self, binary_string)")
370 if has_list:
371 if list_type == None:
372 _p2("for obj in self." + list_var + ":")
373 _p3("binary_string = obj.unpack(binary_string)")
374 elif msg == "packet_out": # Special case this
Dan Talayco6d2470b2010-02-07 22:59:49 -0800375 _p2('binary_string = self.actions.unpack(' +
376 'binary_string, bytes=self.actions_len)')
Dan Talaycof75360a2010-02-05 22:22:54 -0800377 elif msg == "flow_mod": # Special case this
Dan Talayco6d2470b2010-02-07 22:59:49 -0800378 _p2("ai_len = self.header.length - (OFP_FLOW_MOD_BYTES + " +
379 "OFP_HEADER_BYTES)")
380 _p2("binary_string = self.actions.unpack(binary_string, " +
381 "bytes=ai_len)")
Dan Talaycof75360a2010-02-05 22:22:54 -0800382 else:
383 _p2("binary_string = self." + list_var + ".unpack(binary_string)")
384 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:
405 if list_type == None:
406 _p2("for obj in self." + list_var + ":")
407 _p3("length += len(obj)")
408 else:
409 _p2("length += len(self." + list_var + ")")
410 if has_string:
411 _p2("length += len(self.data)")
412 _p2("return length")
Dan Talaycof75360a2010-02-05 22:22:54 -0800413
Dan Talaycob9cb5482010-02-09 15:23:12 -0800414 print """
415 ##@todo Convert this to __str__
416 def show(self, prefix=''):
417 \"""
418 Display the contents of the object in a readable manner
419
420 @param prefix Printed at the beginning of each line.
421
422 \"""
423"""
Dan Talaycof75360a2010-02-05 22:22:54 -0800424 _p2("print prefix + '" + msg + " (" + msg_name + ")'")
425 _p2("prefix += ' '")
426 _p2("self.header.show(prefix)")
427 if has_core_members:
428 _p2(parent + ".show(self, prefix)")
429 if has_list:
430 if list_type == None:
431 _p2('print prefix + "Array ' + list_var + '"')
432 _p2('for obj in self.' + list_var +':')
433 _p3("obj.show(prefix + ' ')")
434 else:
435 _p2('print prefix + "List ' + list_var + '"')
436 _p2('self.' + list_var + ".show(prefix + ' ')")
437 if has_string:
438 _p2("print prefix + 'data is of length ' + str(len(self.data))")
Dan Talayco6d2470b2010-02-07 22:59:49 -0800439 _p2("##@todo Fix this circular reference")
440 _p2("# if len(self.data) > 0:")
441 _p3("# obj = of_message_parse(self.data)")
442 _p3("# if obj != None:")
443 _p4("# obj.show(prefix)")
444 _p3("# else:")
445 _p4('# print prefix + "Unable to parse data"')
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=''):
503 pass
504 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=''):
524 pass
525 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 \"""
539 def __init__(self):
540 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--
545
546 def pack(self, assertstruct=True):
547 packed = ofp_stats_request.pack(self)
548 packed += ofp_--TYPE--_stats_request.pack(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800549 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800550
551 def unpack(self, binary_string):
552 binary_string = ofp_stats_request.unpack(self, binary_string)
553 binary_string = ofp_--TYPE--_stats_request.unpack(self, binary_string)
554 if len(binary_string) != 0:
555 print "Error unpacking --TYPE--: extra data"
556 return binary_string
557
558 def __len__(self):
559 return len(self.header) + OFP_STATS_REQUEST_BYTES + \\
560 OFP_--TYPE_UPPER--_STATS_REQUEST_BYTES
561
562 def show(self, prefix=''):
563 print prefix + "--TYPE--_stats_request"
564 ofp_stats_request.show(self)
565 ofp_--TYPE--_stats_request.show(self)
566
567 def __eq__(self, other):
568 return (ofp_stats_request.__eq__(self, other) and
569 ofp_--TYPE--_stats_request.__eq__(self, other))
570
571 def __ne__(self, other): return not self.__eq__(other)
572"""
573
574################################################################
575#
576# Stats replies always have an array at the end.
577# For aggregate and desc, these arrays are always of length 1
578# This array is always called stats
579#
580################################################################
581
582
583# Template for objects stats reply messages
584stats_reply_template = """
585class --TYPE--_stats_reply(ofp_stats_reply):
586 \"""
587 Wrapper class for --TYPE-- stats reply
588 \"""
589 def __init__(self):
590 self.header = ofp_header()
591 ofp_stats_reply.__init__(self)
592 self.header.type = OFPT_STATS_REPLY
593 self.type = --STATS_NAME--
594 # stats: Array of type --TYPE--_stats_entry
595 self.stats = []
596
597 def pack(self, assertstruct=True):
598 packed = ofp_stats_reply.pack(self)
599 for obj in self.stats:
600 packed += obj.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800601 return packed
Dan Talaycof75360a2010-02-05 22:22:54 -0800602
603 def unpack(self, binary_string):
604 binary_string = ofp_stats_reply.unpack(self, binary_string)
605 dummy = --TYPE--_stats_entry()
606 while len(binary_string) >= len(dummy):
607 obj = --TYPE--_stats_entry()
608 binary_string = obj.unpack(binary_string)
609 self.stats.append(obj)
610 if len(binary_string) != 0:
611 print "ERROR unpacking --TYPE-- stats string: extra bytes"
612 return binary_string
613
614 def __len__(self):
615 length = len(self.header) + OFP_STATS_REPLY_BYTES
616 for obj in self.stats:
617 length += len(obj)
618 return length
619
620 def show(self, prefix=''):
621 print prefix + "--TYPE--_stats_reply"
622 ofp_stats_reply.show(self)
Dan Talayco6d2470b2010-02-07 22:59:49 -0800623 print prefix + "Stats array of length " + str(len(self.stats))
Dan Talaycof75360a2010-02-05 22:22:54 -0800624 for obj in self.stats:
625 obj.show()
626
627 def __eq__(self, other):
Dan Talayco6d2470b2010-02-07 22:59:49 -0800628 if not ofp_stats_reply.__eq__(self, other): return False
Dan Talaycof75360a2010-02-05 22:22:54 -0800629 return self.stats == other.stats
630
631 def __ne__(self, other): return not self.__eq__(other)
632"""
633
634#
635# To address variations in stats reply bodies, the following
636# "_entry" classes are defined for each element in the reply
637#
638
639extra_stats_entry_defs = """
640# Stats entries define the content of one element in a stats
641# reply for the indicated type; define _entry for consistency
642
643aggregate_stats_entry = ofp_aggregate_stats_reply
644desc_stats_entry = ofp_desc_stats
645port_stats_entry = ofp_port_stats
646queue_stats_entry = ofp_queue_stats
647table_stats_entry = ofp_table_stats
648"""
649
650# Special case flow_stats to handle actions_list
651
652flow_stats_entry_def = """
653#
654# Flow stats entry contains an action list of variable length, so
655# it is done by hand
656#
657
658class flow_stats_entry(ofp_flow_stats):
659 \"""
660 Special case flow stats entry to handle action list object
661 \"""
662 def __init__(self):
663 ofp_flow_stats.__init__(self)
664 self.actions = action_list()
665
666 def pack(self, assertstruct=True):
Dan Talayco6d2470b2010-02-07 22:59:49 -0800667 self.length = len(self)
Dan Talaycof75360a2010-02-05 22:22:54 -0800668 packed = ofp_flow_stats.pack(self, assertstruct)
669 packed += self.actions.pack()
Dan Talayco6d2470b2010-02-07 22:59:49 -0800670 if len(packed) != self.length:
671 print("ERROR: flow_stats_entry pack length not equal",
672 self.length, len(packed))
Dan Talaycof75360a2010-02-05 22:22:54 -0800673 return packed
674
675 def unpack(self, binary_string):
676 binary_string = ofp_flow_stats.unpack(self, binary_string)
677 ai_len = self.length - OFP_FLOW_STATS_BYTES
Dan Talayco6d2470b2010-02-07 22:59:49 -0800678 if ai_len < 0:
679 print("ERROR: flow_stats_entry unpack length too small",
680 self.length)
Dan Talaycof75360a2010-02-05 22:22:54 -0800681 binary_string = self.actions.unpack(binary_string, bytes=ai_len)
682 return binary_string
683
684 def __len__(self):
685 return OFP_FLOW_STATS_BYTES + len(self.actions)
686
687 def show(self, prefix=''):
688 print prefix + "flow_stats_entry"
689 ofp_flow_stats.show(self, prefix + ' ')
690 self.actions.show(prefix + ' ')
691
692 def __eq__(self, other):
693 return (ofp_flow_stats.__eq__(self, other) and
694 self.actions == other.actions)
695
696 def __ne__(self, other): return not self.__eq__(other)
697"""
698
699stats_types = [
700 'aggregate',
701 'desc',
702 'flow',
703 'port',
704 'queue',
705 'table']
706
707if __name__ == '__main__':
708
709 print message_top_matter
710
711 print """
712################################################################
713#
714# OpenFlow Message Definitions
715#
716################################################################
717"""
718
719 msg_types = message_class_map.keys()
720 msg_types.sort()
721
722 for t in msg_types:
723 gen_message_wrapper(t)
724 print
725
726 print """
727################################################################
728#
729# Stats request and reply subclass definitions
730#
731################################################################
732"""
733
734 print extra_ofp_stats_req_defs
735 print extra_stats_entry_defs
736 print flow_stats_entry_def
737
738 # Generate stats request and reply subclasses
739 for t in stats_types:
740 stats_name = "OFPST_" + t.upper()
741 to_print = re.sub('--TYPE--', t, stats_request_template)
742 to_print = re.sub('--TYPE_UPPER--', t.upper(), to_print)
743 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
744 print to_print
745 to_print = re.sub('--TYPE--', t, stats_reply_template)
746 to_print = re.sub('--STATS_NAME--', stats_name, to_print)
747 print to_print
748
749
750#
751# OFP match variants
752# ICMP 0x801 (?) ==> icmp_type/code replace tp_src/dst
753#
754
755