[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. Signed-off-by: Daniel Alvarez <dalvarez@redhat.com> Change-Id: Ic8ffa9c4437aab6fb1878b3a1ebf2c3ab86e3d0c
This commit is contained in:
parent
74e4cd31f1
commit
e4fb06b242
@ -31,6 +31,7 @@ from neutron.agent.ovn.metadata import driver as metadata_driver
|
||||
from neutron.agent.ovn.metadata import ovsdb
|
||||
from neutron.agent.ovn.metadata import server as metadata_server
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.common.ovn import utils as ovn_utils
|
||||
from neutron.common import utils
|
||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
|
||||
|
||||
@ -80,9 +81,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
|
||||
@ -323,14 +325,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):
|
||||
@ -340,16 +348,16 @@ 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(datapath)
|
||||
veth_name = self._get_veth_name(dp)
|
||||
self.ovs_idl.del_port(veth_name[0]).execute()
|
||||
if ip_lib.device_exists(veth_name[0]):
|
||||
ip_lib.IPWrapper().del_veth(veth_name[0])
|
||||
|
||||
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:
|
||||
@ -364,9 +372,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
|
||||
@ -385,7 +393,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
|
||||
@ -395,7 +403,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
|
||||
@ -403,10 +411,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.
|
||||
@ -414,9 +422,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
|
||||
|
||||
@ -428,8 +436,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):
|
||||
@ -499,9 +507,9 @@ class MetadataAgent(object):
|
||||
metadata_driver.MetadataDriver.spawn_monitored_metadata_proxy(
|
||||
self._process_monitor, namespace, n_const.METADATA_PORT,
|
||||
self.conf, bind_address=n_const.METADATA_V4_IP,
|
||||
network_id=datapath)
|
||||
network_id=net_name)
|
||||
|
||||
self.update_chassis_metadata_networks(datapath)
|
||||
self.update_chassis_metadata_networks(net_name)
|
||||
return namespace
|
||||
|
||||
def ensure_all_networks_provisioned(self):
|
||||
@ -516,11 +524,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)
|
||||
|
||||
|
@ -561,3 +561,7 @@ def parse_ovn_lb_port_forwarding(ovn_rtr_lb_pfs):
|
||||
fip_dict[protocol] = fip_dict_proto
|
||||
result[fip_id] = fip_dict
|
||||
return result
|
||||
|
||||
|
||||
def get_network_name_from_datapath(datapath):
|
||||
return datapath.external_ids['name'].replace('neutron-', '')
|
||||
|
@ -816,8 +816,14 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend):
|
||||
rows = self.db_list_rows('Port_Binding').execute(check_error=True)
|
||||
# TODO(twilson) It would be useful to have a db_find that takes a
|
||||
# comparison function
|
||||
# TODO(dalvarez): Remove the comparison to r.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.
|
||||
return [r for r in rows
|
||||
if (r.mac and str(r.datapath.uuid) == network) and
|
||||
if (r.mac and (
|
||||
str(r.datapath.uuid) == network or
|
||||
utils.get_network_name_from_datapath(
|
||||
r.datapath) == network)) and
|
||||
ip_address in r.mac[0].split(' ')]
|
||||
|
||||
def set_port_cidrs(self, name, cidrs):
|
||||
|
@ -33,7 +33,7 @@ from neutron.tests import base
|
||||
|
||||
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,
|
||||
@ -119,11 +119,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,\
|
||||
@ -131,40 +134,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):
|
||||
@ -243,7 +248,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,7 @@
|
||||
---
|
||||
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…
Reference in New Issue
Block a user