Merge "Replace "ip route" command in "dvr_local_router""

This commit is contained in:
Zuul 2021-03-16 11:46:41 +00:00 committed by Gerrit Code Review
commit b17a5dacd6
5 changed files with 43 additions and 16 deletions

View File

@ -19,6 +19,7 @@ import netaddr
from neutron_lib import constants as lib_constants from neutron_lib import constants as lib_constants
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
from pyroute2.netlink import exceptions as pyroute2_exc
from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_fip_ns
from neutron.agent.l3 import dvr_router_base from neutron.agent.l3 import dvr_router_base
@ -800,16 +801,17 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
def _update_fip_route_table_with_next_hop_routes(self, operation, route, def _update_fip_route_table_with_next_hop_routes(self, operation, route,
fip_ns_name, tbl_index): fip_ns_name, tbl_index):
cmd = ['ip', 'route', operation, 'to', route['destination'], cmd = (ip_lib.add_ip_route if operation == 'replace' else
'via', route['nexthop'], 'table', tbl_index] ip_lib.delete_ip_route)
ip_wrapper = ip_lib.IPWrapper(namespace=fip_ns_name) try:
if ip_wrapper.netns.exists(fip_ns_name): cmd(fip_ns_name, route['destination'], via=route['nexthop'],
ip_wrapper.netns.execute(cmd, check_exit_code=False, table=tbl_index, proto='boot')
privsep_exec=True) except priv_ip_lib.NetworkNamespaceNotFound:
else:
LOG.debug("The FIP namespace %(ns)s does not exist for " LOG.debug("The FIP namespace %(ns)s does not exist for "
"router %(id)s", "router %(id)s",
{'ns': fip_ns_name, 'id': self.router_id}) {'ns': fip_ns_name, 'id': self.router_id})
except (OSError, pyroute2_exc.NetlinkError):
pass
def _check_if_route_applicable_to_fip_namespace(self, route, def _check_if_route_applicable_to_fip_namespace(self, route,
agent_gateway_port): agent_gateway_port):

View File

@ -1500,14 +1500,14 @@ def ip_monitor(namespace, queue, event_stop, event_started):
def add_ip_route(namespace, cidr, device=None, via=None, table=None, def add_ip_route(namespace, cidr, device=None, via=None, table=None,
metric=None, scope=None, **kwargs): metric=None, scope=None, proto='static', **kwargs):
"""Add an IP route""" """Add an IP route"""
if table: if table:
table = IP_RULE_TABLES.get(table, table) table = IP_RULE_TABLES.get(table, table)
ip_version = common_utils.get_ip_version(cidr or via) ip_version = common_utils.get_ip_version(cidr or via)
privileged.add_ip_route(namespace, cidr, ip_version, privileged.add_ip_route(namespace, cidr, ip_version,
device=device, via=via, table=table, device=device, via=via, table=table,
metric=metric, scope=scope, **kwargs) metric=metric, scope=scope, proto=proto, **kwargs)
def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None, def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None,
@ -1517,6 +1517,12 @@ def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None,
for device in (d for d in devices if d['index'] == index): for device in (d for d in devices if d['index'] == index):
return get_attr(device, 'IFLA_IFNAME') return get_attr(device, 'IFLA_IFNAME')
def get_proto(proto_number):
if proto_number in rtnl.rt_proto:
return rtnl.rt_proto[proto_number]
elif str(proto_number) in constants.IP_PROTOCOL_NUM_TO_NAME_MAP:
return constants.IP_PROTOCOL_NUM_TO_NAME_MAP[str(proto_number)]
table = table if table else 'main' table = table if table else 'main'
table = IP_RULE_TABLES.get(table, table) table = IP_RULE_TABLES.get(table, table)
routes = privileged.list_ip_routes(namespace, ip_version, device=device, routes = privileged.list_ip_routes(namespace, ip_version, device=device,
@ -1532,6 +1538,7 @@ def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None,
table = int(get_attr(route, 'RTA_TABLE')) table = int(get_attr(route, 'RTA_TABLE'))
metric = (get_attr(route, 'RTA_PRIORITY') or metric = (get_attr(route, 'RTA_PRIORITY') or
IP_ROUTE_METRIC_DEFAULT[ip_version]) IP_ROUTE_METRIC_DEFAULT[ip_version])
proto = get_proto(route['proto'])
value = { value = {
'table': IP_RULE_TABLES_NAMES.get(table, table), 'table': IP_RULE_TABLES_NAMES.get(table, table),
'source_prefix': get_attr(route, 'RTA_PREFSRC'), 'source_prefix': get_attr(route, 'RTA_PREFSRC'),
@ -1540,6 +1547,7 @@ def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None,
'device': get_device(int(get_attr(route, 'RTA_OIF')), devices), 'device': get_device(int(get_attr(route, 'RTA_OIF')), devices),
'via': get_attr(route, 'RTA_GATEWAY'), 'via': get_attr(route, 'RTA_GATEWAY'),
'metric': metric, 'metric': metric,
'proto': proto,
} }
ret.append(value) ret.append(value)

View File

@ -720,11 +720,12 @@ def _make_pyroute2_route_args(namespace, ip_version, cidr, device, via, table,
@privileged.default.entrypoint @privileged.default.entrypoint
def add_ip_route(namespace, cidr, ip_version, device=None, via=None, def add_ip_route(namespace, cidr, ip_version, device=None, via=None,
table=None, metric=None, scope=None, **kwargs): table=None, metric=None, scope=None, proto='static',
**kwargs):
"""Add an IP route""" """Add an IP route"""
kwargs.update(_make_pyroute2_route_args( kwargs.update(_make_pyroute2_route_args(
namespace, ip_version, cidr, device, via, table, metric, scope, namespace, ip_version, cidr, device, via, table, metric, scope,
'static')) proto))
try: try:
with get_iproute(namespace) as ip: with get_iproute(namespace) as ip:
ip.route('replace', **kwargs) ip.route('replace', **kwargs)

View File

@ -919,7 +919,8 @@ class IpRouteCommandTestCase(functional_base.BaseSudoTestCase):
'scope': scope, 'scope': scope,
'device': 'test_device', 'device': 'test_device',
'via': via, 'via': via,
'metric': metric} 'metric': metric,
'proto': 'static'}
try: try:
utils.wait_until_true(fn, timeout=5) utils.wait_until_true(fn, timeout=5)
except utils.WaitTimeout: except utils.WaitTimeout:

View File

@ -22,6 +22,7 @@ from neutron_lib import constants as n_cons
from oslo_utils import uuidutils from oslo_utils import uuidutils
from pyroute2.ipdb import routes as ipdb_routes from pyroute2.ipdb import routes as ipdb_routes
from pyroute2.iproute import linux as iproute_linux from pyroute2.iproute import linux as iproute_linux
from pyroute2.netlink import rtnl
import testtools import testtools
from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_lib
@ -476,7 +477,7 @@ class RouteTestCase(functional_base.BaseSudoTestCase):
self.device.link.set_up() self.device.link.set_up()
def _check_routes(self, cidrs, table=None, gateway=None, metric=None, def _check_routes(self, cidrs, table=None, gateway=None, metric=None,
scope=None): scope=None, proto='static'):
table = table or iproute_linux.DEFAULT_TABLE table = table or iproute_linux.DEFAULT_TABLE
if not scope: if not scope:
scope = 'universe' if gateway else 'link' scope = 'universe' if gateway else 'link'
@ -503,6 +504,7 @@ class RouteTestCase(functional_base.BaseSudoTestCase):
self.assertEqual(metric, self.assertEqual(metric,
ip_lib.get_attr(route, 'RTA_PRIORITY')) ip_lib.get_attr(route, 'RTA_PRIORITY'))
self.assertEqual(scope, route['scope']) self.assertEqual(scope, route['scope'])
self.assertEqual(rtnl.rt_proto[proto], route['proto'])
break break
else: else:
self.fail('CIDR %s not found in the list of routes' % cidr) self.fail('CIDR %s not found in the list of routes' % cidr)
@ -533,16 +535,17 @@ class RouteTestCase(functional_base.BaseSudoTestCase):
% gateway) % gateway)
def _add_route_device_and_check(self, table=None, metric=None, def _add_route_device_and_check(self, table=None, metric=None,
scope='link'): scope='link', proto='static'):
cidrs = ['192.168.0.0/24', '172.90.0.0/16', '10.0.0.0/8', cidrs = ['192.168.0.0/24', '172.90.0.0/16', '10.0.0.0/8',
'2001:db8::/64'] '2001:db8::/64']
for cidr in cidrs: for cidr in cidrs:
ip_version = common_utils.get_ip_version(cidr) ip_version = common_utils.get_ip_version(cidr)
priv_ip_lib.add_ip_route(self.namespace, cidr, ip_version, priv_ip_lib.add_ip_route(self.namespace, cidr, ip_version,
device=self.device_name, table=table, device=self.device_name, table=table,
metric=metric, scope=scope) metric=metric, scope=scope, proto=proto)
self._check_routes(cidrs, table=table, metric=metric, scope=scope) self._check_routes(cidrs, table=table, metric=metric, scope=scope,
proto=proto)
def test_add_route_device(self): def test_add_route_device(self):
self._add_route_device_and_check(table=None) self._add_route_device_and_check(table=None)
@ -565,6 +568,18 @@ class RouteTestCase(functional_base.BaseSudoTestCase):
def test_add_route_device_scope_host(self): def test_add_route_device_scope_host(self):
self._add_route_device_and_check(scope='host') self._add_route_device_and_check(scope='host')
def test_add_route_device_proto_static(self):
self._add_route_device_and_check(proto='static') # default
def test_add_route_device_proto_redirect(self):
self._add_route_device_and_check(proto='redirect')
def test_add_route_device_proto_kernel(self):
self._add_route_device_and_check(proto='kernel')
def test_add_route_device_proto_boot(self):
self._add_route_device_and_check(proto='boot')
def test_add_route_via_ipv4(self): def test_add_route_via_ipv4(self):
cidrs = ['192.168.0.0/24', '172.90.0.0/16', '10.0.0.0/8'] cidrs = ['192.168.0.0/24', '172.90.0.0/16', '10.0.0.0/8']
int_cidr = '192.168.20.1/24' int_cidr = '192.168.20.1/24'