From f9402ffe9fbb1ad00fa283a5bf114dc70d2de23f Mon Sep 17 00:00:00 2001 From: Vipin Balachandran Date: Thu, 5 Jun 2014 12:25:53 +0530 Subject: [PATCH] VMware: Optional create backing parameters The current create backing methods do not support specifying an adapter type for the backing VM. These methods always create a backing VM with a single disk and LSI logic adapter. This change adds optional parameters to create backing methods so that a backing VM can be created without a disk or with a specific adapter type. Partial-Bug: #1284284 Partial-Bug: #1287185 Partial-Bug: #1287176 Change-Id: Ifff64eb2be4af1c4218e810366e25dbecdc5847f --- cinder/tests/test_vmware_vmdk.py | 53 +++++-- cinder/tests/test_vmware_volumeops.py | 124 ++++++++++++++-- cinder/volume/drivers/vmware/error_util.py | 5 + cinder/volume/drivers/vmware/vmdk.py | 51 +++++-- cinder/volume/drivers/vmware/volumeops.py | 156 +++++++++++++++++---- 5 files changed, 326 insertions(+), 63 deletions(-) diff --git a/cinder/tests/test_vmware_vmdk.py b/cinder/tests/test_vmware_vmdk.py index 200ebbf1f..51c2f53b4 100644 --- a/cinder/tests/test_vmware_vmdk.py +++ b/cinder/tests/test_vmware_vmdk.py @@ -353,9 +353,9 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase): volume = FakeObject() volume['name'] = 'vol_name' backing = FakeMor('VirtualMachine', 'my_back') - mux = self._driver._create_backing(volume, host1.obj) + mux = self._driver._create_backing(volume, host1.obj, {}) mux.AndRaise(error_util.VimException('Maintenance mode')) - mux = self._driver._create_backing(volume, host2.obj) + mux = self._driver._create_backing(volume, host2.obj, {}) mux.AndReturn(backing) m.StubOutWithMock(self._volumeops, 'cancel_retrieval') self._volumeops.cancel_retrieval(retrieve_result) @@ -537,6 +537,7 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase): mox.IgnoreArg(), folder, resource_pool, host, mox.IgnoreArg(), + mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(backing) m.ReplayAll() @@ -1074,17 +1075,17 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase): timeout = self._config.vmware_image_transfer_timeout_secs image_service.show.return_value = fake_image_meta - volumeops._get_create_spec.return_value = fake_vm_create_spec + volumeops.get_create_spec.return_value = fake_vm_create_spec volumeops.get_backing.return_value = fake_backing - # If _select_ds_for_volume raises an exception, _get_create_spec + # If _select_ds_for_volume raises an exception, get_create_spec # will not be called. _select_ds_for_volume.side_effect = error_util.VimException('Error') self.assertRaises(exception.VolumeBackendAPIException, self._driver.copy_image_to_volume, fake_context, fake_volume, image_service, fake_image_id) - self.assertFalse(volumeops._get_create_spec.called) + self.assertFalse(volumeops.get_create_spec.called) # If the volume size is greater then than the image size, # _extend_vmdk_virtual_disk will be called. @@ -1095,10 +1096,10 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase): image_service, fake_image_id) image_service.show.assert_called_with(fake_context, fake_image_id) _select_ds_for_volume.assert_called_with(fake_volume) - volumeops._get_create_spec.assert_called_with(fake_volume['name'], - 0, - fake_disk_type, - fake_summary.name) + volumeops.get_create_spec.assert_called_with(fake_volume['name'], + 0, + fake_disk_type, + fake_summary.name) self.assertTrue(fetch_optimized_image.called) fetch_optimized_image.assert_called_with(fake_context, timeout, image_service, @@ -1906,3 +1907,37 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase): """Test extend_volume.""" self._test_extend_volume(volume_ops, _extend_virtual_disk, _select_ds_for_volume) + + @mock.patch.object(VMDK_DRIVER, '_get_folder_ds_summary') + @mock.patch.object(VMDK_DRIVER, 'volumeops') + def test_create_backing_with_params(self, vops, get_folder_ds_summary): + resource_pool = mock.sentinel.resource_pool + vops.get_dss_rp.return_value = (mock.Mock(), resource_pool) + folder = mock.sentinel.folder + summary = mock.sentinel.summary + get_folder_ds_summary.return_value = (folder, summary) + + volume = {'name': 'vol-1', 'volume_type_id': None, 'size': 1} + host = mock.Mock() + create_params = {vmdk.CREATE_PARAM_DISK_LESS: True} + self._driver._create_backing(volume, host, create_params) + + vops.create_backing_disk_less.assert_called_once_with('vol-1', + folder, + resource_pool, + host, + summary.name, + None) + + create_params = {vmdk.CREATE_PARAM_ADAPTER_TYPE: 'ide'} + self._driver._create_backing(volume, host, create_params) + + vops.create_backing.assert_called_once_with('vol-1', + units.Mi, + vmdk.THIN_VMDK_TYPE, + folder, + resource_pool, + host, + summary.name, + None, + 'ide') diff --git a/cinder/tests/test_vmware_volumeops.py b/cinder/tests/test_vmware_volumeops.py index d3dade714..e1952eed6 100644 --- a/cinder/tests/test_vmware_volumeops.py +++ b/cinder/tests/test_vmware_volumeops.py @@ -402,29 +402,60 @@ class VolumeOpsTestCase(test.TestCase): self.assertEqual(expected_invoke_api, self.session.invoke_api.mock_calls) - def test_get_create_spec(self): + def test_create_specs_for_ide_disk_add(self): factory = self.session.vim.client.factory factory.create.return_value = mock.Mock(spec=object) - name = mock.sentinel.name + size_kb = 0.5 disk_type = 'thin' - ds_name = mock.sentinel.ds_name - ret = self.vops._get_create_spec(name, size_kb, disk_type, ds_name) - self.assertEqual(name, ret.name) - self.assertEqual('[%s]' % ds_name, ret.files.vmPathName) - self.assertEqual(1, ret.deviceChange[1].device.capacityInKB) - self.assertEqual("vmx-08", ret.version) - expected = [mock.call.create('ns0:VirtualLsiLogicController'), + adapter_type = 'ide' + ret = self.vops._create_specs_for_disk_add(size_kb, disk_type, + adapter_type) + self.assertFalse(hasattr(ret[0].device, 'sharedBus')) + self.assertEqual(1, ret[1].device.capacityInKB) + expected = [mock.call.create('ns0:VirtualIDEController'), mock.call.create('ns0:VirtualDeviceConfigSpec'), mock.call.create('ns0:VirtualDisk'), mock.call.create('ns0:VirtualDiskFlatVer2BackingInfo'), + mock.call.create('ns0:VirtualDeviceConfigSpec')] + factory.create.assert_has_calls(expected, any_order=True) + + def test_create_specs_for_scsi_disk_add(self): + factory = self.session.vim.client.factory + factory.create.return_value = mock.Mock(spec=object) + + size_kb = 2 + disk_type = 'thin' + adapter_type = 'lsiLogicsas' + ret = self.vops._create_specs_for_disk_add(size_kb, disk_type, + adapter_type) + self.assertEqual('noSharing', ret[0].device.sharedBus) + self.assertEqual(size_kb, ret[1].device.capacityInKB) + expected = [mock.call.create('ns0:VirtualLsiLogicSASController'), mock.call.create('ns0:VirtualDeviceConfigSpec'), - mock.call.create('ns0:VirtualMachineFileInfo'), - mock.call.create('ns0:VirtualMachineConfigSpec')] + mock.call.create('ns0:VirtualDisk'), + mock.call.create('ns0:VirtualDiskFlatVer2BackingInfo'), + mock.call.create('ns0:VirtualDeviceConfigSpec')] + factory.create.assert_has_calls(expected, any_order=True) + + def test_get_create_spec_disk_less(self): + factory = self.session.vim.client.factory + factory.create.return_value = mock.Mock(spec=object) + name = mock.sentinel.name + ds_name = mock.sentinel.ds_name + profile_id = mock.sentinel.profile_id + ret = self.vops._get_create_spec_disk_less(name, ds_name, profile_id) + self.assertEqual(name, ret.name) + self.assertEqual('[%s]' % ds_name, ret.files.vmPathName) + self.assertEqual("vmx-08", ret.version) + self.assertEqual(profile_id, ret.vmProfile[0].profileId) + expected = [mock.call.create('ns0:VirtualMachineFileInfo'), + mock.call.create('ns0:VirtualMachineConfigSpec'), + mock.call.create('ns0:VirtualMachineDefinedProfileSpec')] factory.create.assert_has_calls(expected, any_order=True) @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' - '_get_create_spec') + 'get_create_spec') def test_create_backing(self, get_create_spec): create_spec = mock.sentinel.create_spec get_create_spec.return_value = create_spec @@ -436,15 +467,49 @@ class VolumeOpsTestCase(test.TestCase): name = 'backing_name' size_kb = mock.sentinel.size_kb disk_type = mock.sentinel.disk_type + adapter_type = mock.sentinel.adapter_type folder = mock.sentinel.folder resource_pool = mock.sentinel.resource_pool host = mock.sentinel.host ds_name = mock.sentinel.ds_name + profile_id = mock.sentinel.profile_id ret = self.vops.create_backing(name, size_kb, disk_type, folder, - resource_pool, host, ds_name) + resource_pool, host, ds_name, + profile_id, adapter_type) self.assertEqual(mock.sentinel.result, ret) get_create_spec.assert_called_once_with(name, size_kb, disk_type, - ds_name, None) + ds_name, profile_id, + adapter_type) + self.session.invoke_api.assert_called_once_with(self.session.vim, + 'CreateVM_Task', + folder, + config=create_spec, + pool=resource_pool, + host=host) + self.session.wait_for_task.assert_called_once_with(task) + + @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' + '_get_create_spec_disk_less') + def test_create_backing_disk_less(self, get_create_spec_disk_less): + create_spec = mock.sentinel.create_spec + get_create_spec_disk_less.return_value = create_spec + task = mock.sentinel.task + self.session.invoke_api.return_value = task + task_info = mock.Mock(spec=object) + task_info.result = mock.sentinel.result + self.session.wait_for_task.return_value = task_info + name = 'backing_name' + folder = mock.sentinel.folder + resource_pool = mock.sentinel.resource_pool + host = mock.sentinel.host + ds_name = mock.sentinel.ds_name + profile_id = mock.sentinel.profile_id + ret = self.vops.create_backing_disk_less(name, folder, resource_pool, + host, ds_name, profile_id) + + self.assertEqual(mock.sentinel.result, ret) + get_create_spec_disk_less.assert_called_once_with(name, ds_name, + profile_id) self.session.invoke_api.assert_called_once_with(self.session.vim, 'CreateVM_Task', folder, @@ -814,3 +879,34 @@ class VolumeOpsTestCase(test.TestCase): newCapacityKb=fake_size_in_kb, eagerZero=False) self.session.wait_for_task.assert_called_once_with(task) + + +class ControllerTypeTest(test.TestCase): + """Unit tests for ControllerType.""" + + def test_get_controller_type(self): + self.assertEqual(volumeops.ControllerType.LSI_LOGIC, + volumeops.ControllerType.get_controller_type( + 'lsiLogic')) + self.assertEqual(volumeops.ControllerType.BUS_LOGIC, + volumeops.ControllerType.get_controller_type( + 'busLogic')) + self.assertEqual(volumeops.ControllerType.LSI_LOGIC_SAS, + volumeops.ControllerType.get_controller_type( + 'lsiLogicsas')) + self.assertEqual(volumeops.ControllerType.IDE, + volumeops.ControllerType.get_controller_type( + 'ide')) + self.assertRaises(error_util.InvalidAdapterTypeException, + volumeops.ControllerType.get_controller_type, + 'invalid_type') + + def test_is_scsi_controller(self): + self.assertTrue(volumeops.ControllerType.is_scsi_controller( + volumeops.ControllerType.LSI_LOGIC)) + self.assertTrue(volumeops.ControllerType.is_scsi_controller( + volumeops.ControllerType.BUS_LOGIC)) + self.assertTrue(volumeops.ControllerType.is_scsi_controller( + volumeops.ControllerType.LSI_LOGIC_SAS)) + self.assertFalse(volumeops.ControllerType.is_scsi_controller( + volumeops.ControllerType.IDE)) diff --git a/cinder/volume/drivers/vmware/error_util.py b/cinder/volume/drivers/vmware/error_util.py index fc69d4326..ca93d8c0d 100644 --- a/cinder/volume/drivers/vmware/error_util.py +++ b/cinder/volume/drivers/vmware/error_util.py @@ -67,3 +67,8 @@ class VMwaredriverConfigurationException(VMwareDriverException): """Base class for all configuration exceptions. """ message = _("VMware VMDK driver configuration error.") + + +class InvalidAdapterTypeException(VMwareDriverException): + """Thrown when the disk adapter type is invalid.""" + message = _("Invalid disk adapter type: %(invalid_type)s.") diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index cc1cc3151..a7d908603 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -46,6 +46,9 @@ THIN_VMDK_TYPE = 'thin' THICK_VMDK_TYPE = 'thick' EAGER_ZEROED_THICK_VMDK_TYPE = 'eagerZeroedThick' +CREATE_PARAM_ADAPTER_TYPE = 'adapter_type' +CREATE_PARAM_DISK_LESS = 'disk_less' + vmdk_opts = [ cfg.StrOpt('vmware_host_ip', default=None, @@ -426,11 +429,13 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): EAGER_ZEROED_THICK_VMDK_TYPE), THIN_VMDK_TYPE) - def _create_backing(self, volume, host): + def _create_backing(self, volume, host, create_params={}): """Create volume backing under the given host. :param volume: Volume object :param host: Reference of the host + :param create_params: Dictionary specifying optional parameters for + backing VM creation :return: Reference to the created backing """ # Get datastores and resource pool of the host @@ -439,21 +444,41 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): (folder, summary) = self._get_folder_ds_summary(volume, resource_pool, datastores) - disk_type = VMwareEsxVmdkDriver._get_disk_type(volume) - size_kb = volume['size'] * units.Mi + + # check if a storage profile needs to be associated with the backing VM storage_profile = self._get_storage_profile(volume) profileId = None if self._storage_policy_enabled and storage_profile: profile = self.volumeops.retrieve_profile_id(storage_profile) if profile: profileId = profile.uniqueId + + # default is a backing with single disk + disk_less = create_params.get(CREATE_PARAM_DISK_LESS, False) + if disk_less: + # create a disk-less backing-- disk can be added later; for e.g., + # by copying an image + return self.volumeops.create_backing_disk_less(volume['name'], + folder, + resource_pool, + host, + summary.name, + profileId) + + # create a backing with single disk + disk_type = VMwareEsxVmdkDriver._get_disk_type(volume) + size_kb = volume['size'] * units.Mi + adapter_type = create_params.get(CREATE_PARAM_ADAPTER_TYPE, + 'lsiLogic') return self.volumeops.create_backing(volume['name'], size_kb, - disk_type, folder, + disk_type, + folder, resource_pool, host, summary.name, - profileId) + profileId, + adapter_type) def _relocate_backing(self, volume, backing, host): pass @@ -495,13 +520,15 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): LOG.error(msg) raise error_util.VimException(msg) - def _create_backing_in_inventory(self, volume): + def _create_backing_in_inventory(self, volume, create_params={}): """Creates backing under any suitable host. The method tries to pick datastore that can fit the volume under any host in the inventory. :param volume: Volume object + :param create_params: Dictionary specifying optional parameters for + backing VM creation :return: Reference to the created backing """ @@ -513,7 +540,9 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): backing = None for host in hosts: try: - backing = self._create_backing(volume, host.obj) + backing = self._create_backing(volume, + host.obj, + create_params) if backing: break except error_util.VimException as excep: @@ -879,10 +908,10 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): # The size of stream optimized glance image is often suspect, # so better let VC figure out the disk capacity during import. dummy_disk_size = 0 - vm_create_spec = self.volumeops._get_create_spec(volume['name'], - dummy_disk_size, - disk_type, - summary.name) + vm_create_spec = self.volumeops.get_create_spec(volume['name'], + dummy_disk_size, + disk_type, + summary.name) # convert vm_create_spec to vm_import_spec cf = self.session.vim.client.factory vm_import_spec = cf.create('ns0:VirtualMachineImportSpec') diff --git a/cinder/volume/drivers/vmware/volumeops.py b/cinder/volume/drivers/vmware/volumeops.py index 94d34b39b..8bbdd0b76 100644 --- a/cinder/volume/drivers/vmware/volumeops.py +++ b/cinder/volume/drivers/vmware/volumeops.py @@ -57,6 +57,43 @@ def split_datastore_path(datastore_path): return (datastore_name.strip(), folder_path.strip(), file_name.strip()) +class ControllerType: + """Encapsulate various controller types.""" + + LSI_LOGIC = 'VirtualLsiLogicController' + BUS_LOGIC = 'VirtualBusLogicController' + LSI_LOGIC_SAS = 'VirtualLsiLogicSASController' + IDE = 'VirtualIDEController' + + CONTROLLER_TYPE_DICT = {'lsiLogic': LSI_LOGIC, + 'busLogic': BUS_LOGIC, + 'lsiLogicsas': LSI_LOGIC_SAS, + 'ide': IDE} + + @staticmethod + def get_controller_type(adapter_type): + """Get the disk controller type based on the given adapter type. + + :param adapter_type: disk adapter type + :return: controller type corresponding to the given adapter type + :raises: InvalidAdapterTypeException + """ + if adapter_type in ControllerType.CONTROLLER_TYPE_DICT: + return ControllerType.CONTROLLER_TYPE_DICT[adapter_type] + raise error_util.InvalidAdapterTypeException(invalid_type=adapter_type) + + @staticmethod + def is_scsi_controller(controller_type): + """Check if the given controller is a SCSI controller. + + :param controller_type: controller type + :return: True if the controller is a SCSI controller + """ + return controller_type in [ControllerType.LSI_LOGIC, + ControllerType.BUS_LOGIC, + ControllerType.LSI_LOGIC_SAS] + + class VMwareVolumeOps(object): """Manages volume operations.""" @@ -348,22 +385,21 @@ class VMwareVolumeOps(object): "%(size)s GB."), {'name': name, 'size': requested_size_in_gb}) - def _get_create_spec(self, name, size_kb, disk_type, ds_name, - profileId=None): - """Return spec for creating volume backing. + def _create_specs_for_disk_add(self, size_kb, disk_type, adapter_type): + """Create controller and disk specs for adding a new disk. - :param name: Name of the backing - :param size_kb: Size in KB of the backing - :param disk_type: VMDK type for the disk - :param ds_name: Datastore name where the disk is to be provisioned - :param profileId: storage profile ID for the backing - :return: Spec for creation + :param size_kb: disk size in KB + :param disk_type: disk provisioning type + :param adapter_type: disk adapter type + :return: list containing controller and disk specs """ cf = self._session.vim.client.factory - controller_device = cf.create('ns0:VirtualLsiLogicController') + controller_type = ControllerType.get_controller_type(adapter_type) + controller_device = cf.create('ns0:%s' % controller_type) controller_device.key = -100 controller_device.busNumber = 0 - controller_device.sharedBus = 'noSharing' + if ControllerType.is_scsi_controller(controller_type): + controller_device.sharedBus = 'noSharing' controller_spec = cf.create('ns0:VirtualDeviceConfigSpec') controller_spec.operation = 'add' controller_spec.device = controller_device @@ -387,6 +423,17 @@ class VMwareVolumeOps(object): disk_spec.fileOperation = 'create' disk_spec.device = disk_device + return [controller_spec, disk_spec] + + def _get_create_spec_disk_less(self, name, ds_name, profileId=None): + """Return spec for creating disk-less backing. + + :param name: Name of the backing + :param ds_name: Datastore name where the disk is to be provisioned + :param profileId: storage profile ID for the backing + :return: Spec for creation + """ + cf = self._session.vim.client.factory vm_file_info = cf.create('ns0:VirtualMachineFileInfo') vm_file_info.vmPathName = '[%s]' % ds_name @@ -395,7 +442,6 @@ class VMwareVolumeOps(object): create_spec.guestId = 'otherGuest' create_spec.numCPUs = 1 create_spec.memoryMB = 128 - create_spec.deviceChange = [controller_spec, disk_spec] create_spec.files = vm_file_info # set the Hardware version to the lowest version supported by ESXi5.0 # and compatible with vCenter Server 5.0 @@ -408,11 +454,38 @@ class VMwareVolumeOps(object): vmProfile.profileId = profileId create_spec.vmProfile = [vmProfile] - LOG.debug("Spec for creating the backing: %s." % create_spec) return create_spec + def get_create_spec(self, name, size_kb, disk_type, ds_name, + profileId=None, adapter_type='lsiLogic'): + """Return spec for creating backing with a single disk. + + :param name: name of the backing + :param size_kb: disk size in KB + :param disk_type: disk provisioning type + :param ds_name: datastore name where the disk is to be provisioned + :param profileId: storage profile ID for the backing + :param adapter_type: disk adapter type + :return: spec for creation + """ + create_spec = self._get_create_spec_disk_less(name, ds_name, profileId) + create_spec.deviceChange = self._create_specs_for_disk_add( + size_kb, disk_type, adapter_type) + return create_spec + + def _create_backing_int(self, folder, resource_pool, host, create_spec): + """Helper for create backing methods.""" + LOG.debug("Creating volume backing with spec: %s.", create_spec) + task = self._session.invoke_api(self._session.vim, 'CreateVM_Task', + folder, config=create_spec, + pool=resource_pool, host=host) + task_info = self._session.wait_for_task(task) + backing = task_info.result + LOG.info(_("Successfully created volume backing: %s."), backing) + return backing + def create_backing(self, name, size_kb, disk_type, folder, resource_pool, - host, ds_name, profileId=None): + host, ds_name, profileId=None, adapter_type='lsiLogic'): """Create backing for the volume. Creates a VM with one VMDK based on the given inputs. @@ -425,26 +498,51 @@ class VMwareVolumeOps(object): :param host: Host reference :param ds_name: Datastore name where the disk is to be provisioned :param profileId: storage profile ID to be associated with backing + :param adapter_type: Disk adapter type :return: Reference to the created backing entity """ - LOG.debug("Creating volume backing name: %(name)s " - "disk_type: %(disk_type)s size_kb: %(size_kb)s at " - "folder: %(folder)s resourse pool: %(resource_pool)s " - "datastore name: %(ds_name)s profileId: %(profile)s." % + LOG.debug("Creating volume backing with name: %(name)s " + "disk_type: %(disk_type)s size_kb: %(size_kb)s " + "adapter_type: %(adapter_type)s profileId: %(profile)s at " + "folder: %(folder)s resource_pool: %(resource_pool)s " + "host: %(host)s datastore_name: %(ds_name)s.", {'name': name, 'disk_type': disk_type, 'size_kb': size_kb, 'folder': folder, 'resource_pool': resource_pool, - 'ds_name': ds_name, 'profile': profileId}) + 'ds_name': ds_name, 'profile': profileId, 'host': host, + 'adapter_type': adapter_type}) - create_spec = self._get_create_spec(name, size_kb, disk_type, ds_name, - profileId) - task = self._session.invoke_api(self._session.vim, 'CreateVM_Task', - folder, config=create_spec, - pool=resource_pool, host=host) - LOG.debug("Initiated creation of volume backing: %s." % name) - task_info = self._session.wait_for_task(task) - backing = task_info.result - LOG.info(_("Successfully created volume backing: %s.") % backing) - return backing + create_spec = self.get_create_spec(name, size_kb, disk_type, ds_name, + profileId, adapter_type) + return self._create_backing_int(folder, resource_pool, host, + create_spec) + + def create_backing_disk_less(self, name, folder, resource_pool, + host, ds_name, profileId=None): + """Create disk-less volume backing. + + This type of backing is useful for creating volume from image. The + downloaded image from the image service can be copied to a virtual + disk of desired provisioning type and added to the backing VM. + + :param name: Name of the backing + :param folder: Folder where the backing is created + :param resource_pool: Resource pool reference + :param host: Host reference + :param ds_name: Name of the datastore used for VM storage + :param profileId: Storage profile ID to be associated with backing + :return: Reference to the created backing entity + """ + LOG.debug("Creating disk-less volume backing with name: %(name)s " + "profileId: %(profile)s at folder: %(folder)s " + "resource pool: %(resource_pool)s host: %(host)s " + "datastore_name: %(ds_name)s.", + {'name': name, 'profile': profileId, 'folder': folder, + 'resource_pool': resource_pool, 'host': host, + 'ds_name': ds_name}) + + create_spec = self._get_create_spec_disk_less(name, ds_name, profileId) + return self._create_backing_int(folder, resource_pool, host, + create_spec) def get_datastore(self, backing): """Get datastore where the backing resides. -- 2.45.2