Browse Source

Metadata agent: fetch ovn-bridge from OVSDB and not from config

Prior to this patch, metadata agent was fetching the OVN bridge
from the settings but this could lead to inconsistencies if the
config is different from the actual OVSDB configuration as
ovn-controller would be using one bridge and Metadata agent
another one.

In order to eliminate these inconsistencies, this patch is
changing the behavior of the agent so that it reads the OVN
bridge from OVSDB as ovn-controller does. Also, when the
agent is restarted, if the bridge has changed, it'll plug all
the VETH pairs to the right bridge and unplug it from the older
one (this is handy for the migration from ML2/OVS where an
intermediate OVS bridge is used temporarily).

The config option is not removed by this patch but ignored to
avoid issues when it doesn't match OVSDB configuration.

Change-Id: I33f3509b8e5fe0398a27d35923e46f0409a1935d
Closes-Bug: #1799216
Signed-off-by: Daniel Alvarez <dalvarez@redhat.com>
changes/06/612406/11
Daniel Alvarez 3 years ago
parent
commit
3e5d9213aa
  1. 40
      networking_ovn/agent/metadata/agent.py
  2. 12
      networking_ovn/releasenotes/notes/ignore-ovs-integration-bridge-from-metadata-agent-2752193adbbdeec9.yaml
  3. 5
      networking_ovn/tests/functional/test_metadata_agent.py
  4. 12
      networking_ovn/tests/unit/agent/metadata/test_agent.py

40
networking_ovn/agent/metadata/agent.py

@ -21,6 +21,7 @@ from neutron.common import utils
from neutron_lib import constants as n_const
from oslo_concurrency import lockutils
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import uuidutils
from ovsdbapp.backend.ovs_idl import event as row_event
from ovsdbapp.backend.ovs_idl import vlog
@ -153,6 +154,7 @@ class MetadataAgent(object):
# Open the connection to OVS database
self.ovs_idl = ovsdb.MetadataAgentOvsIdl().start()
self.chassis = self._get_own_chassis_name()
self.ovn_bridge = self._get_ovn_bridge()
# Open the connection to OVN SB database.
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
@ -185,6 +187,22 @@ class MetadataAgent(object):
'Open_vSwitch', '.', 'external_ids').execute()
return ext_ids['system-id']
def _get_ovn_bridge(self):
"""Return the external_ids:ovn-bridge value of the Open_vSwitch table.
This is the OVS bridge used to plug the metadata ports to.
If the key doesn't exist, this method will return 'br-int' as default.
"""
ext_ids = self.ovs_idl.db_get(
'Open_vSwitch', '.', 'external_ids').execute()
try:
ovn_bridge = ext_ids['ovn-bridge']
except KeyError:
LOG.warning("Can't read ovn-bridge external-id from OVSDB. Using "
"br-int instead.")
return 'br-int'
return ovn_bridge
@_sync_lock
def sync(self):
"""Agent sync.
@ -234,8 +252,7 @@ class MetadataAgent(object):
self._process_monitor, datapath, self.conf, namespace)
veth_name = self._get_veth_name(datapath)
self.ovs_idl.del_port(
veth_name[0], bridge=self.conf.ovs_integration_bridge).execute()
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])
@ -342,9 +359,26 @@ class MetadataAgent(object):
if utils.get_ip_version(ipaddr) == 4:
ip2.addr.add(ipaddr)
# Check that this port is not attached to any other OVS bridge. This
# can happen when the OVN bridge changes (for example, during a
# migration from ML2/OVS).
ovs_bridges = set(self.ovs_idl.list_br().execute())
try:
ovs_bridges.remove(self.ovn_bridge)
except KeyError:
with excutils.save_and_reraise_exception():
LOG.exception("Configured OVN bridge %s cannot be found in "
"the system.", self.ovn_bridge)
if ovs_bridges:
with self.ovs_idl.transaction() as txn:
for br in ovs_bridges:
txn.add(self.ovs_idl.del_port(veth_name[0], bridge=br,
if_exists=True))
# Configure the OVS port and add external_ids:iface-id so that it
# can be tracked by OVN.
self.ovs_idl.add_port(self.conf.ovs_integration_bridge,
self.ovs_idl.add_port(self.ovn_bridge,
veth_name[0]).execute()
self.ovs_idl.db_set(
'Interface', veth_name[0],

12
networking_ovn/releasenotes/notes/ignore-ovs-integration-bridge-from-metadata-agent-2752193adbbdeec9.yaml

@ -0,0 +1,12 @@
---
fixes:
- |
The configuration option ``ovs_integration_bridge`` used by networking-ovn
metadata agent can only lead to problems as the bridge used by
``ovn-controller`` to install the flows is stored in OVSDB.
The metadata agent will now use OVSDB instead of the configuration option
to plug its ports, as a mismatch between both will break metadata.
There is no real use case for this option to exist and systems currently
using it will *not* be impacted by this change.
For more information see bug `1799216
<https://bugs.launchpad.net/networking-ovn/+bug/1799216>`_.

5
networking_ovn/tests/functional/test_metadata_agent.py

@ -84,8 +84,11 @@ class TestMetadataAgent(base.TestOVNFunctionalBase):
self.chassis_name = self.add_fake_chassis('ovs-host-fake')
with mock.patch.object(agent.MetadataAgent,
'_get_own_chassis_name') as mock_get_ch_name:
'_get_own_chassis_name') as mock_get_ch_name,\
mock.patch.object(agent.MetadataAgent,
'_get_ovn_bridge') as mock_get_ovn_br:
mock_get_ch_name.return_value = self.chassis_name
mock_get_ovn_br.return_value = 'br-int'
agt = agent.MetadataAgent(conf)
agt.start()
# Metadata agent will open connections to OVS and SB databases.

12
networking_ovn/tests/unit/agent/metadata/test_agent.py

@ -63,7 +63,9 @@ class TestMetadataAgent(base.BaseTestCase):
self.agent = agent.MetadataAgent(self.fake_conf)
self.agent.sb_idl = mock.Mock()
self.agent.ovs_idl = mock.Mock()
self.agent.ovs_idl.transaction = mock.MagicMock()
self.agent.chassis = 'chassis'
self.agent.ovn_bridge = 'br-int'
def test_sync(self):
with mock.patch.object(
@ -181,8 +183,7 @@ class TestMetadataAgent(base.BaseTestCase):
self.agent.teardown_datapath('1')
destroy_mdp.assert_called_once()
self.agent.ovs_idl.del_port.assert_called_once_with(
'veth_0', bridge='br-int')
self.agent.ovs_idl.del_port.assert_called_once_with('veth_0')
del_veth.assert_called_once_with('veth_0')
garbage_collect.assert_called_once()
@ -226,8 +227,15 @@ class TestMetadataAgent(base.BaseTestCase):
driver.MetadataDriver,
'spawn_monitored_metadata_proxy') as spawn_mdp:
# Simulate that the VETH pair was already present in 'br-fake'.
# 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')
# Check that the port was deleted from br-fake
self.agent.ovs_idl.del_port.assert_called_once_with(
'veth_0', bridge='br-fake', if_exists=True)
# Check that the VETH pair is created
add_veth.assert_called_once_with('veth_0', 'veth_1', 'namespace')
# Make sure that the two ends of the VETH pair have been set as up.

Loading…
Cancel
Save