From 7699feb4a61661129c79bc7da517f07f1257124c Mon Sep 17 00:00:00 2001 From: Lucas Alvares Gomes Date: Wed, 3 Feb 2021 14:00:13 +0000 Subject: [PATCH] [OVN] Fix RowNotFound exception while waiting for metadata networks In the set_port_status_up() the OVN driver waits for the metadata to be provisioned (15 seconds) [0] prior to sending the event to Nova notifying that the provisioning of the port is done (network-vif-plugged). But there could be a race condition while trying to get that information which results in a RowNotFound being raise in the waiting loop. Once that happens, the exception is bubbled up and the OVN driver end up not sending the event to Nova and the instance will fail to deploy (it will be stuck in BUILD state until it times out). This patch changes the logic of the method looking for the metadata network information to not raise RowNotFound so that the waiting loop can iteract again [0] until the information is available. [0] https://github.com/openstack/neutron/blob/cbd72e2f4846ec64ff6e6ef24099a8e90ddebf31/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py#L1124 Change-Id: I3c106ace74b5c6e4ed0cb7e827baf5d6595ec5d0 Closes-Bug: #1914394 Signed-off-by: Lucas Alvares Gomes (cherry picked from commit b618d98541599ad0ef73be41a3edd83d1fc75a56) (cherry picked from commit cbf3fe098bb44605174b8e582d56814fd36632c7) Conflicts cleanly resolved by removing not relevant code added before the addittion of the TestSBImplIdlOvn class. Also added missing ovsdbapp.backend import: neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py --- .../ovn/mech_driver/ovsdb/impl_idl_ovn.py | 7 ++- neutron/tests/unit/fake_resources.py | 1 + .../mech_driver/ovsdb/test_impl_idl_ovn.py | 50 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index b2cc6ebb854..02e3146517e 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -849,7 +849,12 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): def get_chassis_metadata_networks(self, chassis_name): """Return a list with the metadata networks the chassis is hosting.""" - chassis = self.lookup('Chassis', chassis_name) + try: + chassis = self.lookup('Chassis', chassis_name) + except idlutils.RowNotFound: + LOG.warning("Couldn't find Chassis named %s in OVN while looking " + "for metadata networks", chassis_name) + return [] proxy_networks = chassis.external_ids.get( 'neutron-metadata-proxy-networks', None) return proxy_networks.split(',') if proxy_networks else [] diff --git a/neutron/tests/unit/fake_resources.py b/neutron/tests/unit/fake_resources.py index 4dc87a23ece..3f59d08aafd 100644 --- a/neutron/tests/unit/fake_resources.py +++ b/neutron/tests/unit/fake_resources.py @@ -410,6 +410,7 @@ class FakeOvsdbTable(FakeResource): ovsdb_table_attrs = { 'rows': {}, 'columns': {}, + 'indexes': [], } # Overwrite default attributes. diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py index 3b666c5714f..fafec525533 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py @@ -17,6 +17,8 @@ import uuid import mock +from ovsdbapp.backend import ovs_idl + from neutron.common.ovn import constants as ovn_const from neutron.common.ovn import utils from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import impl_idl_ovn @@ -758,3 +760,51 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): self._tables.pop('Port_Group', None) port_groups = self.nb_ovn_idl.get_port_groups() self.assertEqual({}, port_groups) + + +class TestSBImplIdlOvn(TestDBImplIdlOvn): + + fake_set = { + 'chassis': [ + {'name': 'fake-chassis', + 'external_ids': {'neutron-metadata-proxy-networks': 'fake-id'}}], + } + + def setUp(self): + super(TestSBImplIdlOvn, self).setUp() + self.chassis_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() + + self._tables = {} + self._tables['Chassis'] = self.chassis_table + + with mock.patch.object(impl_idl_ovn.OvsdbSbOvnIdl, 'from_worker', + return_value=mock.Mock()): + with mock.patch.object(ovs_idl.Backend, 'autocreate_indices', + create=True): + impl_idl_ovn.OvsdbSbOvnIdl.ovsdb_connection = None + self.sb_ovn_idl = impl_idl_ovn.OvsdbSbOvnIdl(mock.MagicMock()) + + self.sb_ovn_idl.idl.tables = self._tables + + def _load_sb_db(self): + fake_chassis = TestSBImplIdlOvn.fake_set['chassis'] + self._load_ovsdb_fake_rows(self.chassis_table, fake_chassis) + + @mock.patch.object(impl_idl_ovn.LOG, 'warning') + def test_get_chassis_metadata_networks_chassis_empty(self, mock_log): + chassis_name = 'fake-chassis' + result = self.sb_ovn_idl.get_chassis_metadata_networks(chassis_name) + + mock_log.assert_called_once_with( + "Couldn't find Chassis named %s in OVN while looking " + "for metadata networks", chassis_name) + self.assertEqual([], result) + + @mock.patch.object(impl_idl_ovn.LOG, 'warning') + def test_get_chassis_metadata_networks(self, mock_log): + chassis_name = 'fake-chassis' + self._load_sb_db() + result = self.sb_ovn_idl.get_chassis_metadata_networks(chassis_name) + + self.assertFalse(mock_log.called) + self.assertEqual(['fake-id'], result)