Skip to content

Commit 6b78420

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Disconnecting volume from the compute host" into stable/2023.1
2 parents 51480ee + c534be7 commit 6b78420

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

doc/source/cli/nova-manage.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,9 @@ command.
15311531
* - 5
15321532
- Instance state invalid (must be stopped and unlocked)
15331533
* - 6
1534-
- Instance is not attached to volume
1534+
- Volume is not attached to the instance
1535+
* - 7
1536+
- Connector host is not correct
15351537

15361538

15371539
Libvirt Commands

nova/cmd/manage.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,14 @@ def locked_instance(cell_mapping, instance, reason):
161161
initial_state = 'locked' if instance.locked else 'unlocked'
162162
if not instance.locked:
163163
with context.target_cell(
164-
context.get_admin_context(),
165-
cell_mapping
166-
) as cctxt:
164+
context.get_admin_context(), cell_mapping) as cctxt:
167165
compute_api.lock(cctxt, instance, reason=reason)
168166
try:
169167
yield
170168
finally:
171169
if initial_state == 'unlocked':
172170
with context.target_cell(
173-
context.get_admin_context(),
174-
cell_mapping
175-
) as cctxt:
171+
context.get_admin_context(), cell_mapping) as cctxt:
176172
compute_api.unlock(cctxt, instance)
177173

178174

@@ -3078,8 +3074,15 @@ def _do_refresh(self, cctxt, instance,
30783074
# TODO(lyarwood): Add delete_attachment as a kwarg to
30793075
# remove_volume_connection as is available in the private
30803076
# method within the manager.
3081-
compute_rpcapi.remove_volume_connection(
3082-
cctxt, instance, volume_id, instance.host)
3077+
if instance.host == connector['host']:
3078+
compute_rpcapi.remove_volume_connection(
3079+
cctxt, instance, volume_id, instance.host)
3080+
else:
3081+
msg = (
3082+
f"The compute host '{connector['host']}' in the "
3083+
f"connector does not match the instance host "
3084+
f"'{instance.host}'.")
3085+
raise exception.HostConflict(_(msg))
30833086

30843087
# Delete the existing volume attachment if present in the bdm.
30853088
# This isn't present when the original attachment was made
@@ -3161,6 +3164,7 @@ def refresh(self, instance_uuid=None, volume_id=None, connector_path=None):
31613164
* 4: Instance does not exist.
31623165
* 5: Instance state invalid.
31633166
* 6: Volume is not attached to instance.
3167+
* 7: Connector host is not correct.
31643168
"""
31653169
try:
31663170
# TODO(lyarwood): Make this optional and provide a rpcapi capable
@@ -3176,6 +3180,12 @@ def refresh(self, instance_uuid=None, volume_id=None, connector_path=None):
31763180
# Refresh the volume attachment
31773181
return self._refresh(instance_uuid, volume_id, connector)
31783182

3183+
except exception.HostConflict as e:
3184+
print(
3185+
f"The command 'nova-manage volume_attachment get_connector' "
3186+
f"may have been run on the wrong compute host. Or the "
3187+
f"instance host may be wrong and in need of repair.\n{e}")
3188+
return 7
31793189
except exception.VolumeBDMNotFound as e:
31803190
print(str(e))
31813191
return 6

nova/exception.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2530,3 +2530,7 @@ class NotSupportedComputeForEvacuateV295(NotSupported):
25302530
"instance on destination. To evacuate before upgrades are "
25312531
"complete please use an older microversion. Required version "
25322532
"for compute %(expected), current version %(currently)s")
2533+
2534+
2535+
class HostConflict(Exception):
2536+
pass

nova/tests/unit/cmd/test_manage.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3622,6 +3622,50 @@ def test_refresh_attachment_unknown_failure(
36223622
mock_action_start.assert_called_once()
36233623
mock_action.finish.assert_called_once()
36243624

3625+
@mock.patch('nova.compute.rpcapi.ComputeAPI', autospec=True)
3626+
@mock.patch('nova.volume.cinder.API', autospec=True)
3627+
@mock.patch('nova.compute.api.API', autospec=True)
3628+
@mock.patch.object(objects.BlockDeviceMapping, 'save')
3629+
@mock.patch.object(
3630+
objects.BlockDeviceMapping, 'get_by_volume_and_instance')
3631+
@mock.patch.object(objects.Instance, 'get_by_uuid')
3632+
@mock.patch.object(objects.InstanceAction, 'action_start')
3633+
def test_refresh_invalid_connector_host(
3634+
self, mock_action_start, mock_get_instance,
3635+
mock_get_bdm, mock_save_bdm, mock_compute_api, mock_volume_api,
3636+
mock_compute_rpcapi
3637+
):
3638+
"""Test refresh with a old host not disconnected properly
3639+
and connector host info is not correct, a fake-host is
3640+
passed.
3641+
"""
3642+
3643+
fake_volume_api = mock_volume_api.return_value
3644+
device_name = '/dev/vda'
3645+
3646+
mock_get_instance.return_value = objects.Instance(
3647+
uuid=uuidsentinel.instance,
3648+
vm_state=obj_fields.InstanceState.STOPPED,
3649+
host='old-host', locked=False)
3650+
mock_get_bdm.return_value = objects.BlockDeviceMapping(
3651+
uuid=uuidsentinel.bdm, volume_id=uuidsentinel.volume,
3652+
attachment_id=uuidsentinel.instance,
3653+
device_name=device_name)
3654+
mock_action = mock.Mock(spec=objects.InstanceAction)
3655+
mock_action_start.return_value = mock_action
3656+
3657+
fake_volume_api.attachment_create.return_value = {
3658+
'id': uuidsentinel.new_attachment,
3659+
}
3660+
# in instance we have host as 'old-host'
3661+
# but here 'fake-host' is passed in connector info.
3662+
fake_volume_api.attachment_update.return_value = {
3663+
'connection_info': self._get_fake_connector_info(),
3664+
}
3665+
3666+
ret = self._test_refresh()
3667+
self.assertEqual(7, ret)
3668+
36253669
@mock.patch('nova.compute.rpcapi.ComputeAPI', autospec=True)
36263670
@mock.patch('nova.volume.cinder.API', autospec=True)
36273671
@mock.patch('nova.compute.api.API', autospec=True)
@@ -3644,7 +3688,7 @@ def test_refresh(
36443688
mock_get_instance.return_value = objects.Instance(
36453689
uuid=uuidsentinel.instance,
36463690
vm_state=obj_fields.InstanceState.STOPPED,
3647-
host='foo', locked=False)
3691+
host='fake-host', locked=False)
36483692
mock_get_bdm.return_value = objects.BlockDeviceMapping(
36493693
uuid=uuidsentinel.bdm, volume_id=uuidsentinel.volume,
36503694
attachment_id=uuidsentinel.instance,

0 commit comments

Comments
 (0)