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