diff --git a/neutron/common/ovn/utils.py b/neutron/common/ovn/utils.py index 4dcf097b16e..8a0edabc85c 100644 --- a/neutron/common/ovn/utils.py +++ b/neutron/common/ovn/utils.py @@ -641,8 +641,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): @@ -831,3 +831,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 d6b70201820..c0a234019bf 100644 --- a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py +++ b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py @@ -89,7 +89,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, @@ -141,9 +142,9 @@ class ControllerAgent(NeutronAgent): @staticmethod # it is by default, but this makes pep8 happy def __new__(cls, chassis_private, driver): - 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) @@ -166,8 +167,9 @@ class ControllerAgent(NeutronAgent): def update(self, chassis_private, clear_down=False): super().update(chassis_private, 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 @@ -176,9 +178,10 @@ class ControllerGatewayAgent(ControllerAgent): def update(self, chassis_private, clear_down=False): super().update(chassis_private, 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 1942de61d73..b519c6f6a42 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py @@ -191,7 +191,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: @@ -962,8 +963,9 @@ class OVNMechanismDriver(api.MechanismDriver): 'agent': agent}) return chassis = agent.chassis - datapath_type = chassis.external_ids.get('datapath-type', '') - iface_types = chassis.external_ids.get('iface-types', '') + other_config = ovn_utils.get_ovn_chassis_other_config(chassis) + datapath_type = other_config.get('datapath-type', '') + iface_types = other_config.get('iface-types', '') iface_types = iface_types.split(',') if iface_types else [] chassis_physnets = self.sb_ovn._get_chassis_physnets(chassis) for segment_to_bind in context.segments_to_bind: diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/placement.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/placement.py index eab14543263..f372807fcad 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/placement.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/placement.py @@ -42,7 +42,8 @@ def _parse_ovn_cms_options(chassis): def _parse_bridge_mappings(chassis): - bridge_mappings = chassis.external_ids.get('ovn-bridge-mappings', '') + other_config = ovn_utils.get_ovn_chassis_other_config(chassis) + bridge_mappings = other_config.get('ovn-bridge-mappings', '') bridge_mappings = helpers.parse_mappings(bridge_mappings.split(','), unique_values=False) return {k: [v] for k, v in bridge_mappings.items()} 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 5209126d8b2..8a8049bb83e 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 @@ -847,7 +847,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()) @@ -865,7 +866,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 = {} @@ -890,7 +892,7 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): if ('{}={}' .format(ovn_const.CMS_OPT_CARD_SERIAL_NUMBER, card_serial_number) - in ch.external_ids.get( + in utils.get_ovn_chassis_other_config(ch).get( ovn_const.OVN_CMS_OPTIONS, '').split(',')): return ch msg = _('Chassis with %s %s %s does not exist' 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 ef0856716d1..bb4259996d8 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 55ecf7a256d..8321c2a6eeb 100644 --- a/neutron/tests/functional/base.py +++ b/neutron/tests/functional/base.py @@ -423,7 +423,8 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase, self._start_ovn_northd() def add_fake_chassis(self, host, physical_nets=None, external_ids=None, - name=None, azs=None, enable_chassis_as_gw=False): + name=None, azs=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 @@ -432,19 +433,20 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase, physical_nets = physical_nets or [] external_ids = external_ids or {} + other_config = other_config or {} if azs is None: azs = ['ovn'] if azs: - append_cms_options(external_ids, 'availability-zones=') - external_ids['ovn-cms-options'] += ':'.join(azs) + append_cms_options(other_config, 'availability-zones=') + other_config['ovn-cms-options'] += ':'.join(azs) 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 @@ -455,7 +457,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/extensions/test_placement.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/test_placement.py index 5e808fe7eaf..327718d7c93 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/test_placement.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/test_placement.py @@ -72,7 +72,7 @@ class TestOVNClientQosExtension(base.TestOVNFunctionalBase): self.mock_send_batch = mock.patch.object( placement_extension, '_send_deferred_batch').start() - def _build_external_ids(self, bandwidths, inventory_defaults, hypervisors): + def _build_other_config(self, bandwidths, inventory_defaults, hypervisors): options = [] if bandwidths: options.append(n_const.RP_BANDWIDTHS + '=' + bandwidths) @@ -85,17 +85,17 @@ class TestOVNClientQosExtension(base.TestOVNFunctionalBase): def _create_chassis(self, host, name, physical_nets=None, bandwidths=None, inventory_defaults=None, hypervisors=None): - external_ids = self._build_external_ids(bandwidths, inventory_defaults, + other_config = self._build_other_config(bandwidths, inventory_defaults, hypervisors) self.add_fake_chassis(host, physical_nets=physical_nets, - external_ids=external_ids, name=name) + other_config=other_config, name=name) def _update_chassis(self, name, bandwidths=None, inventory_defaults=None, hypervisors=None): - external_ids = self._build_external_ids(bandwidths, inventory_defaults, + other_config = self._build_other_config(bandwidths, inventory_defaults, hypervisors) self.sb_api.db_set( - 'Chassis', name, ('external_ids', external_ids) + 'Chassis', name, ('other_config', other_config) ).execute(check_error=True) def _check_placement_config(self, expected_chassis): 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 cc5718713b5..8ae4f969f72 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() @@ -97,7 +97,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 8854afeae99..d130ad7e40d 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 @@ -441,9 +441,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) @@ -451,11 +450,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 88e8e3c37ac..e3de3b5e3b4 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 @@ -66,16 +66,16 @@ 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.add_fake_chassis( self.smartnic_dpu_host, - external_ids={ovn_const.OVN_CMS_OPTIONS: '{}={}'.format( + other_config={ovn_const.OVN_CMS_OPTIONS: '{}={}'.format( ovn_const.CMS_OPT_CARD_SERIAL_NUMBER, self.smartnic_dpu_serial)}) self.n1 = self._make_network(self.fmt, 'n1', True) diff --git a/neutron/tests/functional/services/ovn_l3/test_plugin.py b/neutron/tests/functional/services/ovn_l3/test_plugin.py index 8ef3f1d543d..9085682c9d5 100644 --- a/neutron/tests/functional/services/ovn_l3/test_plugin.py +++ b/neutron/tests/functional/services/ovn_l3/test_plugin.py @@ -135,7 +135,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): @@ -143,7 +143,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") @@ -170,11 +170,11 @@ 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'}, azs=['ovn']) self.chassis4 = self.add_fake_chassis( 'ovs-host4', physical_nets=['physnet1'], - external_ids={'ovn-cms-options': 'enable-chassis-as-gw'}, + other_config={'ovn-cms-options': 'enable-chassis-as-gw'}, azs=['ovn2']) self._check_gateway_chassis_candidates([self.chassis3], router_az_hints=['ovn']) @@ -186,10 +186,10 @@ class TestRouter(base.TestOVNFunctionalBase): # add chassis4 is having azs [ovn2], not match routers az_hints [ovn] 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.chassis4 = self.add_fake_chassis( 'ovs-host4', physical_nets=['physnet1'], - external_ids={'ovn-cms-options': 'enable-chassis-as-gw'}, + other_config={'ovn-cms-options': 'enable-chassis-as-gw'}, azs=['ovn2']) ovn_client = self.l3_plugin._ovn_client ext1 = self._create_ext_network( @@ -535,7 +535,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( @@ -567,7 +567,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 @@ -597,8 +597,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 a5674e57e51..f622a997a0e 100644 --- a/neutron/tests/unit/common/ovn/test_utils.py +++ b/neutron/tests/unit/common/ovn/test_utils.py @@ -62,33 +62,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( @@ -97,7 +95,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)) @@ -156,16 +154,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'}}) @@ -183,21 +181,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 0b379d2014a..adedce8f32b 100644 --- a/neutron/tests/unit/fake_resources.py +++ b/neutron/tests/unit/fake_resources.py @@ -877,20 +877,23 @@ class FakeChassis(object): cms_opts.append('%s=%s' % (ovn_const.CMS_OPT_CARD_SERIAL_NUMBER, card_serial_number)) - 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) if bridge_mappings: - external_ids['ovn-bridge-mappings'] = ','.join(bridge_mappings) + other_config['ovn-bridge-mappings'] = ','.join(bridge_mappings) 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': []} 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_impl_idl_ovn.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py index 1b646c567a1..209a41bf98c 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 @@ -820,7 +820,7 @@ class TestSBImplIdlOvnBase(TestDBImplIdlOvn): 'chassis': [ { 'hostname': 'fake-smartnic-dpu-chassis.fqdn', - 'external_ids': { + 'other_config': { ovn_const.OVN_CMS_OPTIONS: ( 'firstoption,' 'card-serial-number=fake-serial,' 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 d7c5f083043..9523be67b03 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, @@ -521,7 +524,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( @@ -545,14 +548,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( @@ -560,7 +567,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( @@ -569,9 +576,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() @@ -579,14 +586,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']) @@ -595,7 +602,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']) @@ -604,7 +611,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.' @@ -637,10 +644,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}) @@ -672,10 +679,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}) @@ -699,10 +706,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 dd744bfed75..26837b03e4e 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 @@ -112,6 +112,7 @@ class MechDriverSetupBase(abc.ABC): 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).