9 # Map from OpenFlow version number to version ID used in ofp_header.
10 version_map = {"1.0": 0x01,
16 version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
20 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
21 tokenRe = "#?" + idRe + "|[0-9]+|."
30 inputFile = open(fileName)
37 line = inputFile.readline()
43 fatal("unexpected end of input")
53 if line.startswith("/*"):
57 commentEnd = line.find("*/")
62 line = line[commentEnd + 2:]
64 match = re.match(tokenRe, line)
65 token = match.group(0)
66 line = line[len(token):]
67 if token.startswith('#'):
69 elif token in macros and not inDirective:
70 line = macros[token] + line
79 line = inputFile.readline()
81 while line.endswith("\\\n"):
82 line = line[:-2] + inputFile.readline()
86 fatal("unexpected end of input")
93 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
106 return re.match(idRe + "$", s) != None
110 fatal("identifier expected")
113 if not re.match('[0-9]+$', token):
114 fatal("integer expected")
125 fatal("%s expected" % t)
127 def parseTaggedName():
128 assert token in ('struct', 'union')
132 name = "%s %s" % (name, token)
136 def print_enum(tag, constants, storage_class):
138 %(storage_class)sconst char *
139 %(tag)s_to_string(uint16_t value)
143 "bufferlen": len(tag) + 32,
144 "storage_class": storage_class})
145 for constant in constants:
146 print (" case %s: return \"%s\";" % (constant, constant))
154 argv0 = os.path.basename(sys.argv[0])
156 %(argv0)s, for extracting OpenFlow error codes from header files
157 usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
159 This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
160 experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
161 It outputs a C source file for translating OpenFlow error codes into
164 ERROR_HEADER should point to lib/ofp-errors.h.
165 VENDOR_HEADER should point to include/openflow/openflow-common.h.
166 The output is suitable for use as lib/ofp-errors.inc.\
167 ''' % {"argv0": argv0})
170 def extract_vendor_ids(fn):
177 m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
182 id_ = int(m.group(2), 0)
184 if name in vendor_map:
185 error("%s: duplicate definition of vendor" % name)
186 sys.stderr.write("%s: Here is the location of the previous "
187 "definition.\n" % vendor_loc[name])
190 vendor_map[name] = id_
191 vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
194 fatal("%s: no vendor definitions found" % fn)
198 vendor_reverse_map = {}
199 for name, id_ in vendor_map.items():
200 if id_ in vendor_reverse_map:
201 fatal("%s: duplicate vendor id for vendors %s and %s"
202 % (id_, vendor_reverse_map[id_], name))
203 vendor_reverse_map[id_] = name
205 def extract_ofp_errors(fn):
212 for domain_name in version_map.values():
213 domain[domain_name] = {}
214 reverse[domain_name] = {}
223 if re.match('enum ofperr', line):
228 if line.startswith('/*') or not line or line.isspace():
230 elif re.match('}', line):
233 if not line.lstrip().startswith('/*'):
234 fatal("unexpected syntax between errors")
236 comment = line.lstrip()[2:].strip()
237 while not comment.endswith('*/'):
239 if line.startswith('/*') or not line or line.isspace():
240 fatal("unexpected syntax within error")
241 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
242 comment = comment[:-2].rstrip()
244 m = re.match('Expected: (.*)\.$', comment)
246 expected_errors[m.group(1)] = (fileName, lineNumber)
249 m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
251 fatal("unexpected syntax between errors")
253 dsts, comment = m.groups()
256 m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
259 fatal("syntax error expecting enum value")
263 fatal("%s specified twice" % enum)
265 comments.append(re.sub('\[[^]]*\]', '', comment))
268 for dst in dsts.split(', '):
269 m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
271 fatal("%r: syntax error in destination" % dst)
272 vendor_name = m.group(1)
273 version1_name = m.group(2)
274 version2_name = m.group(3)
275 type_ = int(m.group(4))
277 code = int(m.group(5))
281 if vendor_name not in vendor_map:
282 fatal("%s: unknown vendor" % vendor_name)
283 vendor = vendor_map[vendor_name]
285 if version1_name not in version_map:
286 fatal("%s: unknown OpenFlow version" % version1_name)
287 v1 = version_map[version1_name]
289 if version2_name is None:
291 elif version2_name == "+":
292 v2 = max(version_map.values())
293 elif version2_name[1:] not in version_map:
294 fatal("%s: unknown OpenFlow version" % version2_name[1:])
296 v2 = version_map[version2_name[1:]]
299 fatal("%s%s: %s precedes %s"
300 % (version1_name, version2_name,
301 version2_name, version1_name))
303 if vendor == vendor_map['OF']:
304 # All standard OpenFlow errors have a type and a code.
306 fatal("%s: %s domain requires code" % (dst, vendor_name))
307 elif vendor == vendor_map['NX']:
308 # Before OpenFlow 1.2, OVS used a Nicira extension to
309 # define errors that included a type and a code.
311 # In OpenFlow 1.2 and later, Nicira extension errors
312 # are defined using the OpenFlow experimenter error
313 # mechanism that includes a type but not a code.
314 if v1 < version_map['1.2'] or v2 < version_map['1.2']:
316 fatal("%s: NX1.0 and NX1.1 domains require code"
317 % (dst, vendor_name))
318 if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
320 fatal("%s: NX1.2+ domains do not have codes" % dst)
322 # Experimenter extension error for OF1.2+ only.
323 if v1 < version_map['1.2']:
324 fatal("%s: %s domain not supported before OF1.2"
325 % (dst, vendor_name))
327 fatal("%s: %s domains do not have codes"
328 % (dst, vendor_name))
332 for version in range(v1, v2 + 1):
333 domain[version].setdefault(vendor, {})
334 domain[version][vendor].setdefault(type_, {})
335 if code in domain[version][vendor][type_]:
336 msg = "%#x,%d,%d in OF%s means both %s and %s" % (
337 vendor, type_, code, version_reverse_map[version],
338 domain[version][vendor][type_][code][0], enum)
339 if msg in expected_errors:
340 del expected_errors[msg]
342 error("%s: %s." % (dst, msg))
343 sys.stderr.write("%s:%d: %s: Here is the location "
344 "of the previous definition.\n"
345 % (domain[version][vendor][type_][code][1],
346 domain[version][vendor][type_][code][2],
349 domain[version][vendor][type_][code] = (enum, fileName,
352 assert enum not in reverse[version]
353 reverse[version][enum] = (vendor, type_, code)
357 for fn, ln in expected_errors.values():
358 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
365 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
367 #define OFPERR_N_ERRORS %d
369 struct ofperr_domain {
372 enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
373 struct triplet errors[OFPERR_N_ERRORS];
376 static const char *error_names[OFPERR_N_ERRORS] = {
380 static const char *error_comments[OFPERR_N_ERRORS] = {
384 '\n'.join(' "%s",' % name for name in names),
385 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
386 for comment in comments)))
388 def output_domain(map, name, description, version):
391 %s_decode(uint32_t vendor, uint16_t type, uint16_t code)
393 switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
398 vendor, type_, code = map[enum]
399 value = (vendor << 32) | (type_ << 16) | code
404 vendor_s = "(%#xULL << 32) | " % vendor
407 print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code))
408 print (" return OFPERR_%s;" % enum)
416 static const struct ofperr_domain %s = {
420 {""" % (name, description, version, name))
423 vendor, type_, code = map[enum]
426 print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
428 print (" { -1, -1, -1 }, /* %s */" % enum)
433 for version_name, id_ in version_map.items():
434 var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
435 description = "OpenFlow %s" % version_name
436 output_domain(reverse[id_], var, description, id_)
438 if __name__ == '__main__':
439 if '--help' in sys.argv:
441 elif len(sys.argv) != 3:
442 sys.stderr.write("exactly two non-options arguments required; "
443 "use --help for help\n")
446 extract_vendor_ids(sys.argv[2])
447 extract_ofp_errors(sys.argv[1])