diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 7e04a3f8643..7ae353c9e92 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -76,6 +76,13 @@ DEFAULT_GW_PATTERN = re.compile(r"via (\S+)") METRIC_PATTERN = re.compile(r"metric (\S+)") DEVICE_NAME_PATTERN = re.compile(r"(\d+?): (\S+?):.*") +# NOTE: no metric is interpreted by the kernel as having the highest priority +# (value 0). "ip route" uses the netlink API to communicate with the kernel. In +# IPv6, when the metric value is not set is translated as 1024 as default: +# https://access.redhat.com/solutions/3659171 +IP_ROUTE_METRIC_DEFAULT = {constants.IP_VERSION_4: 0, + constants.IP_VERSION_6: 1024} + def remove_interface_suffix(interface): """Remove a possible "@" suffix from an interface' name. @@ -1501,6 +1508,8 @@ def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None, else: cidr = constants.IP_ANY[ip_version] table = int(get_attr(route, 'RTA_TABLE')) + metric = (get_attr(route, 'RTA_PRIORITY') or + IP_ROUTE_METRIC_DEFAULT[ip_version]) value = { 'table': IP_RULE_TABLES_NAMES.get(table, table), 'source_prefix': get_attr(route, 'RTA_PREFSRC'), @@ -1508,7 +1517,7 @@ def list_ip_routes(namespace, ip_version, scope=None, via=None, table=None, 'scope': IP_ADDRESS_SCOPE[int(route['scope'])], 'device': get_device(int(get_attr(route, 'RTA_OIF')), devices), 'via': get_attr(route, 'RTA_GATEWAY'), - 'metric': get_attr(route, 'RTA_PRIORITY'), + 'metric': metric, } ret.append(value) diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py index 2f515ba23ba..08f7bfc6dd5 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -397,7 +397,8 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase): if gateway: # Ensure that the gateway can be updated by changing the metric metric = 100 - if 'metric' in gateway: + ip_version = utils.get_ip_version(gateway['cidr']) + if gateway['metric'] != ip_lib.IP_ROUTE_METRIC_DEFAULT[ip_version]: metric = gateway['metric'] - 1 dst_device.route.add_gateway(gateway=gateway['via'], metric=metric) diff --git a/neutron/tests/functional/agent/linux/test_ip_lib.py b/neutron/tests/functional/agent/linux/test_ip_lib.py index cef8f88089a..2bab8a5de2f 100644 --- a/neutron/tests/functional/agent/linux/test_ip_lib.py +++ b/neutron/tests/functional/agent/linux/test_ip_lib.py @@ -866,8 +866,8 @@ class IpRouteCommandTestCase(functional_base.BaseSudoTestCase): scope = ip_lib.IP_ADDRESS_SCOPE[0] elif not scope: scope = 'global' if via else 'link' - if ip_version == constants.IP_VERSION_6 and not metric: - metric = 1024 + if not metric: + metric = ip_lib.IP_ROUTE_METRIC_DEFAULT[ip_version] table = table or iproute_linux.DEFAULT_TABLE table = ip_lib.IP_RULE_TABLES_NAMES.get(table, table) cmp = {'table': table, diff --git a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py index 06fe069a369..37954dff11d 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py @@ -471,7 +471,8 @@ class TestLinuxBridgeManager(base.BaseTestCase): dv6_fn.assert_not_called() def test__update_interface_ip_details(self): - gwdict = dict(via='1.1.1.1', + gwdict = dict(cidr='1.1.1.1/24', + via='1.1.1.1', metric=50) ipdict = dict(cidr='1.1.1.1/24', broadcast='1.1.1.255',