9 # Map from OpenFlow version number to version ID used in ofp_header.
10 version_map = {"1.0": 0x01,
15 version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
19 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
20 tokenRe = "#?" + idRe + "|[0-9]+|."
29 inputFile = open(fileName)
36 line = inputFile.readline()
42 fatal("unexpected end of input")
52 if line.startswith("/*"):
56 commentEnd = line.find("*/")
61 line = line[commentEnd + 2:]
63 match = re.match(tokenRe, line)
64 token = match.group(0)
65 line = line[len(token):]
66 if token.startswith('#'):
68 elif token in macros and not inDirective:
69 line = macros[token] + line
78 line = inputFile.readline()
80 while line.endswith("\\\n"):
81 line = line[:-2] + inputFile.readline()
85 fatal("unexpected end of input")
92 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
105 return re.match(idRe + "$", s) != None
109 fatal("identifier expected")
112 if not re.match('[0-9]+$', token):
113 fatal("integer expected")
124 fatal("%s expected" % t)
126 def parseTaggedName():
127 assert token in ('struct', 'union')
131 name = "%s %s" % (name, token)
135 def print_enum(tag, constants, storage_class):
137 %(storage_class)sconst char *
138 %(tag)s_to_string(uint16_t value)
142 "bufferlen": len(tag) + 32,
143 "storage_class": storage_class})
144 for constant in constants:
145 print (" case %s: return \"%s\";" % (constant, constant))
153 argv0 = os.path.basename(sys.argv[0])
155 %(argv0)s, for extracting OpenFlow error codes from header files
156 usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
158 This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
159 experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
160 It outputs a C source file for translating OpenFlow error codes into
163 ERROR_HEADER should point to lib/ofp-errors.h.
164 VENDOR_HEADER should point to include/openflow/openflow-common.h.
165 The output is suitable for use as lib/ofp-errors.inc.\
166 ''' % {"argv0": argv0})
169 def extract_vendor_ids(fn):
176 m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
181 id_ = int(m.group(2), 0)
183 if name in vendor_map:
184 error("%s: duplicate definition of vendor" % name)
185 sys.stderr.write("%s: Here is the location of the previous "
186 "definition.\n" % vendor_loc[name])
189 vendor_map[name] = id_
190 vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
193 fatal("%s: no vendor definitions found" % fn)
197 vendor_reverse_map = {}
198 for name, id_ in vendor_map.items():
199 if id_ in vendor_reverse_map:
200 fatal("%s: duplicate vendor id for vendors %s and %s"
201 % (id_, vendor_reverse_map[id_], name))
202 vendor_reverse_map[id_] = name
204 def extract_ofp_errors(fn):
211 for domain_name in version_map.values():
212 domain[domain_name] = {}
213 reverse[domain_name] = {}
222 if re.match('enum ofperr', line):
227 if line.startswith('/*') or not line or line.isspace():
229 elif re.match('}', line):
232 if not line.lstrip().startswith('/*'):
233 fatal("unexpected syntax between errors")
235 comment = line.lstrip()[2:].strip()
236 while not comment.endswith('*/'):
238 if line.startswith('/*') or not line or line.isspace():
239 fatal("unexpected syntax within error")
240 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
241 comment = comment[:-2].rstrip()
243 m = re.match('Expected: (.*)\.$', comment)
245 expected_errors[m.group(1)] = (fileName, lineNumber)
248 m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
250 fatal("unexpected syntax between errors")
252 dsts, comment = m.groups()
255 m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
258 fatal("syntax error expecting enum value")
262 fatal("%s specified twice" % enum)
264 comments.append(re.sub('\[[^]]*\]', '', comment))
267 for dst in dsts.split(', '):
268 m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
270 fatal("%r: syntax error in destination" % dst)
271 vendor_name = m.group(1)
272 version1_name = m.group(2)
273 version2_name = m.group(3)
274 type_ = int(m.group(4))
276 code = int(m.group(5))
280 if vendor_name not in vendor_map:
281 fatal("%s: unknown vendor" % vendor_name)
282 vendor = vendor_map[vendor_name]
284 if version1_name not in version_map:
285 fatal("%s: unknown OpenFlow version" % version1_name)
286 v1 = version_map[version1_name]
288 if version2_name is None:
290 elif version2_name == "+":
291 v2 = max(version_map.values())
292 elif version2_name[1:] not in version_map:
293 fatal("%s: unknown OpenFlow version" % version2_name[1:])
295 v2 = version_map[version2_name[1:]]
298 fatal("%s%s: %s precedes %s"
299 % (version1_name, version2_name,
300 version2_name, version1_name))
302 if vendor == vendor_map['NX']:
303 if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
305 fatal("%s: NX1.2+ domains do not have codes" % dst)
307 elif vendor != vendor_map['OF']:
309 fatal("%s: %s domains do not have codes" % vendor_name)
311 for version in range(v1, v2 + 1):
312 domain[version].setdefault(vendor, {})
313 domain[version][vendor].setdefault(type_, {})
314 if code in domain[version][vendor][type_]:
315 msg = "%#x,%d,%d in OF%s means both %s and %s" % (
316 vendor, type_, code, version_reverse_map[version],
317 domain[version][vendor][type_][code][0], enum)
318 if msg in expected_errors:
319 del expected_errors[msg]
321 error("%s: %s." % (dst, msg))
322 sys.stderr.write("%s:%d: %s: Here is the location "
323 "of the previous definition.\n"
324 % (domain[version][vendor][type_][code][1],
325 domain[version][vendor][type_][code][2],
328 domain[version][vendor][type_][code] = (enum, fileName,
331 assert enum not in reverse[version]
332 reverse[version][enum] = (vendor, type_, code)
336 for fn, ln in expected_errors.values():
337 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
344 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
346 #define OFPERR_N_ERRORS %d
348 struct ofperr_domain {
351 enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
352 struct triplet errors[OFPERR_N_ERRORS];
355 static const char *error_names[OFPERR_N_ERRORS] = {
359 static const char *error_comments[OFPERR_N_ERRORS] = {
363 '\n'.join(' "%s",' % name for name in names),
364 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
365 for comment in comments)))
367 def output_domain(map, name, description, version):
370 %s_decode(uint32_t vendor, uint16_t type, uint16_t code)
372 switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
377 vendor, type_, code = map[enum]
380 value = (vendor << 32) | (type_ << 16) | code
385 vendor_s = "(%#xULL << 32) | " % vendor
388 print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code))
389 print (" return OFPERR_%s;" % enum)
397 static const struct ofperr_domain %s = {
401 {""" % (name, description, version, name))
404 vendor, type_, code = map[enum]
407 print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
409 print (" { -1, -1, -1 }, /* %s */" % enum)
414 for version_name, id_ in version_map.items():
415 var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
416 description = "OpenFlow %s" % version_name
417 output_domain(reverse[id_], var, description, id_)
419 if __name__ == '__main__':
420 if '--help' in sys.argv:
422 elif len(sys.argv) != 3:
423 sys.stderr.write("exactly two non-options arguments required; "
424 "use --help for help\n")
427 extract_vendor_ids(sys.argv[2])
428 extract_ofp_errors(sys.argv[1])