From 58295fa8121f76729201135b660041cbe716981b Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Wed, 18 Mar 2015 17:17:55 -0400 Subject: [PATCH] Fix for deletes first preexisting port if second was attached to instance In Ia5367cf064d40690670ffeac3c1f16998464c234, we added a capability to preserve preexisting ports on server delete. There was one scenario that broken as a result as reported in the bug. "If I boot instance with preexisting port and attach one more port then first port is deleted while I delete the instance." Essentially we have to lookup get_preexisting_port_ids during boot as well as when we are attaching an interface. Closes-Bug: #1433524 Change-Id: I3d557dd95f442106c495249a5ef1d2ac36e6a2ea --- nova/network/neutronv2/api.py | 13 ++++++----- nova/tests/unit/network/test_neutronv2.py | 28 ++++++++++++++++------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 12249f9c06..da017cb6bc 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -1500,10 +1500,9 @@ class API(base_api.NetworkAPI): cached value. :param admin_client - a neutron client for the admin context. :param preexisting_port_ids - List of port_ids that nova didn't - allocate and therefore shouldn't be - deleted when an instance is deallocated. - If value is None or empty the value will - be populated from existing cached value. + allocate and there shouldn't be deleted when an instance is + de-allocated. Supplied list will be added to the cached list of + preexisting port IDs for this instance. """ search_opts = {'tenant_id': instance.project_id, @@ -1521,8 +1520,10 @@ class API(base_api.NetworkAPI): context, instance, networks, port_ids) nw_info = network_model.NetworkInfo() - if not preexisting_port_ids: - preexisting_port_ids = self._get_preexisting_port_ids(instance) + if preexisting_port_ids is None: + preexisting_port_ids = [] + preexisting_port_ids = set( + preexisting_port_ids + self._get_preexisting_port_ids(instance)) current_neutron_port_map = {} for current_neutron_port in current_neutron_ports: diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index 9036b90736..146dd6f6c8 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -2604,19 +2604,24 @@ class TestNeutronv2(TestNeutronv2Base): api._get_subnets_from_port(self.context, requested_port ).AndReturn(fake_subnets) + self.mox.StubOutWithMock(api, '_get_preexisting_port_ids') + api._get_preexisting_port_ids(fake_inst).AndReturn(['port5']) self.mox.ReplayAll() neutronapi.get_client('fake') fake_inst.info_cache = objects.InstanceInfoCache.new( self.context, 'fake-uuid') fake_inst.info_cache.network_info = model.NetworkInfo.hydrate([]) - nw_infos = api._build_network_info_model(self.context, fake_inst, - fake_nets, - [fake_ports[2]['id'], - fake_ports[0]['id'], - fake_ports[1]['id'], - fake_ports[3]['id'], - fake_ports[4]['id'], - fake_ports[5]['id']]) + nw_infos = api._build_network_info_model( + self.context, fake_inst, + fake_nets, + [fake_ports[2]['id'], + fake_ports[0]['id'], + fake_ports[1]['id'], + fake_ports[3]['id'], + fake_ports[4]['id'], + fake_ports[5]['id']], + preexisting_port_ids=['port3']) + self.assertEqual(len(nw_infos), 6) index = 0 for nw_info in nw_infos: @@ -2651,6 +2656,13 @@ class TestNeutronv2(TestNeutronv2Base): self.assertEqual(nw_infos[4]['id'], 'port4') self.assertEqual(nw_infos[5]['id'], 'port5') + self.assertFalse(nw_infos[0]['preserve_on_delete']) + self.assertFalse(nw_infos[1]['preserve_on_delete']) + self.assertFalse(nw_infos[2]['preserve_on_delete']) + self.assertTrue(nw_infos[3]['preserve_on_delete']) + self.assertFalse(nw_infos[4]['preserve_on_delete']) + self.assertTrue(nw_infos[5]['preserve_on_delete']) + def test_get_subnets_from_port(self): api = neutronapi.API()