From 282710016968b353d10b6a986f48d7f8a7d0404a Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Mon, 22 Jun 2020 17:08:15 +0200 Subject: [PATCH] [DVR] Related routers should be included if are requested In case when related dvr router is configured by L3 agent, it is first added to the tasks queue and then processed as any other router hosted on the L3 agent. But if L3 agent will ask neutron server about details of such router, it wasn't returned back as this router wasn't really scheduled to the compute node which was asking for it. It was "only" related to some other router scheduled to this compute node. Because of that router's info wasn't found in reply from the neutron-server and L3 agent was removing it from the compute node. Now _get_router_ids_for_agent method from the l3_dvrscheduler_db module will check router serviceable ports for each dvr router hosted on the compute node and will then find all routers related to it. Thanks to that it will return routers which are on the compute node only because of other related routers scheduled to this host and such router will not be deleted anymore. Change-Id: I689d5135b7194475c846731d846ccf6b25b80b4a Closes-Bug: #1884527 (cherry picked from commit 38286dbd2e35f1f94bf06b5f1cfa9227e5d402c6) --- neutron/db/l3_dvrscheduler_db.py | 19 +++++-- .../l3_router/test_l3_dvr_router_plugin.py | 50 +++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index b8e3e234aaa..b79d57b8cd0 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -440,20 +440,24 @@ class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin): for router_id in (router_ids - result_set): subnet_ids = self.get_subnet_ids_on_router( context, router_id, keep_gateway_port=False) - if (subnet_ids and + if (subnet_ids and ( self._check_dvr_serviceable_ports_on_host( context, agent_db['host'], - list(subnet_ids))): + list(subnet_ids)) or + self._is_router_related_to_dvr_routers( + context, router_id, dvr_routers))): result_set.add(router_id) LOG.debug("Routers %(router_ids)s are scheduled or have " "serviceable ports in host %(host)s", {'router_ids': result_set, 'host': agent_db['host']}) - for router_id in router_ids: - result_set |= set( + related_routers = set() + for router_id in result_set: + related_routers |= set( self._get_other_dvr_router_ids_connected_router( context, router_id)) + result_set |= related_routers LOG.debug("Router IDs %(router_ids)s for agent in host %(host)s", {'router_ids': result_set, @@ -506,6 +510,13 @@ class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin): query = query.filter(host_filter) return query.first() is not None + @log_helpers.log_method_call + def _is_router_related_to_dvr_routers(self, context, router_id, + dvr_routers): + related_routers = self._get_other_dvr_router_ids_connected_router( + context, router_id) + return any([r in dvr_routers for r in related_routers]) + def _notify_l3_agent_new_port(resource, event, trigger, **kwargs): LOG.debug('Received %(resource)s %(event)s', { diff --git a/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py b/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py index 96d144b910f..38348fbcc9e 100644 --- a/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py +++ b/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py @@ -1618,6 +1618,56 @@ class L3DvrTestCase(L3DvrTestCaseBase): self.context, self.l3_agent, [router1['id'], router3['id']]) self.assertEqual({router1['id'], router3['id']}, set(ids)) + def test__get_router_ids_for_agent_related_router(self): + router1 = self._create_router() + router2 = self._create_router() + router3 = self._create_router() + arg_list = (portbindings.HOST_ID,) + dvr_l3_agent = helpers.register_l3_agent( + host="host1", agent_mode=constants.L3_AGENT_MODE_DVR) + host = dvr_l3_agent['host'] + with self.subnet() as wan_subnet,\ + self.subnet(cidr='20.0.0.0/24') as subnet1,\ + self.subnet(cidr='30.0.0.0/24') as subnet2,\ + self.subnet(cidr='40.0.0.0/24') as subnet3,\ + self.port(subnet=wan_subnet) as wan_port1,\ + self.port(subnet=wan_subnet) as wan_port2,\ + self.port(subnet=subnet1, + device_owner=constants.DEVICE_OWNER_DHCP, + arg_list=arg_list, + **{portbindings.HOST_ID: host}): + + self.l3_plugin.add_router_interface( + self.context, router1['id'], + {'subnet_id': subnet1['subnet']['id']}) + self.l3_plugin.add_router_interface( + self.context, router2['id'], + {'subnet_id': subnet2['subnet']['id']}) + # Router3 is here just to be sure that it will not be returned as + # is not related to the router1 and router2 in any way + self.l3_plugin.add_router_interface( + self.context, router3['id'], + {'subnet_id': subnet3['subnet']['id']}) + + self.l3_plugin.add_router_interface( + self.context, router1['id'], + {'port_id': wan_port1['port']['id']}) + self.l3_plugin.add_router_interface( + self.context, router2['id'], + {'port_id': wan_port2['port']['id']}) + + ids = self.l3_plugin._get_router_ids_for_agent( + self.context, dvr_l3_agent, []) + self.assertEqual({router1['id'], router2['id']}, set(ids)) + + ids = self.l3_plugin._get_router_ids_for_agent( + self.context, dvr_l3_agent, [router2['id']]) + self.assertEqual({router1['id'], router2['id']}, set(ids)) + + ids = self.l3_plugin._get_router_ids_for_agent( + self.context, dvr_l3_agent, [router1['id']]) + self.assertEqual({router1['id'], router2['id']}, set(ids)) + def test_remove_router_interface(self): HOST1 = 'host1' helpers.register_l3_agent(