115
134
def print_enum(tag, constants, storage_class):
117
136
%(storage_class)sconst char *
118
137
%(tag)s_to_string(uint16_t value)
120
139
switch (value) {\
121
140
""" % {"tag": tag,
122
141
"bufferlen": len(tag) + 32,
123
"storage_class": storage_class}
142
"storage_class": storage_class})
124
143
for constant in constants:
125
print " case %s: return \"%s\";" % (constant, constant)
144
print (" case %s: return \"%s\";" % (constant, constant))
133
152
argv0 = os.path.basename(sys.argv[0])
135
154
%(argv0)s, for extracting OpenFlow error codes from header files
136
usage: %(argv0)s FILE [FILE...]
138
This program reads the header files specified on the command line and
139
outputs a C source file for translating OpenFlow error codes into
140
strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
142
This program is specialized for reading lib/ofp-errors.h. It will not
143
work on arbitrary header files without extensions.\
144
''' % {"argv0": argv0}
155
usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
157
This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
158
experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
159
It outputs a C source file for translating OpenFlow error codes into
162
ERROR_HEADER should point to lib/ofp-errors.h.
163
VENDOR_HEADER should point to include/openflow/openflow-common.h.
164
The output is suitable for use as lib/ofp-errors.inc.\
165
''' % {"argv0": argv0})
147
def extract_ofp_errors(filenames):
168
def extract_vendor_ids(fn):
175
m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
180
id_ = int(m.group(2), 0)
182
if name in vendor_map:
183
error("%s: duplicate definition of vendor" % name)
184
sys.stderr.write("%s: Here is the location of the previous "
185
"definition.\n" % vendor_loc[name])
188
vendor_map[name] = id_
189
vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
192
fatal("%s: no vendor definitions found" % fn)
196
vendor_reverse_map = {}
197
for name, id_ in vendor_map.items():
198
if id_ in vendor_reverse_map:
199
fatal("%s: duplicate vendor id for vendors %s and %s"
200
% (id_, vendor_reverse_map[id_], name))
201
vendor_reverse_map[id_] = name
203
def extract_ofp_errors(fn):
154
for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3",
155
"NX1.0", "NX1.1", "NX1.2", "NX1.3"):
210
for domain_name in version_map.values():
156
211
domain[domain_name] = {}
157
212
reverse[domain_name] = {}
160
215
expected_errors = {}
163
for fileName in filenames:
166
inputFile = open(fileName)
171
if re.match('enum ofperr', line):
221
if re.match('enum ofperr', line):
226
if line.startswith('/*') or not line or line.isspace():
228
elif re.match('}', line):
231
if not line.lstrip().startswith('/*'):
232
fatal("unexpected syntax between errors")
234
comment = line.lstrip()[2:].strip()
235
while not comment.endswith('*/'):
176
237
if line.startswith('/*') or not line or line.isspace():
178
elif re.match('}', line):
181
if not line.lstrip().startswith('/*'):
182
fatal("unexpected syntax between errors")
184
comment = line.lstrip()[2:].strip()
185
while not comment.endswith('*/'):
187
if line.startswith('/*') or not line or line.isspace():
188
fatal("unexpected syntax within error")
189
comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
190
comment = comment[:-2].rstrip()
192
m = re.match('Expected: (.*)\.$', comment)
194
expected_errors[m.group(1)] = (fileName, lineNumber)
197
m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
199
fatal("unexpected syntax between errors")
201
dsts, comment = m.groups()
204
m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
207
fatal("syntax error expecting enum value")
211
comments.append(re.sub('\[[^]]*\]', '', comment))
214
for dst in dsts.split(', '):
215
m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst)
217
fatal("%s: syntax error in destination" % dst)
223
type_ = int(m.group(2), base)
225
code = int(m.group(4))
229
target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
230
"OF1.1+": ("OF1.1", "OF1.2", "OF1.3"),
231
"OF1.2+": ("OF1.2", "OF1.3"),
232
"OF1.3+": ("OF1.3",),
237
"NX1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
238
"NX1.1+": ("OF1.1", "OF1.2", "OF1.3"),
239
"NX1.2+": ("OF1.2", "OF1.3"),
240
"NX1.3+": ("OF1.3",),
245
if targets not in target_map:
246
fatal("%s: unknown error domain" % targets)
247
if targets.startswith('NX') and code < 0x100:
248
fatal("%s: NX domain code cannot be less than 0x100" % dst)
249
if targets.startswith('OF') and code >= 0x100:
250
fatal("%s: OF domain code cannot be greater than 0x100"
252
for target in target_map[targets]:
253
domain[target].setdefault(type_, {})
254
if code in domain[target][type_]:
255
msg = "%d,%d in %s means both %s and %s" % (
257
domain[target][type_][code][0], enum)
258
if msg in expected_errors:
259
del expected_errors[msg]
261
error("%s: %s." % (dst, msg))
262
sys.stderr.write("%s:%d: %s: Here is the location "
263
"of the previous definition.\n"
264
% (domain[target][type_][code][1],
265
domain[target][type_][code][2],
238
fatal("unexpected syntax within error")
239
comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
240
comment = comment[:-2].rstrip()
242
m = re.match('Expected: (.*)\.$', comment)
244
expected_errors[m.group(1)] = (fileName, lineNumber)
247
m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
249
fatal("unexpected syntax between errors")
251
dsts, comment = m.groups()
254
m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
257
fatal("syntax error expecting enum value")
261
fatal("%s specified twice" % enum)
263
comments.append(re.sub('\[[^]]*\]', '', comment))
266
for dst in dsts.split(', '):
267
m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
269
fatal("%r: syntax error in destination" % dst)
270
vendor_name = m.group(1)
271
version1_name = m.group(2)
272
version2_name = m.group(3)
273
type_ = int(m.group(4))
275
code = int(m.group(5))
279
if vendor_name not in vendor_map:
280
fatal("%s: unknown vendor" % vendor_name)
281
vendor = vendor_map[vendor_name]
283
if version1_name not in version_map:
284
fatal("%s: unknown OpenFlow version" % version1_name)
285
v1 = version_map[version1_name]
287
if version2_name is None:
289
elif version2_name == "+":
290
v2 = max(version_map.values())
291
elif version2_name[1:] not in version_map:
292
fatal("%s: unknown OpenFlow version" % version2_name[1:])
294
v2 = version_map[version2_name[1:]]
297
fatal("%s%s: %s precedes %s"
298
% (version1_name, version2_name,
299
version2_name, version1_name))
301
if vendor == vendor_map['NX']:
302
if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
304
fatal("%s: NX1.2+ domains do not have codes" % dst)
306
elif vendor != vendor_map['OF']:
308
fatal("%s: %s domains do not have codes" % vendor_name)
310
for version in range(v1, v2 + 1):
311
domain[version].setdefault(vendor, {})
312
domain[version][vendor].setdefault(type_, {})
313
if code in domain[version][vendor][type_]:
314
msg = "%#x,%d,%d in OF%s means both %s and %s" % (
315
vendor, type_, code, version_reverse_map[version],
316
domain[version][vendor][type_][code][0], enum)
317
if msg in expected_errors:
318
del expected_errors[msg]
268
domain[target][type_][code] = (enum, fileName,
271
if enum in reverse[target]:
272
error("%s: %s in %s means both %d,%d and %d,%d." %
274
reverse[target][enum][0],
275
reverse[target][enum][1],
277
reverse[target][enum] = (type_, code)
281
for fn, ln in expected_errors.itervalues():
320
error("%s: %s." % (dst, msg))
321
sys.stderr.write("%s:%d: %s: Here is the location "
322
"of the previous definition.\n"
323
% (domain[version][vendor][type_][code][1],
324
domain[version][vendor][type_][code][2],
327
domain[version][vendor][type_][code] = (enum, fileName,
330
assert enum not in reverse[version]
331
reverse[version][enum] = (vendor, type_, code)
335
for fn, ln in expected_errors.values():
282
336
sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
289
343
/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
291
345
#define OFPERR_N_ERRORS %d
307
361
""" % (len(names),
308
362
'\n'.join(' "%s",' % name for name in names),
309
363
'\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
310
for comment in comments))
364
for comment in comments)))
312
366
def output_domain(map, name, description, version):
314
368
static enum ofperr
315
%s_decode(uint16_t type, uint16_t code)
369
%s_decode(uint32_t vendor, uint16_t type, uint16_t code)
317
switch ((type << 16) | code) {""" % name
371
switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
319
373
for enum in names:
320
374
if enum not in map:
322
type_, code = map[enum]
376
vendor, type_, code = map[enum]
325
value = (type_ << 16) | code
379
value = (vendor << 32) | (type_ << 16) | code
326
380
if value in found:
329
print " case (%d << 16) | %d:" % (type_, code)
330
print " return OFPERR_%s;" % enum
384
vendor_s = "(%#xULL << 32) | " % vendor
387
print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code))
388
print (" return OFPERR_%s;" % enum)
338
396
static const struct ofperr_domain %s = {
342
{""" % (name, description, version, name)
400
{""" % (name, description, version, name))
343
401
for enum in names:
345
type_, code = map[enum]
403
vendor, type_, code = map[enum]
406
print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
350
print " { %2d, %3d }, /* %s */" % (type_, code, enum)
408
print (" { -1, -1, -1 }, /* %s */" % enum)
355
output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
356
output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
357
output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
358
output_domain(reverse["OF1.3"], "ofperr_of13", "OpenFlow 1.3", 0x04)
413
for version_name, id_ in version_map.items():
414
var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
415
description = "OpenFlow %s" % version_name
416
output_domain(reverse[id_], var, description, id_)
360
418
if __name__ == '__main__':
361
419
if '--help' in sys.argv:
363
elif len(sys.argv) < 2:
364
sys.stderr.write("at least one non-option argument required; "
421
elif len(sys.argv) != 3:
422
sys.stderr.write("exactly two non-options arguments required; "
365
423
"use --help for help\n")
368
extract_ofp_errors(sys.argv[1:])
426
extract_vendor_ids(sys.argv[2])
427
extract_ofp_errors(sys.argv[1])