From 01f4e09753b69688a953a13b56cbe15b1b2d16f9 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 d9f8e6626c2..f581dbdacb9 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -434,20 +434,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, @@ -500,6 +504,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 _dvr_handle_unbound_allowed_addr_pair_add( plugin, context, port, allowed_address_pair): 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 65ee5c6837a..341cb1d2f18 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 @@ -1845,6 +1845,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(