From a0482b8eb6633b3ab6cc36b08cfc6b0b3565dd37 Mon Sep 17 00:00:00 2001 From: zhiyuan_cai Date: Mon, 15 Aug 2016 09:29:24 +0800 Subject: [PATCH] Pre-created port clean during subnet deletion 1. What is the problem Before creating bottom subnet, we need to create some ports to allocate ip address for bottom dhcp port and bottom gateway port. These pre-created ports are not deleted after bottom resources are deleted. 2. What is the solution to the problem Clean these pre-created ports during top subnet deletion. 3. What the features need to be implemented to the Tricircle to realize the solution Pre-created ports now can be cleaned up. Change-Id: I73c0ef87e4104f1db9926a5972c5f36be94d724a --- tricircle/db/api.py | 15 +++++ tricircle/network/plugin.py | 12 ++++ tricircle/tests/unit/network/test_plugin.py | 70 +++++++++++++++++---- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/tricircle/db/api.py b/tricircle/db/api.py index c1e340cb..9705be44 100644 --- a/tricircle/db/api.py +++ b/tricircle/db/api.py @@ -121,6 +121,21 @@ def get_bottom_mappings_by_top_id(context, top_id, resource_type): return mappings +def delete_pre_created_resource_mapping(context, name): + with context.session.begin(): + entries = core.query_resource( + context, models.ResourceRouting, + filters=[{'key': 'top_id', 'comparator': 'eq', + 'value': name}], sorts=[]) + if entries: + core.delete_resources( + context, models.ResourceRouting, + filters=[{'key': 'top_id', 'comparator': 'eq', + 'value': entries[0]['bottom_id']}]) + core.delete_resource(context, models.ResourceRouting, + entries[0]['id']) + + def get_bottom_id_by_top_id_pod_name(context, top_id, pod_name, resource_type): """Get resource bottom id by top id and bottom pod name diff --git a/tricircle/network/plugin.py b/tricircle/network/plugin.py index 366261db..17c9707c 100644 --- a/tricircle/network/plugin.py +++ b/tricircle/network/plugin.py @@ -335,6 +335,13 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, context, res, network, res['id']) return res + def _delete_pre_created_port(self, t_ctx, q_ctx, port_name): + ports = super(TricirclePlugin, self).get_ports( + q_ctx, {'name': [port_name]}) + if ports: + super(TricirclePlugin, self).delete_port(q_ctx, ports[0]['id']) + db_api.delete_pre_created_resource_mapping(t_ctx, port_name) + def delete_subnet(self, context, subnet_id): t_ctx = t_context.get_context_from_neutron_context(context) try: @@ -345,6 +352,9 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, bottom_subnet_id = mapping[1] self._get_client(pod_name).delete_subnets( t_ctx, bottom_subnet_id) + interface_name = t_constants.interface_port_name % ( + mapping[0]['pod_id'], subnet_id) + self._delete_pre_created_port(t_ctx, context, interface_name) with t_ctx.session.begin(): core.delete_resources( t_ctx, models.ResourceRouting, @@ -354,6 +364,8 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, 'value': mapping[0]['pod_id']}]) except Exception: raise + dhcp_port_name = t_constants.dhcp_port_name % subnet_id + self._delete_pre_created_port(t_ctx, context, dhcp_port_name) super(TricirclePlugin, self).delete_subnet(context, subnet_id) def update_subnet(self, context, subnet_id, subnet): diff --git a/tricircle/tests/unit/network/test_plugin.py b/tricircle/tests/unit/network/test_plugin.py index e843e056..c8a02bb8 100644 --- a/tricircle/tests/unit/network/test_plugin.py +++ b/tricircle/tests/unit/network/test_plugin.py @@ -341,6 +341,18 @@ class FakeClient(object): ret_list.append(res) return ret_list + def delete_resources(self, _type, ctx, _id): + index = -1 + if self.pod_name == 'top': + res_list = self._res_map[self.pod_name][_type + 's'] + else: + res_list = self._res_map[self.pod_name][_type] + for i, res in enumerate(res_list): + if res['id'] == _id: + index = i + if index != -1: + del res_list[index] + def list_networks(self, ctx, filters=None): networks = self.list_resources('network', ctx, filters) if self.pod_name != 'top': @@ -363,6 +375,9 @@ class FakeClient(object): 'comparator': 'eq', 'value': subnet_id}])[0] + def delete_subnets(self, ctx, subnet_id): + self.delete_resources('subnet', ctx, subnet_id) + def update_subnets(self, ctx, subnet_id, body): pass @@ -383,16 +398,7 @@ class FakeClient(object): '', params={'filters': {'id': [port_id]}})['ports'][0] def delete_ports(self, ctx, port_id): - index = -1 - if self.pod_name == 'top': - port_list = self._res_map[self.pod_name]['ports'] - else: - port_list = self._res_map[self.pod_name]['port'] - for i, port in enumerate(port_list): - if port['id'] == port_id: - index = i - if index != -1: - del port_list[index] + self.delete_resources('port', ctx, port_id) def add_gateway_routers(self, ctx, *args, **kwargs): # only for mock purpose @@ -797,7 +803,7 @@ class FakeSession(object): RES_MAP[model_obj.__tablename__].append(model_dict) def _cascade_delete(self, model_dict, foreign_key, table, key): - if foreign_key not in model_dict: + if key not in model_dict: return index = -1 for i, instance in enumerate(RES_MAP[table]): @@ -1383,6 +1389,46 @@ class PluginTest(unittest.TestCase, return t_net_id, t_subnet_id, t_router_id, b_net_id, b_subnet_id + @patch.object(driver.Pool, 'get_instance', new=fake_get_instance) + @patch.object(ipam_pluggable_backend.IpamPluggableBackend, + '_allocate_ips_for_port', new=fake_allocate_ips_for_port) + @patch.object(db_base_plugin_common.DbBasePluginCommon, + '_make_subnet_dict', new=fake_make_subnet_dict) + @patch.object(context, 'get_context_from_neutron_context') + def test_subnet_clean(self, mock_context): + self._basic_pod_route_setup() + + fake_plugin = FakePlugin() + q_ctx = FakeNeutronContext() + t_ctx = context.get_db_context() + mock_context.return_value = t_ctx + + tenant_id = 'test_tenant_id' + (t_net_id, t_subnet_id, + t_router_id, b_net_id, b_subnet_id) = self._prepare_router_test( + tenant_id, t_ctx, 'pod_1', 1) + t_port_id = fake_plugin.add_router_interface( + q_ctx, t_router_id, {'subnet_id': t_subnet_id})['port_id'] + _, b_router_id = db_api.get_bottom_mappings_by_top_id( + t_ctx, t_router_id, constants.RT_ROUTER)[0] + + port_num = len(TOP_PORTS) + pre_created_port_num = 0 + for port in TOP_PORTS: + if port.get('name').startswith('dhcp_port_'): + pre_created_port_num += 1 + elif port.get('name').startswith('interface_'): + pre_created_port_num += 1 + elif port.get('device_owner') == 'network:router_interface': + pre_created_port_num += 1 + + fake_plugin.remove_router_interface( + q_ctx, t_router_id, {'port_id': t_port_id}) + fake_plugin.delete_subnet(q_ctx, t_subnet_id) + + # check pre-created ports are all deleted + self.assertEqual(port_num - pre_created_port_num, len(TOP_PORTS)) + @patch.object(driver.Pool, 'get_instance', new=fake_get_instance) @patch.object(ipam_pluggable_backend.IpamPluggableBackend, '_allocate_ips_for_port', new=fake_allocate_ips_for_port) @@ -1591,7 +1637,6 @@ class PluginTest(unittest.TestCase, '_allocate_ips_for_port', new=fake_allocate_ips_for_port) @patch.object(db_base_plugin_common.DbBasePluginCommon, '_make_subnet_dict', new=fake_make_subnet_dict) - @patch.object(FakeRPCAPI, 'configure_extra_routes', new=mock.Mock) @patch.object(FakeClient, 'action_routers') @patch.object(context, 'get_context_from_neutron_context') def test_add_interface_exception(self, mock_context, mock_action): @@ -1649,7 +1694,6 @@ class PluginTest(unittest.TestCase, '_allocate_ips_for_port', new=fake_allocate_ips_for_port) @patch.object(db_base_plugin_common.DbBasePluginCommon, '_make_subnet_dict', new=fake_make_subnet_dict) - @patch.object(FakeBaseRPCAPI, 'configure_extra_routes', new=mock.Mock) @patch.object(FakeClient, '_get_connection') @patch.object(context, 'get_context_from_neutron_context') def test_add_interface_exception_port_left(self, mock_context,