From 9cffd7b3223c2e8c88129c79aa62c255350c55d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elvira=20Garc=C3=ADa?= Date: Fri, 9 Jul 2021 17:55:56 +0200 Subject: [PATCH] [OVN] Change ControllerAgent type dinamically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ControllerAgent class now changes in Openstack when changing the type of an agent in its ovsdb table. This is possible thanks to adding a new condition to the event that triggered the update of the AgentCache. If the right external_ids changed, a new agent with the correct class will replace the old one in the AgentCache. Also adds functional tests to check this. Closes-Bug: #1934420 Signed-off-by: Elvira GarcĂ­a Change-Id: I75380c455864aa055e8049a6beff8619687b52ce --- .../ml2/drivers/ovn/agent/neutron_agent.py | 13 ++++++++ .../ovn/mech_driver/ovsdb/ovsdb_monitor.py | 19 +++++++++++ .../mech_driver/ovsdb/test_ovsdb_monitor.py | 33 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py index a71ec05714a..08fb81aeae6 100644 --- a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py +++ b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py @@ -140,10 +140,23 @@ class ControllerAgent(NeutronAgent): return self.chassis_private.external_ids.get( ovn_const.OVN_AGENT_DESC_KEY, '') + 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', []): + self.__class__ = ControllerGatewayAgent + class ControllerGatewayAgent(ControllerAgent): agent_type = ovn_const.OVN_CONTROLLER_GW_AGENT + 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' not in + external_ids.get('ovn-cms-options', [])): + self.__class__ = ControllerAgent + class MetadataAgent(NeutronAgent): agent_type = ovn_const.OVN_METADATA_AGENT 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 4c4165f5ab8..72c712003b4 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 @@ -277,6 +277,24 @@ class ChassisAgentWriteEvent(ChassisAgentEvent): clear_down=event == self.ROW_CREATE) +class ChassisAgentTypeChangeEvent(ChassisEvent): + """Chassis Agent class change event""" + GLOBAL = True + events = (BaseEvent.ROW_UPDATE,) + + def match_fn(self, event, row, old=None): + if not getattr(old, 'external_ids', 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', [])) + return agent_type_change + + def run(self, event, row, old): + n_agent.AgentCache().update(ovn_const.OVN_CONTROLLER_AGENT, row, + clear_down=event == self.ROW_CREATE) + + class ChassisMetadataAgentWriteEvent(ChassisAgentEvent): events = (BaseEvent.ROW_CREATE, BaseEvent.ROW_UPDATE) @@ -666,6 +684,7 @@ class OvnSbIdl(OvnIdlDistributedLock): ChassisAgentDeleteEvent(self.driver), ChassisAgentDownEvent(self.driver), ChassisAgentWriteEvent(self.driver), + ChassisAgentTypeChangeEvent(self.driver), ChassisMetadataAgentWriteEvent(self.driver)]) @classmethod 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 43d1683d96f..5683eb7aacf 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 @@ -21,6 +21,7 @@ from neutron.common.ovn import constants as ovn_const from neutron.common import utils as n_utils from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf from neutron.db import ovn_hash_ring_db as db_hash_ring +from neutron.plugins.ml2.drivers.ovn.agent import neutron_agent from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovsdb_monitor from neutron.tests.functional import base from neutron.tests.functional.resources.ovsdb import fixtures @@ -286,6 +287,38 @@ class TestNBDbMonitorOverSsl(TestNBDbMonitor): return 'ssl' +class TestAgentMonitor(base.TestOVNFunctionalBase): + FAKE_CHASSIS_HOST = 'fake-chassis-host' + + def setUp(self): + super(TestAgentMonitor, self).setUp() + self.l3_plugin = directory.get_plugin(plugin_constants.L3) + self.mock_ovsdb_idl = mock.Mock() + self.handler = self.sb_api.idl.notify_handler + self.mock_ovsdb_idl = mock.Mock() + self.chassis_name = self.add_fake_chassis(self.FAKE_CHASSIS_HOST, + external_ids={'ovn-cms-options': 'enable-chassis-as-gw'}) + + def test_network_agent_present(self): + chassis_row = self.sb_api.db_find( + 'Chassis', ('name', '=', self.chassis_name)).execute( + check_error=True) + self.assertTrue(chassis_row) + self.assertEqual(neutron_agent.ControllerGatewayAgent, + type(neutron_agent.AgentCache()[self.chassis_name])) + + 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', + {'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'] == '') + self.assertEqual(neutron_agent.ControllerAgent, + type(neutron_agent.AgentCache()[self.chassis_name])) + + class TestOvnIdlProbeInterval(base.TestOVNFunctionalBase): def setUp(self): # We need an OvsdbServer that uses TCP because probe_interval is always