Describe dummy test model. Work on OpenFlow intro.
[openvswitch] / build-aux / extract-ofp-msgs
1 #! /usr/bin/python
2
3 import sys
4 import os.path
5 import re
6
7 line = ""
8
9 OFP10_VERSION = 0x01
10 OFP11_VERSION = 0x02
11 OFP12_VERSION = 0x03
12 OFP13_VERSION = 0x04
13
14 NX_VENDOR_ID = 0x00002320
15
16 OFPT_VENDOR = 4
17 OFPT10_STATS_REQUEST = 16
18 OFPT10_STATS_REPLY = 17
19 OFPT11_STATS_REQUEST = 18
20 OFPT11_STATS_REPLY = 19
21 OFPST_VENDOR = 0xffff
22
23 version_map = {"1.0":     (OFP10_VERSION, OFP10_VERSION),
24                "1.1":     (OFP11_VERSION, OFP11_VERSION),
25                "1.2":     (OFP12_VERSION, OFP12_VERSION),
26                "1.3":     (OFP13_VERSION, OFP13_VERSION),
27                "1.0+":    (OFP10_VERSION, OFP13_VERSION),
28                "1.1+":    (OFP11_VERSION, OFP13_VERSION),
29                "1.2+":    (OFP12_VERSION, OFP13_VERSION),
30                "1.3+":    (OFP13_VERSION, OFP13_VERSION),
31                "1.0-1.1": (OFP10_VERSION, OFP11_VERSION),
32                "1.0-1.2": (OFP10_VERSION, OFP12_VERSION),
33                "1.1-1.2": (OFP11_VERSION, OFP12_VERSION),
34                "<all>":   (0x01, 0xff)}
35
36 def get_line():
37     global line
38     global line_number
39     line = input_file.readline()
40     line_number += 1
41     if line == "":
42         fatal("unexpected end of input")
43
44 n_errors = 0
45 def error(msg):
46     global n_errors
47     sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
48     n_errors += 1
49
50 def fatal(msg):
51     error(msg)
52     sys.exit(1)
53
54 def usage():
55     argv0 = os.path.basename(sys.argv[0])
56     print '''\
57 %(argv0)s, for extracting OpenFlow message types from header files
58 usage: %(argv0)s INPUT OUTPUT
59   where INPUT is the name of the input header file
60     and OUTPUT is the output file name.
61 Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
62 only controls #line directives in the output.\
63 ''' % {"argv0": argv0}
64     sys.exit(0)
65
66 def make_sizeof(s):
67     m = re.match(r'(.*) up to (.*)', s)
68     if m:
69         struct, member = m.groups()
70         return "offsetof(%s, %s)" % (struct, member)
71     else:
72         return "sizeof(%s)" % s
73
74 def extract_ofp_msgs(output_file_name):
75     raw_types = []
76
77     all_hdrs = {}
78     all_raws = {}
79     all_raws_order = []
80
81     while True:
82         get_line()
83         if re.match('enum ofpraw', line):
84             break
85
86     while True:
87         get_line()
88         first_line_number = line_number
89         here = '%s:%d' % (file_name, line_number)
90         if (line.startswith('/*')
91             or line.startswith(' *')
92             or not line
93             or line.isspace()):
94             continue
95         elif re.match('}', line):
96             break
97
98         if not line.lstrip().startswith('/*'):
99             fatal("unexpected syntax between ofpraw types")
100
101         comment = line.lstrip()[2:].strip()
102         while not comment.endswith('*/'):
103             get_line()
104             if line.startswith('/*') or not line or line.isspace():
105                 fatal("unexpected syntax within error")
106             comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
107         comment = comment[:-2].rstrip()
108
109         m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment)
110         if not m:
111             fatal("unexpected syntax between messages")
112         type_, versions, number, contents = m.groups()
113         number = int(number)
114
115         get_line()
116         m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
117                      line)
118         if not m:
119             fatal("syntax error expecting OFPRAW_ enum")
120         vinfix, name = m.groups()
121         rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
122
123         min_version, max_version = version_map[versions]
124
125         human_name = '%s_%s' % (type_, name)
126         if type_.endswith('ST'):
127             if rawname.endswith('_REQUEST'):
128                 human_name = human_name[:-8] + " request"
129             elif rawname.endswith('_REPLY'):
130                 human_name = human_name[:-6] + " reply"
131             else:
132                 fatal("%s messages are statistics but %s doesn't end "
133                       "in _REQUEST or _REPLY" % (type_, rawname))
134
135         these_hdrs = []
136         for version in range(min_version, max_version + 1):
137             if type_ == 'OFPT':
138                 if number == OFPT_VENDOR:
139                     fatal("OFPT (%d) is used for vendor extensions"
140                           % number)
141                 elif (version == OFP10_VERSION
142                       and (number == OFPT10_STATS_REQUEST
143                            or number == OFPT10_STATS_REPLY)):
144                     fatal("OFPT 1.0 (%d) is used for stats messages"
145                           % number)
146                 elif (version != OFP10_VERSION
147                       and (number == OFPT11_STATS_REQUEST
148                            or number == OFPT11_STATS_REPLY)):
149                     fatal("OFPT 1.1+ (%d) is used for stats messages"
150                           % number)
151                 hdrs = (version, number, 0, 0, 0)
152             elif type_ == 'OFPST' and name.endswith('_REQUEST'):
153                 if version == OFP10_VERSION:
154                     hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
155                 else:
156                     hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
157             elif type_ == 'OFPST' and name.endswith('_REPLY'):
158                 if version == OFP10_VERSION:
159                     hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
160                 else:
161                     hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
162             elif type_ == 'NXT':
163                 hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
164             elif type_ == 'NXST' and name.endswith('_REQUEST'):
165                 if version == OFP10_VERSION:
166                     hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
167                             NX_VENDOR_ID, number)
168                 else:
169                     hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
170                             NX_VENDOR_ID, number)
171             elif type_ == 'NXST' and name.endswith('_REPLY'):
172                 if version == OFP10_VERSION:
173                     hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
174                             NX_VENDOR_ID, number)
175                 else:
176                     hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
177                             NX_VENDOR_ID, number)
178             else:
179                 fatal("type '%s' unknown" % type_)
180
181             if hdrs in all_hdrs:
182                 error("Duplicate message definition for %s." % str(hdrs))
183                 sys.stderr.write("%s: Here is the location "
184                                  "of the previous definition.\n"
185                                  % (all_hdrs[hdrs]))
186             all_hdrs[hdrs] = here
187             these_hdrs.append(hdrs)
188
189         extra_multiple = '0'
190         if contents == 'void':
191             min_body = '0'
192         else:
193             min_body_elem = []
194             for c in [s.strip() for s in contents.split(",")]:
195                 if c.endswith('[]'):
196                     if extra_multiple == '0':
197                         extra_multiple = make_sizeof(c[:-2])
198                     else:
199                         error("Cannot have multiple [] elements")
200                 else:
201                     min_body_elem.append(c)
202
203             if min_body_elem:
204                 min_body = " + ".join([make_sizeof(s)
205                                        for s in min_body_elem])
206             else:
207                 if extra_multiple == '0':
208                     error("Must specify contents (use 'void' if empty)")
209                 min_body = 0
210
211         if rawname in all_raws:
212             fatal("%s: Duplicate name" % rawname)
213
214         all_raws[rawname] = {"hdrs": these_hdrs,
215                              "min_version": min_version,
216                              "max_version": max_version,
217                              "min_body": min_body,
218                              "extra_multiple": extra_multiple,
219                              "type": type_,
220                              "human_name": human_name,
221                              "line": first_line_number}
222         all_raws_order.append(rawname)
223
224         continue
225
226     while True:
227         get_line()
228         if re.match('enum ofptype', line):
229             break
230
231     while True:
232         get_line()
233         if re.match(r'\s*/?\*', line) or line.isspace():
234             continue
235         elif re.match('}', line):
236             break
237
238         if not re.match(r'\s*OFPTYPE_.*/\*', line):
239             fatal("unexpected syntax between OFPTYPE_ definitions")
240
241         syntax = line.strip()
242         while not syntax.endswith('*/'):
243             get_line()
244             if not line.strip().startswith('*'):
245                 fatal("unexpected syntax within OFPTYPE_ definition")
246             syntax += ' %s' % line.strip().lstrip('* \t')
247             syntax = syntax.strip()
248
249         m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
250         if not m:
251             fatal("syntax error in OFPTYPE_ definition")
252
253         ofptype, raws_ = m.groups()
254         raws = [s.rstrip('.') for s in raws_.split()]
255         for raw in raws:
256             if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
257                 fatal("%s: invalid OFPRAW_* name syntax" % raw)
258             if raw not in all_raws:
259                 fatal("%s: not a declared OFPRAW_* name" % raw)
260             if "ofptype" in all_raws[raw]:
261                 fatal("%s: already part of %s"
262                       % (raw, all_raws[raw]["ofptype"]))
263             all_raws[raw]["ofptype"] = ofptype
264
265     input_file.close()
266
267     if n_errors:
268         sys.exit(1)
269
270     output = []
271     output.append("/* Generated automatically; do not modify!     "
272                   "-*- buffer-read-only: t -*- */")
273     output.append("")
274
275     for raw in all_raws_order:
276         r = all_raws[raw]
277         output.append("static struct raw_instance %s_instances[] = {"
278                       % raw.lower())
279         for hdrs in r['hdrs']:
280             output.append("    { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
281                           % (hdrs + (raw,)))
282                 
283         output.append("};")
284
285     output.append("")
286
287     output.append("static struct raw_info raw_infos[] = {")
288     for raw in all_raws_order:
289         r = all_raws[raw]
290         if "ofptype" not in r:
291             error("%s: no defined OFPTYPE_" % raw)
292             continue
293         output.append("    {")
294         output.append("        %s_instances," % raw.lower())
295         output.append("        %d, %d," % (r["min_version"], r["max_version"]))
296         output.append("#line %s \"%s\"" % (r["line"], file_name))
297         output.append("        %s," % r["min_body"])
298         output.append("#line %s \"%s\"" % (r["line"], file_name))
299         output.append("        %s," % r["extra_multiple"])
300         output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
301         output.append("        %s," % r["ofptype"])
302         output.append("        \"%s\"," % r["human_name"])
303         output.append("    },")
304
305         if r['type'].endswith("ST"):
306             for hdrs in r['hdrs']:
307                 op_hdrs = list(hdrs)
308                 if hdrs[0] == OFP10_VERSION:
309                     if hdrs[1] == OFPT10_STATS_REQUEST:
310                         op_hdrs[1] = OFPT10_STATS_REPLY
311                     elif hdrs[1] == OFPT10_STATS_REPLY:
312                         op_hdrs[1] = OFPT10_STATS_REQUEST
313                     else:
314                         assert False
315                 else:
316                     if hdrs[1] == OFPT11_STATS_REQUEST:
317                         op_hdrs[1] = OFPT11_STATS_REPLY
318                     elif hdrs[1] == OFPT11_STATS_REPLY:
319                         op_hdrs[1] = OFPT11_STATS_REQUEST
320                     else:
321                         assert False
322                 if tuple(op_hdrs) not in all_hdrs:
323                     if r["human_name"].endswith("request"):
324                         fatal("%s has no corresponding reply"
325                               % r["human_name"])
326                     else:
327                         fatal("%s has no corresponding request"
328                               % r["human_name"])
329     output.append("};")
330
331     if n_errors:
332         sys.exit(1)
333
334     return output
335
336
337 if __name__ == '__main__':
338     if '--help' in sys.argv:
339         usage()
340     elif len(sys.argv) != 3:
341         sys.stderr.write("exactly one non-option arguments required; "
342                          "use --help for help\n")
343         sys.exit(1)
344     else:
345         global file_name
346         global input_file
347         global line_number
348         file_name = sys.argv[1]
349         input_file = open(file_name)
350         line_number = 0
351
352         for line in extract_ofp_msgs(sys.argv[2]):
353             print line
354