mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 01:51:26 +00:00
A few general style issues like extra spacing and line length, semicolons at the end of the line and unused variable 'raw_types'. And a few invalid escape sequences, which are not actual escape sequences, but cause actual syntax warnings starting python 3.12 and will eventually become syntax errors [1]: extract-ofp-msgs:118: SyntaxWarning: invalid escape sequence '\s' m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_, These are fixed by converting to raw strings. [1] https://docs.python.org/3/reference/lexical_analysis.html#escape-sequences Acked-by: Eelco Chaudron <echaudro@redhat.com> Reviewed-By: Ihar Hrachyshka <ihar@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
388 lines
13 KiB
Python
Executable File
388 lines
13 KiB
Python
Executable File
#! /usr/bin/python3
|
|
|
|
import sys
|
|
import os.path
|
|
import re
|
|
|
|
line = ""
|
|
|
|
# Maps from user-friendly version number to its protocol encoding.
|
|
VERSION = {"1.0": 0x01,
|
|
"1.1": 0x02,
|
|
"1.2": 0x03,
|
|
"1.3": 0x04,
|
|
"1.4": 0x05,
|
|
"1.5": 0x06}
|
|
|
|
NX_VENDOR_ID = 0x00002320
|
|
ONF_VENDOR_ID = 0x4f4e4600
|
|
|
|
OFPT_VENDOR = 4
|
|
OFPT10_STATS_REQUEST = 16
|
|
OFPT10_STATS_REPLY = 17
|
|
OFPT11_STATS_REQUEST = 18
|
|
OFPT11_STATS_REPLY = 19
|
|
OFPST_VENDOR = 0xffff
|
|
|
|
n_errors = 0
|
|
|
|
|
|
def decode_version_range(range):
|
|
if range in VERSION:
|
|
return (VERSION[range], VERSION[range])
|
|
elif range.endswith('+'):
|
|
return (VERSION[range[:-1]], max(VERSION.values()))
|
|
elif range == '<all>':
|
|
return (0x01, 0xff)
|
|
else:
|
|
a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups()
|
|
return (VERSION[a], VERSION[b])
|
|
|
|
|
|
def get_line():
|
|
global line
|
|
global line_number
|
|
line = input_file.readline()
|
|
line_number += 1
|
|
if line == "":
|
|
fatal("unexpected end of input")
|
|
|
|
|
|
def error(msg):
|
|
global n_errors
|
|
sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
|
|
n_errors += 1
|
|
|
|
|
|
def fatal(msg):
|
|
error(msg)
|
|
sys.exit(1)
|
|
|
|
|
|
def usage():
|
|
argv0 = os.path.basename(sys.argv[0])
|
|
print('''\
|
|
%(argv0)s, for extracting OpenFlow message types from header files
|
|
usage: %(argv0)s INPUT OUTPUT
|
|
where INPUT is the name of the input header file
|
|
and OUTPUT is the output file name.
|
|
Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
|
|
only controls #line directives in the output.\
|
|
''' % {"argv0": argv0})
|
|
sys.exit(0)
|
|
|
|
|
|
def make_sizeof(s):
|
|
m = re.match(r'(.*) up to (.*)', s)
|
|
if m:
|
|
struct, member = m.groups()
|
|
return "offsetof(%s, %s)" % (struct, member)
|
|
else:
|
|
return "sizeof(%s)" % s
|
|
|
|
|
|
def extract_ofp_msgs(output_file_name):
|
|
all_hdrs = {}
|
|
all_raws = {}
|
|
all_raws_order = []
|
|
|
|
while True:
|
|
get_line()
|
|
if re.match('enum ofpraw', line):
|
|
break
|
|
|
|
while True:
|
|
get_line()
|
|
first_line_number = line_number
|
|
here = '%s:%d' % (file_name, line_number)
|
|
if (line.startswith('/*')
|
|
or line.startswith(' *')
|
|
or not line
|
|
or line.isspace()):
|
|
continue
|
|
elif re.match('}', line):
|
|
break
|
|
|
|
if not line.lstrip().startswith('/*'):
|
|
fatal("unexpected syntax between ofpraw types")
|
|
|
|
comment = line.lstrip()[2:].strip()
|
|
while not comment.endswith('*/'):
|
|
get_line()
|
|
if line.startswith('/*') or not line or line.isspace():
|
|
fatal("unexpected syntax within message")
|
|
comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
|
|
comment = comment[:-2].rstrip()
|
|
|
|
m = re.match(
|
|
r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment
|
|
)
|
|
if not m:
|
|
fatal("unexpected syntax between messages")
|
|
type_, versions, number, contents = m.groups()
|
|
number = int(number)
|
|
|
|
get_line()
|
|
m = re.match(r'\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_, line)
|
|
if not m:
|
|
fatal("syntax error expecting OFPRAW_ enum")
|
|
vinfix, name = m.groups()
|
|
rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
|
|
|
|
min_version, max_version = decode_version_range(versions)
|
|
|
|
human_name = '%s_%s' % (type_, name)
|
|
if type_.endswith('ST'):
|
|
if rawname.endswith('_REQUEST'):
|
|
human_name = human_name[:-8] + " request"
|
|
elif rawname.endswith('_REPLY'):
|
|
human_name = human_name[:-6] + " reply"
|
|
else:
|
|
fatal("%s messages are statistics but %s doesn't end "
|
|
"in _REQUEST or _REPLY" % (type_, rawname))
|
|
|
|
these_hdrs = []
|
|
for version in range(min_version, max_version + 1):
|
|
if type_ == 'OFPT':
|
|
if number == OFPT_VENDOR:
|
|
fatal("OFPT (%d) is used for vendor extensions"
|
|
% number)
|
|
elif (version == VERSION["1.0"]
|
|
and (number == OFPT10_STATS_REQUEST
|
|
or number == OFPT10_STATS_REPLY)):
|
|
fatal("OFPT 1.0 (%d) is used for stats messages"
|
|
% number)
|
|
elif (version != VERSION["1.0"]
|
|
and (number == OFPT11_STATS_REQUEST
|
|
or number == OFPT11_STATS_REPLY)):
|
|
fatal("OFPT 1.1+ (%d) is used for stats messages"
|
|
% number)
|
|
hdrs = (version, number, 0, 0, 0)
|
|
elif type_ == 'OFPST' and name.endswith('_REQUEST'):
|
|
if version == VERSION["1.0"]:
|
|
hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
|
|
else:
|
|
hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
|
|
elif type_ == 'OFPST' and name.endswith('_REPLY'):
|
|
if version == VERSION["1.0"]:
|
|
hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
|
|
else:
|
|
hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
|
|
elif type_ == 'ONFT':
|
|
hdrs = (version, OFPT_VENDOR, 0, ONF_VENDOR_ID, number)
|
|
elif type_ == 'ONFST' and name.endswith('_REQUEST'):
|
|
if version == VERSION["1.0"]:
|
|
hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
|
|
ONF_VENDOR_ID, number)
|
|
else:
|
|
hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
|
|
ONF_VENDOR_ID, number)
|
|
elif type_ == 'ONFST' and name.endswith('_REPLY'):
|
|
if version == VERSION["1.0"]:
|
|
hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
|
|
ONF_VENDOR_ID, number)
|
|
else:
|
|
hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
|
|
ONF_VENDOR_ID, number)
|
|
elif type_ == 'NXT':
|
|
hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
|
|
elif type_ == 'NXST' and name.endswith('_REQUEST'):
|
|
if version == VERSION["1.0"]:
|
|
hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
|
|
NX_VENDOR_ID, number)
|
|
else:
|
|
hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
|
|
NX_VENDOR_ID, number)
|
|
elif type_ == 'NXST' and name.endswith('_REPLY'):
|
|
if version == VERSION["1.0"]:
|
|
hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
|
|
NX_VENDOR_ID, number)
|
|
else:
|
|
hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
|
|
NX_VENDOR_ID, number)
|
|
else:
|
|
fatal("type '%s' unknown" % type_)
|
|
|
|
if hdrs in all_hdrs:
|
|
error("Duplicate message definition for %s." % str(hdrs))
|
|
sys.stderr.write("%s: Here is the location "
|
|
"of the previous definition.\n"
|
|
% (all_hdrs[hdrs]))
|
|
all_hdrs[hdrs] = here
|
|
these_hdrs.append(hdrs)
|
|
|
|
extra_multiple = '0'
|
|
if contents == 'void':
|
|
min_body = '0'
|
|
else:
|
|
min_body_elem = []
|
|
for c in [s.strip() for s in contents.split(",")]:
|
|
if c.endswith('[]'):
|
|
if extra_multiple == '0':
|
|
extra_multiple = make_sizeof(c[:-2])
|
|
else:
|
|
error("Cannot have multiple [] elements")
|
|
else:
|
|
min_body_elem.append(c)
|
|
|
|
if min_body_elem:
|
|
min_body = " + ".join([make_sizeof(s)
|
|
for s in min_body_elem])
|
|
else:
|
|
if extra_multiple == '0':
|
|
error("Must specify contents (use 'void' if empty)")
|
|
min_body = 0
|
|
|
|
if rawname in all_raws:
|
|
fatal("%s: Duplicate name" % rawname)
|
|
|
|
all_raws[rawname] = {"hdrs": these_hdrs,
|
|
"min_version": min_version,
|
|
"max_version": max_version,
|
|
"min_body": min_body,
|
|
"extra_multiple": extra_multiple,
|
|
"type": type_,
|
|
"human_name": human_name,
|
|
"line": first_line_number}
|
|
all_raws_order.append(rawname)
|
|
|
|
continue
|
|
|
|
while True:
|
|
get_line()
|
|
if re.match('enum ofptype', line):
|
|
break
|
|
|
|
all_types = []
|
|
while True:
|
|
get_line()
|
|
if re.match(r'\s*/?\*', line) or line.isspace():
|
|
continue
|
|
elif re.match('}', line):
|
|
break
|
|
|
|
if not re.match(r'\s*OFPTYPE_.*/\*', line):
|
|
fatal("unexpected syntax between OFPTYPE_ definitions")
|
|
|
|
syntax = line.strip()
|
|
while not syntax.endswith('*/'):
|
|
get_line()
|
|
if not line.strip().startswith('*'):
|
|
fatal("unexpected syntax within OFPTYPE_ definition")
|
|
syntax += ' %s' % line.strip().lstrip('* \t')
|
|
syntax = syntax.strip()
|
|
|
|
m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
|
|
if not m:
|
|
fatal("syntax error in OFPTYPE_ definition")
|
|
|
|
ofptype, raws_ = m.groups()
|
|
raws = [s.rstrip('.') for s in raws_.split()]
|
|
for raw in raws:
|
|
if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
|
|
fatal("%s: invalid OFPRAW_* name syntax" % raw)
|
|
if raw not in all_raws:
|
|
fatal("%s: not a declared OFPRAW_* name" % raw)
|
|
if "ofptype" in all_raws[raw]:
|
|
fatal("%s: already part of %s"
|
|
% (raw, all_raws[raw]["ofptype"]))
|
|
all_raws[raw]["ofptype"] = ofptype
|
|
|
|
all_types.append(all_raws[raws[0]]["human_name"])
|
|
|
|
input_file.close()
|
|
|
|
if n_errors:
|
|
sys.exit(1)
|
|
|
|
output = []
|
|
output.append("/* Generated automatically; do not modify! "
|
|
"-*- buffer-read-only: t -*- */")
|
|
output.append("")
|
|
|
|
for raw in all_raws_order:
|
|
r = all_raws[raw]
|
|
output.append("static struct raw_instance %s_instances[] = {"
|
|
% raw.lower())
|
|
for hdrs in r['hdrs']:
|
|
output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
|
|
% (hdrs + (raw,)))
|
|
|
|
output.append("};")
|
|
|
|
output.append("")
|
|
|
|
output.append("static struct raw_info raw_infos[] = {")
|
|
for raw in all_raws_order:
|
|
r = all_raws[raw]
|
|
if "ofptype" not in r:
|
|
error("%s: no defined OFPTYPE_" % raw)
|
|
continue
|
|
output.append(" {")
|
|
output.append(" %s_instances," % raw.lower())
|
|
output.append(" %d, %d," % (r["min_version"], r["max_version"]))
|
|
output.append("#line %s \"%s\"" % (r["line"], file_name))
|
|
output.append(" %s," % r["min_body"])
|
|
output.append("#line %s \"%s\"" % (r["line"], file_name))
|
|
output.append(" %s," % r["extra_multiple"])
|
|
output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
|
|
output.append(" %s," % r["ofptype"])
|
|
output.append(" \"%s\"," % r["human_name"])
|
|
output.append(" },")
|
|
|
|
if r['type'].endswith("ST"):
|
|
for hdrs in r['hdrs']:
|
|
op_hdrs = list(hdrs)
|
|
if hdrs[0] == VERSION["1.0"]:
|
|
if hdrs[1] == OFPT10_STATS_REQUEST:
|
|
op_hdrs[1] = OFPT10_STATS_REPLY
|
|
elif hdrs[1] == OFPT10_STATS_REPLY:
|
|
op_hdrs[1] = OFPT10_STATS_REQUEST
|
|
else:
|
|
assert False
|
|
else:
|
|
if hdrs[1] == OFPT11_STATS_REQUEST:
|
|
op_hdrs[1] = OFPT11_STATS_REPLY
|
|
elif hdrs[1] == OFPT11_STATS_REPLY:
|
|
op_hdrs[1] = OFPT11_STATS_REQUEST
|
|
else:
|
|
assert False
|
|
if tuple(op_hdrs) not in all_hdrs:
|
|
if r["human_name"].endswith("request"):
|
|
fatal("%s has no corresponding reply"
|
|
% r["human_name"])
|
|
else:
|
|
fatal("%s has no corresponding request"
|
|
% r["human_name"])
|
|
output.append("};")
|
|
|
|
output.append("")
|
|
output.append("static const char *type_names[] = {")
|
|
for t in all_types:
|
|
output.append(" \"%s\"," % t)
|
|
output.append("};")
|
|
|
|
if n_errors:
|
|
sys.exit(1)
|
|
|
|
return output
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if '--help' in sys.argv:
|
|
usage()
|
|
elif len(sys.argv) != 3:
|
|
sys.stderr.write("exactly two non-option arguments required; "
|
|
"use --help for help\n")
|
|
sys.exit(1)
|
|
else:
|
|
global file_name
|
|
global input_file
|
|
global line_number
|
|
file_name = sys.argv[1]
|
|
input_file = open(file_name)
|
|
line_number = 0
|
|
|
|
for line in extract_ofp_msgs(sys.argv[2]):
|
|
print(line)
|