[ovn] Add neutron network to metadata namespace names
Until this patch the metadata namespace had the format 'ovnmeta-<OVN datapath uuid>' which makes it hard to relate to the Neutron network for which metadata is provisioned to. This applies to the name of the tap interface that connects the network namespace to the integration bridge. This patch is changing the name convention and providing an upgrade path to include the Neutron network ID to make debugging and troubleshooting easier for developers and operators. The new name pattern is: 'ovnmeta-<Neutron network uuid>' Please, note that with this patch, the old namespaces will be deleted and new ones will be recreated. (Manually cherry picked from Neutron e4fb06b24299a7ecf10b05ef6ddc2d883c40e5a1) Conflicts: networking_ovn/agent/metadata/agent.py networking_ovn/common/utils.py networking_ovn/ovsdb/impl_idl_ovn.py Signed-off-by: Daniel Alvarez <dalvarez@redhat.com> Change-Id: Ic8ffa9c4437aab6fb1878b3a1ebf2c3ab86e3d0c
This commit is contained in:
parent
249e530913
commit
e7e0796179
networking_ovn
releasenotes/notes
@ -34,6 +34,7 @@ from networking_ovn.agent.metadata import ovsdb
|
||||
from networking_ovn.agent.metadata import server as metadata_server
|
||||
from networking_ovn.common import config
|
||||
from networking_ovn.common import constants as ovn_const
|
||||
from networking_ovn.common import utils as ovn_utils
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -87,9 +88,10 @@ class PortBindingChassisEvent(row_event.RowEvent):
|
||||
return
|
||||
with _SYNC_STATE_LOCK.read_lock():
|
||||
try:
|
||||
LOG.info(self.LOG_MSG, row.logical_port,
|
||||
str(row.datapath.uuid))
|
||||
self.agent.update_datapath(str(row.datapath.uuid))
|
||||
net_name = ovn_utils.get_network_name_from_datapath(
|
||||
row.datapath)
|
||||
LOG.info(self.LOG_MSG, row.logical_port, net_name)
|
||||
self.agent.update_datapath(str(row.datapath.uuid), net_name)
|
||||
except ConfigException:
|
||||
# We're now in the reader lock mode, we need to exit the
|
||||
# context and then use writer lock
|
||||
@ -348,14 +350,20 @@ class MetadataAgent(object):
|
||||
def _vif_ports(self, ports):
|
||||
return (p for p in ports if p.type in OVN_VIF_PORT_TYPES)
|
||||
|
||||
def teardown_datapath(self, datapath):
|
||||
def teardown_datapath(self, datapath, net_name=None):
|
||||
"""Unprovision this datapath to stop serving metadata.
|
||||
|
||||
This function will shutdown metadata proxy if it's running and delete
|
||||
the VETH pair, the OVS port and the namespace.
|
||||
"""
|
||||
self.update_chassis_metadata_networks(datapath, remove=True)
|
||||
namespace = self._get_namespace_name(datapath)
|
||||
|
||||
# TODO(dalvarez): Remove this in Y cycle when we are sure that all
|
||||
# namespaces will be created with the Neutron network UUID and not
|
||||
# anymore with the OVN datapath UUID.
|
||||
dp = net_name or datapath
|
||||
|
||||
namespace = self._get_namespace_name(dp)
|
||||
ip = ip_lib.IPWrapper(namespace)
|
||||
# If the namespace doesn't exist, return
|
||||
if not ip.netns.exists(namespace):
|
||||
@ -365,7 +373,8 @@ class MetadataAgent(object):
|
||||
namespace)
|
||||
|
||||
metadata_driver.MetadataDriver.destroy_monitored_metadata_proxy(
|
||||
self._process_monitor, datapath, self.conf, namespace)
|
||||
self._process_monitor, dp, self.conf, namespace)
|
||||
veth_name = self._get_veth_name(dp)
|
||||
|
||||
veth_name = self._get_veth_name(datapath)
|
||||
self.ovs_idl.del_port(veth_name[0]).execute()
|
||||
@ -374,7 +383,7 @@ class MetadataAgent(object):
|
||||
|
||||
ip.garbage_collect_namespace()
|
||||
|
||||
def update_datapath(self, datapath):
|
||||
def update_datapath(self, datapath, net_name):
|
||||
"""Update the metadata service for this datapath.
|
||||
|
||||
This function will:
|
||||
@ -389,9 +398,9 @@ class MetadataAgent(object):
|
||||
datapath_ports = [p for p in self._vif_ports(ports) if
|
||||
str(p.datapath.uuid) == datapath]
|
||||
if datapath_ports:
|
||||
self.provision_datapath(datapath)
|
||||
self.provision_datapath(datapath, net_name)
|
||||
else:
|
||||
self.teardown_datapath(datapath)
|
||||
self.teardown_datapath(datapath, net_name)
|
||||
|
||||
def _ensure_datapath_checksum(self, namespace):
|
||||
"""Ensure the correct checksum in the metadata packets in DPDK bridges
|
||||
@ -411,7 +420,7 @@ class MetadataAgent(object):
|
||||
iptables_mgr.ipv4['mangle'].add_rule('POSTROUTING', rule, wrap=False)
|
||||
iptables_mgr.apply()
|
||||
|
||||
def provision_datapath(self, datapath):
|
||||
def provision_datapath(self, datapath, net_name):
|
||||
"""Provision the datapath so that it can serve metadata.
|
||||
|
||||
This function will create the namespace and VETH pair if needed
|
||||
@ -421,7 +430,7 @@ class MetadataAgent(object):
|
||||
|
||||
:return: The metadata namespace name of this datapath
|
||||
"""
|
||||
LOG.debug("Provisioning datapath %s", datapath)
|
||||
LOG.debug("Provisioning metadata for network %s", net_name)
|
||||
port = self.sb_idl.get_metadata_port_network(datapath)
|
||||
# If there's no metadata port or it doesn't have a MAC or IP
|
||||
# addresses, then tear the namespace down if needed. This might happen
|
||||
@ -429,10 +438,10 @@ class MetadataAgent(object):
|
||||
# an IP address.
|
||||
if not (port and port.mac and
|
||||
port.external_ids.get(ovn_const.OVN_CIDRS_EXT_ID_KEY, None)):
|
||||
LOG.debug("There is no metadata port for datapath %s or it has no "
|
||||
LOG.debug("There is no metadata port for network %s or it has no "
|
||||
"MAC or IP addresses configured, tearing the namespace "
|
||||
"down if needed", datapath)
|
||||
self.teardown_datapath(datapath)
|
||||
"down if needed", net_name)
|
||||
self.teardown_datapath(datapath, net_name)
|
||||
return
|
||||
|
||||
# First entry of the mac field must be the MAC address.
|
||||
@ -440,9 +449,9 @@ class MetadataAgent(object):
|
||||
# If it is not, we can't provision the namespace. Tear it down if
|
||||
# needed and log the error.
|
||||
if not match:
|
||||
LOG.error("Metadata port for datapath %s doesn't have a MAC "
|
||||
LOG.error("Metadata port for network %s doesn't have a MAC "
|
||||
"address, tearing the namespace down if needed",
|
||||
datapath)
|
||||
net_name)
|
||||
self.teardown_datapath(datapath)
|
||||
return
|
||||
|
||||
@ -454,8 +463,8 @@ class MetadataAgent(object):
|
||||
|
||||
# Create the VETH pair if it's not created. Also the add_veth function
|
||||
# will create the namespace for us.
|
||||
namespace = self._get_namespace_name(datapath)
|
||||
veth_name = self._get_veth_name(datapath)
|
||||
namespace = self._get_namespace_name(net_name)
|
||||
veth_name = self._get_veth_name(net_name)
|
||||
|
||||
ip1 = ip_lib.IPDevice(veth_name[0])
|
||||
if ip_lib.device_exists(veth_name[1], namespace):
|
||||
@ -524,9 +533,9 @@ class MetadataAgent(object):
|
||||
# Spawn metadata proxy if it's not already running.
|
||||
metadata_driver.MetadataDriver.spawn_monitored_metadata_proxy(
|
||||
self._process_monitor, namespace, METADATA_PORT,
|
||||
self.conf, bind_address=METADATA_DEFAULT_IP, network_id=datapath)
|
||||
|
||||
self.update_chassis_metadata_networks(datapath)
|
||||
self.conf, bind_address=METADATA_DEFAULT_IP,
|
||||
network_id=net_name)
|
||||
self.update_chassis_metadata_networks(net_name)
|
||||
return namespace
|
||||
|
||||
def ensure_all_networks_provisioned(self):
|
||||
@ -541,11 +550,13 @@ class MetadataAgent(object):
|
||||
"""
|
||||
# Retrieve all VIF ports in our Chassis
|
||||
ports = self.sb_idl.get_ports_on_chassis(self.chassis)
|
||||
datapaths = {str(p.datapath.uuid) for p in self._vif_ports(ports)}
|
||||
nets = {(str(p.datapath.uuid),
|
||||
ovn_utils.get_network_name_from_datapath(p.datapath))
|
||||
for p in self._vif_ports(ports)}
|
||||
namespaces = []
|
||||
# Make sure that all those datapaths are serving metadata
|
||||
for datapath in datapaths:
|
||||
netns = self.provision_datapath(datapath)
|
||||
for datapath, net_name in nets:
|
||||
netns = self.provision_datapath(datapath, net_name)
|
||||
if netns:
|
||||
namespaces.append(netns)
|
||||
|
||||
|
@ -559,3 +559,7 @@ def connection_config_to_target_string(connection_config):
|
||||
_dict['ip'])
|
||||
elif _dict['file']:
|
||||
return 'p' + _dict['proto'] + ':' + _dict['file']
|
||||
|
||||
|
||||
def get_network_name_from_datapath(datapath):
|
||||
return datapath.external_ids['name'].replace('neutron-', '')
|
||||
|
@ -892,7 +892,15 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend):
|
||||
if not port.chassis:
|
||||
return False
|
||||
|
||||
return port.mac and str(port.datapath.uuid) == network and (
|
||||
# TODO(dalvarez): Remove the comparison to port.datapath.uuid in Y
|
||||
# cycle when we are sure that all namespaces will be created with
|
||||
# the Neutron network UUID and not anymore with the OVN datapath
|
||||
# UUID.
|
||||
is_in_network = lambda port: (
|
||||
str(port.datapath.uuid) == network or
|
||||
utils.get_network_name_from_datapath(port.datapath) == network)
|
||||
|
||||
return port.mac and is_in_network(port) and (
|
||||
ip_address in port.mac[0].split(' '))
|
||||
|
||||
return [r for r in rows if check_net_and_ip(r)]
|
||||
|
@ -31,7 +31,7 @@ from networking_ovn.conf.agent.metadata import config as meta_conf
|
||||
|
||||
OvnPortInfo = collections.namedtuple(
|
||||
'OvnPortInfo', ['datapath', 'type', 'mac', 'external_ids', 'logical_port'])
|
||||
DatapathInfo = collections.namedtuple('DatapathInfo', 'uuid')
|
||||
DatapathInfo = collections.namedtuple('DatapathInfo', ['uuid', 'external_ids'])
|
||||
|
||||
|
||||
def makePort(datapath=None, type='', mac=None, external_ids=None,
|
||||
@ -117,11 +117,14 @@ class TestMetadataAgent(base.BaseTestCase):
|
||||
|
||||
ports = []
|
||||
for i in range(0, 3):
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid=str(i))))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='1')))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='3'),
|
||||
type='external'))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='5'), type='unknown'))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid=str(i),
|
||||
external_ids={'name': 'neutron-%d' % i})))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='1',
|
||||
external_ids={'name': 'neutron-1'})))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='3',
|
||||
external_ids={'name': 'neutron-3'}), type='external'))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='5',
|
||||
external_ids={'name': 'neutron-5'}), type='unknown'))
|
||||
|
||||
with mock.patch.object(self.agent, 'provision_datapath',
|
||||
return_value=None) as pdp,\
|
||||
@ -129,40 +132,42 @@ class TestMetadataAgent(base.BaseTestCase):
|
||||
return_value=ports):
|
||||
self.agent.ensure_all_networks_provisioned()
|
||||
|
||||
expected_calls = [mock.call(str(i)) for i in range(0, 4)]
|
||||
expected_calls = [mock.call(str(i), str(i)) for i in range(0, 4)]
|
||||
self.assertEqual(sorted(expected_calls),
|
||||
sorted(pdp.call_args_list))
|
||||
|
||||
def test_update_datapath_provision(self):
|
||||
ports = []
|
||||
for i in range(0, 3):
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid=str(i))))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='3'),
|
||||
type='external'))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid=str(i),
|
||||
external_ids={'name': 'neutron-%d' % i})))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid='3',
|
||||
external_ids={'name': 'neutron-3'}), type='external'))
|
||||
|
||||
with mock.patch.object(self.agent, 'provision_datapath',
|
||||
return_value=None) as pdp,\
|
||||
mock.patch.object(self.agent, 'teardown_datapath') as tdp,\
|
||||
mock.patch.object(self.agent.sb_idl, 'get_ports_on_chassis',
|
||||
return_value=ports):
|
||||
self.agent.update_datapath('1')
|
||||
self.agent.update_datapath('3')
|
||||
expected_calls = [mock.call('1'), mock.call('3')]
|
||||
self.agent.update_datapath('1', 'a')
|
||||
self.agent.update_datapath('3', 'b')
|
||||
expected_calls = [mock.call('1', 'a'), mock.call('3', 'b')]
|
||||
pdp.assert_has_calls(expected_calls)
|
||||
tdp.assert_not_called()
|
||||
|
||||
def test_update_datapath_teardown(self):
|
||||
ports = []
|
||||
for i in range(0, 3):
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid=str(i))))
|
||||
ports.append(makePort(datapath=DatapathInfo(uuid=str(i),
|
||||
external_ids={'name': 'neutron-%d' % i})))
|
||||
|
||||
with mock.patch.object(self.agent, 'provision_datapath',
|
||||
return_value=None) as pdp,\
|
||||
mock.patch.object(self.agent, 'teardown_datapath') as tdp,\
|
||||
mock.patch.object(self.agent.sb_idl, 'get_ports_on_chassis',
|
||||
return_value=ports):
|
||||
self.agent.update_datapath('5')
|
||||
tdp.assert_called_once_with('5')
|
||||
self.agent.update_datapath('5', 'a')
|
||||
tdp.assert_called_once_with('5', 'a')
|
||||
pdp.assert_not_called()
|
||||
|
||||
def test_teardown_datapath(self):
|
||||
@ -240,7 +245,7 @@ class TestMetadataAgent(base.BaseTestCase):
|
||||
# We need to assert that it was deleted first.
|
||||
self.agent.ovs_idl.list_br.return_value.execute.return_value = (
|
||||
['br-int', 'br-fake'])
|
||||
self.agent.provision_datapath('1')
|
||||
self.agent.provision_datapath('1', '1')
|
||||
|
||||
# Check that the port was deleted from br-fake
|
||||
self.agent.ovs_idl.del_port.assert_called_once_with(
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
other:
|
||||
- |
|
||||
The ``OVN Metadata Agent`` now creates the network namespaces including the
|
||||
Neutron network UUID in its name. Previously, the OVN datapath UUID was used
|
||||
and it was not obvious for operators and during debugging to figure out which
|
||||
namespace corresponded to what Neutron network.
|
||||
|
Loading…
x
Reference in New Issue
Block a user