diff --git a/nova/compute/api.py b/nova/compute/api.py index ded1352510..c99455bcee 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -599,9 +599,12 @@ class API(base.Base): def _check_and_transform_bdm(self, base_options, min_count, max_count, block_device_mapping, legacy_bdm): if legacy_bdm: + # NOTE (ndipanov): Assume root dev name is 'vda' if not supplied. + # It's needed for legacy conversion to work. + root_device_name = (base_options.get('root_device_name') or 'vda') block_device_mapping = block_device.from_legacy_mapping( block_device_mapping, base_options.get('image_ref', ''), - base_options.get('root_device_name')) + root_device_name) if min_count > 1 or max_count > 1: if any(map(lambda bdm: bdm['source_type'] == 'volume', diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 26fa77e19e..a19c58e1fd 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -40,6 +40,7 @@ import uuid from eventlet import greenthread from oslo.config import cfg +from nova import block_device from nova.cells import rpcapi as cells_rpcapi from nova.cloudpipe import pipelib from nova import compute @@ -1004,6 +1005,10 @@ class ComputeManager(manager.SchedulerDependentManager): bdms = self.conductor_api.block_device_mapping_get_all_by_instance( context, instance, legacy=False) + # Verify that all the BDMs have a device_name set and assign a default + # one to the ones missing it with the help of the driver. + self._default_block_device_names(context, instance, image_meta, bdms) + # b64 decode the files to inject: injected_files_orig = injected_files injected_files = self._decode_files(injected_files) @@ -1266,6 +1271,92 @@ class ComputeManager(manager.SchedulerDependentManager): requested_networks, macs, security_groups, is_vpn, dhcp_options) + def _default_root_device_name(self, instance, image_meta, root_bdm): + try: + return self.driver.default_root_device_name(instance, + image_meta, + root_bdm) + except NotImplementedError: + return compute_utils.get_next_device_name(instance, []) + + def _default_device_names_for_instance(self, instance, + root_device_name, + update_function, + *block_device_lists): + try: + self.driver.default_device_names_for_instance(instance, + root_device_name, + *block_device_lists) + except NotImplementedError: + compute_utils.default_device_names_for_instance( + instance, root_device_name, + update_function, *block_device_lists) + + def _default_block_device_names(self, context, instance, + image_meta, block_devices): + """Verify that all the devices have the device_name set. If not, + provide a default name. + + It also ensures that there is a root_device_name and is set to the + first block device in the boot sequence (boot_index=0). + """ + try: + root_bdm = (bdm for bdm in block_devices + if bdm['boot_index'] == 0).next() + except StopIteration: + return + + # Get the root_device_name from the root BDM or the instance + root_device_name = None + update_instance = False + update_root_bdm = False + + if root_bdm['device_name']: + root_device_name = root_bdm['device_name'] + instance['root_device_name'] = root_device_name + update_instance = True + elif instance['root_device_name']: + root_device_name = instance['root_device_name'] + root_bdm['device_name'] = root_device_name + update_root_bdm = True + else: + root_device_name = self._default_root_device_name(instance, + image_meta, + root_bdm) + + instance['root_device_name'] = root_device_name + root_bdm['device_name'] = root_device_name + update_instance = update_root_bdm = True + + if update_instance: + self._instance_update(context, instance['uuid'], + root_device_name=root_device_name) + if update_root_bdm: + self.conductor_api.block_device_mapping_update( + context, root_bdm['id'], {'device_name': root_device_name}) + + def _is_mapping(bdm): + return (bdm['source_type'] in ('image', 'volume', 'snapshot') and + driver_block_device.is_implemented(bdm)) + + ephemerals = filter(block_device.new_format_is_ephemeral, + block_devices) + swap = filter(block_device.new_format_is_swap, + block_devices) + block_device_mapping = filter(_is_mapping, block_devices) + + def _update_bdm(bdm_for_update): + self.conductor_api.block_device_mapping_update( + context, instance['uuid'], bdm_for_update['id'], + bdm_for_update['device_name']) + + self._default_device_names_for_instance(instance, + root_device_name, + _update_bdm, + ephemerals, + swap, + block_device_mapping) + def _prep_block_device(self, context, instance, bdms): """Set up the block device for an instance with error logging.""" try: diff --git a/nova/compute/utils.py b/nova/compute/utils.py index f63da1b661..0edee9d624 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -16,6 +16,7 @@ """Compute-related Utilities and helpers.""" +import itertools import re import string import traceback @@ -122,12 +123,78 @@ def pack_action_event_finish(context, instance_uuid, event_name, exc_val=None, def get_device_name_for_instance(context, instance, bdms, device): """Validates (or generates) a device name for instance. + This method is a wrapper for get_next_device_name that gets the list + of used devices and the root device from a block device mapping. + """ + mappings = block_device.instance_block_mapping(instance, bdms) + return get_next_device_name(instance, mappings.values(), + mappings['root'], device) + + +def default_device_names_for_instance(instance, root_device_name, + update_function, *block_device_lists): + """Generate missing device names for an instance.""" + + def _device_names(iterables): + return [bdm['device_name'] + for bdm in itertools.chain(*iterables) if bdm['device_name']] + + dev_list = _device_names(block_device_lists) + if root_device_name not in dev_list: + dev_list.append(root_device_name) + inst_type = flavors.extract_flavor(instance) + + is_libvirt = driver.compute_driver_matches('libvirt.LibvirtDriver') + libvirt_default_ephemerals = [] + + bdm_named_lists = zip(('ephemerals', 'swap', 'block_device_mapping'), + block_device_lists) + + for name, bdm_list in bdm_named_lists: + # Libvirt will create a default ephemeral if instance allows + # and was not overridden. + if (is_libvirt and name == 'ephemerals' and not bdm_list and + instance['ephemeral_gb'] > 0): + default_eph = get_next_device_name(instance, + [root_device_name], + root_device_name) + if default_eph not in dev_list: + dev_list.append(default_eph) + libvirt_default_ephemerals.append(default_eph) + + # Libvirt will create a default swap if it's in instance type + # and it was not supplied or overridden + if (is_libvirt and name == 'swap' and not bdm_list and + inst_type['swap'] > 0): + + ephemerals = (_device_names(bdm_named_lists[0][1]) or + libvirt_default_ephemerals) + default_swap = get_next_device_name(instance, + [root_device_name] + ephemerals, root_device_name) + if default_swap not in dev_list: + dev_list.append(default_swap) + + for bdm in bdm_list: + dev = bdm.get('device_name') + if not dev: + dev = get_next_device_name(instance, dev_list, + root_device_name) + bdm['device_name'] = dev + if update_function: + update_function(bdm) + dev_list.append(dev) + + +def get_next_device_name(instance, device_name_list, + root_device_name=None, device=None): + """Validates (or generates) a device name for instance. + If device is not set, it will generate a unique device appropriate - for the instance. It uses the block device mapping table to find - valid device names. If the device name is valid but applicable to - a different backend (for example /dev/vdc is specified but the - backend uses /dev/xvdc), the device name will be converted to the - appropriate format. + for the instance. It uses the root_device_name (if provided) and + the list of used devices to find valid device names. If the device + name is valid but applicable to a different backend (for example + /dev/vdc is specified but the backend uses /dev/xvdc), the device + name will be converted to the appropriate format. """ req_prefix = None req_letter = None @@ -138,23 +205,29 @@ def get_device_name_for_instance(context, instance, bdms, device): except (TypeError, AttributeError, ValueError): raise exception.InvalidDevicePath(path=device) - mappings = block_device.instance_block_mapping(instance, bdms) + if not root_device_name: + root_device_name = block_device.DEFAULT_ROOT_DEV_NAME try: - prefix = block_device.match_device(mappings['root'])[0] + prefix = block_device.match_device(root_device_name)[0] except (TypeError, AttributeError, ValueError): - raise exception.InvalidDevicePath(path=mappings['root']) + raise exception.InvalidDevicePath(path=root_device_name) # NOTE(vish): remove this when xenapi is setting default_root_device if driver.compute_driver_matches('xenapi.XenAPIDriver'): prefix = '/dev/xvd' + # NOTE(xqueralt): This can be removed when we have libvirt + # defaulting it's own device names. + if driver.compute_driver_matches('libvirt.LibvirtDriver'): + prefix = '/dev/vd' + if req_prefix != prefix: LOG.debug(_("Using %(prefix)s instead of %(req_prefix)s"), {'prefix': prefix, 'req_prefix': req_prefix}) used_letters = set() - for device_path in mappings.itervalues(): + for device_path in device_name_list: letter = block_device.strip_prefix(device_path) # NOTE(vish): delete numbers in case we have something like # /dev/sda1 @@ -177,8 +250,7 @@ def get_device_name_for_instance(context, instance, bdms, device): if req_letter in used_letters: raise exception.DevicePathInUse(path=device) - device_name = prefix + req_letter - return device_name + return prefix + req_letter def _get_unused_letter(used_letters): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 4e294e4cbc..70dfdc5517 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3451,32 +3451,35 @@ def block_device_mapping_update(context, bdm_id, values, legacy=True): def block_device_mapping_update_or_create(context, values, legacy=True): _scrub_empty_str_values(values, ['volume_size']) + values = _from_legacy_values(values, legacy, allow_updates=True) + session = get_session() with session.begin(): - result = _block_device_mapping_get_query(context, session=session).\ - filter_by(instance_uuid=values['instance_uuid']).\ - filter_by(device_name=values['device_name']).\ - first() - if not result: - values = _from_legacy_values(values, legacy) - bdm_ref = models.BlockDeviceMapping() - bdm_ref.update(values) - bdm_ref.save(session=session) - result = bdm_ref - else: - values = _from_legacy_values(values, legacy, allow_updates=True) - result.update(values) + result = None + # NOTE(xqueralt): Only update a BDM when device_name was provided. We + # allow empty device names so they will be set later by the manager. + if values['device_name']: + query = _block_device_mapping_get_query(context, session=session) + result = query.filter_by(instance_uuid=values['instance_uuid'], + device_name=values['device_name']).first() - # NOTE(xqueralt): prevent from having multiple swap devices for the - # same instance. So delete the existing ones. + if result: + result.update(values) + else: + # Either the device_name doesn't exist in the database yet, or no + # device_name was provided. Both cases mean creating a new BDM. + result = models.BlockDeviceMapping(**values) + result.save(session=session) + + # NOTE(xqueralt): Prevent from having multiple swap devices for the + # same instance. This will delete all the existing ones. if block_device.new_format_is_swap(values): - query = (_block_device_mapping_get_query(context, session=session). - filter_by(instance_uuid=values['instance_uuid']). - filter_by(source_type='blank'). - filter(models.BlockDeviceMapping.device_name != - values['device_name']). - filter_by(guest_format='swap')) + query = _block_device_mapping_get_query(context, session=session) + query = query.filter_by(instance_uuid=values['instance_uuid'], + source_type='blank', guest_format='swap') + query = query.filter(models.BlockDeviceMapping.id != result.id) query.soft_delete() + return result diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index dce93113ab..0c67475081 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -5512,6 +5512,81 @@ class ComputeTestCase(BaseTestCase): instance.refresh() self.assertEqual(vm_states.ACTIVE, instance['vm_state']) + def _get_instance_and_bdm_for_dev_defaults_tests(self): + instance = self._create_fake_instance( + params={'root_device_name': '/dev/vda'}) + block_device_mapping = [ + {'id': 3, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vda', + 'source_type': 'volume', + 'destination_type': 'volume', + 'image_id': 'fake-image-id-1', + 'boot_index': 0}] + + return instance, block_device_mapping + + def test_default_block_device_names_empty_instance_root_dev(self): + instance, bdms = self._get_instance_and_bdm_for_dev_defaults_tests() + instance['root_device_name'] = None + self.mox.StubOutWithMock(self.compute, '_instance_update') + self.mox.StubOutWithMock(self.compute, + '_default_device_names_for_instance') + self.compute._instance_update(self.context, instance['uuid'], + root_device_name='/dev/vda') + self.compute._default_device_names_for_instance(instance, + '/dev/vda', + mox.IgnoreArg(), + [], [], bdms) + self.mox.ReplayAll() + self.compute._default_block_device_names(self.context, + instance, + {}, bdms) + + def test_default_block_device_names_empty_root_device(self): + instance, bdms = self._get_instance_and_bdm_for_dev_defaults_tests() + bdms[0]['device_name'] = None + self.mox.StubOutWithMock(self.compute.conductor_api, + 'block_device_mapping_update') + self.mox.StubOutWithMock(self.compute, + '_default_device_names_for_instance') + self.compute.conductor_api.block_device_mapping_update( + self.context, bdms[0]['id'], {'device_name': '/dev/vda'}) + self.compute._default_device_names_for_instance(instance, + '/dev/vda', + mox.IgnoreArg(), + [], [], bdms) + self.mox.ReplayAll() + self.compute._default_block_device_names(self.context, + instance, + {}, bdms) + + def test_default_block_device_names_no_root_device(self): + instance, bdms = self._get_instance_and_bdm_for_dev_defaults_tests() + instance['root_device_name'] = None + bdms[0]['device_name'] = None + self.mox.StubOutWithMock(self.compute, '_instance_update') + self.mox.StubOutWithMock(self.compute.conductor_api, + 'block_device_mapping_update') + self.mox.StubOutWithMock(self.compute, + '_default_root_device_name') + self.mox.StubOutWithMock(self.compute, + '_default_device_names_for_instance') + + self.compute._default_root_device_name(instance, mox.IgnoreArg(), + bdms[0]).AndReturn('/dev/vda') + self.compute._instance_update(self.context, instance['uuid'], + root_device_name='/dev/vda') + self.compute.conductor_api.block_device_mapping_update( + self.context, bdms[0]['id'], {'device_name': '/dev/vda'}) + self.compute._default_device_names_for_instance(instance, + '/dev/vda', + mox.IgnoreArg(), + [], [], bdms) + self.mox.ReplayAll() + self.compute._default_block_device_names(self.context, + instance, + {}, bdms) + class ComputeAPITestCase(BaseTestCase): def setUp(self): diff --git a/nova/tests/compute/test_compute_utils.py b/nova/tests/compute/test_compute_utils.py index accc9dd7f8..b9f1afa623 100644 --- a/nova/tests/compute/test_compute_utils.py +++ b/nova/tests/compute/test_compute_utils.py @@ -18,6 +18,7 @@ """Tests For miscellaneous util methods used with compute.""" +import copy import string from oslo.config import cfg @@ -37,6 +38,8 @@ from nova.tests import fake_instance_actions from nova.tests import fake_network from nova.tests import fake_notifier import nova.tests.image.fake +from nova.tests import matchers +from nova.virt import driver CONF = cfg.CONF CONF.import_opt('compute_manager', 'nova.service') @@ -226,6 +229,178 @@ class ComputeValidateDeviceTestCase(test.TestCase): self.assertEqual(device, '/dev/xvdd') +class DefaultDeviceNamesForInstanceTestCase(test.TestCase): + + def setUp(self): + super(DefaultDeviceNamesForInstanceTestCase, self).setUp() + self.ephemerals = [ + {'id': 1, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vdb', + 'source_type': 'blank', + 'destination_type': 'local', + 'delete_on_termination': True, + 'guest_format': None, + 'boot_index': -1}] + + self.swap = [ + {'id': 2, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vdc', + 'source_type': 'blank', + 'destination_type': 'local', + 'delete_on_termination': True, + 'guest_format': 'swap', + 'boot_index': -1}] + + self.block_device_mapping = [ + {'id': 3, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vda', + 'source_type': 'volume', + 'destination_type': 'volume', + 'volume_id': 'fake-volume-id-1', + 'boot_index': 0}, + {'id': 4, 'instance_uuid': 'fake-instance', + 'device_name': '/dev/vdd', + 'source_type': 'snapshot', + 'destination_type': 'volume', + 'snapshot_id': 'fake-snapshot-id-1', + 'boot_index': -1}] + self.instance_type = {'swap': 4} + self.instance = {'uuid': 'fake_instance', 'ephemeral_gb': 2} + self.is_libvirt = False + self.root_device_name = '/dev/vda' + self.update_called = False + + def fake_extract_flavor(instance): + return self.instance_type + + def fake_driver_matches(driver_string): + if driver_string == 'libvirt.LibvirtDriver': + return self.is_libvirt + return False + + self.stubs.Set(flavors, 'extract_flavor', fake_extract_flavor) + self.stubs.Set(driver, 'compute_driver_matches', fake_driver_matches) + + def _test_default_device_names(self, update_function, *block_device_lists): + compute_utils.default_device_names_for_instance(self.instance, + self.root_device_name, + update_function, + *block_device_lists) + + def test_only_block_device_mapping(self): + # Test no-op + original_bdm = copy.deepcopy(self.block_device_mapping) + self._test_default_device_names(None, [], [], + self.block_device_mapping) + self.assertThat(original_bdm, + matchers.DictListMatches(self.block_device_mapping)) + + # Asser it defaults the missing one as expected + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, [], [], + self.block_device_mapping) + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdb') + + def test_with_ephemerals(self): + # Test ephemeral gets assigned + self.ephemerals[0]['device_name'] = None + self._test_default_device_names(None, self.ephemerals, [], + self.block_device_mapping) + self.assertEquals(self.ephemerals[0]['device_name'], '/dev/vdb') + + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, self.ephemerals, [], + self.block_device_mapping) + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdc') + + def test_with_swap(self): + # Test swap only + self.swap[0]['device_name'] = None + self._test_default_device_names(None, [], self.swap, []) + self.assertEquals(self.swap[0]['device_name'], '/dev/vdb') + + # Test swap and block_device_mapping + self.swap[0]['device_name'] = None + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, [], self.swap, + self.block_device_mapping) + self.assertEquals(self.swap[0]['device_name'], '/dev/vdb') + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdc') + + def test_all_together(self): + # Test swap missing + self.swap[0]['device_name'] = None + self._test_default_device_names(None, self.ephemerals, + self.swap, self.block_device_mapping) + self.assertEquals(self.swap[0]['device_name'], '/dev/vdc') + + # Test swap and eph missing + self.swap[0]['device_name'] = None + self.ephemerals[0]['device_name'] = None + self._test_default_device_names(None, self.ephemerals, + self.swap, self.block_device_mapping) + self.assertEquals(self.ephemerals[0]['device_name'], '/dev/vdb') + self.assertEquals(self.swap[0]['device_name'], '/dev/vdc') + + # Test all missing + self.swap[0]['device_name'] = None + self.ephemerals[0]['device_name'] = None + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, self.ephemerals, + self.swap, self.block_device_mapping) + self.assertEquals(self.ephemerals[0]['device_name'], '/dev/vdb') + self.assertEquals(self.swap[0]['device_name'], '/dev/vdc') + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdd') + + def test_libvirt_default_eph(self): + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, [], [], + self.block_device_mapping) + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdb') + + # Test that default eph will be taken into account on libvirt + self.is_libvirt = True + self.instance_type['swap'] = 0 + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, [], [], + self.block_device_mapping) + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdc') + + def test_libvirt_default_swap(self): + # Test that default swap will be taken into account on libvirt + self.is_libvirt = True + self.instance['ephemeral_gb'] = 0 + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, [], [], + self.block_device_mapping) + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdc') + + def test_libvirt_default_swap_ephemeral(self): + self.is_libvirt = True + self.block_device_mapping[1]['device_name'] = None + self._test_default_device_names(None, [], [], + self.block_device_mapping) + self.assertEquals(self.block_device_mapping[1]['device_name'], + '/dev/vdd') + + def test_update_fn_gets_called(self): + def _update(bdm): + self.update_called = True + + self.block_device_mapping[1]['device_name'] = None + compute_utils.default_device_names_for_instance( + self.instance, self.root_device_name, _update, [], [], + self.block_device_mapping) + self.assertTrue(self.update_called) + + class UsageInfoTestCase(test.TestCase): def setUp(self): diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py index 08206e7ac7..370e192703 100644 --- a/nova/tests/db/test_db_api.py +++ b/nova/tests/db/test_db_api.py @@ -4226,6 +4226,24 @@ class BlockDeviceMappingTestCase(test.TestCase): self.assertEqual(bdm_real['device_name'], 'fake_name') self.assertEqual(bdm_real['destination_type'], 'camelot') + # check create without device_name + bdm1 = dict(values) + bdm1['device_name'] = None + db.block_device_mapping_update_or_create(self.ctxt, bdm1, legacy=False) + bdm_real = db.block_device_mapping_get_all_by_instance(self.ctxt, uuid) + self.assertEqual(len(bdm_real), 2) + bdm_real = bdm_real[1] + self.assertEqual(bdm_real['device_name'], None) + + # check create multiple devices without device_name + bdm2 = dict(values) + bdm2['device_name'] = None + db.block_device_mapping_update_or_create(self.ctxt, bdm2, legacy=False) + bdm_real = db.block_device_mapping_get_all_by_instance(self.ctxt, uuid) + self.assertEqual(len(bdm_real), 3) + bdm_real = bdm_real[2] + self.assertEqual(bdm_real['device_name'], None) + def test_block_device_mapping_update_or_create_multiple_ephemeral(self): uuid = self.instance['uuid'] values = { diff --git a/nova/tests/virt/test_block_device.py b/nova/tests/virt/test_block_device.py index c5bbf07b20..5928c1f601 100644 --- a/nova/tests/virt/test_block_device.py +++ b/nova/tests/virt/test_block_device.py @@ -468,3 +468,11 @@ class TestDriverBlockDevice(test.TestCase): driver_block_device.get_swap(legacy_swap)) self.assertEquals(no_swap, driver_block_device.get_swap(no_swap)) self.assertEquals(None, driver_block_device.get_swap([])) + + def test_is_implemented(self): + for bdm in (self.image_bdm, self.volume_bdm, self.swap_bdm, + self.ephemeral_bdm, self.snapshot_bdm): + self.assertTrue(driver_block_device.is_implemented(bdm)) + local_image = self.image_bdm.copy() + local_image['destination_type'] = 'local' + self.assertFalse(driver_block_device.is_implemented(local_image)) diff --git a/nova/virt/block_device.py b/nova/virt/block_device.py index bf0bac6f0d..f58777a1bc 100644 --- a/nova/virt/block_device.py +++ b/nova/virt/block_device.py @@ -327,3 +327,18 @@ def get_swap(transformed_list): return transformed_list.pop() except IndexError: return None + + +_IMPLEMENTED_CLASSES = (DriverSwapBlockDevice, DriverEphemeralBlockDevice, + DriverVolumeBlockDevice, DriverSnapshotBlockDevice, + DriverImageBlockDevice) + + +def is_implemented(bdm): + for cls in _IMPLEMENTED_CLASSES: + try: + cls(bdm) + return True + except _NotTransformable: + pass + return False diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 3ba3b802d7..5c356065d3 100755 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -1068,6 +1068,15 @@ class ComputeDriver(object): """ raise NotImplementedError() + def default_root_device_name(self, instance, image_meta, root_bdm): + """Provide a default root device name for the driver.""" + raise NotImplementedError() + + def default_device_names_for_instance(self, instance, root_device_name, + *block_device_lists): + """Default the missing device names in the block device mapping.""" + raise NotImplementedError() + def load_compute_driver(virtapi, compute_driver=None): """Load a compute driver module.