summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2022-09-25 08:44:37 +0000
committerGerrit Code Review <review@openstack.org>2022-09-25 08:44:37 +0000
commit853872152da038397d80db4d5a87a95a76142f14 (patch)
treedd07d397ed88d325e5d3f6b4b4677d9ae0676c1b
parente812b6f580d84402f085a1171a230d88609dc228 (diff)
parentd322f8e8b5a95fd60dab9c6c543b4c77ea893836 (diff)
downloadnova-853872152da038397d80db4d5a87a95a76142f14.tar.gz
Merge "neutron: Unbind remaining ports after PortNotFound" into stable/xena
-rw-r--r--nova/network/neutron.py3
-rw-r--r--nova/tests/unit/network/test_neutron.py84
2 files changed, 64 insertions, 23 deletions
diff --git a/nova/network/neutron.py b/nova/network/neutron.py
index d8c58c6e13..3ec9d03c26 100644
--- a/nova/network/neutron.py
+++ b/nova/network/neutron.py
@@ -621,6 +621,7 @@ class API:
# in case the caller forgot to filter the list.
if port_id is None:
continue
+
port_req_body: ty.Dict[str, ty.Any] = {
'port': {
'device_id': '',
@@ -635,7 +636,7 @@ class API:
except exception.PortNotFound:
LOG.debug('Unable to show port %s as it no longer '
'exists.', port_id)
- return
+ continue
except Exception:
# NOTE: In case we can't retrieve the binding:profile or
# network info assume that they are empty
diff --git a/nova/tests/unit/network/test_neutron.py b/nova/tests/unit/network/test_neutron.py
index 5056b70c4e..0d6e84c505 100644
--- a/nova/tests/unit/network/test_neutron.py
+++ b/nova/tests/unit/network/test_neutron.py
@@ -5212,7 +5212,8 @@ class TestAPI(TestAPIBase):
self.assertEqual(['2', '3'], result, "Invalid preexisting ports")
@mock.patch('nova.network.neutron.API._show_port')
- def _test_unbind_ports_get_client(self, mock_neutron, mock_show):
+ @mock.patch('nova.network.neutron.get_client')
+ def test_unbind_ports_get_client(self, mock_neutron, mock_show):
mock_ctx = mock.Mock(is_admin=False)
ports = ["1", "2", "3"]
@@ -5228,23 +5229,16 @@ class TestAPI(TestAPIBase):
self.assertEqual(1, mock_neutron.call_count)
mock_neutron.assert_has_calls(get_client_calls, True)
- @mock.patch('nova.network.neutron.get_client')
- def test_unbind_ports_get_client_binding_extension(self,
- mock_neutron):
- self._test_unbind_ports_get_client(mock_neutron)
-
- @mock.patch('nova.network.neutron.get_client')
- def test_unbind_ports_get_client(self, mock_neutron):
- self._test_unbind_ports_get_client(mock_neutron)
-
@mock.patch('nova.network.neutron.API._show_port')
- def _test_unbind_ports(self, mock_neutron, mock_show):
+ @mock.patch('nova.network.neutron.get_client')
+ def test_unbind_ports(self, mock_neutron, mock_show):
mock_client = mock.Mock()
mock_update_port = mock.Mock()
mock_client.update_port = mock_update_port
mock_ctx = mock.Mock(is_admin=False)
ports = ["1", "2", "3"]
mock_show.side_effect = [{"id": "1"}, {"id": "2"}, {"id": "3"}]
+
api = neutronapi.API()
api._unbind_ports(mock_ctx, ports, mock_neutron, mock_client)
@@ -5258,14 +5252,6 @@ class TestAPI(TestAPIBase):
self.assertEqual(3, mock_update_port.call_count)
mock_update_port.assert_has_calls(update_port_calls)
- @mock.patch('nova.network.neutron.get_client')
- def test_unbind_ports_binding_ext(self, mock_neutron):
- self._test_unbind_ports(mock_neutron)
-
- @mock.patch('nova.network.neutron.get_client')
- def test_unbind_ports(self, mock_neutron):
- self._test_unbind_ports(mock_neutron)
-
def test_unbind_ports_no_port_ids(self):
# Tests that None entries in the ports list are filtered out.
mock_client = mock.Mock()
@@ -6014,7 +6000,6 @@ class TestAPI(TestAPIBase):
def test_unbind_ports_port_show_portnotfound(self, mock_log, mock_show):
api = neutronapi.API()
neutron_client = mock.Mock()
- mock_show.return_value = {'id': uuids.port}
api._unbind_ports(self.context, [uuids.port_id],
neutron_client, neutron_client)
mock_show.assert_called_once_with(
@@ -6023,6 +6008,59 @@ class TestAPI(TestAPIBase):
neutron_client=mock.ANY)
mock_log.assert_not_called()
+ @mock.patch('nova.network.neutron.API._show_port')
+ @mock.patch.object(neutronapi, 'LOG')
+ def test_unbind_ports_port_show_portnotfound_multiple_ports(
+ self, mock_log, mock_show,
+ ):
+ """Ensure we continue unbinding ports even when one isn't found."""
+ mock_show.side_effect = [
+ exception.PortNotFound(port_id=uuids.port_a),
+ {'id': uuids.port_b},
+ ]
+ api = neutronapi.API()
+ neutron_client = mock.Mock()
+
+ api._unbind_ports(
+ self.context,
+ [uuids.port_a, uuids.port_b],
+ neutron_client,
+ neutron_client,
+ )
+
+ mock_show.assert_has_calls(
+ [
+ mock.call(
+ self.context,
+ uuids.port_a,
+ fields=['binding:profile', 'network_id'],
+ neutron_client=neutron_client,
+ ),
+ mock.call(
+ self.context,
+ uuids.port_b,
+ fields=['binding:profile', 'network_id'],
+ neutron_client=neutron_client,
+ ),
+ ]
+ )
+ # Only the port that exists should be updated
+ neutron_client.update_port.assert_called_once_with(
+ uuids.port_b,
+ {
+ 'port': {
+ 'device_id': '',
+ 'device_owner': '',
+ 'binding:profile': {},
+ 'binding:host_id': None,
+ }
+ }
+ )
+ mock_log.exception.assert_not_called()
+ mock_log.debug.assert_called_with(
+ 'Unable to show port %s as it no longer exists.', uuids.port_a,
+ )
+
@mock.patch('nova.network.neutron.API._show_port',
side_effect=Exception)
@mock.patch.object(neutronapi.LOG, 'exception')
@@ -6042,7 +6080,7 @@ class TestAPI(TestAPIBase):
@mock.patch('nova.network.neutron.API._show_port')
@mock.patch.object(neutronapi.LOG, 'exception')
- def test_unbind_ports_portnotfound(self, mock_log, mock_show):
+ def test_unbind_ports_port_update_portnotfound(self, mock_log, mock_show):
api = neutronapi.API()
neutron_client = mock.Mock()
neutron_client.update_port = mock.Mock(
@@ -6058,7 +6096,9 @@ class TestAPI(TestAPIBase):
@mock.patch('nova.network.neutron.API._show_port')
@mock.patch.object(neutronapi.LOG, 'exception')
- def test_unbind_ports_unexpected_error(self, mock_log, mock_show):
+ def test_unbind_ports_port_update_unexpected_error(
+ self, mock_log, mock_show,
+ ):
api = neutronapi.API()
neutron_client = mock.Mock()
neutron_client.update_port = mock.Mock(