From 7647e3e56f630708ad4da12ba1af429d49ac1fc1 Mon Sep 17 00:00:00 2001 From: Omer Anson Date: Tue, 8 Nov 2016 18:08:50 +0200 Subject: [PATCH] Parse the output of ip route more robustly The code parsing the output of ip route assumes that the output is the destination network, and then key/value pairs. This is not necessarily the case, as the output may contain flags. This change modifies the parsing of ip route's output to look specifically for the keys of interest, and read their values exclusively. Change-Id: I34b6c0afb05550c970b1cacda8ec472294215403 Closes-Bug: 1640008 --- neutron/agent/linux/ip_lib.py | 55 +++++++++++++------ neutron/tests/unit/agent/linux/test_ip_lib.py | 10 ++++ 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 2a091edadd0..5053499b530 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -961,6 +961,41 @@ def get_device_mac(device_name, namespace=None): return IPDevice(device_name, namespace=namespace).link.address +_IP_ROUTE_PARSE_KEYS = { + 'via': 'nexthop', + 'dev': 'device', + 'scope': 'scope' +} + + +def _parse_ip_route_line(line): + """Parse a line output from ip route. + Example for output from 'ip route': + default via 192.168.3.120 dev wlp3s0 proto static metric 1024 + 10.0.0.0/8 dev tun0 proto static scope link metric 1024 + 10.0.1.0/8 dev tun1 proto static scope link metric 1024 linkdown + The first column is the destination, followed by key/value pairs and flags. + @param line A line output from ip route + @return: a dictionary representing a route. + """ + line = line.split() + result = { + 'destination': line[0], + 'nexthop': None, + 'device': None, + 'scope': None + } + idx = 1 + while idx < len(line): + field = _IP_ROUTE_PARSE_KEYS.get(line[idx]) + if not field: + idx = idx + 1 + else: + result[field] = line[idx + 1] + idx = idx + 2 + return result + + def get_routing_table(ip_version, namespace=None): """Return a list of dictionaries, each representing a route. @@ -978,24 +1013,8 @@ def get_routing_table(ip_version, namespace=None): ['ip', '-%s' % ip_version, 'route'], check_exit_code=True) - routes = [] - # Example for route_lines: - # default via 192.168.3.120 dev wlp3s0 proto static metric 1024 - # 10.0.0.0/8 dev tun0 proto static scope link metric 1024 - # The first column is the destination, followed by key/value pairs. - # The generator splits the routing table by newline, then strips and splits - # each individual line. - route_lines = (line.split() for line in table.split('\n') if line.strip()) - for route in route_lines: - network = route[0] - # Create a dict of key/value pairs (For example - 'dev': 'tun0') - # excluding the first column. - data = dict(route[i:i + 2] for i in range(1, len(route), 2)) - routes.append({'destination': network, - 'nexthop': data.get('via'), - 'device': data.get('dev'), - 'scope': data.get('scope')}) - return routes + return [_parse_ip_route_line(line) + for line in table.split('\n') if line.strip()] def ensure_device_is_ready(device_name, namespace=None): diff --git a/neutron/tests/unit/agent/linux/test_ip_lib.py b/neutron/tests/unit/agent/linux/test_ip_lib.py index 76b08cf4764..ac1539015d2 100644 --- a/neutron/tests/unit/agent/linux/test_ip_lib.py +++ b/neutron/tests/unit/agent/linux/test_ip_lib.py @@ -1320,6 +1320,7 @@ class TestGetRoutingTable(base.BaseTestCase): ip_route_output = (""" default via 192.168.3.120 dev wlp3s0 proto static metric 1024 10.0.0.0/8 dev tun0 proto static scope link metric 1024 +10.0.1.0/8 dev tun1 proto static scope link metric 1024 linkdown """) expected = [{'destination': 'default', 'nexthop': '192.168.3.120', @@ -1328,18 +1329,27 @@ default via 192.168.3.120 dev wlp3s0 proto static metric 1024 {'destination': '10.0.0.0/8', 'nexthop': None, 'device': 'tun0', + 'scope': 'link'}, + {'destination': '10.0.1.0/8', + 'nexthop': None, + 'device': 'tun1', 'scope': 'link'}] self._test_get_routing_table(4, ip_route_output, expected) def test_get_routing_table_6(self): ip_route_output = (""" 2001:db8:0:f101::/64 dev tap-1 proto kernel metric 256 pref medium +2001:db8:0:f102::/64 dev tap-2 proto kernel metric 256 pref medium linkdown default via 2001:db8:0:f101::4 dev tap-1 metric 1024 pref medium """) expected = [{'destination': '2001:db8:0:f101::/64', 'nexthop': None, 'device': 'tap-1', 'scope': None}, + {'destination': '2001:db8:0:f102::/64', + 'nexthop': None, + 'device': 'tap-2', + 'scope': None}, {'destination': 'default', 'nexthop': '2001:db8:0:f101::4', 'device': 'tap-1',