X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=build-aux%2Fextract-ofp-errors;h=16bfbc7211bca563f49950abd21ae3b2bbd62b9d;hb=HEAD;hp=fd53001dec723b9760d01ef400c56127fd3eb87d;hpb=df30f9b1cde861e157059d225f46e16826fe13c0;p=cascardo%2Fovs.git diff --git a/build-aux/extract-ofp-errors b/build-aux/extract-ofp-errors index fd53001de..16bfbc721 100755 --- a/build-aux/extract-ofp-errors +++ b/build-aux/extract-ofp-errors @@ -6,6 +6,15 @@ import re macros = {} +# Map from OpenFlow version number to version ID used in ofp_header. +version_map = {"1.0": 0x01, + "1.1": 0x02, + "1.2": 0x03, + "1.3": 0x04, + "1.4": 0x05, + "1.5": 0x06} +version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems()) + token = None line = "" idRe = "[a-zA-Z_][a-zA-Z_0-9]*" @@ -13,12 +22,24 @@ tokenRe = "#?" + idRe + "|[0-9]+|." inComment = False inDirective = False -def getLine(): +def open_file(fn): + global fileName + global inputFile + global lineNumber + fileName = fn + inputFile = open(fileName) + lineNumber = 0 + +def tryGetLine(): + global inputFile global line global lineNumber line = inputFile.readline() lineNumber += 1 - if line == "": + return line != "" + +def getLine(): + if not tryGetLine(): fatal("unexpected end of input") def getToken(): @@ -113,179 +134,234 @@ def parseTaggedName(): return name def print_enum(tag, constants, storage_class): - print """ + print (""" %(storage_class)sconst char * %(tag)s_to_string(uint16_t value) { switch (value) {\ """ % {"tag": tag, "bufferlen": len(tag) + 32, - "storage_class": storage_class} + "storage_class": storage_class}) for constant in constants: - print " case %s: return \"%s\";" % (constant, constant) - print """\ + print (" case %s: return \"%s\";" % (constant, constant)) + print ("""\ } return NULL; }\ -""" % {"tag": tag} +""" % {"tag": tag}) def usage(): argv0 = os.path.basename(sys.argv[0]) - print '''\ + print ('''\ %(argv0)s, for extracting OpenFlow error codes from header files -usage: %(argv0)s FILE [FILE...] +usage: %(argv0)s ERROR_HEADER VENDOR_HEADER -This program reads the header files specified on the command line and -outputs a C source file for translating OpenFlow error codes into -strings, for use as lib/ofp-errors.c in the Open vSwitch source tree. +This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka +experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number. +It outputs a C source file for translating OpenFlow error codes into +strings. -This program is specialized for reading lib/ofp-errors.h. It will not -work on arbitrary header files without extensions.\ -''' % {"argv0": argv0} +ERROR_HEADER should point to lib/ofp-errors.h. +VENDOR_HEADER should point to include/openflow/openflow-common.h. +The output is suitable for use as lib/ofp-errors.inc.\ +''' % {"argv0": argv0}) sys.exit(0) -def extract_ofp_errors(filenames): +def extract_vendor_ids(fn): + global vendor_map + vendor_map = {} + vendor_loc = {} + + open_file(fn) + while tryGetLine(): + m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line) + if not m: + continue + + name = m.group(1) + id_ = int(m.group(2), 0) + + if name in vendor_map: + error("%s: duplicate definition of vendor" % name) + sys.stderr.write("%s: Here is the location of the previous " + "definition.\n" % vendor_loc[name]) + sys.exit(1) + + vendor_map[name] = id_ + vendor_loc[name] = "%s:%d" % (fileName, lineNumber) + + if not vendor_map: + fatal("%s: no vendor definitions found" % fn) + + inputFile.close() + + vendor_reverse_map = {} + for name, id_ in vendor_map.items(): + if id_ in vendor_reverse_map: + fatal("%s: duplicate vendor id for vendors %s and %s" + % (id_, vendor_reverse_map[id_], name)) + vendor_reverse_map[id_] = name + +def extract_ofp_errors(fn): error_types = {} comments = [] names = [] domain = {} reverse = {} - for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3", - "NX1.0", "NX1.1", "NX1.2", "NX1.3"): + for domain_name in version_map.values(): domain[domain_name] = {} reverse[domain_name] = {} n_errors = 0 expected_errors = {} - global fileName - for fileName in filenames: - global inputFile - global lineNumber - inputFile = open(fileName) - lineNumber = 0 + open_file(fn) - while True: - getLine() - if re.match('enum ofperr', line): - break + while True: + getLine() + if re.match('enum ofperr', line): + break + + while True: + getLine() + if line.startswith('/*') or not line or line.isspace(): + continue + elif re.match('}', line): + break + + if not line.lstrip().startswith('/*'): + fatal("unexpected syntax between errors") - while True: + comment = line.lstrip()[2:].strip() + while not comment.endswith('*/'): getLine() if line.startswith('/*') or not line or line.isspace(): - continue - elif re.match('}', line): - break - - if not line.lstrip().startswith('/*'): - fatal("unexpected syntax between errors") - - comment = line.lstrip()[2:].strip() - while not comment.endswith('*/'): - getLine() - if line.startswith('/*') or not line or line.isspace(): - fatal("unexpected syntax within error") - comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') - comment = comment[:-2].rstrip() - - m = re.match('Expected: (.*)\.$', comment) - if m: - expected_errors[m.group(1)] = (fileName, lineNumber) - continue + fatal("unexpected syntax within error") + comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') + comment = comment[:-2].rstrip() - m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment) - if not m: - fatal("unexpected syntax between errors") + m = re.match('Expected: (.*)\.$', comment) + if m: + expected_errors[m.group(1)] = (fileName, lineNumber) + continue - dsts, comment = m.groups() + m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment) + if not m: + fatal("unexpected syntax between errors") - getLine() - m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,', - line) - if not m: - fatal("syntax error expecting enum value") + dsts, comment = m.groups() - enum = m.group(1) + getLine() + m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,', + line) + if not m: + fatal("syntax error expecting enum value") - comments.append(re.sub('\[[^]]*\]', '', comment)) - names.append(enum) + enum = m.group(1) + if enum in names: + fatal("%s specified twice" % enum) - for dst in dsts.split(', '): - m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst) - if not m: - fatal("%s: syntax error in destination" % dst) - targets = m.group(1) - if m.group(3): - base = 16 - else: - base = 10 - type_ = int(m.group(2), base) - if m.group(4): - code = int(m.group(4)) - else: - code = None - - target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"), - "OF1.1+": ("OF1.1", "OF1.2", "OF1.3"), - "OF1.2+": ("OF1.2", "OF1.3"), - "OF1.3+": ("OF1.3",), - "OF1.0": ("OF1.0",), - "OF1.1": ("OF1.1",), - "OF1.2": ("OF1.2",), - "OF1.3": ("OF1.3",), - "NX1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"), - "NX1.1+": ("OF1.1", "OF1.2", "OF1.3"), - "NX1.2+": ("OF1.2", "OF1.3"), - "NX1.3+": ("OF1.3",), - "NX1.0": ("OF1.0",), - "NX1.1": ("OF1.1",), - "NX1.2": ("OF1.2",), - "NX1.3": ("OF1.3",)} - if targets not in target_map: - fatal("%s: unknown error domain" % targets) - if targets.startswith('NX') and code < 0x100: - fatal("%s: NX domain code cannot be less than 0x100" % dst) - if targets.startswith('OF') and code >= 0x100: - fatal("%s: OF domain code cannot be greater than 0x100" - % dst) - for target in target_map[targets]: - domain[target].setdefault(type_, {}) - if code in domain[target][type_]: - msg = "%d,%d in %s means both %s and %s" % ( - type_, code, target, - domain[target][type_][code][0], enum) - if msg in expected_errors: - del expected_errors[msg] - else: - error("%s: %s." % (dst, msg)) - sys.stderr.write("%s:%d: %s: Here is the location " - "of the previous definition.\n" - % (domain[target][type_][code][1], - domain[target][type_][code][2], - dst)) + comments.append(re.sub('\[[^]]*\]', '', comment)) + names.append(enum) + + for dst in dsts.split(', '): + m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst) + if not m: + fatal("%r: syntax error in destination" % dst) + vendor_name = m.group(1) + version1_name = m.group(2) + version2_name = m.group(3) + type_ = int(m.group(4)) + if m.group(5): + code = int(m.group(5)) + else: + code = None + + if vendor_name not in vendor_map: + fatal("%s: unknown vendor" % vendor_name) + vendor = vendor_map[vendor_name] + + if version1_name not in version_map: + fatal("%s: unknown OpenFlow version" % version1_name) + v1 = version_map[version1_name] + + if version2_name is None: + v2 = v1 + elif version2_name == "+": + v2 = max(version_map.values()) + elif version2_name[1:] not in version_map: + fatal("%s: unknown OpenFlow version" % version2_name[1:]) + else: + v2 = version_map[version2_name[1:]] + + if v2 < v1: + fatal("%s%s: %s precedes %s" + % (version1_name, version2_name, + version2_name, version1_name)) + + if vendor == vendor_map['OF']: + # All standard OpenFlow errors have a type and a code. + if code is None: + fatal("%s: %s domain requires code" % (dst, vendor_name)) + elif vendor == vendor_map['NX']: + # Before OpenFlow 1.2, OVS used a Nicira extension to + # define errors that included a type and a code. + # + # In OpenFlow 1.2 and later, Nicira extension errors + # are defined using the OpenFlow experimenter error + # mechanism that includes a type but not a code. + if v1 < version_map['1.2'] or v2 < version_map['1.2']: + if code is None: + fatal("%s: NX1.0 and NX1.1 domains require code" + % (dst, vendor_name)) + if v1 >= version_map['1.2'] or v2 >= version_map['1.2']: + if code is not None: + fatal("%s: NX1.2+ domains do not have codes" % dst) + else: + # Experimenter extension error for OF1.2+ only. + if v1 < version_map['1.2']: + fatal("%s: %s domain not supported before OF1.2" + % (dst, vendor_name)) + if code is not None: + fatal("%s: %s domains do not have codes" + % (dst, vendor_name)) + if code is None: + code = 0 + + for version in range(v1, v2 + 1): + domain[version].setdefault(vendor, {}) + domain[version][vendor].setdefault(type_, {}) + if code in domain[version][vendor][type_]: + msg = "%#x,%d,%d in OF%s means both %s and %s" % ( + vendor, type_, code, version_reverse_map[version], + domain[version][vendor][type_][code][0], enum) + if msg in expected_errors: + del expected_errors[msg] else: - domain[target][type_][code] = (enum, fileName, - lineNumber) + error("%s: %s." % (dst, msg)) + sys.stderr.write("%s:%d: %s: Here is the location " + "of the previous definition.\n" + % (domain[version][vendor][type_][code][1], + domain[version][vendor][type_][code][2], + dst)) + else: + domain[version][vendor][type_][code] = (enum, fileName, + lineNumber) - if enum in reverse[target]: - error("%s: %s in %s means both %d,%d and %d,%d." % - (dst, enum, target, - reverse[target][enum][0], - reverse[target][enum][1], - type_, code)) - reverse[target][enum] = (type_, code) + assert enum not in reverse[version] + reverse[version][enum] = (vendor, type_, code) - inputFile.close() + inputFile.close() - for fn, ln in expected_errors.itervalues(): + for fn, ln in expected_errors.values(): sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln)) n_errors += 1 if n_errors: sys.exit(1) - print """\ + print ("""\ /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ #define OFPERR_N_ERRORS %d @@ -293,8 +369,8 @@ def extract_ofp_errors(filenames): struct ofperr_domain { const char *name; uint8_t version; - enum ofperr (*decode)(uint16_t type, uint16_t code); - struct pair errors[OFPERR_N_ERRORS]; + enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code); + struct triplet errors[OFPERR_N_ERRORS]; }; static const char *error_names[OFPERR_N_ERRORS] = { @@ -307,62 +383,65 @@ static const char *error_comments[OFPERR_N_ERRORS] = { """ % (len(names), '\n'.join(' "%s",' % name for name in names), '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment) - for comment in comments)) + for comment in comments))) def output_domain(map, name, description, version): - print """ + print (""" static enum ofperr -%s_decode(uint16_t type, uint16_t code) +%s_decode(uint32_t vendor, uint16_t type, uint16_t code) { - switch ((type << 16) | code) {""" % name + switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name) found = set() for enum in names: if enum not in map: continue - type_, code = map[enum] - if code is None: - continue - value = (type_ << 16) | code + vendor, type_, code = map[enum] + value = (vendor << 32) | (type_ << 16) | code if value in found: continue found.add(value) - print " case (%d << 16) | %d:" % (type_, code) - print " return OFPERR_%s;" % enum - print """\ + if vendor: + vendor_s = "(%#xULL << 32) | " % vendor + else: + vendor_s = "" + print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code)) + print (" return OFPERR_%s;" % enum) + print ("""\ } return 0; -}""" +}""") - print """ + print (""" static const struct ofperr_domain %s = { "%s", %d, %s_decode, - {""" % (name, description, version, name) + {""" % (name, description, version, name)) for enum in names: if enum in map: - type_, code = map[enum] + vendor, type_, code = map[enum] if code == None: code = -1 + print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum) else: - type_ = code = -1 - print " { %2d, %3d }, /* %s */" % (type_, code, enum) - print """\ + print (" { -1, -1, -1 }, /* %s */" % enum) + print ("""\ }, -};""" +};""") - output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01) - output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02) - output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03) - output_domain(reverse["OF1.3"], "ofperr_of13", "OpenFlow 1.3", 0x04) + for version_name, id_ in version_map.items(): + var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name) + description = "OpenFlow %s" % version_name + output_domain(reverse[id_], var, description, id_) if __name__ == '__main__': if '--help' in sys.argv: usage() - elif len(sys.argv) < 2: - sys.stderr.write("at least one non-option argument required; " + elif len(sys.argv) != 3: + sys.stderr.write("exactly two non-options arguments required; " "use --help for help\n") sys.exit(1) else: - extract_ofp_errors(sys.argv[1:]) + extract_vendor_ids(sys.argv[2]) + extract_ofp_errors(sys.argv[1])