From 70e179e9b74282a6b8272c48c78800b33176aaa9 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Fri, 23 Sep 2022 09:11:25 +0200 Subject: [PATCH] Since OVN 20.06, config is stored in "Chassis.other_config" Since OVN 20.06 [1], the OVN configuration is stored in "Chassis.other_config". Since OVN 22.09, the "Chassis" configuration stored in "Chassis.other_config" will not be replicated to "Chassis.external_ids". The ML2/OVN plugin tries to retrieve the "Chassis" configuration from the "other_config" field first; if this field does not exist (in OVN versions before 20.06), the plugin will use "external_ids" field instead. Neutron will be compatible with the different OVN versions (with and without "other_config" field). [1]https://github.com/ovn-org/ovn/commit/74d90c2223d0a8c123823fb849b4c2de58c296e4 [2]https://github.com/ovn-org/ovn/commit/51309429cc3032a0cb422603e7bbda4905ca01ae NOTE: this patch is similar to [1], but in this case neutron keeps compatibility with the different OVN versions (with and without "other_config" field). Since [2], the Neutron CI has a new job that uses the OVN/OVS packages distributed by the operating system installed by the CI (in this case, Ubuntu 20.04 and OVN 20.03). [1]https://review.opendev.org/c/openstack/neutron/+/859642 [2]https://review.opendev.org/c/openstack/neutron/+/860636 Conflicts: neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/placement.py neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/test_placement.py neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py neutron/tests/unit/fake_resources.py neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py Closes-Bug: #1990229 Change-Id: I54c8fd4d065ae537f396408df16832b158ee8998 (cherry picked from commit 536498a29a4e7662a4d0b1bb923e2521509ad77a) (cherry picked from commit 8a4c62d094a5419d878c38aa9017e40931a08f21) --- neutron/common/ovn/utils.py | 12 +++++- .../ml2/drivers/ovn/agent/neutron_agent.py | 19 +++++---- .../drivers/ovn/mech_driver/mech_driver.py | 3 +- .../ovn/mech_driver/ovsdb/impl_idl_ovn.py | 11 +++-- .../ovn/mech_driver/ovsdb/ovsdb_monitor.py | 41 +++++++++++++------ neutron/tests/functional/base.py | 11 +++-- .../ovn/mech_driver/ovsdb/test_impl_idl.py | 10 ++--- .../mech_driver/ovsdb/test_ovsdb_monitor.py | 9 ++-- .../ovn/mech_driver/test_mech_driver.py | 4 +- .../functional/services/ovn_l3/test_plugin.py | 11 +++-- neutron/tests/unit/common/ovn/test_utils.py | 32 +++++++-------- neutron/tests/unit/fake_resources.py | 18 ++++---- .../drivers/ovn/agent/test_neutron_agent.py | 2 +- .../mech_driver/ovsdb/test_ovsdb_monitor.py | 39 ++++++++++-------- .../ovn/mech_driver/test_mech_driver.py | 1 + ...chassis-other-config-7db15b9d10bf7f04.yaml | 10 +++++ 16 files changed, 143 insertions(+), 90 deletions(-) create mode 100644 releasenotes/notes/ovn-chassis-other-config-7db15b9d10bf7f04.yaml diff --git a/neutron/common/ovn/utils.py b/neutron/common/ovn/utils.py index edc46b9da53..286b6ca4754 100644 --- a/neutron/common/ovn/utils.py +++ b/neutron/common/ovn/utils.py @@ -594,8 +594,8 @@ def compute_address_pairs_diff(ovn_port, neutron_port): def get_ovn_cms_options(chassis): """Return the list of CMS options in a Chassis.""" - return [opt.strip() for opt in chassis.external_ids.get( - constants.OVN_CMS_OPTIONS, '').split(',')] + return [opt.strip() for opt in get_ovn_chassis_other_config(chassis).get( + constants.OVN_CMS_OPTIONS, '').split(',')] def is_gateway_chassis(chassis): @@ -784,3 +784,11 @@ def create_neutron_pg_drop(): }] OvsdbClientTransactCommand.run(command) + + +def get_ovn_chassis_other_config(chassis): + # NOTE(ralonsoh): LP#1990229 to be removed when min OVN version is 22.09 + try: + return chassis.other_config + except AttributeError: + return chassis.external_ids diff --git a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py index 77d45d56be0..5578bd9894c 100644 --- a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py +++ b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py @@ -90,7 +90,8 @@ class NeutronAgent(abc.ABC): 'configurations': { 'chassis_name': self.chassis.name, 'bridge-mappings': - self.chassis.external_ids.get('ovn-bridge-mappings', '')}, + ovn_utils.get_ovn_chassis_other_config(self.chassis).get( + 'ovn-bridge-mappings', '')}, 'start_flag': True, 'agent_type': self.agent_type, 'id': self.agent_id, @@ -142,9 +143,9 @@ class ControllerAgent(NeutronAgent): @staticmethod # it is by default, but this makes pep8 happy def __new__(cls, chassis_private, driver, updated_at=None): - external_ids = cls.chassis_from_private(chassis_private).external_ids - if ('enable-chassis-as-gw' in - external_ids.get('ovn-cms-options', [])): + _chassis = cls.chassis_from_private(chassis_private) + other_config = ovn_utils.get_ovn_chassis_other_config(_chassis) + if 'enable-chassis-as-gw' in other_config.get('ovn-cms-options', []): cls = ControllerGatewayAgent return super().__new__(cls) @@ -167,8 +168,9 @@ class ControllerAgent(NeutronAgent): def update(self, chassis_private, updated_at=None, clear_down=False): super().update(chassis_private, updated_at, clear_down) - external_ids = self.chassis_from_private(chassis_private).external_ids - if 'enable-chassis-as-gw' in external_ids.get('ovn-cms-options', []): + _chassis = self.chassis_from_private(chassis_private) + other_config = ovn_utils.get_ovn_chassis_other_config(_chassis) + if 'enable-chassis-as-gw' in other_config.get('ovn-cms-options', []): self.__class__ = ControllerGatewayAgent @@ -177,9 +179,10 @@ class ControllerGatewayAgent(ControllerAgent): def update(self, chassis_private, updated_at=None, clear_down=False): super().update(chassis_private, updated_at, clear_down) - external_ids = self.chassis_from_private(chassis_private).external_ids + _chassis = self.chassis_from_private(chassis_private) + other_config = ovn_utils.get_ovn_chassis_other_config(_chassis) if ('enable-chassis-as-gw' not in - external_ids.get('ovn-cms-options', [])): + other_config.get('ovn-cms-options', [])): self.__class__ = ControllerAgent diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py index ef5ca1b1566..81df09b616a 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py @@ -181,7 +181,8 @@ class OVNMechanismDriver(api.MechanismDriver): def get_supported_vif_types(self): vif_types = set() for ch in self.sb_ovn.chassis_list().execute(check_error=True): - dp_type = ch.external_ids.get('datapath-type', '') + other_config = ovn_utils.get_ovn_chassis_other_config(ch) + dp_type = other_config.get('datapath-type', '') if dp_type == ovn_const.CHASSIS_DATAPATH_NETDEV: vif_types.add(portbindings.VIF_TYPE_VHOST_USER) else: 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 eed3a9e95ff..b628e823323 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 @@ -836,7 +836,8 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): return cls(conn) def _get_chassis_physnets(self, chassis): - bridge_mappings = chassis.external_ids.get('ovn-bridge-mappings', '') + other_config = utils.get_ovn_chassis_other_config(chassis) + bridge_mappings = other_config.get('ovn-bridge-mappings', '') mapping_dict = helpers.parse_mappings(bridge_mappings.split(',')) return list(mapping_dict.keys()) @@ -854,7 +855,8 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): return [ch.name if name_only else ch for ch in self.chassis_list().execute(check_error=True) if ovn_const.CMS_OPT_CHASSIS_AS_GW in - ch.external_ids.get(ovn_const.OVN_CMS_OPTIONS, '').split(',')] + utils.get_ovn_chassis_other_config(ch).get( + ovn_const.OVN_CMS_OPTIONS, '').split(',')] def get_chassis_and_physnets(self): chassis_info_dict = {} @@ -874,8 +876,9 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): except StopIteration: msg = _('Chassis with hostname %s does not exist') % hostname raise RuntimeError(msg) - return (chassis.external_ids.get('datapath-type', ''), - chassis.external_ids.get('iface-types', ''), + other_config = utils.get_ovn_chassis_other_config(chassis) + return (other_config.get('datapath-type', ''), + other_config.get('iface-types', ''), self._get_chassis_physnets(chassis)) def get_metadata_port_network(self, network): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py index 969384cc89a..87e1fecb564 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py @@ -172,12 +172,21 @@ class ChassisEvent(row_event.RowEvent): def match_fn(self, event, row, old): if event != self.ROW_UPDATE: return True - # NOTE(lucasgomes): If the external_ids column wasn't updated - # (meaning, Chassis "gateway" status didn't change) just returns - if not hasattr(old, 'external_ids') and event == self.ROW_UPDATE: + + # NOTE(ralonsoh): LP#1990229 to be removed when min OVN version is + # 22.09 + other_config = ('other_config' if hasattr(row, 'other_config') else + 'external_ids') + # NOTE(lucasgomes): If the other_config/external_ids column wasn't + # updated (meaning, Chassis "gateway" status didn't change) just + # returns + if not hasattr(old, other_config) and event == self.ROW_UPDATE: return False - if (old.external_ids.get('ovn-bridge-mappings') != - row.external_ids.get('ovn-bridge-mappings')): + old_br_mappings = utils.get_ovn_chassis_other_config(old).get( + 'ovn-bridge-mappings') + new_br_mappings = utils.get_ovn_chassis_other_config(row).get( + 'ovn-bridge-mappings') + if old_br_mappings != new_br_mappings: return True # Check if either the Gateway status or Availability Zones has # changed in the Chassis @@ -192,8 +201,9 @@ class ChassisEvent(row_event.RowEvent): def run(self, event, row, old): host = row.hostname phy_nets = [] + new_other_config = utils.get_ovn_chassis_other_config(row) if event != self.ROW_DELETE: - bridge_mappings = row.external_ids.get('ovn-bridge-mappings', '') + bridge_mappings = new_other_config.get('ovn-bridge-mappings', '') mapping_dict = helpers.parse_mappings(bridge_mappings.split(',')) phy_nets = list(mapping_dict) @@ -208,9 +218,10 @@ class ChassisEvent(row_event.RowEvent): if event == self.ROW_DELETE: kwargs['event_from_chassis'] = row.name elif event == self.ROW_UPDATE: - old_mappings = old.external_ids.get('ovn-bridge-mappings', + old_other_config = utils.get_ovn_chassis_other_config(old) + old_mappings = old_other_config.get('ovn-bridge-mappings', set()) or set() - new_mappings = row.external_ids.get('ovn-bridge-mappings', + new_mappings = new_other_config.get('ovn-bridge-mappings', set()) or set() if old_mappings: old_mappings = set(old_mappings.split(',')) @@ -339,11 +350,17 @@ class ChassisAgentTypeChangeEvent(ChassisEvent): events = (BaseEvent.ROW_UPDATE,) def match_fn(self, event, row, old=None): - if not getattr(old, 'external_ids', False): + # NOTE(ralonsoh): LP#1990229 to be removed when min OVN version is + # 22.09 + other_config = ('other_config' if hasattr(row, 'other_config') else + 'external_ids') + if not getattr(old, other_config, False): return False - agent_type_change = n_agent.NeutronAgent.chassis_from_private( - row).external_ids.get('ovn-cms-options', []) != ( - old.external_ids.get('ovn-cms-options', [])) + chassis = n_agent.NeutronAgent.chassis_from_private(row) + new_other_config = utils.get_ovn_chassis_other_config(chassis) + old_other_config = utils.get_ovn_chassis_other_config(old) + agent_type_change = new_other_config.get('ovn-cms-options', []) != ( + old_other_config.get('ovn-cms-options', [])) return agent_type_change def run(self, event, row, old): diff --git a/neutron/tests/functional/base.py b/neutron/tests/functional/base.py index b0e84388da8..b6faf6747c2 100644 --- a/neutron/tests/functional/base.py +++ b/neutron/tests/functional/base.py @@ -414,7 +414,8 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase, self._start_ovn_northd() def add_fake_chassis(self, host, physical_nets=None, external_ids=None, - name=None, enable_chassis_as_gw=False): + name=None, enable_chassis_as_gw=False, + other_config=None): def append_cms_options(ext_ids, value): if 'ovn-cms-options' not in ext_ids: ext_ids['ovn-cms-options'] = value @@ -423,14 +424,15 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase, physical_nets = physical_nets or [] external_ids = external_ids or {} + other_config = other_config or {} if enable_chassis_as_gw: - append_cms_options(external_ids, 'enable-chassis-as-gw') + append_cms_options(other_config, 'enable-chassis-as-gw') bridge_mapping = ",".join(["%s:br-provider%s" % (phys_net, i) for i, phys_net in enumerate(physical_nets)]) if name is None: name = uuidutils.generate_uuid() - external_ids['ovn-bridge-mappings'] = bridge_mapping + other_config['ovn-bridge-mappings'] = bridge_mapping # We'll be using different IP addresses every time for the Encap of # the fake chassis as the SB schema doesn't allow to have two entries # with same (ip,type) pairs as of OVS 2.11. This shouldn't have any @@ -441,7 +443,8 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase, self._counter += 1 chassis = self.sb_api.chassis_add( name, ['geneve'], '172.24.4.%d' % self._counter, - external_ids=external_ids, hostname=host).execute(check_error=True) + external_ids=external_ids, hostname=host, + other_config=other_config).execute(check_error=True) if self.sb_api.is_table_present('Chassis_Private'): nb_cfg_timestamp = timeutils.utcnow_ts() * 1000 self.sb_api.db_create( diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py index cc85252f9bd..c5eb8b23aa1 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl.py @@ -52,11 +52,11 @@ class TestSbApi(BaseOvnIdlTest): super(TestSbApi, self).setUp() self.data = { 'chassis': [ - {'external_ids': {'ovn-bridge-mappings': + {'other_config': {'ovn-bridge-mappings': 'public:br-ex,private:br-0'}}, - {'external_ids': {'ovn-bridge-mappings': + {'other_config': {'ovn-bridge-mappings': 'public:br-ex,public2:br-ex2'}}, - {'external_ids': {'ovn-bridge-mappings': + {'other_config': {'ovn-bridge-mappings': 'public:br-ex'}}, ] } @@ -70,7 +70,7 @@ class TestSbApi(BaseOvnIdlTest): txn.add(self.api.chassis_add( chassis['name'], ['geneve'], chassis['hostname'], hostname=chassis['hostname'], - external_ids=chassis['external_ids'])) + other_config=chassis['other_config'])) def test_get_chassis_hostname_and_physnets(self): mapping = self.api.get_chassis_hostname_and_physnets() @@ -104,7 +104,7 @@ class TestSbApi(BaseOvnIdlTest): def test_multiple_physnets_in_one_bridge(self): self.data = { 'chassis': [ - {'external_ids': {'ovn-bridge-mappings': 'p1:br-ex,p2:br-ex'}} + {'other_config': {'ovn-bridge-mappings': 'p1:br-ex,p2:br-ex'}} ] } self.load_test_data() diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py index b6867ba54ba..03724708c5a 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py @@ -437,9 +437,8 @@ class TestAgentMonitor(base.TestOVNFunctionalBase): chassis_name, self.mech_driver.agent_chassis_table) self.mech_driver.sb_ovn.idl.notify_handler.watch_event(row_event) self.chassis_name = self.add_fake_chassis( - self.FAKE_CHASSIS_HOST, - external_ids={'ovn-cms-options': 'enable-chassis-as-gw'}, - name=chassis_name) + self.FAKE_CHASSIS_HOST, name=chassis_name, + enable_chassis_as_gw=True) self.assertTrue(row_event.wait()) n_utils.wait_until_true( lambda: len(list(neutron_agent.AgentCache())) == 1) @@ -447,11 +446,11 @@ class TestAgentMonitor(base.TestOVNFunctionalBase): def test_agent_change_controller(self): self.assertEqual(neutron_agent.ControllerGatewayAgent, type(neutron_agent.AgentCache()[self.chassis_name])) - self.sb_api.db_set('Chassis', self.chassis_name, ('external_ids', + self.sb_api.db_set('Chassis', self.chassis_name, ('other_config', {'ovn-cms-options': ''})).execute(check_error=True) n_utils.wait_until_true(lambda: neutron_agent.AgentCache()[self.chassis_name]. - chassis.external_ids['ovn-cms-options'] == '') + chassis.other_config['ovn-cms-options'] == '') self.assertEqual(neutron_agent.ControllerAgent, type(neutron_agent.AgentCache()[self.chassis_name])) diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index 0df43892e8e..22b59c09ab7 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -59,12 +59,12 @@ class TestPortBinding(base.TestOVNFunctionalBase): self.add_fake_chassis(self.ovs_host) self.add_fake_chassis( self.dpdk_host, - external_ids={'datapath-type': 'netdev', + other_config={'datapath-type': 'netdev', 'iface-types': 'dummy,dummy-internal,dpdkvhostuser'}) self.add_fake_chassis( self.invalid_dpdk_host, - external_ids={'datapath-type': 'netdev', + other_config={'datapath-type': 'netdev', 'iface-types': 'dummy,dummy-internal,geneve,vxlan'}) self.n1 = self._make_network(self.fmt, 'n1', True) res = self._create_subnet(self.fmt, self.n1['network']['id'], diff --git a/neutron/tests/functional/services/ovn_l3/test_plugin.py b/neutron/tests/functional/services/ovn_l3/test_plugin.py index 37fd0c329cc..6ad26a2d0e6 100644 --- a/neutron/tests/functional/services/ovn_l3/test_plugin.py +++ b/neutron/tests/functional/services/ovn_l3/test_plugin.py @@ -129,7 +129,7 @@ class TestRouter(base.TestOVNFunctionalBase): # Test if chassis3 is selected as candidate or not. self.chassis3 = self.add_fake_chassis( 'ovs-host3', physical_nets=['physnet1'], - external_ids={'ovn-cms-options': 'enable-chassis-as-gw'}) + other_config={'ovn-cms-options': 'enable-chassis-as-gw'}) self._check_gateway_chassis_candidates([self.chassis3]) def test_gateway_chassis_with_cms_and_no_bridge_mappings(self): @@ -137,7 +137,7 @@ class TestRouter(base.TestOVNFunctionalBase): # chassis3 is having enable-chassis-as-gw, but no bridge mappings. self.chassis3 = self.add_fake_chassis( 'ovs-host3', - external_ids={'ovn-cms-options': 'enable-chassis-as-gw'}) + other_config={'ovn-cms-options': 'enable-chassis-as-gw'}) ovn_client = self.l3_plugin._ovn_client ext1 = self._create_ext_network( 'ext1', 'vlan', 'physnet1', 1, "10.0.0.1", "10.0.0.0/24") @@ -482,7 +482,7 @@ class TestRouter(base.TestOVNFunctionalBase): self.skipTest('L3 HA not supported') ovn_client = self.l3_plugin._ovn_client chassis4 = self.add_fake_chassis( - 'ovs-host4', physical_nets=['physnet4'], external_ids={ + 'ovs-host4', physical_nets=['physnet4'], other_config={ 'ovn-cms-options': 'enable-chassis-as-gw'}) ovn_client._ovn_scheduler = l3_sched.OVNGatewayLeastLoadedScheduler() ext1 = self._create_ext_network( @@ -504,7 +504,7 @@ class TestRouter(base.TestOVNFunctionalBase): # Add another chassis as a gateway chassis chassis5 = self.add_fake_chassis( - 'ovs-host5', physical_nets=['physnet4'], external_ids={ + 'ovs-host5', physical_nets=['physnet4'], other_config={ 'ovn-cms-options': 'enable-chassis-as-gw'}) # Add a node as compute node. Compute node wont be # used to schedule the router gateway ports therefore @@ -534,8 +534,7 @@ class TestRouter(base.TestOVNFunctionalBase): chassis_list.append( self.add_fake_chassis( 'ovs-host%s' % i, physical_nets=['physnet1'], - external_ids={ - 'ovn-cms-options': 'enable-chassis-as-gw'})) + other_config={'ovn-cms-options': 'enable-chassis-as-gw'})) ext1 = self._create_ext_network( 'ext1', 'vlan', 'physnet1', 1, "10.0.0.1", "10.0.0.0/24") diff --git a/neutron/tests/unit/common/ovn/test_utils.py b/neutron/tests/unit/common/ovn/test_utils.py index 63109835b6d..24a038c24ca 100644 --- a/neutron/tests/unit/common/ovn/test_utils.py +++ b/neutron/tests/unit/common/ovn/test_utils.py @@ -61,33 +61,31 @@ class TestUtils(base.BaseTestCase): def test_is_gateway_chassis(self): chassis = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': {'ovn-cms-options': 'enable-chassis-as-gw'}}) + 'other_config': {'ovn-cms-options': 'enable-chassis-as-gw'}}) non_gw_chassis_0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': {'ovn-cms-options': ''}}) - non_gw_chassis_1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={}) - non_gw_chassis_2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': {}}) + 'other_config': {'ovn-cms-options': ''}}) + non_gw_chassis_1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ + 'other_config': {}}) self.assertTrue(utils.is_gateway_chassis(chassis)) self.assertFalse(utils.is_gateway_chassis(non_gw_chassis_0)) self.assertFalse(utils.is_gateway_chassis(non_gw_chassis_1)) - self.assertFalse(utils.is_gateway_chassis(non_gw_chassis_2)) def test_get_chassis_availability_zones_no_azs(self): chassis = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': {'ovn-cms-options': 'enable-chassis-as-gw'}}) + 'other_config': {'ovn-cms-options': 'enable-chassis-as-gw'}}) self.assertEqual(set(), utils.get_chassis_availability_zones(chassis)) def test_get_chassis_availability_zones_one_az(self): chassis = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': {'ovn-cms-options': + 'other_config': {'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az0'}}) self.assertEqual( {'az0'}, utils.get_chassis_availability_zones(chassis)) def test_get_chassis_availability_zones_multiple_az(self): chassis = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az0:az1 :az2:: :'}}) self.assertEqual( @@ -96,7 +94,7 @@ class TestUtils(base.BaseTestCase): def test_get_chassis_availability_zones_malformed(self): chassis = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ - 'external_ids': {'ovn-cms-options': + 'other_config': {'ovn-cms-options': 'enable-chassis-as-gw,availability-zones:az0'}}) self.assertEqual( set(), utils.get_chassis_availability_zones(chassis)) @@ -155,16 +153,16 @@ class TestUtils(base.BaseTestCase): def test_get_chassis_in_azs(self): ch0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch0', - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az0:az1:az2'}}) ch1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch1', - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw'}}) ch2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch2', - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az1:az5'}}) @@ -182,21 +180,21 @@ class TestUtils(base.BaseTestCase): def test_get_gateway_chassis_without_azs(self): ch0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch0', - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az0:az1:az2'}}) ch1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch1', - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw'}}) ch2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch2', - 'external_ids': { + 'other_config': { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az1:az5'}}) ch3 = fakes.FakeOvsdbRow.create_one_ovsdb_row(attrs={ 'name': 'ch3', - 'external_ids': {}}) + 'other_config': {}}) chassis_list = [ch0, ch1, ch2, ch3] self.assertEqual( diff --git a/neutron/tests/unit/fake_resources.py b/neutron/tests/unit/fake_resources.py index 3934b157c5c..4ac79552b96 100644 --- a/neutron/tests/unit/fake_resources.py +++ b/neutron/tests/unit/fake_resources.py @@ -827,20 +827,24 @@ class FakeChassis(object): if chassis_as_gw: cms_opts.append(ovn_const.CMS_OPT_CHASSIS_AS_GW) - external_ids = {} + # NOTE(ralonsoh): LP#1990229, once min OVN version >= 20.06, the CMS + # options and the bridge mappings should be stored only in + # "other_config". + other_config = {} if cms_opts: - external_ids[ovn_const.OVN_CMS_OPTIONS] = ','.join(cms_opts) + other_config[ovn_const.OVN_CMS_OPTIONS] = ','.join(cms_opts) - attrs = { + chassis_attrs = { 'encaps': [], - 'external_ids': external_ids, + 'external_ids': '', 'hostname': '', 'name': uuidutils.generate_uuid(), 'nb_cfg': 0, - 'other_config': {}, + 'other_config': other_config, 'transport_zones': [], 'vtep_logical_switches': []} # Overwrite default attributes. - attrs.update(attrs) - return type('Chassis', (object, ), attrs) + if attrs: + chassis_attrs.update(attrs) + return type('Chassis', (object, ), chassis_attrs) diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/agent/test_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/agent/test_neutron_agent.py index ed434cab186..cd92c937534 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/agent/test_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/agent/test_neutron_agent.py @@ -31,7 +31,7 @@ class AgentCacheTestCase(base.BaseTestCase): self.names_ref = [] for i in range(10): # Add 10 agents. chassis_private = fakes.FakeOvsdbRow.create_one_ovsdb_row( - attrs={'name': 'chassis' + str(i)}) + attrs={'name': 'chassis' + str(i), 'other_config': {}}) self.agent_cache.update(ovn_const.OVN_CONTROLLER_AGENT, chassis_private) self.names_ref.append('chassis' + str(i)) diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py index 8bade0eb617..26b56858b6b 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py @@ -81,6 +81,9 @@ OVN_SB_SCHEMA = { "name": {"type": "string"}, "hostname": {"type": "string"}, "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": True, @@ -524,7 +527,7 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): self.row_json = { "name": "fake-name", "hostname": "fake-hostname", - "external_ids": ['map', [["ovn-bridge-mappings", + "other_config": ['map', [["ovn-bridge-mappings", "fake-phynet1:fake-br1"]]] } self._mock_hash_ring = mock.patch.object( @@ -548,14 +551,18 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): self.sb_idl.notify_handler.notify_loop() def test_chassis_create_event(self): - self._test_chassis_helper('create', self.row_json) + old_row_json = {'other_config': ['map', []]} + self._test_chassis_helper('create', self.row_json, + old_row_json=old_row_json) self.mech_driver.update_segment_host_mapping.assert_called_once_with( 'fake-hostname', ['fake-phynet1']) self.l3_plugin.schedule_unhosted_gateways.assert_called_once_with( event_from_chassis=None) def test_chassis_delete_event(self): - self._test_chassis_helper('delete', self.row_json) + old_row_json = {'other_config': ['map', []]} + self._test_chassis_helper('delete', self.row_json, + old_row_json=old_row_json) self.mech_driver.update_segment_host_mapping.assert_called_once_with( 'fake-hostname', []) self.l3_plugin.schedule_unhosted_gateways.assert_called_once_with( @@ -563,7 +570,7 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): def test_chassis_update_event(self): old_row_json = copy.deepcopy(self.row_json) - old_row_json['external_ids'][1][0][1] = ( + old_row_json['other_config'][1][0][1] = ( "fake-phynet2:fake-br2") self._test_chassis_helper('update', self.row_json, old_row_json) self.mech_driver.update_segment_host_mapping.assert_called_once_with( @@ -572,9 +579,9 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): event_from_chassis=None) def test_chassis_update_event_reschedule_not_needed(self): - self.row_json['external_ids'][1].append(['foo_field', 'foo_value_new']) + self.row_json['other_config'][1].append(['foo_field', 'foo_value_new']) old_row_json = copy.deepcopy(self.row_json) - old_row_json['external_ids'][1][1][1] = ( + old_row_json['other_config'][1][1][1] = ( "foo_value") self._test_chassis_helper('update', self.row_json, old_row_json) self.mech_driver.update_segment_host_mapping.assert_not_called() @@ -582,14 +589,14 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): def test_chassis_update_event_reschedule_lost_physnet(self): old_row_json = copy.deepcopy(self.row_json) - self.row_json['external_ids'][1][0][1] = '' + self.row_json['other_config'][1][0][1] = '' self._test_chassis_helper('update', self.row_json, old_row_json) self.l3_plugin.schedule_unhosted_gateways.assert_called_once_with( event_from_chassis='fake-name') def test_chassis_update_event_reschedule_add_physnet(self): old_row_json = copy.deepcopy(self.row_json) - self.row_json['external_ids'][1][0][1] += ',foo_physnet:foo_br' + self.row_json['other_config'][1][0][1] += ',foo_physnet:foo_br' self._test_chassis_helper('update', self.row_json, old_row_json) self.mech_driver.update_segment_host_mapping.assert_called_once_with( 'fake-hostname', ['fake-phynet1', 'foo_physnet']) @@ -598,7 +605,7 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): def test_chassis_update_event_reschedule_add_and_remove_physnet(self): old_row_json = copy.deepcopy(self.row_json) - self.row_json['external_ids'][1][0][1] = 'foo_physnet:foo_br' + self.row_json['other_config'][1][0][1] = 'foo_physnet:foo_br' self._test_chassis_helper('update', self.row_json, old_row_json) self.mech_driver.update_segment_host_mapping.assert_called_once_with( 'fake-hostname', ['foo_physnet']) @@ -607,7 +614,7 @@ class TestOvnSbIdlNotifyHandler(test_mech_driver.OVNMechanismDriverTestCase): def test_chassis_update_empty_no_external_ids(self): old_row_json = copy.deepcopy(self.row_json) - old_row_json.pop('external_ids') + old_row_json.pop('other_config') with mock.patch( 'neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.' 'ovsdb_monitor.ChassisEvent.' @@ -640,10 +647,10 @@ class TestChassisEvent(base.BaseTestCase): def _test_handle_ha_chassis_group_changes_create(self, event): # Chassis - ext_ids = { + other_config = { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az-0'} row = fakes.FakeOvsdbTable.create_one_ovsdb_table( - attrs={'name': 'SpongeBob', 'external_ids': ext_ids}) + attrs={'name': 'SpongeBob', 'other_config': other_config}) # HA Chassis ch0 = fakes.FakeOvsdbTable.create_one_ovsdb_table( attrs={'priority': 10}) @@ -675,10 +682,10 @@ class TestChassisEvent(base.BaseTestCase): def _test_handle_ha_chassis_group_changes_delete(self, event): # Chassis - ext_ids = { + other_config = { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az-0'} row = fakes.FakeOvsdbTable.create_one_ovsdb_table( - attrs={'name': 'SpongeBob', 'external_ids': ext_ids}) + attrs={'name': 'SpongeBob', 'other_config': other_config}) # HA Chassis ha_ch = fakes.FakeOvsdbTable.create_one_ovsdb_table( attrs={'priority': 10}) @@ -702,10 +709,10 @@ class TestChassisEvent(base.BaseTestCase): def test_handle_ha_chassis_group_changes_update_no_changes(self): # Assert nothing was done because the update didn't # change the gateway chassis status or the availability zones - ext_ids = { + other_config = { 'ovn-cms-options': 'enable-chassis-as-gw,availability-zones=az-0'} new = fakes.FakeOvsdbTable.create_one_ovsdb_table( - attrs={'name': 'SpongeBob', 'external_ids': ext_ids}) + attrs={'name': 'SpongeBob', 'other_config': other_config}) old = new self.assertIsNone(self.event.handle_ha_chassis_group_changes( self.event.ROW_UPDATE, new, old)) diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index 4c400b76f08..cb7a70bd4ba 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -2096,6 +2096,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase): chassis_private.nb_cfg_timestamp, mock.Mock): del chassis_private.nb_cfg_timestamp chassis_private.external_ids = {} + chassis_private.other_config = {} if agent_type == ovn_const.OVN_METADATA_AGENT: chassis_private.external_ids.update({ ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg, diff --git a/releasenotes/notes/ovn-chassis-other-config-7db15b9d10bf7f04.yaml b/releasenotes/notes/ovn-chassis-other-config-7db15b9d10bf7f04.yaml new file mode 100644 index 00000000000..8123a03be6d --- /dev/null +++ b/releasenotes/notes/ovn-chassis-other-config-7db15b9d10bf7f04.yaml @@ -0,0 +1,10 @@ +--- +other: + - | + Since OVN 20.06, the "Chassis" register configuration is stored in the + "other_config" field and replicated into "external_ids". This replication + is stopped in OVN 22.09. The ML2/OVN plugin tries to retrieve the "Chassis" + configuration from the "other_config" field first; if this field does not + exist (in OVN versions before 20.06), the plugin will use "external_ids" + field instead. Neutron will be compatible with the different OVN versions + (with and without "other_config" field).