From da4ee8c8d26880b6b1a20d18f5cbd38e7d5e4b04 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Fri, 28 Aug 2015 21:28:39 +0000 Subject: [PATCH] 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 --- neutron/agent/linux/interface.py | 14 +++--- neutron/agent/linux/ip_lib.py | 50 ++++++++++++++----- .../tests/unit/agent/linux/test_interface.py | 5 +- neutron/tests/unit/agent/linux/test_ip_lib.py | 39 ++++++++++++++- 4 files changed, 87 insertions(+), 21 deletions(-) diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index 090b62def..44a0ad834 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -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) diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 961d55791..1f26561c7 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -597,19 +597,45 @@ class IpRouteCommand(IpDeviceCommandBase): raise exceptions.DeviceNotFoundError( device_name=self.name) + def _parse_routes(self, ip_version, output, **kwargs): + for line in output.splitlines(): + parts = line.split() + + # Format of line is: "|default [ ] ..." + 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): - 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 - - return [x for x in iterate_routes()] + 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) diff --git a/neutron/tests/unit/agent/linux/test_interface.py b/neutron/tests/unit/agent/linux/test_interface.py index ffa0f6e06..38185ead6 100644 --- a/neutron/tests/unit/agent/linux/test_interface.py +++ b/neutron/tests/unit/agent/linux/test_interface.py @@ -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' diff --git a/neutron/tests/unit/agent/linux/test_ip_lib.py b/neutron/tests/unit/agent/linux/test_ip_lib.py index b49a0d0ff..dfa6d04bc 100644 --- a/neutron/tests/unit/agent/linux/test_ip_lib.py +++ b/neutron/tests/unit/agent/linux/test_ip_lib.py @@ -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 -- 2.45.2