argument_validator: fix IPRouteUtils.get_route_tables_mapping() for whitespace sequence

Accept any whitespace sequence, according to Python's `rb"\s"` class.
This way, tabs are also accepted. This is also what iproute2 does.
Use just one regex for this.

Also add a unit test for the default iproute2 file.

Signed-off-by: Thomas Haller <thaller@redhat.com>
This commit is contained in:
Thomas Haller 2022-06-02 11:00:55 +02:00 committed by liangwen12year
parent 200cf67507
commit a74092634a
2 changed files with 65 additions and 41 deletions

View file

@ -2568,65 +2568,61 @@ class ArgValidator_ListConnections(ArgValidatorList):
class IPRouteUtils(object):
# iproute2 does not care much about the valid characters of a
# table alias (it doesn't even require UTF-8 encoding, the only
# forbidden parts are whitespace).
#
# We don't allow such flexibility. Aliases must only contain a
# certain set of ASCII characters. These aliases are what we accept
# as input (in the playbook), and there is no need to accept
# user input with unusual characters or non-ASCII names.
ROUTE_TABLE_ALIAS_RE = re.compile("^[a-zA-Z0-9_.-]+$")
@classmethod
def _parse_route_tables_mapping(cls, file_content, mapping):
# This parses the /etc/iproute2/rt_tables file and constructs
# the mapping from table aliases the table numeric IDs.
#
# It is thus similar to rtnl_rttable_a2n(), from here:
# https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/rt_names.c?id=11e41a635cfab54e8e02fbff2a03715467e77ae9#n447
regex = re.compile(
b"^\\s*(0x[0-9a-fA-F]+|[0-9]+)\\s+([a-zA-Z0-9_.-]+)(\\s*|\\s+#.*)$"
)
for line in file_content.split(b"\n"):
# iproute2 only skips over leading ' ' and '\t'.
line = line.lstrip()
# skip empty lines or comments
if not line:
rmatch = regex.match(line)
if not rmatch:
continue
# In Python 2.x, there is no new type called `bytes`. And Python 2.x
# `bytes` is just an alias to the str type
if Util.PY3:
if line[0] == ord(b"#"):
continue
else:
if line[0] == "#":
continue
table = rmatch.group(1)
name = rmatch.group(2)
# iproute2 splits at the first space.
ll = line.split(b" ", 1)
if len(ll) != 2:
continue
line1 = ll[0]
line2 = ll[1]
name = name.decode("utf-8")
comment_space = line2.find(b" ")
if comment_space >= 0:
# when commenting the route table entry in the same line,
# iproute2 only accepts one ' ', followed by '#'
if not re.match(b"^[a-zA-Z0-9_.-]+ #", line2):
continue
line2 = line2[:comment_space]
# convert to UTF-8 and only accept benign characters.
try:
line2 = line2.decode("utf-8")
except Exception:
continue
if not cls.ROUTE_TABLE_ALIAS_RE.match(line2):
continue
if not cls.ROUTE_TABLE_ALIAS_RE.match(name):
raise AssertionError(
"bug: table alias contains unexpected characters: %s" % (name,)
)
tableid = None
try:
tableid = int(line1)
tableid = int(table)
except Exception:
if line.startswith(b"0x"):
if table.startswith(b"0x"):
try:
tableid = int(line1[2:], 16)
tableid = int(table[2:], 16)
except Exception:
pass
if tableid is None or tableid < 0 or tableid > 0xFFFFFFFF:
continue
mapping[line2] = tableid
# In case of duplicates, the latter wins. That is unlike iproute2's
# rtnl_rttable_a2n(), which does a linear search over the
# hash table (thus, the first found name depends on the content
# of the hash table and the result in face of duplicates is
# not well defined).
mapping[name] = tableid
@classmethod
def _parse_route_tables_mapping_from_file(cls, filename, mapping):

View file

@ -4551,12 +4551,40 @@ class TestValidatorRouteTable(Python26CompatTestCase):
return mapping
self.assertEqual(parse(b""), {})
self.assertEqual(parse(b"5x"), {})
self.assertEqual(parse(b"0xF5 x"), {"x": 0xF5})
self.assertEqual(parse(b"0x5a x"), {"x": 0x5A})
self.assertEqual(parse(b" 0x5a x"), {"x": 0x5A})
self.assertEqual(parse(b"0x0 x"), {"x": 0x0})
self.assertEqual(parse(b"0x x"), {})
self.assertEqual(parse(b"5 x"), {"x": 5})
self.assertEqual(parse(b" 7 y "), {})
self.assertEqual(parse(b" 7 y "), {"y": 7})
self.assertEqual(parse(b"5 x\n0x4 y"), {"x": 5, "y": 4})
self.assertEqual(parse(b"5 x #df\n0x4 y"), {"x": 5, "y": 4})
self.assertEqual(parse(b"5 x #df\n0x4 y\n7\ty"), {"x": 5, "y": 4})
self.assertEqual(parse(b"5 x #df\n0x4 y\n7\ty"), {"x": 5, "y": 7})
self.assertEqual(parse(b"-1 x #df\n0x4 y\n5 x"), {"x": 5, "y": 4})
self.assertEqual(
parse(b"5 x #df\n0x4 y\n7\t \ty\n\t\t44 7\n 66 a%\n 67 ab"),
{"x": 5, "y": 7, "7": 44, "ab": 67},
)
# https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/etc/iproute2/rt_tables?id=11e41a635cfab54e8e02fbff2a03715467e77ae9
self.assertEqual(
parse(
b"#\n"
b"# reserved values\n"
b"#\n"
b"255 local\n"
b"254 main\n"
b"253 default\n"
b"0 unspec\n"
b"#\n"
b"# local\n"
b"#\n"
b"#1 inr.ruhep\n"
),
{"local": 255, "main": 254, "default": 253, "unspec": 0},
)
def test_table_found_when_validate_route_tables(self):
"""