Skip to content

Commit 9e0dcb5

Browse files
committed
neutron: Unbind remaining ports after PortNotFound
Just because we encountered a PortNotFound error when unbinding a port doesn't mean we should stop unbinding the remaining ports. If this error is encountered, simply continue with the other ports. While we're here, we clean up some other tests related to '_unbind_port' since they're clearly duplicates. Change-Id: Id04e0df12829df4d8929e03a8b76b5cbe0549059 Signed-off-by: Stephen Finucane <[email protected]> Closes-Bug: #1974173
1 parent 4939318 commit 9e0dcb5

File tree

2 files changed

+68
-23
lines changed

2 files changed

+68
-23
lines changed

nova/network/neutron.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ def _unbind_ports(self, context, ports,
636636
# in case the caller forgot to filter the list.
637637
if port_id is None:
638638
continue
639+
639640
port_req_body: ty.Dict[str, ty.Any] = {
640641
'port': {
641642
'device_id': '',
@@ -650,7 +651,7 @@ def _unbind_ports(self, context, ports,
650651
except exception.PortNotFound:
651652
LOG.debug('Unable to show port %s as it no longer '
652653
'exists.', port_id)
653-
return
654+
continue
654655
except Exception:
655656
# NOTE: In case we can't retrieve the binding:profile or
656657
# network info assume that they are empty

nova/tests/unit/network/test_neutron.py

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5248,7 +5248,8 @@ def test_get_preexisting_port_ids(self, mock_get_nw_info):
52485248
self.assertEqual(['2', '3'], result, "Invalid preexisting ports")
52495249

52505250
@mock.patch('nova.network.neutron.API._show_port')
5251-
def _test_unbind_ports_get_client(self, mock_neutron, mock_show):
5251+
@mock.patch('nova.network.neutron.get_client')
5252+
def test_unbind_ports_get_client(self, mock_neutron, mock_show):
52525253
mock_ctx = mock.Mock(is_admin=False)
52535254
ports = ["1", "2", "3"]
52545255

@@ -5264,25 +5265,18 @@ def _test_unbind_ports_get_client(self, mock_neutron, mock_show):
52645265
self.assertEqual(1, mock_neutron.call_count)
52655266
mock_neutron.assert_has_calls(get_client_calls, True)
52665267

5267-
@mock.patch('nova.network.neutron.get_client')
5268-
def test_unbind_ports_get_client_binding_extension(self,
5269-
mock_neutron):
5270-
self._test_unbind_ports_get_client(mock_neutron)
5271-
5272-
@mock.patch('nova.network.neutron.get_client')
5273-
def test_unbind_ports_get_client(self, mock_neutron):
5274-
self._test_unbind_ports_get_client(mock_neutron)
5275-
52765268
@mock.patch('nova.network.neutron.API.has_dns_extension',
52775269
new=mock.Mock(return_value=False))
52785270
@mock.patch('nova.network.neutron.API._show_port')
5279-
def _test_unbind_ports(self, mock_neutron, mock_show):
5271+
@mock.patch('nova.network.neutron.get_client')
5272+
def test_unbind_ports(self, mock_neutron, mock_show):
52805273
mock_client = mock.Mock()
52815274
mock_update_port = mock.Mock()
52825275
mock_client.update_port = mock_update_port
52835276
mock_ctx = mock.Mock(is_admin=False)
52845277
ports = ["1", "2", "3"]
52855278
mock_show.side_effect = [{"id": "1"}, {"id": "2"}, {"id": "3"}]
5279+
52865280
api = neutronapi.API()
52875281
api._unbind_ports(mock_ctx, ports, mock_neutron, mock_client)
52885282

@@ -5296,14 +5290,6 @@ def _test_unbind_ports(self, mock_neutron, mock_show):
52965290
self.assertEqual(3, mock_update_port.call_count)
52975291
mock_update_port.assert_has_calls(update_port_calls)
52985292

5299-
@mock.patch('nova.network.neutron.get_client')
5300-
def test_unbind_ports_binding_ext(self, mock_neutron):
5301-
self._test_unbind_ports(mock_neutron)
5302-
5303-
@mock.patch('nova.network.neutron.get_client')
5304-
def test_unbind_ports(self, mock_neutron):
5305-
self._test_unbind_ports(mock_neutron)
5306-
53075293
def test_unbind_ports_no_port_ids(self):
53085294
# Tests that None entries in the ports list are filtered out.
53095295
mock_client = mock.Mock()
@@ -6068,7 +6054,6 @@ def test_get_instance_id_by_floating_address_port_not_found(self,
60686054
def test_unbind_ports_port_show_portnotfound(self, mock_log, mock_show):
60696055
api = neutronapi.API()
60706056
neutron_client = mock.Mock()
6071-
mock_show.return_value = {'id': uuids.port}
60726057
api._unbind_ports(self.context, [uuids.port_id],
60736058
neutron_client, neutron_client)
60746059
mock_show.assert_called_once_with(
@@ -6077,6 +6062,63 @@ def test_unbind_ports_port_show_portnotfound(self, mock_log, mock_show):
60776062
neutron_client=mock.ANY)
60786063
mock_log.assert_not_called()
60796064

6065+
@mock.patch(
6066+
'nova.network.neutron.API.has_dns_extension',
6067+
new=mock.Mock(return_value=False),
6068+
)
6069+
@mock.patch('nova.network.neutron.API._show_port')
6070+
@mock.patch.object(neutronapi, 'LOG')
6071+
def test_unbind_ports_port_show_portnotfound_multiple_ports(
6072+
self, mock_log, mock_show,
6073+
):
6074+
"""Ensure we continue unbinding ports even when one isn't found."""
6075+
mock_show.side_effect = [
6076+
exception.PortNotFound(port_id=uuids.port_a),
6077+
{'id': uuids.port_b},
6078+
]
6079+
api = neutronapi.API()
6080+
neutron_client = mock.Mock()
6081+
6082+
api._unbind_ports(
6083+
self.context,
6084+
[uuids.port_a, uuids.port_b],
6085+
neutron_client,
6086+
neutron_client,
6087+
)
6088+
6089+
mock_show.assert_has_calls(
6090+
[
6091+
mock.call(
6092+
self.context,
6093+
uuids.port_a,
6094+
fields=['binding:profile', 'network_id'],
6095+
neutron_client=neutron_client,
6096+
),
6097+
mock.call(
6098+
self.context,
6099+
uuids.port_b,
6100+
fields=['binding:profile', 'network_id'],
6101+
neutron_client=neutron_client,
6102+
),
6103+
]
6104+
)
6105+
# Only the port that exists should be updated
6106+
neutron_client.update_port.assert_called_once_with(
6107+
uuids.port_b,
6108+
{
6109+
'port': {
6110+
'device_id': '',
6111+
'device_owner': '',
6112+
'binding:profile': {},
6113+
'binding:host_id': None,
6114+
}
6115+
}
6116+
)
6117+
mock_log.exception.assert_not_called()
6118+
mock_log.debug.assert_called_with(
6119+
'Unable to show port %s as it no longer exists.', uuids.port_a,
6120+
)
6121+
60806122
@mock.patch('nova.network.neutron.API.has_dns_extension',
60816123
new=mock.Mock(return_value=False))
60826124
@mock.patch('nova.network.neutron.API._show_port',
@@ -6100,7 +6142,7 @@ def test_unbind_ports_port_show_unexpected_error(self,
61006142
new=mock.Mock(return_value=False))
61016143
@mock.patch('nova.network.neutron.API._show_port')
61026144
@mock.patch.object(neutronapi.LOG, 'exception')
6103-
def test_unbind_ports_portnotfound(self, mock_log, mock_show):
6145+
def test_unbind_ports_port_update_portnotfound(self, mock_log, mock_show):
61046146
api = neutronapi.API()
61056147
neutron_client = mock.Mock()
61066148
neutron_client.update_port = mock.Mock(
@@ -6118,7 +6160,9 @@ def test_unbind_ports_portnotfound(self, mock_log, mock_show):
61186160
new=mock.Mock(return_value=False))
61196161
@mock.patch('nova.network.neutron.API._show_port')
61206162
@mock.patch.object(neutronapi.LOG, 'exception')
6121-
def test_unbind_ports_unexpected_error(self, mock_log, mock_show):
6163+
def test_unbind_ports_port_update_unexpected_error(
6164+
self, mock_log, mock_show,
6165+
):
61226166
api = neutronapi.API()
61236167
neutron_client = mock.Mock()
61246168
neutron_client.update_port = mock.Mock(

0 commit comments

Comments
 (0)