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)
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: "<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):
- 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)
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'
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'
'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))
{'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