9 # Map from OpenFlow version number to version ID used in ofp_header.
10 version_map = {"1.0": 0x01,
14 version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
18 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
19 tokenRe = "#?" + idRe + "|[0-9]+|."
28 inputFile = open(fileName)
35 line = inputFile.readline()
41 fatal("unexpected end of input")
51 if line.startswith("/*"):
55 commentEnd = line.find("*/")
60 line = line[commentEnd + 2:]
62 match = re.match(tokenRe, line)
63 token = match.group(0)
64 line = line[len(token):]
65 if token.startswith('#'):
67 elif token in macros and not inDirective:
68 line = macros[token] + line
77 line = inputFile.readline()
79 while line.endswith("\\\n"):
80 line = line[:-2] + inputFile.readline()
84 fatal("unexpected end of input")
91 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
104 return re.match(idRe + "$", s) != None
108 fatal("identifier expected")
111 if not re.match('[0-9]+$', token):
112 fatal("integer expected")
123 fatal("%s expected" % t)
125 def parseTaggedName():
126 assert token in ('struct', 'union')
130 name = "%s %s" % (name, token)
134 def print_enum(tag, constants, storage_class):
136 %(storage_class)sconst char *
137 %(tag)s_to_string(uint16_t value)
141 "bufferlen": len(tag) + 32,
142 "storage_class": storage_class})
143 for constant in constants:
144 print (" case %s: return \"%s\";" % (constant, constant))
152 argv0 = os.path.basename(sys.argv[0])
154 %(argv0)s, for extracting OpenFlow error codes from header files
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})
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):
210 for domain_name in version_map.values():
211 domain[domain_name] = {}
212 reverse[domain_name] = {}
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('*/'):
237 if line.startswith('/*') or not line or line.isspace():
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]
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():
336 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
343 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
345 #define OFPERR_N_ERRORS %d
347 struct ofperr_domain {
350 enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
351 struct triplet errors[OFPERR_N_ERRORS];
354 static const char *error_names[OFPERR_N_ERRORS] = {
358 static const char *error_comments[OFPERR_N_ERRORS] = {
362 '\n'.join(' "%s",' % name for name in names),
363 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
364 for comment in comments)))
366 def output_domain(map, name, description, version):
369 %s_decode(uint32_t vendor, uint16_t type, uint16_t code)
371 switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
376 vendor, type_, code = map[enum]
379 value = (vendor << 32) | (type_ << 16) | code
384 vendor_s = "(%#xULL << 32) | " % vendor
387 print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code))
388 print (" return OFPERR_%s;" % enum)
396 static const struct ofperr_domain %s = {
400 {""" % (name, description, version, name))
403 vendor, type_, code = map[enum]
406 print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
408 print (" { -1, -1, -1 }, /* %s */" % enum)
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_)
418 if __name__ == '__main__':
419 if '--help' in sys.argv:
421 elif len(sys.argv) != 3:
422 sys.stderr.write("exactly two non-options arguments required; "
423 "use --help for help\n")
426 extract_vendor_ids(sys.argv[2])
427 extract_ofp_errors(sys.argv[1])