Add list routes

This adds list routes while refactoring list_onlink_routes to share
implementation.  It changes test_onlink_routes to be consistent in the
type of data that it returns with the new list_routes.

Change-Id: I386a8e2cb146385bb59a7a8387a29dddbec48d8a
Partially-Implements: blueprint address-scopes
This commit is contained in:
Carl Baldwin 2015-08-28 21:28:39 +00:00 committed by Carl Baldwin
parent d91f6ee911
commit da4ee8c8d2
4 changed files with 87 additions and 21 deletions

View File

@ -183,14 +183,16 @@ class LinuxInterfaceDriver(object):
device = ip_lib.IPDevice(device_name, namespace=namespace)
# Manage on-link routes (routes without an associated address)
new_onlink_routes = set(s['cidr'] for s in extra_subnets or [])
existing_onlink_routes = set(
device.route.list_onlink_routes(n_const.IP_VERSION_4) +
device.route.list_onlink_routes(n_const.IP_VERSION_6))
for route in new_onlink_routes - existing_onlink_routes:
new_onlink_cidrs = set(s['cidr'] for s in extra_subnets or [])
v4_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_4)
v6_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_6)
existing_onlink_cidrs = set(r['cidr'] for r in v4_onlink + v6_onlink)
for route in new_onlink_cidrs - existing_onlink_cidrs:
LOG.debug("adding onlink route(%s)", route)
device.route.add_onlink_route(route)
for route in existing_onlink_routes - new_onlink_routes:
for route in existing_onlink_cidrs - new_onlink_cidrs:
LOG.debug("deleting onlink route(%s)", route)
device.route.delete_onlink_route(route)

View File

@ -597,19 +597,45 @@ class IpRouteCommand(IpDeviceCommandBase):
raise exceptions.DeviceNotFoundError(
device_name=self.name)
def list_onlink_routes(self, ip_version):
def iterate_routes():
args = ['list']
args += self._dev_args()
args += ['scope', 'link']
args += self._table_args()
output = self._run([ip_version], tuple(args))
for line in output.split('\n'):
line = line.strip()
if line and not line.count('src'):
yield line
def _parse_routes(self, ip_version, output, **kwargs):
for line in output.splitlines():
parts = line.split()
return [x for x in iterate_routes()]
# Format of line is: "<cidr>|default [<key> <value>] ..."
route = {k: v for k, v in zip(parts[1::2], parts[2::2])}
route['cidr'] = parts[0]
# Avoids having to explicitly pass around the IP version
if route['cidr'] == 'default':
route['cidr'] = constants.IP_ANY[ip_version]
# ip route drops things like scope and dev from the output if it
# was specified as a filter. This allows us to add them back.
if self.name:
route['dev'] = self.name
if self._table:
route['table'] = self._table
# Callers add any filters they use as kwargs
route.update(kwargs)
yield route
def list_routes(self, ip_version):
args = ['list']
args += self._dev_args()
args += self._table_args()
output = self._run([ip_version], tuple(args))
return [r for r in self._parse_routes(ip_version, output)]
def list_onlink_routes(self, ip_version):
args = ['list']
args += self._dev_args()
args += ['scope', 'link']
args += self._table_args()
output = self._run([ip_version], tuple(args))
return [r for r in self._parse_routes(ip_version, output, scope='link')
if 'src' not in r]
def add_onlink_route(self, cidr):
ip_version = get_ip_version(cidr)

View File

@ -104,7 +104,8 @@ class TestABCDriver(TestBase):
addresses = [dict(scope='global',
dynamic=False, cidr='172.16.77.240/24')]
self.ip_dev().addr.list = mock.Mock(return_value=addresses)
self.ip_dev().route.list_onlink_routes.return_value = ['172.20.0.0/24']
self.ip_dev().route.list_onlink_routes.return_value = [
{'cidr': '172.20.0.0/24'}]
bc = BaseChild(self.conf)
ns = '12345678-1234-5678-90ab-ba0987654321'
@ -218,7 +219,7 @@ class TestABCDriver(TestBase):
dynamic=False, cidr='2001:db8:a::123/64')]
route = '2001:db8:a::/64'
self.ip_dev().addr.list = mock.Mock(return_value=addresses)
self.ip_dev().route.list_onlink_routes.return_value = [route]
self.ip_dev().route.list_onlink_routes.return_value = [{'cidr': route}]
bc = BaseChild(self.conf)
ns = '12345678-1234-5678-90ab-ba0987654321'

View File

@ -959,13 +959,34 @@ class TestIpRouteCommand(TestIPCmdBase):
'dev', self.parent.name,
'table', self.table))
def test_list_routes(self):
self.parent._run.return_value = (
"default via 172.124.4.1 dev eth0 metric 100\n"
"10.0.0.0/22 dev eth0 scope link\n"
"172.24.4.0/24 dev eth0 proto kernel src 172.24.4.2\n")
routes = self.route_cmd.table(self.table).list_routes(self.ip_version)
self.assertEqual([{'cidr': '0.0.0.0/0',
'dev': 'eth0',
'metric': '100',
'table': 14,
'via': '172.124.4.1'},
{'cidr': '10.0.0.0/22',
'dev': 'eth0',
'scope': 'link',
'table': 14},
{'cidr': '172.24.4.0/24',
'dev': 'eth0',
'proto': 'kernel',
'src': '172.24.4.2',
'table': 14}], routes)
def test_list_onlink_routes_subtable(self):
self.parent._run.return_value = (
"10.0.0.0/22\n"
"172.24.4.0/24 proto kernel src 172.24.4.2\n")
routes = self.route_cmd.table(self.table).list_onlink_routes(
self.ip_version)
self.assertEqual(['10.0.0.0/22'], routes)
self.assertEqual(['10.0.0.0/22'], [r['cidr'] for r in routes])
self._assert_call([self.ip_version],
('list', 'dev', self.parent.name, 'scope', 'link',
'table', self.table))
@ -1012,6 +1033,22 @@ class TestIPv6IpRouteCommand(TestIpRouteCommand):
{'gateway': '2001:470:9:1224:4508:b885:5fb:740b',
'metric': 1024}}]
def test_list_routes(self):
self.parent._run.return_value = (
"default via 2001:db8::1 dev eth0 metric 100\n"
"2001:db8::/64 dev eth0 proto kernel src 2001:db8::2\n")
routes = self.route_cmd.table(self.table).list_routes(self.ip_version)
self.assertEqual([{'cidr': '::/0',
'dev': 'eth0',
'metric': '100',
'table': 14,
'via': '2001:db8::1'},
{'cidr': '2001:db8::/64',
'dev': 'eth0',
'proto': 'kernel',
'src': '2001:db8::2',
'table': 14}], routes)
class TestIPRoute(TestIpRouteCommand):
"""Leverage existing tests for IpRouteCommand for IPRoute