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
This commit is contained in:
parent
f51c872a12
commit
7647e3e56f
|
@ -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):
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue