Merge "Remove check_detach"
This commit is contained in:
commit
c45b88654a
@ -411,8 +411,7 @@ class VolumeAttachmentController(wsgi.Controller):
|
|||||||
new_volume)
|
new_volume)
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
except (exception.VolumeUnattached,
|
except (exception.VolumeBDMNotFound):
|
||||||
exception.VolumeBDMNotFound):
|
|
||||||
# The volume is not attached. Treat it as NotFound
|
# The volume is not attached. Treat it as NotFound
|
||||||
# by falling through.
|
# by falling through.
|
||||||
pass
|
pass
|
||||||
@ -467,10 +466,6 @@ class VolumeAttachmentController(wsgi.Controller):
|
|||||||
self.compute_api.detach_volume(context, instance, volume)
|
self.compute_api.detach_volume(context, instance, volume)
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
except exception.VolumeUnattached:
|
|
||||||
# The volume is not attached. Treat it as NotFound
|
|
||||||
# by falling through.
|
|
||||||
pass
|
|
||||||
except exception.InvalidVolume as e:
|
except exception.InvalidVolume as e:
|
||||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||||
except exception.InstanceUnknownCell as e:
|
except exception.InstanceUnknownCell as e:
|
||||||
|
@ -3657,17 +3657,16 @@ class API(base.Base):
|
|||||||
return self._attach_volume(context, instance, volume_id, device,
|
return self._attach_volume(context, instance, volume_id, device,
|
||||||
disk_bus, device_type, tag=tag)
|
disk_bus, device_type, tag=tag)
|
||||||
|
|
||||||
def _check_and_begin_detach(self, context, volume, instance):
|
|
||||||
self.volume_api.check_detach(context, volume, instance=instance)
|
|
||||||
self.volume_api.begin_detaching(context, volume['id'])
|
|
||||||
|
|
||||||
def _detach_volume(self, context, instance, volume):
|
def _detach_volume(self, context, instance, volume):
|
||||||
"""Detach volume from instance.
|
"""Detach volume from instance.
|
||||||
|
|
||||||
This method is separated to make it easier for cells version
|
This method is separated to make it easier for cells version
|
||||||
to override.
|
to override.
|
||||||
"""
|
"""
|
||||||
self._check_and_begin_detach(context, volume, instance)
|
try:
|
||||||
|
self.volume_api.begin_detaching(context, volume['id'])
|
||||||
|
except exception.InvalidInput as exc:
|
||||||
|
raise exception.InvalidVolume(reason=exc.format_message())
|
||||||
attachments = volume.get('attachments', {})
|
attachments = volume.get('attachments', {})
|
||||||
attachment_id = None
|
attachment_id = None
|
||||||
if attachments and instance.uuid in attachments:
|
if attachments and instance.uuid in attachments:
|
||||||
@ -3684,7 +3683,10 @@ class API(base.Base):
|
|||||||
If the volume has delete_on_termination option set then we call the
|
If the volume has delete_on_termination option set then we call the
|
||||||
volume api delete as well.
|
volume api delete as well.
|
||||||
"""
|
"""
|
||||||
self._check_and_begin_detach(context, volume, instance)
|
try:
|
||||||
|
self.volume_api.begin_detaching(context, volume['id'])
|
||||||
|
except exception.InvalidInput as exc:
|
||||||
|
raise exception.InvalidVolume(reason=exc.format_message())
|
||||||
bdms = [objects.BlockDeviceMapping.get_by_volume_id(
|
bdms = [objects.BlockDeviceMapping.get_by_volume_id(
|
||||||
context, volume['id'], instance.uuid)]
|
context, volume['id'], instance.uuid)]
|
||||||
self._local_cleanup_bdm_volumes(bdms, instance, context)
|
self._local_cleanup_bdm_volumes(bdms, instance, context)
|
||||||
@ -3707,8 +3709,6 @@ class API(base.Base):
|
|||||||
vm_states.RESIZED, vm_states.SOFT_DELETED])
|
vm_states.RESIZED, vm_states.SOFT_DELETED])
|
||||||
def swap_volume(self, context, instance, old_volume, new_volume):
|
def swap_volume(self, context, instance, old_volume, new_volume):
|
||||||
"""Swap volume attached to an instance."""
|
"""Swap volume attached to an instance."""
|
||||||
if old_volume['attach_status'] == 'detached':
|
|
||||||
raise exception.VolumeUnattached(volume_id=old_volume['id'])
|
|
||||||
# The caller likely got the instance from volume['attachments']
|
# The caller likely got the instance from volume['attachments']
|
||||||
# in the first place, but let's sanity check.
|
# in the first place, but let's sanity check.
|
||||||
if not old_volume.get('attachments', {}).get(instance.uuid):
|
if not old_volume.get('attachments', {}).get(instance.uuid):
|
||||||
@ -3720,10 +3720,12 @@ class API(base.Base):
|
|||||||
if int(new_volume['size']) < int(old_volume['size']):
|
if int(new_volume['size']) < int(old_volume['size']):
|
||||||
msg = _("New volume must be the same size or larger.")
|
msg = _("New volume must be the same size or larger.")
|
||||||
raise exception.InvalidVolume(reason=msg)
|
raise exception.InvalidVolume(reason=msg)
|
||||||
self.volume_api.check_detach(context, old_volume)
|
|
||||||
self.volume_api.check_availability_zone(context, new_volume,
|
self.volume_api.check_availability_zone(context, new_volume,
|
||||||
instance=instance)
|
instance=instance)
|
||||||
self.volume_api.begin_detaching(context, old_volume['id'])
|
try:
|
||||||
|
self.volume_api.begin_detaching(context, old_volume['id'])
|
||||||
|
except exception.InvalidInput as exc:
|
||||||
|
raise exception.InvalidVolume(reason=exc.format_message())
|
||||||
|
|
||||||
# Get the BDM for the attached (old) volume so we can tell if it was
|
# Get the BDM for the attached (old) volume so we can tell if it was
|
||||||
# attached with the new-style Cinder 3.27 API.
|
# attached with the new-style Cinder 3.27 API.
|
||||||
|
@ -472,7 +472,6 @@ class ComputeCellsAPI(compute_api.API):
|
|||||||
@check_instance_cell
|
@check_instance_cell
|
||||||
def _detach_volume(self, context, instance, volume):
|
def _detach_volume(self, context, instance, volume):
|
||||||
"""Detach a volume from an instance."""
|
"""Detach a volume from an instance."""
|
||||||
self.volume_api.check_detach(context, volume, instance=instance)
|
|
||||||
self._cast_to_cells(context, instance, 'detach_volume',
|
self._cast_to_cells(context, instance, 'detach_volume',
|
||||||
volume)
|
volume)
|
||||||
|
|
||||||
|
@ -2954,8 +2954,6 @@ class ComputeManager(manager.Manager):
|
|||||||
#
|
#
|
||||||
# API-detach
|
# API-detach
|
||||||
LOG.info("Detaching from volume api: %s", volume_id)
|
LOG.info("Detaching from volume api: %s", volume_id)
|
||||||
volume = self.volume_api.get(context, volume_id)
|
|
||||||
self.volume_api.check_detach(context, volume)
|
|
||||||
self.volume_api.begin_detaching(context, volume_id)
|
self.volume_api.begin_detaching(context, volume_id)
|
||||||
|
|
||||||
# Manager-detach
|
# Manager-detach
|
||||||
|
@ -254,10 +254,6 @@ class VolumeAttachFailed(Invalid):
|
|||||||
"Reason: %(reason)s")
|
"Reason: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class VolumeUnattached(Invalid):
|
|
||||||
msg_fmt = _("Volume %(volume_id)s is not attached to anything")
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeNotCreated(NovaException):
|
class VolumeNotCreated(NovaException):
|
||||||
msg_fmt = _("Volume %(volume_id)s did not finish being created"
|
msg_fmt = _("Volume %(volume_id)s did not finish being created"
|
||||||
" even after we waited %(seconds)s seconds or %(attempts)s"
|
" even after we waited %(seconds)s seconds or %(attempts)s"
|
||||||
|
@ -1358,10 +1358,6 @@ class CinderFixture(fixtures.Fixture):
|
|||||||
|
|
||||||
self.test.stub_out('nova.volume.cinder.API.begin_detaching',
|
self.test.stub_out('nova.volume.cinder.API.begin_detaching',
|
||||||
lambda *args, **kwargs: None)
|
lambda *args, **kwargs: None)
|
||||||
self.test.stub_out('nova.volume.cinder.API.check_attach',
|
|
||||||
lambda *args, **kwargs: None)
|
|
||||||
self.test.stub_out('nova.volume.cinder.API.check_detach',
|
|
||||||
lambda *args, **kwargs: None)
|
|
||||||
self.test.stub_out('nova.volume.cinder.API.get',
|
self.test.stub_out('nova.volume.cinder.API.get',
|
||||||
fake_get)
|
fake_get)
|
||||||
self.test.stub_out('nova.volume.cinder.API.initialize_connection',
|
self.test.stub_out('nova.volume.cinder.API.initialize_connection',
|
||||||
|
@ -994,8 +994,8 @@ class ServerTestV220(ServersTestBase):
|
|||||||
|
|
||||||
# Test detach volume
|
# Test detach volume
|
||||||
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
|
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
|
||||||
with test.nested(mock.patch.object(compute_api.API,
|
with test.nested(mock.patch.object(volume.cinder.API,
|
||||||
'_check_and_begin_detach'),
|
'begin_detaching'),
|
||||||
mock.patch.object(objects.BlockDeviceMappingList,
|
mock.patch.object(objects.BlockDeviceMappingList,
|
||||||
'get_by_instance_uuid'),
|
'get_by_instance_uuid'),
|
||||||
mock.patch.object(rpcapi.ComputeAPI,
|
mock.patch.object(rpcapi.ComputeAPI,
|
||||||
@ -1034,8 +1034,8 @@ class ServerTestV220(ServersTestBase):
|
|||||||
|
|
||||||
# Test detach volume
|
# Test detach volume
|
||||||
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
|
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
|
||||||
with test.nested(mock.patch.object(compute_api.API,
|
with test.nested(mock.patch.object(volume.cinder.API,
|
||||||
'_check_and_begin_detach'),
|
'begin_detaching'),
|
||||||
mock.patch.object(objects.BlockDeviceMappingList,
|
mock.patch.object(objects.BlockDeviceMappingList,
|
||||||
'get_by_instance_uuid'),
|
'get_by_instance_uuid'),
|
||||||
mock.patch.object(compute_api.API,
|
mock.patch.object(compute_api.API,
|
||||||
|
@ -10143,16 +10143,12 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
# Set attach_status to 'fake' as nothing is reading the value.
|
# Set attach_status to 'fake' as nothing is reading the value.
|
||||||
volume = {'id': 1, 'attach_status': 'fake'}
|
volume = {'id': 1, 'attach_status': 'fake'}
|
||||||
|
|
||||||
def fake_check_detach(*args, **kwargs):
|
|
||||||
called['fake_check_detach'] = True
|
|
||||||
|
|
||||||
def fake_begin_detaching(*args, **kwargs):
|
def fake_begin_detaching(*args, **kwargs):
|
||||||
called['fake_begin_detaching'] = True
|
called['fake_begin_detaching'] = True
|
||||||
|
|
||||||
def fake_rpc_detach_volume(self, context, **kwargs):
|
def fake_rpc_detach_volume(self, context, **kwargs):
|
||||||
called['fake_rpc_detach_volume'] = True
|
called['fake_rpc_detach_volume'] = True
|
||||||
|
|
||||||
self.stub_out('nova.volume.cinder.API.check_detach', fake_check_detach)
|
|
||||||
self.stub_out('nova.volume.cinder.API.begin_detaching',
|
self.stub_out('nova.volume.cinder.API.begin_detaching',
|
||||||
fake_begin_detaching)
|
fake_begin_detaching)
|
||||||
self.stub_out('nova.compute.rpcapi.ComputeAPI.detach_volume',
|
self.stub_out('nova.compute.rpcapi.ComputeAPI.detach_volume',
|
||||||
@ -10160,17 +10156,16 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
|
|
||||||
self.compute_api.detach_volume(self.context,
|
self.compute_api.detach_volume(self.context,
|
||||||
instance, volume)
|
instance, volume)
|
||||||
self.assertTrue(called.get('fake_check_detach'))
|
|
||||||
self.assertTrue(called.get('fake_begin_detaching'))
|
self.assertTrue(called.get('fake_begin_detaching'))
|
||||||
self.assertTrue(called.get('fake_rpc_detach_volume'))
|
self.assertTrue(called.get('fake_rpc_detach_volume'))
|
||||||
|
|
||||||
@mock.patch.object(compute_api.API, '_check_and_begin_detach')
|
@mock.patch.object(nova.volume.cinder.API, 'begin_detaching')
|
||||||
@mock.patch.object(compute_api.API, '_local_cleanup_bdm_volumes')
|
@mock.patch.object(compute_api.API, '_local_cleanup_bdm_volumes')
|
||||||
@mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume_id')
|
@mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume_id')
|
||||||
def test_detach_volume_shelved_offloaded(self,
|
def test_detach_volume_shelved_offloaded(self,
|
||||||
mock_block_dev,
|
mock_block_dev,
|
||||||
mock_local_cleanup,
|
mock_local_cleanup,
|
||||||
mock_check_begin_detach):
|
mock_begin_detaching):
|
||||||
|
|
||||||
mock_block_dev.return_value = [block_device_obj.BlockDeviceMapping(
|
mock_block_dev.return_value = [block_device_obj.BlockDeviceMapping(
|
||||||
context=context)]
|
context=context)]
|
||||||
@ -10179,12 +10174,13 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api._detach_volume_shelved_offloaded(self.context,
|
self.compute_api._detach_volume_shelved_offloaded(self.context,
|
||||||
instance,
|
instance,
|
||||||
volume)
|
volume)
|
||||||
mock_check_begin_detach.assert_called_once_with(self.context,
|
mock_begin_detaching.assert_called_once_with(self.context,
|
||||||
volume,
|
volume['id'])
|
||||||
instance)
|
|
||||||
self.assertTrue(mock_local_cleanup.called)
|
self.assertTrue(mock_local_cleanup.called)
|
||||||
|
|
||||||
def test_detach_invalid_volume(self):
|
@mock.patch.object(nova.volume.cinder.API, 'begin_detaching',
|
||||||
|
side_effect=exception.InvalidInput(reason='error'))
|
||||||
|
def test_detach_invalid_volume(self, mock_begin_detaching):
|
||||||
# Ensure exception is raised while detaching an un-attached volume
|
# Ensure exception is raised while detaching an un-attached volume
|
||||||
fake_instance = self._fake_instance({
|
fake_instance = self._fake_instance({
|
||||||
'uuid': 'f7000000-0000-0000-0000-000000000001',
|
'uuid': 'f7000000-0000-0000-0000-000000000001',
|
||||||
@ -10198,22 +10194,6 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.detach_volume, self.context,
|
self.compute_api.detach_volume, self.context,
|
||||||
fake_instance, volume)
|
fake_instance, volume)
|
||||||
|
|
||||||
def test_detach_unattached_volume(self):
|
|
||||||
# Ensure exception is raised when volume's idea of attached
|
|
||||||
# instance doesn't match.
|
|
||||||
fake_instance = self._fake_instance({
|
|
||||||
'uuid': 'f7000000-0000-0000-0000-000000000001',
|
|
||||||
'locked': False,
|
|
||||||
'launched_at': timeutils.utcnow(),
|
|
||||||
'vm_state': vm_states.ACTIVE,
|
|
||||||
'task_state': None})
|
|
||||||
volume = {'id': 1, 'attach_status': 'attached', 'status': 'in-use',
|
|
||||||
'attachments': {'fake_uuid': {'attachment_id': 'fakeid'}}}
|
|
||||||
|
|
||||||
self.assertRaises(exception.VolumeUnattached,
|
|
||||||
self.compute_api.detach_volume, self.context,
|
|
||||||
fake_instance, volume)
|
|
||||||
|
|
||||||
def test_detach_suspended_instance_fails(self):
|
def test_detach_suspended_instance_fails(self):
|
||||||
fake_instance = self._fake_instance({
|
fake_instance = self._fake_instance({
|
||||||
'uuid': 'f7000000-0000-0000-0000-000000000001',
|
'uuid': 'f7000000-0000-0000-0000-000000000001',
|
||||||
|
@ -2214,13 +2214,6 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
exception.InstanceInvalidState,
|
exception.InstanceInvalidState,
|
||||||
instance_update={'vm_state': vm_states.BUILDING})
|
instance_update={'vm_state': vm_states.BUILDING})
|
||||||
|
|
||||||
def test_swap_volume_with_old_volume_not_attached(self):
|
|
||||||
# Should fail if old volume is not attached
|
|
||||||
self._test_swap_volume_for_precheck_with_exception(
|
|
||||||
exception.VolumeUnattached,
|
|
||||||
volume_update={'target': uuids.old_volume,
|
|
||||||
'value': {'attach_status': 'detached'}})
|
|
||||||
|
|
||||||
def test_swap_volume_with_another_server_volume(self):
|
def test_swap_volume_with_another_server_volume(self):
|
||||||
# Should fail if old volume's instance_uuid is not that of the instance
|
# Should fail if old volume's instance_uuid is not that of the instance
|
||||||
self._test_swap_volume_for_precheck_with_exception(
|
self._test_swap_volume_for_precheck_with_exception(
|
||||||
|
@ -185,18 +185,6 @@ class API(object):
|
|||||||
msg = "Instance and volume not in same availability_zone"
|
msg = "Instance and volume not in same availability_zone"
|
||||||
raise exception.InvalidVolume(reason=msg)
|
raise exception.InvalidVolume(reason=msg)
|
||||||
|
|
||||||
def check_detach(self, context, volume, instance=None):
|
|
||||||
if volume['status'] == "available":
|
|
||||||
msg = "already detached"
|
|
||||||
raise exception.InvalidVolume(reason=msg)
|
|
||||||
|
|
||||||
if volume['attach_status'] == 'detached':
|
|
||||||
msg = "Volume must be attached in order to detach."
|
|
||||||
raise exception.InvalidVolume(reason=msg)
|
|
||||||
|
|
||||||
if instance and not volume.get('attachments', {}).get(instance.uuid):
|
|
||||||
raise exception.VolumeUnattached(volume_id=volume['id'])
|
|
||||||
|
|
||||||
def attach(self, context, volume_id, instance_uuid, mountpoint, mode='rw'):
|
def attach(self, context, volume_id, instance_uuid, mountpoint, mode='rw'):
|
||||||
LOG.info('attaching volume %s', volume_id)
|
LOG.info('attaching volume %s', volume_id)
|
||||||
volume = self.get(context, volume_id)
|
volume = self.get(context, volume_id)
|
||||||
|
@ -200,29 +200,6 @@ class CinderApiTestCase(test.NoDBTestCase):
|
|||||||
self.ctx, volume, instance)
|
self.ctx, volume, instance)
|
||||||
mock_get_instance_az.assert_called_once_with(self.ctx, instance)
|
mock_get_instance_az.assert_called_once_with(self.ctx, instance)
|
||||||
|
|
||||||
def test_check_detach(self):
|
|
||||||
volume = {'id': 'fake', 'status': 'in-use',
|
|
||||||
'attach_status': 'attached',
|
|
||||||
'attachments': {uuids.instance: {
|
|
||||||
'attachment_id': uuids.attachment}}
|
|
||||||
}
|
|
||||||
self.assertIsNone(self.api.check_detach(self.ctx, volume))
|
|
||||||
instance = fake_instance_obj(self.ctx)
|
|
||||||
instance.uuid = uuids.instance
|
|
||||||
self.assertIsNone(self.api.check_detach(self.ctx, volume, instance))
|
|
||||||
instance.uuid = uuids.instance2
|
|
||||||
self.assertRaises(exception.VolumeUnattached,
|
|
||||||
self.api.check_detach, self.ctx, volume, instance)
|
|
||||||
volume['attachments'] = {}
|
|
||||||
self.assertRaises(exception.VolumeUnattached,
|
|
||||||
self.api.check_detach, self.ctx, volume, instance)
|
|
||||||
volume['status'] = 'available'
|
|
||||||
self.assertRaises(exception.InvalidVolume,
|
|
||||||
self.api.check_detach, self.ctx, volume)
|
|
||||||
volume['attach_status'] = 'detached'
|
|
||||||
self.assertRaises(exception.InvalidVolume,
|
|
||||||
self.api.check_detach, self.ctx, volume)
|
|
||||||
|
|
||||||
@mock.patch('nova.volume.cinder.cinderclient')
|
@mock.patch('nova.volume.cinder.cinderclient')
|
||||||
def test_reserve_volume(self, mock_cinderclient):
|
def test_reserve_volume(self, mock_cinderclient):
|
||||||
mock_volumes = mock.MagicMock()
|
mock_volumes = mock.MagicMock()
|
||||||
|
@ -337,24 +337,6 @@ class API(object):
|
|||||||
'vol_zone': volume['availability_zone']}
|
'vol_zone': volume['availability_zone']}
|
||||||
raise exception.InvalidVolume(reason=msg)
|
raise exception.InvalidVolume(reason=msg)
|
||||||
|
|
||||||
def check_detach(self, context, volume, instance=None):
|
|
||||||
# TODO(vish): abstract status checking?
|
|
||||||
if volume['status'] == "available":
|
|
||||||
msg = _("volume %s already detached") % volume['id']
|
|
||||||
raise exception.InvalidVolume(reason=msg)
|
|
||||||
|
|
||||||
if volume['attach_status'] == 'detached':
|
|
||||||
msg = _("Volume must be attached in order to detach.")
|
|
||||||
raise exception.InvalidVolume(reason=msg)
|
|
||||||
|
|
||||||
# NOTE(ildikov):Preparation for multiattach support, when a volume
|
|
||||||
# can be attached to multiple hosts and/or instances,
|
|
||||||
# so just check the attachment specific to this instance
|
|
||||||
if instance is not None and instance.uuid not in volume['attachments']:
|
|
||||||
# TODO(ildikov): change it to a better exception, when enable
|
|
||||||
# multi-attach.
|
|
||||||
raise exception.VolumeUnattached(volume_id=volume['id'])
|
|
||||||
|
|
||||||
@translate_volume_exception
|
@translate_volume_exception
|
||||||
def reserve_volume(self, context, volume_id):
|
def reserve_volume(self, context, volume_id):
|
||||||
cinderclient(context).volumes.reserve(volume_id)
|
cinderclient(context).volumes.reserve(volume_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user