diff --git a/ironic/common/neutron.py b/ironic/common/neutron.py index bf42942743..a9918dcb4d 100644 --- a/ironic/common/neutron.py +++ b/ironic/common/neutron.py @@ -88,6 +88,9 @@ def unbind_neutron_port(port_id, client=None): try: client.update_port(port_id, body) + # NOTE(vsaienko): Ignore if port was deleted before calling vif detach. + except neutron_exceptions.PortNotFoundClient: + LOG.info('Port %s was not found while unbinding.', port_id) except neutron_exceptions.NeutronClientException as e: msg = (_('Unable to clear binding profile for ' 'neutron port %(port_id)s. Error: ' diff --git a/ironic/tests/unit/common/test_neutron.py b/ironic/tests/unit/common/test_neutron.py index 737667c513..5bba84d50a 100644 --- a/ironic/tests/unit/common/test_neutron.py +++ b/ironic/tests/unit/common/test_neutron.py @@ -686,3 +686,21 @@ class TestUnbindPort(base.TestCase): mock_client.assert_called_once_with() mock_client.return_value.update_port.assert_called_once_with(port_id, body) + + @mock.patch.object(neutron, 'LOG') + def test_unbind_neutron_port_not_found(self, mock_log, mock_client): + port_id = 'fake-port-id' + mock_client.return_value.update_port.side_effect = ( + neutron_client_exc.PortNotFoundClient()) + body = { + 'port': { + 'binding:host_id': '', + 'binding:profile': {} + } + } + neutron.unbind_neutron_port(port_id) + mock_client.assert_called_once_with() + mock_client.return_value.update_port.assert_called_once_with(port_id, + body) + mock_log.info.assert_called_once_with('Port %s was not found while ' + 'unbinding.', port_id) diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py index 507513b53c..25fb74eb29 100644 --- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py +++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py @@ -330,6 +330,8 @@ class BaremetalStandaloneScenarioTest(BaremetalStandaloneManager): def resource_cleanup(cls): cls.cleanup_floating_ip(cls.node_ip) vifs = cls.get_node_vifs(cls.node['uuid']) + # Remove ports before deleting node, to catch regression for cases + # when user did this prior unprovision node. for vif in vifs: cls.ports_client.delete_port(vif) cls.terminate_node(cls.node['uuid'])