]>
Commit | Line | Data |
---|---|---|
982697a4 BP |
1 | #! /usr/bin/python |
2 | ||
3 | import sys | |
4 | import os.path | |
5 | import re | |
6 | ||
7 | line = "" | |
8 | ||
42dccab5 BP |
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} | |
982697a4 BP |
16 | |
17 | NX_VENDOR_ID = 0x00002320 | |
90219696 | 18 | ONF_VENDOR_ID = 0x4f4e4600 |
982697a4 BP |
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 | ||
42dccab5 BP |
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]) | |
982697a4 BP |
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(): | |
91148d4b | 107 | fatal("unexpected syntax within message") |
982697a4 BP |
108 | comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') |
109 | comment = comment[:-2].rstrip() | |
110 | ||
cf7cf630 | 111 | m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment) |
982697a4 BP |
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 | ||
42dccab5 | 125 | min_version, max_version = decode_version_range(versions) |
982697a4 BP |
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) | |
42dccab5 | 143 | elif (version == VERSION["1.0"] |
982697a4 BP |
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) | |
42dccab5 | 148 | elif (version != VERSION["1.0"] |
982697a4 BP |
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'): | |
42dccab5 | 155 | if version == VERSION["1.0"]: |
982697a4 BP |
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'): | |
42dccab5 | 160 | if version == VERSION["1.0"]: |
982697a4 BP |
161 | hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0) |
162 | else: | |
163 | hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0) | |
90219696 SH |
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) | |
982697a4 BP |
180 | elif type_ == 'NXT': |
181 | hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number) | |
182 | elif type_ == 'NXST' and name.endswith('_REQUEST'): | |
42dccab5 | 183 | if version == VERSION["1.0"]: |
982697a4 BP |
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'): | |
42dccab5 | 190 | if version == VERSION["1.0"]: |
982697a4 BP |
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) | |
42dccab5 | 326 | if hdrs[0] == VERSION["1.0"]: |
982697a4 BP |
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: | |
91148d4b | 359 | sys.stderr.write("exactly two non-option arguments required; " |
982697a4 BP |
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 |