def _test_destroy_mocked(self, cell_type=None): values = { 'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'id': 1, 'instance_uuid': 'fake-instance', 'device_name': 'fake' } if cell_type: self.flags(enable=True, cell_type=cell_type, group='cells') else: self.flags(enable=False, group='cells') with contextlib.nested( mock.patch.object(db, 'block_device_mapping_destroy'), mock.patch.object(cells_rpcapi.CellsAPI, 'bdm_destroy_at_top')) as (bdm_del, cells_destroy): bdm = objects.BlockDeviceMapping(context=self.context, **values) bdm.destroy() bdm_del.assert_called_once_with(self.context, values['id']) if cell_type != 'compute': self.assertFalse(cells_destroy.called) else: cells_destroy.assert_called_once_with( self.context, values['instance_uuid'], device_name=values['device_name'], volume_id=values['volume_id'])
def test_validate_unshelve_az_cross_az_attach_false( self, mock_get_azs, mock_get_bdms, mock_get): """Tests a case where the new AZ to unshelve does not match the volume attached to the server and cross_az_attach=False so it's an error. """ # Ensure instance can be unshelved. instance = self._create_fake_instance_obj( params=dict(vm_state=vm_states.SHELVED_OFFLOADED)) new_az = "west_az" mock_get_azs.return_value = ["west_az", "east_az"] bdms = [objects.BlockDeviceMapping(destination_type='volume', volume_id=uuids.volume_id)] mock_get_bdms.return_value = bdms volume = {'id': uuids.volume_id, 'availability_zone': 'east_az'} mock_get.return_value = volume self.flags(cross_az_attach=False, group='cinder') self.assertRaises(exception.MismatchVolumeAZException, self.compute_api._validate_unshelve_az, self.context, instance, new_az) mock_get_azs.assert_called_once_with( self.context, self.compute_api.host_api, get_only_available=True) mock_get_bdms.assert_called_once_with(self.context, instance.uuid) mock_get.assert_called_once_with(self.context, uuids.volume_id)
def test_attach_volume_to_server(self): self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get) self.stub_out('nova.volume.cinder.API.check_attach', lambda *a, **k: None) self.stub_out('nova.volume.cinder.API.reserve_volume', lambda *a, **k: None) device_name = '/dev/vdd' bdm = objects.BlockDeviceMapping() bdm['device_name'] = device_name self.stub_out( 'nova.compute.manager.ComputeManager.reserve_block_device_name', lambda *a, **k: bdm) self.stub_out( 'nova.compute.manager.ComputeManager.attach_volume', lambda *a, **k: None) self.stub_out( 'nova.objects.BlockDeviceMapping.get_by_volume_and_instance', classmethod(lambda *a, **k: None)) volume = fakes.stub_volume_get(None, context.get_admin_context(), 'a26887c6-c47b-4654-abb5-dfadf7d3f803') subs = { 'volume_id': volume['id'], 'device': device_name } server_id = self._post_server() response = self._do_post('servers/%s/os-volume_attachments' % server_id, 'attach-volume-to-server-req', subs) self._verify_response('attach-volume-to-server-resp', subs, response, 200)
def test_create_instances_here(self): # Just grab the first instance type inst_type = objects.Flavor.get_by_id(self.ctxt, 1) image = {'properties': {}} instance_uuids = self.instance_uuids instance_props = { 'id': 'removed', 'security_groups': 'removed', 'info_cache': 'removed', 'name': 'instance-00000001', 'hostname': 'meow', 'display_name': 'moo', 'image_ref': uuidsentinel.fake_image_ref, 'user_id': self.ctxt.user_id, # Test these as lists 'metadata': { 'moo': 'cow' }, 'system_metadata': { 'meow': 'cat' }, 'flavor': inst_type, 'project_id': self.ctxt.project_id } call_info = {'uuids': []} block_device_mapping = objects.BlockDeviceMappingList(objects=[ objects.BlockDeviceMapping( context=self.ctxt, **fake_block_device.FakeDbBlockDeviceDict( block_device.create_image_bdm(uuidsentinel.fake_image_ref), anon=True)) ]) def _fake_instance_update_at_top(self, _ctxt, instance): call_info['uuids'].append(instance['uuid']) self.stub_out( 'nova.cells.messaging.MessageRunner.' 'instance_update_at_top', _fake_instance_update_at_top) self.scheduler._create_instances_here(self.ctxt, instance_uuids, instance_props, inst_type, image, ['default'], block_device_mapping) self.assertEqual(instance_uuids, call_info['uuids']) for count, instance_uuid in enumerate(instance_uuids): bdms = db.block_device_mapping_get_all_by_instance( self.ctxt, instance_uuid) self.assertIsNotNone(bdms) instance = db.instance_get_by_uuid(self.ctxt, instance_uuid) meta = utils.instance_meta(instance) self.assertEqual('cow', meta['moo']) sys_meta = utils.instance_sys_meta(instance) self.assertEqual('cat', sys_meta['meow']) self.assertEqual('meow', instance['hostname']) self.assertEqual('moo-%d' % (count + 1), instance['display_name']) self.assertEqual(uuidsentinel.fake_image_ref, instance['image_ref'])
def test_snapshot_from_object(self): for bdm in self.new_mapping[:-1]: obj = objects.BlockDeviceMapping() obj = objects.BlockDeviceMapping._from_db_object( None, obj, fake_block_device.FakeDbBlockDeviceDict( bdm)) self._test_snapshot_from_bdm(obj)
def test_obj_load_attr_orphaned(self): """Tests that lazy-loading the instance field on an orphaned BDM results in an error. """ bdm = objects.BlockDeviceMapping(context=None, **self.fake_bdm()) self.assertRaises(exception.OrphanedObjectError, bdm.obj_load_attr, 'instance')
def _test_save(self, cell_type=None): if cell_type: self.flags(enable=True, cell_type=cell_type, group='cells') else: self.flags(enable=False, group='cells') fake_bdm = self.fake_bdm() with contextlib.nested( mock.patch.object(db, 'block_device_mapping_update', return_value=fake_bdm), mock.patch.object( cells_rpcapi.CellsAPI, 'bdm_update_or_create_at_top')) as (bdm_update_mock, cells_update_mock): bdm_object = objects.BlockDeviceMapping(context=self.context) bdm_object.id = 123 bdm_object.volume_id = 'fake_volume_id' bdm_object.save() bdm_update_mock.assert_called_once_with( self.context, 123, {'volume_id': 'fake_volume_id'}, legacy=False) if cell_type != 'compute': self.assertFalse(cells_update_mock.called) else: self.assertEqual(1, cells_update_mock.call_count) self.assertTrue(len(cells_update_mock.call_args[0]) > 1) self.assertIsInstance(cells_update_mock.call_args[0][1], block_device_obj.BlockDeviceMapping) self.assertEqual(cells_update_mock.call_args[1], {})
def test_attach_volume_to_server(self): self.stub_out('nova.objects.Service.get_minimum_version', lambda *a, **k: COMPUTE_VERSION_OLD_ATTACH_FLOW) self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get) self.stub_out('nova.volume.cinder.API.reserve_volume', lambda *a, **k: None) device_name = '/dev/vdd' bdm = objects.BlockDeviceMapping() bdm['device_name'] = device_name self.stub_out( 'nova.compute.manager.ComputeManager.reserve_block_device_name', lambda *a, **k: bdm) self.stub_out( 'nova.compute.manager.ComputeManager.attach_volume', lambda *a, **k: None) volume = fakes.stub_volume_get(None, context.get_admin_context(), self.OLD_VOLUME_ID) subs = { 'volume_id': volume['id'], 'device': device_name } server_id = self._post_server() response = self._do_post('servers/%s/os-volume_attachments' % server_id, 'attach-volume-to-server-req', subs) self._verify_response('attach-volume-to-server-resp', subs, response, 200)
def _test_create_mocked(self, update_or_create=False): values = { 'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': uuids.instance, 'attachment_id': None } fake_bdm = fake_block_device.FakeDbBlockDeviceDict(values) with test.nested( mock.patch.object(db, 'block_device_mapping_create', return_value=fake_bdm), mock.patch.object(db, 'block_device_mapping_update_or_create', return_value=fake_bdm), ) as (bdm_create_mock, bdm_update_or_create_mock): bdm = objects.BlockDeviceMapping(context=self.context, **values) if update_or_create: method = bdm.update_or_create else: method = bdm.create method() if update_or_create: bdm_update_or_create_mock.assert_called_once_with(self.context, values, legacy=False) else: bdm_create_mock.assert_called_once_with(self.context, values, legacy=False)
def test_attach_volume_to_server(self): self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get) self.stub_out('nova.volume.cinder.API.attachment_create', lambda *a, **k: {'id': uuids.volume}) device_name = '/dev/vdd' bdm = objects.BlockDeviceMapping() bdm['device_name'] = device_name bdm['delete_on_termination'] = True self.stub_out( 'nova.compute.manager.ComputeManager.reserve_block_device_name', lambda *a, **k: bdm) # 2.79+ will save the delete_on_termination value on the BDM after # reserve_block_device_name "creates" the BDM. self.stub_out('nova.objects.BlockDeviceMapping.save', lambda *a, **k: None) self.stub_out( 'nova.compute.manager.ComputeManager.attach_volume', lambda *a, **k: None) volume = fakes.stub_volume_get(None, context.get_admin_context(), 'a26887c6-c47b-4654-abb5-dfadf7d3f803') subs = { 'volume_id': volume['id'], 'device': device_name } server_id = self._post_server() subs = self._get_vol_attachment_subs(subs) response = self._do_post('servers/%s/os-volume_attachments' % server_id, 'attach-volume-to-server-req', subs) self._verify_response('attach-volume-to-server-resp', subs, response, 200)
def test_obj_load_attr_not_instance(self): """Tests that lazy-loading something other than the instance field results in an error. """ bdm = objects.BlockDeviceMapping(self.context, **self.fake_bdm()) self.assertRaises(exception.ObjectActionError, bdm.obj_load_attr, 'invalid')
def test_attach_volume(self): bdm = objects.BlockDeviceMapping() device_name = '/dev/vdd' bdm['device_name'] = device_name self.stubs.Set(cinder.API, 'get', fakes.stub_volume_get) self.stubs.Set(cinder.API, 'check_attach', lambda *a, **k: None) self.stubs.Set(cinder.API, 'reserve_volume', lambda *a, **k: None) self.stubs.Set(compute_manager.ComputeManager, "reserve_block_device_name", lambda *a, **k: bdm) self.stubs.Set(compute_manager.ComputeManager, 'attach_volume', lambda *a, **k: None) volume = fakes.stub_volume_get(None, context.get_admin_context(), 'a26887c6-c47b-4654-abb5-dfadf7d3f803') subs = { 'volume_id': volume['id'], 'device': device_name, 'disk_bus': 'ide', 'device_type': 'cdrom' } server_id = self._post_server() response = self._do_post('servers/%s/action' % server_id, 'attach-volume-req', subs) self.assertEqual(response.status_code, 202) self.assertEqual(response.content, '')
def test_attach_volume_to_server_new_flow(self): self.stub_out('nova.volume.cinder.is_microversion_supported', lambda *a, **k: None) self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get) self.stub_out('nova.volume.cinder.API.attachment_create', lambda *a, **k: {'id': uuids.volume}) self.stub_out('nova.objects.BlockDeviceMapping.save', lambda *a, **k: None) device_name = '/dev/vdd' bdm = objects.BlockDeviceMapping() bdm['device_name'] = device_name self.stub_out( 'nova.compute.manager.ComputeManager.reserve_block_device_name', lambda *a, **k: bdm) self.stub_out( 'nova.compute.manager.ComputeManager.attach_volume', lambda *a, **k: None) volume = fakes.stub_volume_get(None, context.get_admin_context(), 'a26887c6-c47b-4654-abb5-dfadf7d3f803') subs = { 'volume_id': volume['id'], 'device': device_name } server_id = self._post_server() response = self._do_post('servers/%s/os-volume_attachments' % server_id, 'attach-volume-to-server-req', subs) self._verify_response('attach-volume-to-server-resp', subs, response, 200)
def _test_create_mocked(self, cell_type=None, update_or_create=False, device_name=None): if cell_type: self.flags(enable=True, cell_type=cell_type, group='cells') else: self.flags(enable=False, group='cells') values = { 'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': uuids.instance, 'attachment_id': None } if device_name: values['device_name'] = device_name fake_bdm = fake_block_device.FakeDbBlockDeviceDict(values) with test.nested( mock.patch.object(db, 'block_device_mapping_create', return_value=fake_bdm), mock.patch.object(db, 'block_device_mapping_update_or_create', return_value=fake_bdm), mock.patch.object(cells_rpcapi.CellsAPI, 'bdm_update_or_create_at_top')) as ( bdm_create_mock, bdm_update_or_create_mock, cells_update_mock): bdm = objects.BlockDeviceMapping(context=self.context, **values) if update_or_create: method = bdm.update_or_create else: method = bdm.create if cell_type == 'api': self.assertRaises(exception.ObjectActionError, method) else: method() if update_or_create: bdm_update_or_create_mock.assert_called_once_with( self.context, values, legacy=False) else: bdm_create_mock.assert_called_once_with(self.context, values, legacy=False) if cell_type == 'compute' and 'device_name' in values: self.assertEqual(1, cells_update_mock.call_count) self.assertGreater(len(cells_update_mock.call_args[0]), 1) self.assertEqual(self.context, cells_update_mock.call_args[0][0]) self.assertIsInstance(cells_update_mock.call_args[0][1], block_device_obj.BlockDeviceMapping) self.assertEqual({'create': update_or_create or None}, cells_update_mock.call_args[1]) else: self.assertFalse(cells_update_mock.called)
def test_create_fails_instance(self): values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': uuids.instance, 'instance': objects.Instance()} bdm = objects.BlockDeviceMapping(context=self.context, **values) self.assertRaises(exception.ObjectActionError, bdm.create)
def test_destroy(self): values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'id': 1, 'instance_uuid': uuids.instance, 'device_name': 'fake'} with mock.patch.object(db, 'block_device_mapping_destroy') as bdm_del: bdm = objects.BlockDeviceMapping(context=self.context, **values) bdm.destroy() bdm_del.assert_called_once_with(self.context, values['id'])
def test_obj_make_compatible_pre_1_18(self): values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': uuids.instance, 'attachment_id': uuids.attachment_id} bdm = objects.BlockDeviceMapping(context=self.context, **values) primitive = bdm.obj_to_primitive(target_version='1.17') self.assertNotIn('attachment_id', primitive)
def fake_bdm_get_by_volume_and_instance( self, ctxt, volume_id, instance_uuid, expected_attrs=None): return objects.BlockDeviceMapping._from_db_object( ctxt, objects.BlockDeviceMapping(), fake_block_device.FakeDbBlockDeviceDict( {'id': 1, 'volume_id': self.OLD_VOLUME_ID, 'instance_uuid': instance_uuid, 'source_type': 'volume', 'destination_type': 'volume', 'device_name': '/dev/sdd'}) )
def test_attach_detach_different_power_states(self, _): instance_ref, network_info = self._get_running_instance() connection_info = { "driver_volume_type": "fake", "serial": "fake_serial", "data": {} } self.connection.power_off(instance_ref) self.connection.attach_volume(None, connection_info, instance_ref, '/dev/sda') bdm = { 'root_device_name': None, 'swap': None, 'ephemerals': [], 'block_device_mapping': driver_block_device.convert_volumes([ objects.BlockDeviceMapping( self.ctxt, **fake_block_device.FakeDbBlockDeviceDict({ 'id': 1, 'instance_uuid': instance_ref['uuid'], 'device_name': '/dev/sda', 'source_type': 'volume', 'destination_type': 'volume', 'delete_on_termination': False, 'snapshot_id': None, 'volume_id': 'abcdedf', 'volume_size': None, 'no_device': None })), ]) } bdm['block_device_mapping'][0]['connection_info'] = ({ 'driver_volume_type': 'fake', 'data': {} }) with mock.patch.object(driver_block_device.DriverVolumeBlockDevice, 'save'): self.connection.power_on(self.ctxt, instance_ref, network_info, bdm) self.connection.detach_volume(mock.sentinel.context, connection_info, instance_ref, '/dev/sda')
def test_create_fails(self): values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': 'fake-instance'} bdm = objects.BlockDeviceMapping(**values) bdm.create(self.context) self.assertRaises(exception.ObjectActionError, bdm.create, self.context)
def test_obj_make_compatible_pre_1_19(self): values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': uuids.instance, 'uuid': uuids.bdm} bdm = objects.BlockDeviceMapping(context=self.context, **values) data = lambda x: x['nova_object.data'] primitive = data(bdm.obj_to_primitive(target_version='1.18')) self.assertNotIn('uuid', primitive) self.assertIn('volume_id', primitive)
def test_create_instances_here(self): # Just grab the first instance type inst_type = flavors.get_flavor(1) image = {'properties': {}} instance_uuids = self.instance_uuids instance_props = { 'id': 'removed', 'security_groups': 'removed', 'info_cache': 'removed', 'name': 'instance-00000001', 'hostname': 'meow', 'display_name': 'moo', 'image_ref': 'fake_image_ref', 'user_id': self.ctxt.user_id, # Test these as lists 'metadata': { 'moo': 'cow' }, 'system_metadata': { 'meow': 'cat' }, 'flavor': inst_type, 'project_id': self.ctxt.project_id } call_info = {'uuids': []} block_device_mapping = [ objects.BlockDeviceMapping( context=self.ctxt, **fake_block_device.FakeDbBlockDeviceDict( block_device.create_image_bdm('fake_image_ref'), anon=True)) ] def _fake_instance_update_at_top(_ctxt, instance): call_info['uuids'].append(instance['uuid']) self.stubs.Set(self.msg_runner, 'instance_update_at_top', _fake_instance_update_at_top) self.scheduler._create_instances_here(self.ctxt, instance_uuids, instance_props, inst_type, image, ['default'], block_device_mapping) self.assertEqual(instance_uuids, call_info['uuids']) for count, instance_uuid in enumerate(instance_uuids): instance = db.instance_get_by_uuid(self.ctxt, instance_uuid) meta = utils.instance_meta(instance) self.assertEqual('cow', meta['moo']) sys_meta = utils.instance_sys_meta(instance) self.assertEqual('cat', sys_meta['meow']) self.assertEqual('meow', instance['hostname']) self.assertEqual('moo-%d' % (count + 1), instance['display_name']) self.assertEqual('fake_image_ref', instance['image_ref'])
def fake_bdm_get_by_volume_and_instance( self, ctxt, volume_id, instance_uuid, expected_attrs=None): tag = self._get_tags_per_volume().get(self.OLD_VOLUME_ID) return objects.BlockDeviceMapping._from_db_object( ctxt, objects.BlockDeviceMapping(), fake_block_device.FakeDbBlockDeviceDict( {'id': 1, 'volume_id': self.OLD_VOLUME_ID, 'instance_uuid': instance_uuid, 'source_type': 'volume', 'destination_type': 'volume', 'device_name': '/dev/sdd', 'tag': tag, 'delete_on_termination': False}) )
def test_create(self): values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', 'destination_type': 'volume', 'instance_uuid': 'fake-instance'} bdm = objects.BlockDeviceMapping(**values) with mock.patch.object(cells_rpcapi.CellsAPI, 'bdm_update_or_create_at_top'): bdm.create(self.context) for k, v in values.iteritems(): self.assertEqual(v, getattr(bdm, k))
def fake_bdm_object(context, bdm_dict): """Creates a BlockDeviceMapping object from the given bdm_dict :param context: nova request context :param bdm_dict: dict of block device mapping info :returns: nova.objects.block_device.BlockDeviceMapping """ # FakeDbBlockDeviceDict mutates the bdm_dict so make a copy of it. return objects.BlockDeviceMapping._from_db_object( context, objects.BlockDeviceMapping(), FakeDbBlockDeviceDict(bdm_dict.copy()))
def test_save(self): fake_bdm = self.fake_bdm() with mock.patch.object(db, 'block_device_mapping_update', return_value=fake_bdm) as bdm_update_mock: bdm_object = objects.BlockDeviceMapping(context=self.context) bdm_object.id = 123 bdm_object.volume_id = 'fake_volume_id' bdm_object.save() bdm_update_mock.assert_called_once_with( self.context, 123, {'volume_id': 'fake_volume_id'}, legacy=False)
def test_payload_contains_volume_bdms_if_requested(self, mock_get_bdms): self.flags(bdms_in_notifications='True', group='notifications') context = mock.Mock() instance = instance_obj.Instance(uuid=uuids.instance_uuid) image_bdm = objects.BlockDeviceMapping( **{ 'context': context, 'source_type': 'image', 'destination_type': 'local', 'image_id': uuids.image_id, 'volume_id': None, 'device_name': '/dev/vda', 'instance_uuid': instance.uuid }) volume_bdm = objects.BlockDeviceMapping( **{ 'context': context, 'source_type': 'volume', 'destination_type': 'volume', 'volume_id': uuids.volume_id, 'device_name': '/dev/vdb', 'instance_uuid': instance.uuid, 'boot_index': 0, 'delete_on_termination': True, 'tag': 'my-tag' }) mock_get_bdms.return_value = [image_bdm, volume_bdm] bdms = instance_notification.BlockDevicePayload.from_instance(instance) self.assertEqual(1, len(bdms)) bdm = bdms[0] self.assertIsInstance(bdm, instance_notification.BlockDevicePayload) self.assertEqual('/dev/vdb', bdm.device_name) self.assertEqual(0, bdm.boot_index) self.assertTrue(bdm.delete_on_termination) self.assertEqual('my-tag', bdm.tag) self.assertEqual(uuids.volume_id, bdm.volume_id)
def block_device_mapping_update_or_create(self, context, values, create): if create is None: bdm = self.db.block_device_mapping_update_or_create( context, values) elif create is True: bdm = self.db.block_device_mapping_create(context, values) else: bdm = self.db.block_device_mapping_update(context, values['id'], values) bdm_obj = objects.BlockDeviceMapping._from_db_object( context, objects.BlockDeviceMapping(), bdm) self.cells_rpcapi.bdm_update_or_create_at_top(context, bdm_obj, create=create)
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 __init__(self, bdm): # TODO(ndipanov): Remove this check when we have all the rpc methods # use objects for block devices. if isinstance(bdm, obj_base.NovaObject): self.__dict__['_bdm_obj'] = bdm else: self.__dict__['_bdm_obj'] = objects.BlockDeviceMapping() self._bdm_obj.update(block_device.BlockDeviceDict(bdm)) self._bdm_obj.obj_reset_changes() if self._bdm_obj.no_device: raise _NotTransformable() self.update({field: None for field in self._fields}) self._transform()