Merge "Parse the output of ip route more robustly"
This commit is contained in:
commit
d41bed0ee6
|
@ -961,6 +961,41 @@ def get_device_mac(device_name, namespace=None):
|
||||||
return IPDevice(device_name, namespace=namespace).link.address
|
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):
|
def get_routing_table(ip_version, namespace=None):
|
||||||
"""Return a list of dictionaries, each representing a route.
|
"""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'],
|
['ip', '-%s' % ip_version, 'route'],
|
||||||
check_exit_code=True)
|
check_exit_code=True)
|
||||||
|
|
||||||
routes = []
|
return [_parse_ip_route_line(line)
|
||||||
# Example for route_lines:
|
for line in table.split('\n') if line.strip()]
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_device_is_ready(device_name, namespace=None):
|
def ensure_device_is_ready(device_name, namespace=None):
|
||||||
|
|
|
@ -1320,6 +1320,7 @@ class TestGetRoutingTable(base.BaseTestCase):
|
||||||
ip_route_output = ("""
|
ip_route_output = ("""
|
||||||
default via 192.168.3.120 dev wlp3s0 proto static metric 1024
|
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.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',
|
expected = [{'destination': 'default',
|
||||||
'nexthop': '192.168.3.120',
|
'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',
|
{'destination': '10.0.0.0/8',
|
||||||
'nexthop': None,
|
'nexthop': None,
|
||||||
'device': 'tun0',
|
'device': 'tun0',
|
||||||
|
'scope': 'link'},
|
||||||
|
{'destination': '10.0.1.0/8',
|
||||||
|
'nexthop': None,
|
||||||
|
'device': 'tun1',
|
||||||
'scope': 'link'}]
|
'scope': 'link'}]
|
||||||
self._test_get_routing_table(4, ip_route_output, expected)
|
self._test_get_routing_table(4, ip_route_output, expected)
|
||||||
|
|
||||||
def test_get_routing_table_6(self):
|
def test_get_routing_table_6(self):
|
||||||
ip_route_output = ("""
|
ip_route_output = ("""
|
||||||
2001:db8:0:f101::/64 dev tap-1 proto kernel metric 256 pref medium
|
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
|
default via 2001:db8:0:f101::4 dev tap-1 metric 1024 pref medium
|
||||||
""")
|
""")
|
||||||
expected = [{'destination': '2001:db8:0:f101::/64',
|
expected = [{'destination': '2001:db8:0:f101::/64',
|
||||||
'nexthop': None,
|
'nexthop': None,
|
||||||
'device': 'tap-1',
|
'device': 'tap-1',
|
||||||
'scope': None},
|
'scope': None},
|
||||||
|
{'destination': '2001:db8:0:f102::/64',
|
||||||
|
'nexthop': None,
|
||||||
|
'device': 'tap-2',
|
||||||
|
'scope': None},
|
||||||
{'destination': 'default',
|
{'destination': 'default',
|
||||||
'nexthop': '2001:db8:0:f101::4',
|
'nexthop': '2001:db8:0:f101::4',
|
||||||
'device': 'tap-1',
|
'device': 'tap-1',
|
||||||
|
|
Loading…
Reference in New Issue