9 # Map from OpenFlow version number to version ID used in ofp_header.
10 version_map = {"1.0": 0x01,
17 version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
21 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
22 tokenRe = "#?" + idRe + "|[0-9]+|."
31 inputFile = open(fileName)
38 line = inputFile.readline()
44 fatal("unexpected end of input")
54 if line.startswith("/*"):
58 commentEnd = line.find("*/")
63 line = line[commentEnd + 2:]
65 match = re.match(tokenRe, line)
66 token = match.group(0)
67 line = line[len(token):]
68 if token.startswith('#'):
70 elif token in macros and not inDirective:
71 line = macros[token] + line
80 line = inputFile.readline()
82 while line.endswith("\\\n"):
83 line = line[:-2] + inputFile.readline()
87 fatal("unexpected end of input")
94 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
107 return re.match(idRe + "$", s) != None
111 fatal("identifier expected")
114 if not re.match('[0-9]+$', token):
115 fatal("integer expected")
126 fatal("%s expected" % t)
128 def parseTaggedName():
129 assert token in ('struct', 'union')
133 name = "%s %s" % (name, token)
137 def print_enum(tag, constants, storage_class):
139 %(storage_class)sconst char *
140 %(tag)s_to_string(uint16_t value)
144 "bufferlen": len(tag) + 32,
145 "storage_class": storage_class})
146 for constant in constants:
147 print (" case %s: return \"%s\";" % (constant, constant))
155 argv0 = os.path.basename(sys.argv[0])
157 %(argv0)s, for extracting OpenFlow error codes from header files
158 usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
160 This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
161 experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
162 It outputs a C source file for translating OpenFlow error codes into
165 ERROR_HEADER should point to include/openvswitch/ofp-errors.h.
166 VENDOR_HEADER should point to include/openflow/openflow-common.h.
167 The output is suitable for use as lib/ofp-errors.inc.\
168 ''' % {"argv0": argv0})
171 def extract_vendor_ids(fn):
178 m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
183 id_ = int(m.group(2), 0)
185 if name in vendor_map:
186 error("%s: duplicate definition of vendor" % name)
187 sys.stderr.write("%s: Here is the location of the previous "
188 "definition.\n" % vendor_loc[name])
191 vendor_map[name] = id_
192 vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
195 fatal("%s: no vendor definitions found" % fn)
199 vendor_reverse_map = {}
200 for name, id_ in vendor_map.items():
201 if id_ in vendor_reverse_map:
202 fatal("%s: duplicate vendor id for vendors %s and %s"
203 % (id_, vendor_reverse_map[id_], name))
204 vendor_reverse_map[id_] = name
206 def extract_ofp_errors(fn):
213 for domain_name in version_map.values():
214 domain[domain_name] = {}
215 reverse[domain_name] = {}
224 if re.match('enum ofperr', line):
229 if line.startswith('/*') or not line or line.isspace():
231 elif re.match('}', line):
234 if not line.lstrip().startswith('/*'):
235 fatal("unexpected syntax between errors")
237 comment = line.lstrip()[2:].strip()
238 while not comment.endswith('*/'):
240 if line.startswith('/*') or not line or line.isspace():
241 fatal("unexpected syntax within error")
242 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
243 comment = comment[:-2].rstrip()
245 m = re.match('Expected: (.*)\.$', comment)
247 expected_errors[m.group(1)] = (fileName, lineNumber)
250 m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
252 fatal("unexpected syntax between errors")
254 dsts, comment = m.groups()
257 m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
260 fatal("syntax error expecting enum value")
264 fatal("%s specified twice" % enum)
266 comments.append(re.sub('\[[^]]*\]', '', comment))
269 for dst in dsts.split(', '):
270 m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
272 fatal("%r: syntax error in destination" % dst)
273 vendor_name = m.group(1)
274 version1_name = m.group(2)
275 version2_name = m.group(3)
276 type_ = int(m.group(4))
278 code = int(m.group(5))
282 if vendor_name not in vendor_map:
283 fatal("%s: unknown vendor" % vendor_name)
284 vendor = vendor_map[vendor_name]
286 if version1_name not in version_map:
287 fatal("%s: unknown OpenFlow version" % version1_name)
288 v1 = version_map[version1_name]
290 if version2_name is None:
292 elif version2_name == "+":
293 v2 = max(version_map.values())
294 elif version2_name[1:] not in version_map:
295 fatal("%s: unknown OpenFlow version" % version2_name[1:])
297 v2 = version_map[version2_name[1:]]
300 fatal("%s%s: %s precedes %s"
301 % (version1_name, version2_name,
302 version2_name, version1_name))
304 if vendor == vendor_map['OF']:
305 # All standard OpenFlow errors have a type and a code.
307 fatal("%s: %s domain requires code" % (dst, vendor_name))
308 elif vendor == vendor_map['NX']:
309 # Before OpenFlow 1.2, OVS used a Nicira extension to
310 # define errors that included a type and a code.
312 # In OpenFlow 1.2 and later, Nicira extension errors
313 # are defined using the OpenFlow experimenter error
314 # mechanism that includes a type but not a code.
315 if v1 < version_map['1.2'] or v2 < version_map['1.2']:
317 fatal("%s: NX1.0 and NX1.1 domains require code"
318 % (dst, vendor_name))
319 if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
321 fatal("%s: NX1.2+ domains do not have codes" % dst)
323 # Experimenter extension error for OF1.2+ only.
324 if v1 < version_map['1.2']:
325 fatal("%s: %s domain not supported before OF1.2"
326 % (dst, vendor_name))
328 fatal("%s: %s domains do not have codes"
329 % (dst, vendor_name))
333 for version in range(v1, v2 + 1):
334 domain[version].setdefault(vendor, {})
335 domain[version][vendor].setdefault(type_, {})
336 if code in domain[version][vendor][type_]:
337 msg = "%#x,%d,%d in OF%s means both %s and %s" % (
338 vendor, type_, code, version_reverse_map[version],
339 domain[version][vendor][type_][code][0], enum)
340 if msg in expected_errors:
341 del expected_errors[msg]
343 error("%s: %s." % (dst, msg))
344 sys.stderr.write("%s:%d: %s: Here is the location "
345 "of the previous definition.\n"
346 % (domain[version][vendor][type_][code][1],
347 domain[version][vendor][type_][code][2],
350 domain[version][vendor][type_][code] = (enum, fileName,
353 assert enum not in reverse[version]
354 reverse[version][enum] = (vendor, type_, code)
358 for fn, ln in expected_errors.values():
359 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
366 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
368 #define OFPERR_N_ERRORS %d
370 struct ofperr_domain {
373 enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
374 struct triplet errors[OFPERR_N_ERRORS];
377 static const char *error_names[OFPERR_N_ERRORS] = {
381 static const char *error_comments[OFPERR_N_ERRORS] = {
385 '\n'.join(' "%s",' % name for name in names),
386 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
387 for comment in comments)))
389 def output_domain(map, name, description, version):
392 %s_decode(uint32_t vendor, uint16_t type, uint16_t code)
394 switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
399 vendor, type_, code = map[enum]
400 value = (vendor << 32) | (type_ << 16) | code
405 vendor_s = "(%#xULL << 32) | " % vendor
408 print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code))
409 print (" return OFPERR_%s;" % enum)
417 static const struct ofperr_domain %s = {
421 {""" % (name, description, version, name))
424 vendor, type_, code = map[enum]
427 print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
429 print (" { -1, -1, -1 }, /* %s */" % enum)
434 for version_name, id_ in version_map.items():
435 var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
436 description = "OpenFlow %s" % version_name
437 output_domain(reverse[id_], var, description, id_)
439 if __name__ == '__main__':
440 if '--help' in sys.argv:
442 elif len(sys.argv) != 3:
443 sys.stderr.write("exactly two non-options arguments required; "
444 "use --help for help\n")
447 extract_vendor_ids(sys.argv[2])
448 extract_ofp_errors(sys.argv[1])