Delete nic metadata when detaching interface

If an interface that was tagged with a device role tag is detached, this
patch deletes its metadata from the instance's device_metadata.

Change-Id: Iede60a31e877adbda98d7676f2e6a5b31ded05f3
Implements: blueprint virt-device-tagged-attach-detach
This commit is contained in:
Artom Lifshitz 2017-02-17 11:17:05 +00:00
parent 520b6d6ba9
commit 4289d86b32
4 changed files with 51 additions and 14 deletions

View File

@ -162,7 +162,8 @@ class InterfaceAttachmentController(wsgi.Controller):
context.can(ai_policies.POLICY_ROOT % 'delete')
port_id = id
instance = common.get_instance(self.compute_api, context, server_id)
instance = common.get_instance(self.compute_api, context, server_id,
expected_attrs=['device_metadata'])
try:
self.compute_api.detach_interface(context,
instance, port_id=port_id)

View File

@ -1224,6 +1224,8 @@ class API(base_api.NetworkAPI):
# Delete the VirtualInterface for the given port_id.
vif = objects.VirtualInterface.get_by_uuid(context, port_id)
if vif:
if 'tag' in vif and vif.tag:
self._delete_nic_metadata(self, instance, vif)
vif.destroy()
else:
LOG.debug('VirtualInterface not found for port: %s',
@ -1231,6 +1233,14 @@ class API(base_api.NetworkAPI):
return self.get_instance_nw_info(context, instance)
def _delete_nic_metadata(self, instance, vif):
for device in instance.device_metadata.devices:
if (isinstance(device, objects.NetworkInterfaceMetadata)
and device.mac == vif.address):
instance.device_metadata.devices.remove(device)
instance.save()
break
def list_ports(self, context, **search_opts):
"""List ports for the client based on search options."""
return get_client(context).list_ports(**search_opts)

View File

@ -16,6 +16,7 @@
import mock
from webob import exc
from nova.api.openstack import common
from nova.api.openstack.compute import attach_interfaces \
as attach_interfaces_v21
from nova.compute import api as compute_api
@ -177,15 +178,23 @@ class InterfaceAttachTestsV21(test.NoDBTestCase):
self.stub_out('nova.compute.api.API.detach_interface',
fake_detach_interface)
result = self.attachments.delete(self.req, FAKE_UUID1, FAKE_PORT_ID1)
# NOTE: on v2.1, http status code is set as wsgi_code of API
# method instead of status_int in a response object.
if isinstance(self.attachments,
attach_interfaces_v21.InterfaceAttachmentController):
status_int = self.attachments.delete.wsgi_code
else:
status_int = result.status_int
self.assertEqual(202, status_int)
inst = objects.Instance(uuid=FAKE_UUID1)
with mock.patch.object(common, 'get_instance',
return_value=inst) as mock_get_instance:
result = self.attachments.delete(self.req, FAKE_UUID1,
FAKE_PORT_ID1)
# NOTE: on v2.1, http status code is set as wsgi_code of API
# method instead of status_int in a response object.
if isinstance(self.attachments,
attach_interfaces_v21.InterfaceAttachmentController):
status_int = self.attachments.delete.wsgi_code
else:
status_int = result.status_int
self.assertEqual(202, status_int)
ctxt = self.req.environ['nova.context']
mock_get_instance.assert_called_with(
self.attachments.compute_api, ctxt, FAKE_UUID1,
expected_attrs=['device_metadata'])
def test_detach_interface_instance_locked(self):
def fake_detach_interface_from_locked_server(self, context,

View File

@ -4501,6 +4501,7 @@ class TestNeutronv2WithMock(test.TestCase):
raise_if_fail=True)
mock_delete_vifs.assert_called_once_with(mock.sentinel.ctx, 'inst-1')
@mock.patch('nova.network.neutronv2.api.API._delete_nic_metadata')
@mock.patch('nova.network.neutronv2.api.API.get_instance_nw_info')
@mock.patch('nova.network.neutronv2.api.API._unbind_ports')
@mock.patch('nova.objects.Instance.get_network_info')
@ -4511,7 +4512,8 @@ class TestNeutronv2WithMock(test.TestCase):
mock_ntrn,
mock_inst_get_nwinfo,
mock_unbind,
mock_netinfo):
mock_netinfo,
mock_del_nic_meta):
mock_inst = mock.Mock(project_id="proj-1",
availability_zone='zone-1',
uuid='inst-1')
@ -4521,14 +4523,29 @@ class TestNeutronv2WithMock(test.TestCase):
id='3', preserve_on_delete=True)]
mock_client = mock.Mock()
mock_ntrn.return_value = mock_client
mock_vif = mock.MagicMock(spec=objects.VirtualInterface)
mock_get_vif_by_uuid.return_value = mock_vif
vif = objects.VirtualInterface()
vif.tag = 'foo'
vif.destroy = mock.MagicMock()
mock_get_vif_by_uuid.return_value = vif
self.api.deallocate_port_for_instance(mock.sentinel.ctx,
mock_inst, '2')
mock_unbind.assert_called_once_with(mock.sentinel.ctx, ['2'],
mock_client)
mock_get_vif_by_uuid.assert_called_once_with(mock.sentinel.ctx, '2')
mock_vif.destroy.assert_called_once_with()
mock_del_nic_meta.assert_called_once_with(self.api, mock_inst,
vif)
vif.destroy.assert_called_once_with()
def test_delete_nic_metadata(self):
vif = objects.VirtualInterface(address='aa:bb:cc:dd:ee:ff', tag='foo')
instance = fake_instance.fake_instance_obj(self.context)
instance.device_metadata = objects.InstanceDeviceMetadata(
devices=[objects.NetworkInterfaceMetadata(
mac='aa:bb:cc:dd:ee:ff', tag='foo')])
instance.save = mock.Mock()
self.api._delete_nic_metadata(instance, vif)
self.assertEqual(0, len(instance.device_metadata.devices))
instance.save.assert_called_once_with()
@mock.patch('nova.network.neutronv2.api.API.'
'_check_external_network_attach')