Revert "Don't try to lock for vif detach"

This is causing more serious issues, as there is a race
between tenant VIF removal and cleaning VIF adding.

This reverts commit 4f79cb3932.

The release note is not deleted from it, because the change has
already been released. A new one is added instead.

Change-Id: I922f24293645ff6bb79ad753f49dc9548b9f2485
Closes-Bug: #1750785
This commit is contained in:
Dmitry Tantsur 2018-02-21 17:21:59 +00:00
parent a97fa8e3ce
commit 08ed859ce2
6 changed files with 17 additions and 37 deletions

View File

@ -2951,12 +2951,7 @@ class ConductorManager(base_manager.BaseConductorManager):
""" """
LOG.debug("RPC vif_detach called for the node %(node_id)s with " LOG.debug("RPC vif_detach called for the node %(node_id)s with "
"vif_id %(vif_id)s", {'node_id': node_id, 'vif_id': vif_id}) "vif_id %(vif_id)s", {'node_id': node_id, 'vif_id': vif_id})
# NOTE(TheJulia): This task is explicitly called without a lock as
# long lived locks occur during node tear-down as a node goes into
# cleaning. The lack of a lock allows nova to remove the VIF record.
# See: https://bugs.launchpad.net/ironic/+bug/1743652
with task_manager.acquire(context, node_id, with task_manager.acquire(context, node_id,
shared=True,
purpose='detach vif') as task: purpose='detach vif') as task:
task.driver.network.validate(task) task.driver.network.validate(task)
task.driver.network.vif_detach(task, vif_id) task.driver.network.vif_detach(task, vif_id)

View File

@ -1042,10 +1042,6 @@ class NetworkInterface(BaseInterface):
def vif_detach(self, task, vif_id): def vif_detach(self, task, vif_id):
"""Detach a virtual network interface from a node """Detach a virtual network interface from a node
Warning: This method is called by the conductor as a shared
task, to ensure that a vif can be detached during a long-lived lock.
Such as those that occur when a node is being unprovisioned.
:param task: A TaskManager instance. :param task: A TaskManager instance.
:param vif_id: A VIF ID to detach :param vif_id: A VIF ID to detach
:raises: NetworkError, VifNotAttached :raises: NetworkError, VifNotAttached

View File

@ -581,8 +581,8 @@ class NeutronVIFPortIDMixin(VIFPortIDMixin):
# attached, and fail if not. # attached, and fail if not.
port_like_obj = self._get_port_like_obj_by_vif_id(task, vif_id) port_like_obj = self._get_port_like_obj_by_vif_id(task, vif_id)
self._clear_vif_from_port_like_obj(port_like_obj)
# NOTE(vsaienko) allow to unplug VIFs from ACTIVE instance. # NOTE(vsaienko) allow to unplug VIFs from ACTIVE instance.
if task.node.provision_state == states.ACTIVE: if task.node.provision_state == states.ACTIVE:
neutron.unbind_neutron_port(vif_id, context=task.context) neutron.unbind_neutron_port(vif_id, context=task.context)
self._clear_vif_from_port_like_obj(port_like_obj)

View File

@ -36,7 +36,6 @@ from ironic.common import boot_devices
from ironic.common import driver_factory from ironic.common import driver_factory
from ironic.common import exception from ironic.common import exception
from ironic.common import images from ironic.common import images
from ironic.common import neutron
from ironic.common import states from ironic.common import states
from ironic.common import swift from ironic.common import swift
from ironic.conductor import manager from ironic.conductor import manager
@ -4481,17 +4480,17 @@ class VifTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
mock_detach.assert_called_once_with(mock.ANY, "interface") mock_detach.assert_called_once_with(mock.ANY, "interface")
mock_valid.assert_called_once_with(mock.ANY, mock.ANY) mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
@mock.patch.object(neutron, 'unbind_neutron_port', autpspec=True) @mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True)
def test_vif_detach_node_is_locked(self, mock_detach, mock_valid): def test_vif_detach_node_locked(self, mock_detach, mock_valid):
node = obj_utils.create_test_node(self.context, driver='fake', node = obj_utils.create_test_node(self.context, driver='fake',
reservation='fake-reserv') reservation='fake-reserv')
obj_utils.create_test_port(self.context, exc = self.assertRaises(messaging.rpc.ExpectedException,
node_id=node.id, self.service.vif_detach,
internal_info={ self.context, node.uuid, "interface")
'tenant_vif_port_id': 'fake-id'}) # Compare true exception hidden by @messaging.expected_exceptions
self.service.vif_detach(self.context, node.uuid, 'fake-id') self.assertEqual(exception.NodeLocked, exc.exc_info[0])
self.assertFalse(mock_detach.called) self.assertFalse(mock_detach.called)
self.assertTrue(mock_valid.called) self.assertFalse(mock_valid.called)
@mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True) @mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True)
def test_vif_detach_raises_network_error(self, mock_detach, def test_vif_detach_raises_network_error(self, mock_detach,

View File

@ -941,28 +941,12 @@ class TestNeutronVifPortIDMixin(db_base.DbTestCase):
self.node.save() self.node.save()
mock_get.return_value = self.port mock_get.return_value = self.port
mock_unp.side_effect = exception.NetworkError mock_unp.side_effect = exception.NetworkError
with task_manager.acquire(self.context, self.node.id, with task_manager.acquire(self.context, self.node.id) as task:
shared=True) as task:
self.assertRaises(exception.NetworkError, self.assertRaises(exception.NetworkError,
self.interface.vif_detach, task, 'fake_vif_id') self.interface.vif_detach, task, 'fake_vif_id')
mock_unp.assert_called_once_with('fake_vif_id', mock_unp.assert_called_once_with('fake_vif_id',
context=task.context) context=task.context)
mock_get.assert_called_once_with(task, 'fake_vif_id') mock_get.assert_called_once_with(task, 'fake_vif_id')
mock_clear.assert_not_called()
@mock.patch.object(common.VIFPortIDMixin, '_clear_vif_from_port_like_obj')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
@mock.patch.object(common.VIFPortIDMixin, '_get_port_like_obj_by_vif_id')
def test_vif_detach_deleting_node_success(self, mock_get, mock_unp,
mock_clear):
self.node.provision_state = states.DELETING
self.node.save()
mock_get.return_value = self.port
with task_manager.acquire(self.context, self.node.id,
shared=True) as task:
self.interface.vif_detach(task, 'fake_vif_id')
self.assertFalse(mock_unp.called)
mock_get.assert_called_once_with(task, 'fake_vif_id')
mock_clear.assert_called_once_with(self.port) mock_clear.assert_called_once_with(self.port)
@mock.patch.object(common_utils, 'warn_about_deprecated_extra_vif_port_id', @mock.patch.object(common_utils, 'warn_about_deprecated_extra_vif_port_id',

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Reverts the fix for orphaned VIF records from the previous release, as
it causes a regression. See `bug 1750785
<https://bugs.launchpad.net/ironic/+bug/1750785>`_ for details.