def get_by_volume_id(cls, context, volume_id, instance_uuid=None, expected_attrs=None): if expected_attrs is None: expected_attrs = [] db_bdms = db.block_device_mapping_get_all_by_volume_id( context, volume_id, _expected_cols(expected_attrs)) if not db_bdms: raise exception.VolumeBDMNotFound(volume_id=volume_id) if len(db_bdms) > 1: LOG.warning( 'Legacy get_by_volume_id() call found multiple ' 'BDMs for volume %(volume)s', {'volume': volume_id}) db_bdm = db_bdms[0] # NOTE (ndipanov): Move this to the db layer into a # get_by_instance_and_volume_id method if instance_uuid and instance_uuid != db_bdm['instance_uuid']: raise exception.InvalidVolume( reason=_("Volume does not belong to the " "requested instance.")) return cls._from_db_object(context, cls(), db_bdm, expected_attrs=expected_attrs)
def get_by_volume(cls, context, volume_id, use_slave=False): db_bdms = cls._db_block_device_mapping_get_all_by_volume( context, volume_id, use_slave=use_slave) if not db_bdms: raise exception.VolumeBDMNotFound(volume_id=volume_id) return base.obj_make_list( context, cls(), objects.BlockDeviceMapping, db_bdms)
def get_by_volume_id(cls, context, volume_id, expected_attrs=None): if expected_attrs is None: expected_attrs = [] db_bdm = db.block_device_mapping_get_by_volume_id( context, volume_id, _expected_cols(expected_attrs)) if not db_bdm: raise exception.VolumeBDMNotFound(volume_id=volume_id) return cls._from_db_object(context, cls(), db_bdm, expected_attrs=expected_attrs)
def get_by_volume(cls, context, volume_id, expected_attrs=None): if expected_attrs is None: expected_attrs = [] db_bdms = db.block_device_mapping_get_all_by_volume_id( context, volume_id, _expected_cols(expected_attrs)) if not db_bdms: raise exception.VolumeBDMNotFound(volume_id=volume_id) if len(db_bdms) > 1: raise exception.VolumeBDMIsMultiAttach(volume_id=volume_id) return cls._from_db_object(context, cls(), db_bdms[0], expected_attrs=expected_attrs)
def get_by_instance_and_volume_id(cls, context, volume_id, instance_uuid, expected_attrs=None): db_bdm = db.block_device_mapping_get_by_instance_and_volume_id( context, volume_id, instance_uuid) if not db_bdm: raise exception.VolumeBDMNotFound(volume_id=volume_id) return cls._from_db_object(context, cls(), db_bdm, expected_attrs=expected_attrs)
def fake_bdm_get_by_volume_and_instance(cls, ctxt, volume_id, instance_uuid): if volume_id != FAKE_UUID_A: raise exception.VolumeBDMNotFound(volume_id=volume_id) db_bdm = fake_block_device.FakeDbBlockDeviceDict( {'id': 1, 'instance_uuid': instance_uuid, 'device_name': '/dev/fake0', 'delete_on_termination': 'False', 'source_type': 'volume', 'destination_type': 'volume', 'snapshot_id': None, 'volume_id': FAKE_UUID_A, 'volume_size': 1}) return objects.BlockDeviceMapping._from_db_object( ctxt, objects.BlockDeviceMapping(), db_bdm)
def get_by_volume_id(cls, context, volume_id, instance_uuid=None, expected_attrs=None): if expected_attrs is None: expected_attrs = [] db_bdm = db.block_device_mapping_get_by_volume_id( context, volume_id, _expected_cols(expected_attrs)) if not db_bdm: raise exception.VolumeBDMNotFound(volume_id=volume_id) # NOTE (ndipanov): Move this to the db layer into a # get_by_instance_and_volume_id method if instance_uuid and instance_uuid != db_bdm['instance_uuid']: raise exception.InvalidVolume( reason=_("Volume does not belong to the " "requested instance.")) return cls._from_db_object(context, cls(), db_bdm, expected_attrs=expected_attrs)
def fake_swap_volume_for_bdm_not_found(self, context, instance, old_volume, new_volume): raise exception.VolumeBDMNotFound(volume_id=FAKE_UUID_C)
def fake_swap_volume(self, context, instance, old_volume, new_volume): if old_volume['id'] != FAKE_UUID_A: raise exception.VolumeBDMNotFound(volume_id=old_volume['id'])
class VolumeAttachTestsV21(test.NoDBTestCase): validation_error = exception.ValidationError def setUp(self): super(VolumeAttachTestsV21, self).setUp() self.stub_out('nova.objects.BlockDeviceMapping' '.get_by_volume_and_instance', fake_bdm_get_by_volume_and_instance) self.stubs.Set(compute_api.API, 'get', fake_get_instance) self.stubs.Set(cinder.API, 'get', fake_get_volume) self.context = context.get_admin_context() self.expected_show = {'volumeAttachment': {'device': '/dev/fake0', 'serverId': FAKE_UUID, 'id': FAKE_UUID_A, 'volumeId': FAKE_UUID_A }} self.attachments = volumes_v21.VolumeAttachmentController() self.req = fakes.HTTPRequest.blank( '/v2/servers/id/os-volume_attachments/uuid') self.req.body = jsonutils.dump_as_bytes({}) self.req.headers['content-type'] = 'application/json' self.req.environ['nova.context'] = self.context def test_show(self): result = self.attachments.show(self.req, FAKE_UUID, FAKE_UUID_A) self.assertEqual(self.expected_show, result) @mock.patch.object(compute_api.API, 'get', side_effect=exception.InstanceNotFound(instance_id=FAKE_UUID)) def test_show_no_instance(self, mock_mr): self.assertRaises(exc.HTTPNotFound, self.attachments.show, self.req, FAKE_UUID, FAKE_UUID_A) @mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume_and_instance', side_effect=exception.VolumeBDMNotFound( volume_id=FAKE_UUID_A)) def test_show_no_bdms(self, mock_mr): self.assertRaises(exc.HTTPNotFound, self.attachments.show, self.req, FAKE_UUID, FAKE_UUID_A) def test_show_bdms_no_mountpoint(self): FAKE_UUID_NOTEXIST = '00000000-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.assertRaises(exc.HTTPNotFound, self.attachments.show, self.req, FAKE_UUID, FAKE_UUID_NOTEXIST) def test_detach(self): self.stubs.Set(compute_api.API, 'detach_volume', fake_detach_volume) inst = fake_instance.fake_instance_obj(self.context, **{'uuid': FAKE_UUID}) with mock.patch.object(common, 'get_instance', return_value=inst) as mock_get_instance: result = self.attachments.delete(self.req, FAKE_UUID, FAKE_UUID_A) # NOTE: on v2.1, http status code is set as wsgi_code of API # method instead of status_int in a response object. if isinstance(self.attachments, volumes_v21.VolumeAttachmentController): status_int = self.attachments.delete.wsgi_code else: status_int = result.status_int self.assertEqual(202, status_int) mock_get_instance.assert_called_with( self.attachments.compute_api, self.context, FAKE_UUID, expected_attrs=['device_metadata']) @mock.patch.object(common, 'get_instance') def test_detach_vol_shelved_not_supported(self, mock_get_instance): inst = fake_instance.fake_instance_obj(self.context, **{'uuid': FAKE_UUID}) inst.vm_state = vm_states.SHELVED mock_get_instance.return_value = inst req = fakes.HTTPRequest.blank( '/v2/servers/id/os-volume_attachments/uuid', version='2.19') req.method = 'DELETE' req.headers['content-type'] = 'application/json' req.environ['nova.context'] = self.context self.assertRaises(webob.exc.HTTPConflict, self.attachments.delete, req, FAKE_UUID, FAKE_UUID_A) @mock.patch.object(compute_api.API, 'detach_volume') @mock.patch.object(common, 'get_instance') def test_detach_vol_shelved_supported(self, mock_get_instance, mock_detach): inst = fake_instance.fake_instance_obj(self.context, **{'uuid': FAKE_UUID}) inst.vm_state = vm_states.SHELVED mock_get_instance.return_value = inst req = fakes.HTTPRequest.blank( '/v2/servers/id/os-volume_attachments/uuid', version='2.20') req.method = 'DELETE' req.headers['content-type'] = 'application/json' req.environ['nova.context'] = self.context self.attachments.delete(req, FAKE_UUID, FAKE_UUID_A) self.assertTrue(mock_detach.called) def test_detach_vol_not_found(self): self.stubs.Set(compute_api.API, 'detach_volume', fake_detach_volume) self.assertRaises(exc.HTTPNotFound, self.attachments.delete, self.req, FAKE_UUID, FAKE_UUID_C) @mock.patch('nova.objects.BlockDeviceMapping.is_root', new_callable=mock.PropertyMock) def test_detach_vol_root(self, mock_isroot): mock_isroot.return_value = True self.assertRaises(exc.HTTPForbidden, self.attachments.delete, self.req, FAKE_UUID, FAKE_UUID_A) def test_detach_volume_from_locked_server(self): def fake_detach_volume_from_locked_server(self, context, instance, volume): raise exception.InstanceIsLocked(instance_uuid=instance['uuid']) self.stubs.Set(compute_api.API, 'detach_volume', fake_detach_volume_from_locked_server) self.assertRaises(webob.exc.HTTPConflict, self.attachments.delete, self.req, FAKE_UUID, FAKE_UUID_A) def test_attach_volume(self): self.stubs.Set(compute_api.API, 'attach_volume', fake_attach_volume) body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} result = self.attachments.create(self.req, FAKE_UUID, body=body) self.assertEqual('00000000-aaaa-aaaa-aaaa-000000000000', result['volumeAttachment']['id']) @mock.patch.object(compute_api.API, 'attach_volume', side_effect=exception.VolumeTaggedAttachNotSupported()) def test_tagged_volume_attach_not_supported(self, mock_attach_volume): body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} self.assertRaises(webob.exc.HTTPBadRequest, self.attachments.create, self.req, FAKE_UUID, body=body) @mock.patch.object(common, 'get_instance') def test_attach_vol_shelved_not_supported(self, mock_get_instance): body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} inst = fake_instance.fake_instance_obj(self.context, **{'uuid': FAKE_UUID}) inst.vm_state = vm_states.SHELVED mock_get_instance.return_value = inst self.assertRaises(webob.exc.HTTPConflict, self.attachments.create, self.req, FAKE_UUID, body=body) @mock.patch.object(compute_api.API, 'attach_volume', return_value='/dev/myfake') @mock.patch.object(common, 'get_instance') def test_attach_vol_shelved_supported(self, mock_get_instance, mock_attach): body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} inst = fake_instance.fake_instance_obj(self.context, **{'uuid': FAKE_UUID}) inst.vm_state = vm_states.SHELVED mock_get_instance.return_value = inst req = fakes.HTTPRequest.blank('/v2/servers/id/os-volume_attachments', version='2.20') req.method = 'POST' req.body = jsonutils.dump_as_bytes({}) req.headers['content-type'] = 'application/json' req.environ['nova.context'] = self.context result = self.attachments.create(req, FAKE_UUID, body=body) self.assertEqual('00000000-aaaa-aaaa-aaaa-000000000000', result['volumeAttachment']['id']) self.assertEqual('/dev/myfake', result['volumeAttachment']['device']) @mock.patch.object(compute_api.API, 'attach_volume', return_value='/dev/myfake') def test_attach_volume_with_auto_device(self, mock_attach): body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': None}} result = self.attachments.create(self.req, FAKE_UUID, body=body) self.assertEqual('00000000-aaaa-aaaa-aaaa-000000000000', result['volumeAttachment']['id']) self.assertEqual('/dev/myfake', result['volumeAttachment']['device']) def test_attach_volume_to_locked_server(self): def fake_attach_volume_to_locked_server(self, context, instance, volume_id, device=None, tag=None): raise exception.InstanceIsLocked(instance_uuid=instance['uuid']) self.stubs.Set(compute_api.API, 'attach_volume', fake_attach_volume_to_locked_server) body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} self.assertRaises(webob.exc.HTTPConflict, self.attachments.create, self.req, FAKE_UUID, body=body) def test_attach_volume_bad_id(self): self.stubs.Set(compute_api.API, 'attach_volume', fake_attach_volume) body = { 'volumeAttachment': { 'device': None, 'volumeId': 'TESTVOLUME', } } self.assertRaises(self.validation_error, self.attachments.create, self.req, FAKE_UUID, body=body) @mock.patch.object(compute_api.API, 'attach_volume', side_effect=exception.DevicePathInUse(path='/dev/sda')) def test_attach_volume_device_in_use(self, mock_attach): body = { 'volumeAttachment': { 'device': '/dev/sda', 'volumeId': FAKE_UUID_A, } } self.assertRaises(webob.exc.HTTPConflict, self.attachments.create, self.req, FAKE_UUID, body=body) def test_attach_volume_without_volumeId(self): self.stubs.Set(compute_api.API, 'attach_volume', fake_attach_volume) body = { 'volumeAttachment': { 'device': None } } self.assertRaises(self.validation_error, self.attachments.create, self.req, FAKE_UUID, body=body) def test_attach_volume_with_extra_arg(self): body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake', 'extra': 'extra_arg'}} self.assertRaises(self.validation_error, self.attachments.create, self.req, FAKE_UUID, body=body) @mock.patch.object(compute_api.API, 'attach_volume') def test_attach_volume_with_invalid_input(self, mock_attach): mock_attach.side_effect = exception.InvalidInput( reason='Invalid volume') body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} req = fakes.HTTPRequest.blank('/v2/servers/id/os-volume_attachments') req.method = 'POST' req.body = jsonutils.dump_as_bytes({}) req.headers['content-type'] = 'application/json' req.environ['nova.context'] = self.context self.assertRaises(exc.HTTPBadRequest, self.attachments.create, req, FAKE_UUID, body=body) def _test_swap(self, attachments, uuid=FAKE_UUID_A, fake_func=None, body=None): fake_func = fake_func or fake_swap_volume self.stubs.Set(compute_api.API, 'swap_volume', fake_func) body = body or {'volumeAttachment': {'volumeId': FAKE_UUID_B}} return attachments.update(self.req, FAKE_UUID, uuid, body=body) def test_swap_volume_for_locked_server(self): def fake_swap_volume_for_locked_server(self, context, instance, old_volume, new_volume): raise exception.InstanceIsLocked(instance_uuid=instance['uuid']) self.assertRaises(webob.exc.HTTPConflict, self._test_swap, self.attachments, fake_func=fake_swap_volume_for_locked_server) def test_swap_volume(self): result = self._test_swap(self.attachments) # NOTE: on v2.1, http status code is set as wsgi_code of API # method instead of status_int in a response object. if isinstance(self.attachments, volumes_v21.VolumeAttachmentController): status_int = self.attachments.update.wsgi_code else: status_int = result.status_int self.assertEqual(202, status_int) def test_swap_volume_with_nonexistent_uri(self): self.assertRaises(exc.HTTPNotFound, self._test_swap, self.attachments, uuid=FAKE_UUID_C) @mock.patch.object(cinder.API, 'get') def test_swap_volume_with_nonexistent_dest_in_body(self, mock_update): mock_update.side_effect = [ None, exception.VolumeNotFound(volume_id=FAKE_UUID_C)] body = {'volumeAttachment': {'volumeId': FAKE_UUID_C}} self.assertRaises(exc.HTTPBadRequest, self._test_swap, self.attachments, body=body) def test_swap_volume_without_volumeId(self): body = {'volumeAttachment': {'device': '/dev/fake'}} self.assertRaises(self.validation_error, self._test_swap, self.attachments, body=body) def test_swap_volume_with_extra_arg(self): body = {'volumeAttachment': {'volumeId': FAKE_UUID_A, 'device': '/dev/fake'}} self.assertRaises(self.validation_error, self._test_swap, self.attachments, body=body) def test_swap_volume_for_bdm_not_found(self): def fake_swap_volume_for_bdm_not_found(self, context, instance, old_volume, new_volume): raise exception.VolumeBDMNotFound(volume_id=FAKE_UUID_C) self.assertRaises(webob.exc.HTTPNotFound, self._test_swap, self.attachments, fake_func=fake_swap_volume_for_bdm_not_found) def _test_list_with_invalid_filter(self, url): prefix = '/servers/id/os-volume_attachments' req = fakes.HTTPRequest.blank(prefix + url) self.assertRaises(exception.ValidationError, self.attachments.index, req, FAKE_UUID) def test_list_with_invalid_non_int_limit(self): self._test_list_with_invalid_filter('?limit=-9') def test_list_with_invalid_string_limit(self): self._test_list_with_invalid_filter('?limit=abc') def test_list_duplicate_query_with_invalid_string_limit(self): self._test_list_with_invalid_filter( '?limit=1&limit=abc') def test_list_with_invalid_non_int_offset(self): self._test_list_with_invalid_filter('?offset=-9') def test_list_with_invalid_string_offset(self): self._test_list_with_invalid_filter('?offset=abc') def test_list_duplicate_query_with_invalid_string_offset(self): self._test_list_with_invalid_filter( '?offset=1&offset=abc') @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid') def test_list_duplicate_query_parameters_validation(self, mock_get): fake_bdms = objects.BlockDeviceMappingList() mock_get.return_value = fake_bdms params = { 'limit': 1, 'offset': 1 } for param, value in params.items(): req = fakes.HTTPRequest.blank( '/servers/id/os-volume_attachments' + '?%s=%s&%s=%s' % (param, value, param, value)) self.attachments.index(req, FAKE_UUID) @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid') def test_list_with_additional_filter(self, mock_get): fake_bdms = objects.BlockDeviceMappingList() mock_get.return_value = fake_bdms req = fakes.HTTPRequest.blank( '/servers/id/os-volume_attachments?limit=1&additional=something') self.attachments.index(req, FAKE_UUID)