diff options
Diffstat (limited to 'cinder/volume')
-rw-r--r-- | cinder/volume/drivers/vmware/datastore.py | 6 | ||||
-rw-r--r-- | cinder/volume/drivers/vmware/vmdk.py | 102 | ||||
-rw-r--r-- | cinder/volume/drivers/vmware/volumeops.py | 43 |
3 files changed, 121 insertions, 30 deletions
diff --git a/cinder/volume/drivers/vmware/datastore.py b/cinder/volume/drivers/vmware/datastore.py index 0f3a914fc..2e4e9b0ca 100644 --- a/cinder/volume/drivers/vmware/datastore.py +++ b/cinder/volume/drivers/vmware/datastore.py @@ -36,6 +36,12 @@ class DatastoreType(object): VMFS = "vmfs" VSAN = "vsan" + _ALL_TYPES = {NFS, VMFS, VSAN} + + @staticmethod + def get_all_types(): + return DatastoreType._ALL_TYPES + class DatastoreSelector(object): """Class for selecting datastores which satisfy input requirements.""" diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index 60225d04b..8a4da232f 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -58,6 +58,8 @@ CREATE_PARAM_ADAPTER_TYPE = 'adapter_type' CREATE_PARAM_DISK_LESS = 'disk_less' CREATE_PARAM_BACKING_NAME = 'name' +TMP_IMAGES_DATASTORE_FOLDER_PATH = "cinder_temp/" + vmdk_opts = [ cfg.StrOpt('vmware_host_ip', default=None, @@ -564,7 +566,22 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): def _relocate_backing(self, volume, backing, host): pass - def _select_ds_for_volume(self, volume, host=None): + def _select_datastore(self, req, host=None): + """Selects datastore satisfying the given requirements. + + :return: (host, resource_pool, summary) + """ + + hosts = [host] if host else None + best_candidate = self.ds_sel.select_datastore(req, hosts=hosts) + if not best_candidate: + LOG.error(_LE("There is no valid datastore satisfying " + "requirements: %s."), req) + raise vmdk_exceptions.NoValidDatastoreException() + + return best_candidate + + def _select_ds_for_volume(self, volume, host=None, create_params=None): """Select datastore that can accommodate the given volume's backing. Returns the selected datastore summary along with a compute host and @@ -577,16 +594,7 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): req[hub.DatastoreSelector.PROFILE_NAME] = self._get_storage_profile( volume) - # Select datastore satisfying the requirements. - hosts = [host] if host else None - best_candidate = self.ds_sel.select_datastore(req, hosts=hosts) - if not best_candidate: - LOG.error(_LE("There is no valid datastore to create backing for " - "volume: %s."), - volume['name']) - raise vmdk_exceptions.NoValidDatastoreException() - - (host_ref, resource_pool, summary) = best_candidate + (host_ref, resource_pool, summary) = self._select_datastore(req, host) dc = self.volumeops.get_dc(resource_pool) folder = self._get_volume_group_folder(dc) @@ -906,13 +914,14 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): descriptor_ds_file_path, exc_info=True) - def _copy_temp_virtual_disk(self, dc_ref, src_path, dest_path): + def _copy_temp_virtual_disk(self, src_dc_ref, src_path, dest_dc_ref, + dest_path): """Clones a temporary virtual disk and deletes it finally.""" try: self.volumeops.copy_vmdk_file( - dc_ref, src_path.get_descriptor_ds_file_path(), - dest_path.get_descriptor_ds_file_path()) + src_dc_ref, src_path.get_descriptor_ds_file_path(), + dest_path.get_descriptor_ds_file_path(), dest_dc_ref) except exceptions.VimException: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error occurred while copying %(src)s to " @@ -922,7 +931,29 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): finally: # Delete temporary disk. self._delete_temp_disk(src_path.get_descriptor_ds_file_path(), - dc_ref) + src_dc_ref) + + def _get_temp_image_folder(self, image_size_in_bytes): + """Get datastore folder for downloading temporary images.""" + # Form requirements for datastore selection. + req = {} + req[hub.DatastoreSelector.SIZE_BYTES] = image_size_in_bytes + # vSAN datastores don't support virtual disk with + # flat extent; skip such datastores. + req[hub.DatastoreSelector.HARD_AFFINITY_DS_TYPE] = ( + hub.DatastoreType.get_all_types() - {hub.DatastoreType.VSAN}) + + # Select datastore satisfying the requirements. + (host_ref, _resource_pool, summary) = self._select_datastore(req) + + ds_name = summary.name + dc_ref = self.volumeops.get_dc(host_ref) + + # Create temporary datastore folder. + folder_path = TMP_IMAGES_DATASTORE_FOLDER_PATH + self.volumeops.create_datastore_folder(ds_name, folder_path, dc_ref) + + return (dc_ref, ds_name, folder_path) def _create_virtual_disk_from_sparse_image( self, context, image_service, image_id, image_size_in_bytes, @@ -947,19 +978,42 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): dest_path = volumeops.FlatExtentVirtualDiskPath(ds_name, folder_path, disk_name) - self._copy_temp_virtual_disk(dc_ref, src_path, dest_path) + self._copy_temp_virtual_disk(dc_ref, src_path, dc_ref, dest_path) LOG.debug("Created virtual disk: %s from sparse vmdk image.", dest_path.get_descriptor_ds_file_path()) return dest_path def _create_virtual_disk_from_preallocated_image( self, context, image_service, image_id, image_size_in_bytes, - dc_ref, ds_name, folder_path, disk_name, adapter_type): + dest_dc_ref, dest_ds_name, dest_folder_path, dest_disk_name, + adapter_type): """Creates virtual disk from an image which is a flat extent.""" - path = volumeops.FlatExtentVirtualDiskPath(ds_name, - folder_path, - disk_name) + # Upload the image and use it as a flat extent to create a virtual + # disk. First, find the datastore folder to download the image. + (dc_ref, ds_name, + folder_path) = self._get_temp_image_folder(image_size_in_bytes) + + # pylint: disable=E1101 + if ds_name == dest_ds_name and dc_ref.value == dest_dc_ref.value: + # Temporary image folder and destination path are on the same + # datastore. We can directly download the image to the destination + # folder to save one virtual disk copy. + path = volumeops.FlatExtentVirtualDiskPath(dest_ds_name, + dest_folder_path, + dest_disk_name) + dest_path = path + else: + # Use the image to create a temporary virtual disk which is then + # copied to the destination folder. + disk_name = uuidutils.generate_uuid() + path = volumeops.FlatExtentVirtualDiskPath(ds_name, + folder_path, + disk_name) + dest_path = volumeops.FlatExtentVirtualDiskPath(dest_ds_name, + dest_folder_path, + dest_disk_name) + LOG.debug("Creating virtual disk: %(path)s from (flat extent) image: " "%(image_id)s.", {'path': path.get_descriptor_ds_file_path(), @@ -992,9 +1046,13 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): path.get_descriptor_ds_file_path(), exc_info=True) + if dest_path != path: + # Copy temporary disk to given destination. + self._copy_temp_virtual_disk(dc_ref, path, dest_dc_ref, dest_path) + LOG.debug("Created virtual disk: %s from flat extent image.", - path.get_descriptor_ds_file_path()) - return path + dest_path.get_descriptor_ds_file_path()) + return dest_path def _check_disk_conversion(self, image_disk_type, extra_spec_disk_type): """Check if disk type conversion is needed.""" diff --git a/cinder/volume/drivers/vmware/volumeops.py b/cinder/volume/drivers/vmware/volumeops.py index 44d6e7685..e401f7126 100644 --- a/cinder/volume/drivers/vmware/volumeops.py +++ b/cinder/volume/drivers/vmware/volumeops.py @@ -1183,6 +1183,28 @@ class VMwareVolumeOps(object): self._session.wait_for_task(task) LOG.info(_LI("Successfully deleted file: %s."), file_path) + def create_datastore_folder(self, ds_name, folder_path, datacenter): + """Creates a datastore folder. + + This method returns silently if the folder already exists. + + :param ds_name: datastore name + :param folder_path: path of folder to create + :param datacenter: datacenter of target datastore + """ + fileManager = self._session.vim.service_content.fileManager + ds_folder_path = "[%s] %s" % (ds_name, folder_path) + LOG.debug("Creating datastore folder: %s.", ds_folder_path) + try: + self._session.invoke_api(self._session.vim, + 'MakeDirectory', + fileManager, + name=ds_folder_path, + datacenter=datacenter) + LOG.info(_LI("Created datastore folder: %s."), folder_path) + except exceptions.FileAlreadyExistsException: + LOG.debug("Datastore folder: %s already exists.", folder_path) + def get_path_name(self, backing): """Get path name of the backing. @@ -1308,26 +1330,31 @@ class VMwareVolumeOps(object): LOG.debug("Created descriptor: %s.", path.get_descriptor_ds_file_path()) - def copy_vmdk_file(self, dc_ref, src_vmdk_file_path, dest_vmdk_file_path): + def copy_vmdk_file(self, src_dc_ref, src_vmdk_file_path, + dest_vmdk_file_path, dest_dc_ref=None): """Copy contents of the src vmdk file to dest vmdk file. - During the copy also coalesce snapshots of src if present. - dest_vmdk_file_path will be created if not already present. - - :param dc_ref: Reference to datacenter containing src and dest + :param src_dc_ref: Reference to datacenter containing src datastore :param src_vmdk_file_path: Source vmdk file path :param dest_vmdk_file_path: Destination vmdk file path + :param dest_dc_ref: Reference to datacenter of dest datastore. + If unspecified, source datacenter is used. """ - LOG.debug('Copying disk data before snapshot of the VM') + LOG.debug('Copying disk: %(src)s to %(dest)s.', + {'src': src_vmdk_file_path, + 'dest': dest_vmdk_file_path}) + + dest_dc_ref = dest_dc_ref or src_dc_ref diskMgr = self._session.vim.service_content.virtualDiskManager task = self._session.invoke_api(self._session.vim, 'CopyVirtualDisk_Task', diskMgr, sourceName=src_vmdk_file_path, - sourceDatacenter=dc_ref, + sourceDatacenter=src_dc_ref, destName=dest_vmdk_file_path, - destDatacenter=dc_ref, + destDatacenter=dest_dc_ref, force=True) + LOG.debug("Initiated copying disk data via task: %s.", task) self._session.wait_for_task(task) LOG.info(_LI("Successfully copied disk at: %(src)s to: %(dest)s."), |