Fix support of IPv6 only networks in OVN metadata agent
When an IPv6 only network is used as the sole network for a VM and there are no other bound ports on the same network in the same chassis, the OVN metadata agent concludes that the associated namespace is not needed and deletes it. As a consequence, the VM cannot access the metadata service. With this change, the namespace is preserved if there is at least one bound port on the chassis with either IPv4 or IPv6 addresses. Closes-Bug: #2069482 Change-Id: Ie15c3344161ad521bf10b98303c7bb730351e2d8 (cherry picked from commit f7000f3d57bc59732522c4943d6ff2e9dfcf7d31)
This commit is contained in:
parent
d0f11ca346
commit
b98003cf9f
neutron
@ -617,9 +617,10 @@ class MetadataAgent(object):
|
||||
iptables_mgr.ipv4['mangle'].add_rule('POSTROUTING', rule, wrap=False)
|
||||
iptables_mgr.apply()
|
||||
|
||||
def _get_port_ip4_ips(self, port):
|
||||
def _get_port_ip4_ips_and_ip6_flag(self, port):
|
||||
# Retrieve IPv4 addresses from the port mac column which is in form
|
||||
# ["<port_mac> <ip1> <ip2> ... <ipN>"]
|
||||
# ["<port_mac> <ip1> <ip2> ... <ipN>"]. Also return True if the port
|
||||
# has at least one IPv6 address
|
||||
if not port.mac:
|
||||
LOG.warning("Port %s MAC column is empty, cannot retrieve IP "
|
||||
"addresses", port.uuid)
|
||||
@ -629,10 +630,17 @@ class MetadataAgent(object):
|
||||
if not ips:
|
||||
LOG.debug("Port %s IP addresses were not retrieved from the "
|
||||
"Port_Binding MAC column %s", port.uuid, mac_field_attrs)
|
||||
return [ip for ip in ips if (
|
||||
utils.get_ip_version(ip) == n_const.IP_VERSION_4)]
|
||||
ip4_ips = []
|
||||
any_ip6 = False
|
||||
for ip in ips:
|
||||
if utils.get_ip_version(ip) == n_const.IP_VERSION_4:
|
||||
ip4_ips.append(ip)
|
||||
else:
|
||||
any_ip6 = True
|
||||
return ip4_ips, any_ip6
|
||||
|
||||
def _active_subnets_cidrs(self, datapath_ports_ips, metadata_port_cidrs):
|
||||
def _active_subnets_cidrs(self, datapath_ports_ip4_ips,
|
||||
metadata_port_cidrs):
|
||||
active_subnets_cidrs = set()
|
||||
# Prepopulate a dictionary where each metadata_port_cidr(string) maps
|
||||
# to its netaddr.IPNetwork object. This is so we dont have to
|
||||
@ -642,7 +650,7 @@ class MetadataAgent(object):
|
||||
for metadata_port_cidr in metadata_port_cidrs if metadata_port_cidr
|
||||
}
|
||||
|
||||
for datapath_port_ip in datapath_ports_ips:
|
||||
for datapath_port_ip in datapath_ports_ip4_ips:
|
||||
ip_obj = netaddr.IPAddress(datapath_port_ip)
|
||||
for metadata_cidr, metadata_cidr_obj in \
|
||||
metadata_cidrs_to_network_objects.items():
|
||||
@ -652,9 +660,10 @@ class MetadataAgent(object):
|
||||
return active_subnets_cidrs
|
||||
|
||||
def _process_cidrs(self, current_namespace_cidrs,
|
||||
datapath_ports_ips, metadata_port_subnet_cidrs, lla):
|
||||
datapath_ports_ip4_ips,
|
||||
metadata_port_subnet_cidrs, lla):
|
||||
active_subnets_cidrs = self._active_subnets_cidrs(
|
||||
datapath_ports_ips, metadata_port_subnet_cidrs)
|
||||
datapath_ports_ip4_ips, metadata_port_subnet_cidrs)
|
||||
|
||||
cidrs_to_add = active_subnets_cidrs - current_namespace_cidrs
|
||||
|
||||
@ -713,18 +722,22 @@ class MetadataAgent(object):
|
||||
|
||||
chassis_ports = self.sb_idl.get_ports_on_chassis(
|
||||
self._chassis, include_additional_chassis=True)
|
||||
datapath_ports_ips = []
|
||||
datapath_ports_ip4_ips = []
|
||||
any_ip6 = False
|
||||
for chassis_port in self._vif_ports(chassis_ports):
|
||||
if str(chassis_port.datapath.uuid) == datapath_uuid:
|
||||
datapath_ports_ips.extend(self._get_port_ip4_ips(chassis_port))
|
||||
ip4_ips, ip6_flag = self._get_port_ip4_ips_and_ip6_flag(
|
||||
chassis_port)
|
||||
datapath_ports_ip4_ips.extend(ip4_ips)
|
||||
any_ip6 = any_ip6 or ip6_flag
|
||||
|
||||
if not datapath_ports_ips:
|
||||
if not (datapath_ports_ip4_ips or any_ip6):
|
||||
LOG.debug("No valid VIF ports were found for network %s, "
|
||||
"tearing the namespace down if needed", net_name)
|
||||
self.teardown_datapath(net_name)
|
||||
return
|
||||
|
||||
return net_name, datapath_ports_ips, metadata_port_info
|
||||
return net_name, datapath_ports_ip4_ips, metadata_port_info
|
||||
|
||||
def provision_datapath(self, port_binding):
|
||||
"""Provision the datapath so that it can serve metadata.
|
||||
@ -744,7 +757,7 @@ class MetadataAgent(object):
|
||||
provision_params = self._get_provision_params(datapath)
|
||||
if not provision_params:
|
||||
return
|
||||
net_name, datapath_ports_ips, metadata_port_info = provision_params
|
||||
net_name, datapath_ports_ip4_ips, metadata_port_info = provision_params
|
||||
|
||||
LOG.info("Provisioning metadata for network %s", net_name)
|
||||
# Create the VETH pair if it's not created. Also the add_veth function
|
||||
@ -780,7 +793,7 @@ class MetadataAgent(object):
|
||||
|
||||
cidrs_to_add, cidrs_to_delete = self._process_cidrs(
|
||||
{dev['cidr'] for dev in ip2.addr.list()},
|
||||
datapath_ports_ips,
|
||||
datapath_ports_ip4_ips,
|
||||
metadata_port_info.ip_addresses,
|
||||
ip_lib.get_ipv6_lladdr(metadata_port_info.mac)
|
||||
)
|
||||
|
@ -30,6 +30,7 @@ from neutron.agent.linux import utils as linux_utils
|
||||
from neutron.agent.ovn.metadata import agent
|
||||
from neutron.agent.ovn.metadata import driver
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.common import utils
|
||||
from neutron.conf.agent.metadata import config as meta_conf
|
||||
from neutron.conf.agent.ovn.metadata import config as ovn_meta_conf
|
||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||
@ -357,12 +358,12 @@ class TestMetadataAgent(base.BaseTestCase):
|
||||
self.assertIsNone(self.agent._get_provision_params(datapath))
|
||||
tdp.assert_called_once_with(network_id)
|
||||
|
||||
def test__get_provision_params_returns_provision_parameters(self):
|
||||
def _test__get_provision_params_returns_provision_parameters(self,
|
||||
port_ip):
|
||||
"""The happy path when datapath has ports with "external" or ""(blank)
|
||||
types and metadata port contains MAC and subnet CIDRs.
|
||||
"""
|
||||
network_id = '1'
|
||||
port_ip = '1.2.3.4'
|
||||
metada_port_mac = "fa:16:3e:22:65:18"
|
||||
metada_port_subnet_cidr = "10.204.0.10/29"
|
||||
metada_port_logical_port = "3b66c176-199b-48ec-8331-c1fd3f6e2b44"
|
||||
@ -388,13 +389,23 @@ class TestMetadataAgent(base.BaseTestCase):
|
||||
net_name, datapath_port_ips, metadata_port_info = actual_params
|
||||
|
||||
self.assertEqual(network_id, net_name)
|
||||
self.assertListEqual([port_ip], datapath_port_ips)
|
||||
|
||||
if utils.get_ip_version(port_ip) == n_const.IP_VERSION_4:
|
||||
self.assertListEqual([port_ip], datapath_port_ips)
|
||||
self.assertEqual(metada_port_mac, metadata_port_info.mac)
|
||||
self.assertSetEqual(set([metada_port_subnet_cidr]),
|
||||
metadata_port_info.ip_addresses)
|
||||
self.assertEqual(metada_port_logical_port,
|
||||
metadata_port_info.logical_port)
|
||||
|
||||
def test__get_provision_params_returns_provision_parameters(self):
|
||||
self._test__get_provision_params_returns_provision_parameters(
|
||||
'1.2.3.4')
|
||||
|
||||
def test__get_provision_params_returns_provision_parameters_ipv6(self):
|
||||
self._test__get_provision_params_returns_provision_parameters(
|
||||
'fe80::f816:3eff:feb6:c0c0')
|
||||
|
||||
def test_provision_datapath(self):
|
||||
"""Test datapath provisioning.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user