def delete_tmp_dir(self, datastore_id, tmp_dir): """ Deletes a temp image directory by moving it to a GC directory """ file_path = os_datastore_path(datastore_id, tmp_dir) if not os.path.exists(file_path): self._logger.info("Tmp dir %s not" % file_path) raise DirectoryNotFound("Directory %s not found" % file_path) rm_rf(file_path)
def create_image(self, image_id, datastore_id): """ Create a temp image on given datastore, return its path. """ datastore_type = self._get_datastore_type(datastore_id) if datastore_type == DatastoreType.VSAN: # on VSAN, tmp_dir is [datastore]/image_[image_id]/tmp_image_[uuid] # Because VSAN does not allow moving top-level directories, we place tmp_image # under image's dir. relative_path = os.path.join(compond_path_join(IMAGE_FOLDER_NAME_PREFIX, image_id), compond_path_join(TMP_IMAGE_FOLDER_NAME_PREFIX, str(uuid.uuid4()))) tmp_dir = os_datastore_path(datastore_id, relative_path) else: # on VMFS/NFS/etc, tmp_dir is [datastore]/tmp_image_[uuid] tmp_dir = os_datastore_path(datastore_id, compond_path_join(TMP_IMAGE_FOLDER_NAME_PREFIX, str(uuid.uuid4()))) self._host_client.make_directory(tmp_dir) # return datastore path, so that it can be passed to nfc client return os_to_datastore_path(tmp_dir)
def _copy_to_tmp_image(self, source_datastore, source_id, dest_datastore, dest_id): """ Copy an image into a temp location. 1. Lock a tmp image destination file with an exclusive lock. This is to prevent the GC thread from garbage collecting directories that are actively being used. The temp directory name contains a random UUID to prevent collisions with concurrent copies 2. Create the temp directory. 3. Copy the metadata file over. 4. Copy the vmdk over. @return the tmp image directory on success. """ ds_type = self._get_datastore_type(dest_datastore) if ds_type == DatastoreType.VSAN: tmp_image_dir = os_datastore_path(dest_datastore, compond_path_join(IMAGE_FOLDER_NAME_PREFIX, dest_id), compond_path_join(TMP_IMAGE_FOLDER_NAME_PREFIX, str(uuid.uuid4()))) else: tmp_image_dir = os_datastore_path(dest_datastore, compond_path_join(TMP_IMAGE_FOLDER_NAME_PREFIX, str(uuid.uuid4()))) # Create the temp directory self._host_client.make_directory(tmp_image_dir) # Copy the metadata file if it exists. source_meta = os_metadata_path(source_datastore, source_id, IMAGE_FOLDER_NAME_PREFIX) if os.path.exists(source_meta): try: dest_meta = os.path.join(tmp_image_dir, metadata_filename(dest_id)) shutil.copy(source_meta, dest_meta) except: self._logger.exception("Failed to copy metadata file %s", source_meta) raise # Create the timestamp file self._create_image_timestamp_file(tmp_image_dir) self._host_client.copy_disk(vmdk_path(source_datastore, source_id, IMAGE_FOLDER_NAME_PREFIX), os.path.join(tmp_image_dir, "%s.vmdk" % dest_id)) return tmp_image_dir
def _move_image(self, image_id, datastore, tmp_dir): """ Atomic move of a tmp folder into the image datastore. Handles concurrent moves by locking a well know derivative of the image_id while doing the atomic move. The exclusive file lock ensures that only one move is successful. Has the following side effects: a - If the destination image already exists, it is assumed that someone else successfully copied the image over and the temp directory is deleted. b - If we fail to acquire the file lock after retrying 3 times, or the atomic move fails, the tmp image directory will be left behind and needs to be garbage collected later. image_id: String.The image id of the image being moved. datastore: String. The datastore id of the datastore. tmp_dir: String. The absolute path of the temp image directory. raises: OsError if the move fails AcquireLockFailure, InvalidFile if we fail to lock the destination image. """ ds_type = self._get_datastore_type(datastore) image_path = os_datastore_path( datastore, compond_path_join(IMAGE_FOLDER_NAME_PREFIX, image_id)) self._logger.info("_move_image: %s => %s, ds_type: %s" % (tmp_dir, image_path, ds_type)) if not os.path.exists(tmp_dir): raise ImageNotFoundException("Temp image %s not found" % tmp_dir) try: with FileBackedLock(image_path, ds_type, retry=300, wait_secs=0.1): # wait lock for 30 seconds if self._check_image_repair(image_id, datastore): raise DiskAlreadyExistException("Image already exists") if ds_type == DatastoreType.VSAN: # on VSAN, move all files under [datastore]/image_[image_id]/tmp_image_[uuid]/* to # [datastore]/image_[image_id]/*. # Also we do not delete tmp_image folder in success case, because VSAN accesses it # when creating linked VM, even the folder is now empty. for entry in os.listdir(tmp_dir): shutil.move(os.path.join(tmp_dir, entry), os.path.join(image_path, entry)) else: # on VMFS/NFS/etc, rename [datastore]/tmp_image_[uuid] to [datastore]/tmp_image_[image_id] self._host_client.move_file(tmp_dir, image_path) except: self._logger.exception("Move image %s to %s failed" % (image_id, image_path)) self._host_client.delete_file(tmp_dir) raise
def _move_image(self, image_id, datastore, tmp_dir): """ Atomic move of a tmp folder into the image datastore. Handles concurrent moves by locking a well know derivative of the image_id while doing the atomic move. The exclusive file lock ensures that only one move is successful. Has the following side effects: a - If the destination image already exists, it is assumed that someone else successfully copied the image over and the temp directory is deleted. b - If we fail to acquire the file lock after retrying 3 times, or the atomic move fails, the tmp image directory will be left behind and needs to be garbage collected later. image_id: String.The image id of the image being moved. datastore: String. The datastore id of the datastore. tmp_dir: String. The absolute path of the temp image directory. raises: OsError if the move fails AcquireLockFailure, InvalidFile if we fail to lock the destination image. """ ds_type = self._get_datastore_type(datastore) image_path = os_datastore_path(datastore, compond_path_join(IMAGE_FOLDER_NAME_PREFIX, image_id)) self._logger.info("_move_image: %s => %s, ds_type: %s" % (tmp_dir, image_path, ds_type)) if not os.path.exists(tmp_dir): raise ImageNotFoundException("Temp image %s not found" % tmp_dir) try: with FileBackedLock(image_path, ds_type, retry=300, wait_secs=0.1): # wait lock for 30 seconds if self._check_image_repair(image_id, datastore): raise DiskAlreadyExistException("Image already exists") if ds_type == DatastoreType.VSAN: # on VSAN, move all files under [datastore]/image_[image_id]/tmp_image_[uuid]/* to # [datastore]/image_[image_id]/*. # Also we do not delete tmp_image folder in success case, because VSAN accesses it # when creating linked VM, even the folder is now empty. for entry in os.listdir(tmp_dir): shutil.move(os.path.join(tmp_dir, entry), os.path.join(image_path, entry)) else: # on VMFS/NFS/etc, rename [datastore]/tmp_image_[uuid] to [datastore]/tmp_image_[image_id] self._host_client.move_file(tmp_dir, image_path) except: self._logger.exception("Move image %s to %s failed" % (image_id, image_path)) self._host_client.delete_file(tmp_dir) raise
def get_datastore(self, disk_id): for datastore in self._ds_manager.get_datastore_ids(): disk = os_vmdk_path(datastore, disk_id) if os.path.isfile(disk): return datastore # Extra logging to help debug failures where host2 cannot find disk created by host1 on a shared datastore self._logger.error("get_disk_datastore failed: disk=%s, datastores=%s" % (disk_id, self._ds_manager.get_datastore_ids())) for datastore in self._ds_manager.get_datastore_ids(): p1 = os_datastore_root(datastore) p2 = os_datastore_path(datastore, compond_path_join(DISK_FOLDER_NAME_PREFIX, disk_id)) p3 = os_vmdk_path(datastore, disk_id) self._logger.error("get_disk_datastore check_path: %s:%s, %s:%s, %s:%s" % (p1, os.path.isdir(p1), p2, os.path.isdir(p2), p3, os.path.isfile(p3))) return None
def get_datastore(self, disk_id): for datastore in self._ds_manager.get_datastore_ids(): disk = os_vmdk_path(datastore, disk_id) if os.path.isfile(disk): return datastore # Extra logging to help debug failures where host2 cannot find disk created by host1 on a shared datastore self._logger.error( "get_disk_datastore failed: disk=%s, datastores=%s" % (disk_id, self._ds_manager.get_datastore_ids())) for datastore in self._ds_manager.get_datastore_ids(): p1 = os_datastore_root(datastore) p2 = os_datastore_path( datastore, compond_path_join(DISK_FOLDER_NAME_PREFIX, disk_id)) p3 = os_vmdk_path(datastore, disk_id) self._logger.error( "get_disk_datastore check_path: %s:%s, %s:%s, %s:%s" % (p1, os.path.isdir(p1), p2, os.path.isdir(p2), p3, os.path.isfile(p3))) return None