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