def create_image_with_vm_disk(self, datastore_id, tmp_dir, image_id,
                                  vm_disk_os_path):
        """ Fills a temp image directory with a disk from a VM,
            then installs directory in the shared image folder.
        """
        dst_vmdk_path = os_datastore_path(datastore_id,
                                          "%s/%s.vmdk" % (tmp_dir, image_id))
        if os.path.exists(dst_vmdk_path):
            self._logger.warning(
                "Unexpected disk %s present, overwriting" % dst_vmdk_path)
        dst_vmdk_ds_path = os_to_datastore_path(dst_vmdk_path)

        _vd_spec = self._prepare_virtual_disk_spec(
            vim.VirtualDiskManager.VirtualDiskType.thin,
            vim.VirtualDiskManager.VirtualDiskAdapterType.lsiLogic)

        self._manage_disk(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                          sourceName=os_to_datastore_path(vm_disk_os_path),
                          destName=dst_vmdk_ds_path,
                          destSpec=_vd_spec)

        try:
            self.create_image(datastore_id, tmp_dir, image_id)
        except:
            self._logger.warning("Delete copied disk %s" % dst_vmdk_ds_path)
            self._manage_disk(vim.VirtualDiskManager.DeleteVirtualDisk_Task,
                              name=dst_vmdk_ds_path)
            raise
    def get_images(self, datastore):
        """ Get image list from datastore
        :param datastore: datastore id
        :return: list of string, image id list
        """
        image_ids = []

        # image_folder is /vmfs/volumes/${datastore}/images
        image_folder = os_datastore_path(datastore, IMAGE_FOLDER_NAME)

        if not os.path.exists(image_folder):
            raise DatastoreNotFoundException()

        # prefix is the 2-digit prefix of image id
        for prefix in os.listdir(image_folder):
            # outer path is something like
            # /vmfs/volumes/${datastore}/images/${image_id}[0:2]
            outer_path = os.path.join(image_folder, prefix)
            if not os.path.isdir(outer_path):
                continue

            for image_id in os.listdir(outer_path):
                if self.check_image(image_id, datastore):
                    image_ids.append(image_id)

        return image_ids
Beispiel #3
0
    def delete_vm(self, vm_id, force=False):
        """Delete a Virtual Machine

        :param vm_id: Name of the VM
        :type vm_id: str
        :param force: Not to check persistent disk, forcefully delete vm.
        :type force: boolean
        :raise VmPowerStateException when vm is not powered off
        """
        vm = self.vim_client.get_vm(vm_id)
        if vm.runtime.powerState != 'poweredOff':
            raise VmPowerStateException("Can only delete vm in state %s" %
                                        vm.runtime.powerState)

        # Getting the path for the new dir structure if we have upgraded from older structure
        datastore_name = self.get_vm_datastore(vm.config)
        vm_path = os_datastore_path(datastore_name, compond_path_join(VM_FOLDER_NAME_PREFIX, vm_id))

        if not force:
            self._verify_disks(vm)

        self._logger.info("Destroy VM at %s" % vm_path)
        self._invoke_vm(vm, "Destroy")

        self._ensure_directory_cleanup(vm_path)

        self.vim_client.wait_for_vm_delete(vm_id)
 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 get_images(self, datastore):
        """ Get image list from datastore
        :param datastore: datastore id
        :return: list of string, image id list
        """
        image_ids = []

        # image_folder is /vmfs/volumes/${datastore}/images
        image_folder = os_datastore_path(datastore, IMAGE_FOLDER_NAME)

        if not os.path.exists(image_folder):
            raise DatastoreNotFoundException()

        # prefix is the 2-digit prefix of image id
        for prefix in os.listdir(image_folder):
            # outer path is something like
            # /vmfs/volumes/${datastore}/images/${image_id}[0:2]
            outer_path = os.path.join(image_folder, prefix)
            if not os.path.isdir(outer_path):
                continue

            for image_id in os.listdir(outer_path):
                if self.check_image(image_id, datastore):
                    image_ids.append(image_id)

        return image_ids
Beispiel #6
0
 def _clean_gc_dir(self, datastore_id):
     """
     Clean may fail but can be retried later
     """
     dir_path = os_datastore_path(datastore_id, GC_IMAGE_FOLDER)
     for sub_dir in os.listdir(dir_path):
         rm_rf(os.path.join(dir_path, sub_dir))
 def _clean_gc_dir(self, datastore_id):
     """
     Clean may fail but can be retried later
     """
     dir_path = os_datastore_path(datastore_id, GC_IMAGE_FOLDER)
     for sub_dir in os.listdir(dir_path):
         shutil.rmtree(os.path.join(dir_path, sub_dir), ignore_errors=True)
    def create_image_with_vm_disk(self, datastore_id, tmp_dir, image_id,
                                  vm_disk_os_path):
        """ Fills a temp image directory with a disk from a VM,
            then installs directory in the shared image folder.
        """
        dst_vmdk_path = os_datastore_path(datastore_id,
                                          "%s/%s.vmdk" % (tmp_dir, image_id))
        if os.path.exists(dst_vmdk_path):
            self._logger.warning("Unexpected disk %s present, overwriting" %
                                 dst_vmdk_path)
        dst_vmdk_ds_path = os_to_datastore_path(dst_vmdk_path)

        _vd_spec = self._prepare_virtual_disk_spec(
            vim.VirtualDiskManager.VirtualDiskType.thin,
            vim.VirtualDiskManager.VirtualDiskAdapterType.lsiLogic)

        self._manage_disk(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                          sourceName=os_to_datastore_path(vm_disk_os_path),
                          destName=dst_vmdk_ds_path,
                          destSpec=_vd_spec)

        try:
            self.create_image(datastore_id, tmp_dir, image_id)
        except:
            self._logger.warning("Delete copied disk %s" % dst_vmdk_ds_path)
            self._manage_disk(vim.VirtualDiskManager.DeleteVirtualDisk_Task,
                              name=dst_vmdk_ds_path)
            raise
 def _clean_gc_dir(self, datastore_id):
     """
     Clean may fail but can be retried later
     """
     dir_path = os_datastore_path(datastore_id, GC_IMAGE_FOLDER)
     for sub_dir in os.listdir(dir_path):
         rm_rf(os.path.join(dir_path, sub_dir))
 def _clean_gc_dir(self, datastore_id):
     """
     Clean may fail but can be retried later
     """
     dir_path = os_datastore_path(datastore_id, GC_IMAGE_FOLDER)
     for sub_dir in os.listdir(dir_path):
         shutil.rmtree(os.path.join(dir_path, sub_dir), ignore_errors=True)
 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 _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._vim_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)

        _vd_spec = self._prepare_virtual_disk_spec(
            vim.VirtualDiskManager.VirtualDiskType.thin,
            vim.VirtualDiskManager.VirtualDiskAdapterType.lsiLogic)

        self._manage_disk(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                          sourceName=vmdk_path(source_datastore, source_id, IMAGE_FOLDER_NAME_PREFIX),
                          destName=os_to_datastore_path(os.path.join(tmp_image_dir, "%s.vmdk" % dest_id)),
                          destSpec=_vd_spec)
        return tmp_image_dir
Beispiel #13
0
    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._vim_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)

        _vd_spec = self._prepare_virtual_disk_spec(
            vim.VirtualDiskManager.VirtualDiskType.thin,
            vim.VirtualDiskManager.VirtualDiskAdapterType.lsiLogic)

        self._manage_disk(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                          sourceName=vmdk_path(source_datastore, source_id, IMAGE_FOLDER_NAME_PREFIX),
                          destName=os_to_datastore_path(os.path.join(tmp_image_dir, "%s.vmdk" % dest_id)),
                          destSpec=_vd_spec)
        return tmp_image_dir
    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._vim_client.make_directory(tmp_dir)
        # return datastore path, so that it can be passed to nfc client
        return os_to_datastore_path(tmp_dir)
Beispiel #15
0
    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._vim_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 delete_unused(self, image_sweeper):
        images_dir_path = os_datastore_path(image_sweeper.datastore_id, IMAGE_FOLDER_NAME)
        # Log messages with prefix: "IMAGE SWEEPER" are for debugging
        # and will be removed after basic testing
        self._logger.info("IMAGE SWEEPER: images_dir: %s" % images_dir_path)
        if not os.path.isdir(images_dir_path):
            self._logger.info("images_dir_path: images_dir: %s, doesn't exist" % images_dir_path)
            raise DatastoreNotFoundException(
                "Image sweeper, cannot find image " "directory for datastore: %s" % image_sweeper.datastore_id
            )

        return self._delete_unused_images(image_sweeper, images_dir_path)
def datastore_mkdirs(vim_client, datastore):
    for path in [DISK_FOLDER_NAME, VM_FOLDER_NAME, IMAGE_FOLDER_NAME,
                 TMP_IMAGE_FOLDER_NAME]:
        ds_path = os_datastore_path(datastore, path)
        # On shared folders os.mkdir races with other hosts attempting to
        # create ds_path. try - pass works around that.
        try:
            os.mkdir(ds_path)
        except OSError as e:
            if e.errno == errno.EEXIST:
                pass
            else:
                raise
def datastore_mkdirs(vim_client, datastore):
    for path in [DISK_FOLDER_NAME, VM_FOLDER_NAME, IMAGE_FOLDER_NAME,
                 TMP_IMAGE_FOLDER_NAME]:
        ds_path = os_datastore_path(datastore, path)
        # On shared folders os.mkdir races with other hosts attempting to
        # create ds_path. try - pass works around that.
        try:
            os.mkdir(ds_path)
        except OSError as e:
            if e.errno == errno.EEXIST:
                pass
            else:
                raise
Beispiel #19
0
    def send_image_to_host(self, image_id, image_datastore,
                           destination_image_id, destination_datastore, host,
                           port):
        if destination_image_id is None:
            destination_image_id = image_id
        metadata = self._read_metadata(image_datastore, image_id)

        shadow_vm_id = self._create_shadow_vm()

        # place transfer.vmdk under shadow_vm_path to work around VSAN's restriction on
        # files at datastore top-level
        shadow_vm_path = os_datastore_path(
            self._get_shadow_vm_datastore(),
            compond_path_join(VM_FOLDER_NAME_PREFIX, shadow_vm_id))
        transfer_vmdk_path = os.path.join(shadow_vm_path, "transfer.vmdk")
        self._logger.info("transfer_vmdk_path = %s" % transfer_vmdk_path)

        agent_client = None
        try:
            read_lease, disk_url = self._get_image_stream_from_shadow_vm(
                image_id, image_datastore, shadow_vm_id)

            try:
                self.download_file(disk_url, transfer_vmdk_path, read_lease)
            finally:
                read_lease.Complete()

            agent_client = DirectClient("Host", Host.Client, host, port)
            agent_client.connect()

            vm_path, vm_id = self._prepare_receive_image(
                agent_client, destination_image_id, destination_datastore)
            spec = self._create_import_vm_spec(vm_id, destination_datastore,
                                               vm_path)

            self._send_image(agent_client, host, transfer_vmdk_path, spec)
            self._register_imported_image_at_host(agent_client,
                                                  destination_image_id,
                                                  destination_datastore, vm_id,
                                                  metadata)

            return vm_id
        finally:
            try:
                os.unlink(transfer_vmdk_path)
            except OSError:
                pass
            self._delete_shadow_vm(shadow_vm_id)
            rm_rf(shadow_vm_path)
            if agent_client:
                agent_client.close()
Beispiel #20
0
    def get_vm_images(self, image_scanner):
        vms_dir_path = os_datastore_path(image_scanner.datastore_id,
                                         VM_FOLDER_NAME)
        # Log messages with prefix: "IMAGE SCANNER" are for debugging
        # and will be removed after basic testing
        self._logger.info("IMAGE SCANNER: vms_dir: %s" % vms_dir_path)
        if not os.path.isdir(vms_dir_path):
            self._logger.info("get_vm_images: vms_dir: %s, doesn't exist" %
                              vms_dir_path)
            raise DatastoreNotFoundException("Image scanner, cannot find vms "
                                             "directory for datastore: %s" %
                                             image_scanner.datastore_id)

        return self._collect_active_images(image_scanner, vms_dir_path)
    def send_image_to_host(self, image_id, image_datastore,
                           destination_image_id, destination_datastore, host,
                           port):
        if destination_image_id is None:
            destination_image_id = image_id
        metadata = self._read_metadata(image_datastore, image_id)

        shadow_vm_id = self._create_shadow_vm()
        tmp_path = "/vmfs/volumes/%s/%s_transfer.vmdk" % (
            self._get_shadow_vm_datastore(), shadow_vm_id)
        self._logger.info("http_disk_transfer: tmp_path = %s" % tmp_path)

        agent_client = None
        try:
            read_lease, disk_url = self._get_image_stream_from_shadow_vm(
                image_id, image_datastore, shadow_vm_id)

            try:
                self.download_file(disk_url, tmp_path, read_lease)
            finally:
                read_lease.Complete()

            agent_client = DirectClient("Host", Host.Client, host, port)
            agent_client.connect()

            vm_path, vm_id = self._prepare_receive_image(
                agent_client, destination_image_id, destination_datastore)
            spec = self._create_import_vm_spec(vm_id, destination_datastore,
                                               vm_path)

            self._send_image(agent_client, host, tmp_path, spec)
            self._register_imported_image_at_host(agent_client,
                                                  destination_image_id,
                                                  destination_datastore, vm_id,
                                                  metadata)

            return vm_id
        finally:
            try:
                os.unlink(tmp_path)
            except OSError:
                pass
            self._delete_shadow_vm(shadow_vm_id)
            rm_rf(
                os_datastore_path(
                    self._get_shadow_vm_datastore(),
                    compond_path_join(VM_FOLDER_NAME_PREFIX, shadow_vm_id)))
            if agent_client:
                agent_client.close()
    def delete_unused(self, image_sweeper):
        images_dir_path = os_datastore_path(image_sweeper.datastore_id,
                                            IMAGE_FOLDER_NAME)
        # Log messages with prefix: "IMAGE SWEEPER" are for debugging
        # and will be removed after basic testing
        self._logger.info("IMAGE SWEEPER: images_dir: %s" % images_dir_path)
        if not os.path.isdir(images_dir_path):
            self._logger.info(
                "images_dir_path: images_dir: %s, doesn't exist" %
                images_dir_path)
            raise DatastoreNotFoundException(
                "Image sweeper, cannot find image "
                "directory for datastore: %s" % image_sweeper.datastore_id)

        return self._delete_unused_images(image_sweeper, images_dir_path)
    def get_vm_images(self, image_scanner):
        vms_dir_path = os_datastore_path(image_scanner.datastore_id,
                                         VM_FOLDER_NAME)
        # Log messages with prefix: "IMAGE SCANNER" are for debugging
        # and will be removed after basic testing
        self._logger.info("IMAGE SCANNER: vms_dir: %s" % vms_dir_path)
        if not os.path.isdir(vms_dir_path):
            self._logger.info("get_vm_images: vms_dir: %s, doesn't exist"
                              % vms_dir_path)
            raise DatastoreNotFoundException(
                "Image scanner, cannot find vms "
                "directory for datastore: %s"
                % image_scanner.datastore_id)

        return self._collect_active_images(image_scanner, vms_dir_path)
    def create_image(self, datastore_id, tmp_dir, image_id):
        """ Installs an image using image data staged at a temp directory.
        """
        src_path = os_datastore_path(datastore_id, tmp_dir)
        if not os.path.exists(src_path):
            self._logger.info("Tmp dir %s on datastore %s not found" % (tmp_dir, datastore_id))
            raise ImageNotFoundException("Image %s not found" % src_path)

        # Check if the dest image id already exists
        if self.check_image_dir(image_id, datastore_id):
            self._logger.info("Image %s on datastore %s already exists" % (image_id, datastore_id))
            raise DiskAlreadyExistException()

        self._move_image(image_id, datastore_id, src_path)
        self._create_image_timestamp_file_from_ids(datastore_id, image_id)
Beispiel #25
0
    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.01):  # wait lock for 3 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._vim_client.move_file(tmp_dir, image_path)
        except:
            self._logger.exception("Move image %s to %s failed" % (image_id, image_path))
            self._vim_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.01):  # wait lock for 3 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._vim_client.move_file(tmp_dir, image_path)
        except:
            self._logger.exception("Move image %s to %s failed" % (image_id, image_path))
            self._vim_client.delete_file(tmp_dir)
            raise
    def create_image(self, datastore_id, tmp_dir, image_id):
        """ Installs an image using image data staged at a temp directory.
        """
        src_path = os_datastore_path(datastore_id, tmp_dir)
        if not os.path.exists(src_path):
            self._logger.info("Tmp dir %s on datastore %s not found" %
                              (tmp_dir, datastore_id))
            raise ImageNotFoundException("Image %s not found" % src_path)

        # Check if the dest image id already exists
        if self.check_image_dir(image_id, datastore_id):
            self._logger.info("Image %s on datastore %s already exists" %
                              (image_id, datastore_id))
            raise DiskAlreadyExistException()

        self._move_image(image_id, datastore_id, src_path)
        self._create_image_timestamp_file_from_ids(datastore_id, image_id)
    def send_image_to_host(self, image_id, image_datastore,
                           destination_image_id, destination_datastore,
                           host, port):
        manifest, metadata = self._read_metadata(image_datastore, image_id)

        shadow_vm_id = self._create_shadow_vm()
        tmp_path = "/vmfs/volumes/%s/%s_transfer.vmdk" % (
            self._get_shadow_vm_datastore(), shadow_vm_id)

        try:
            read_lease, disk_url =\
                self._get_image_stream_from_shadow_vm(
                    image_id, image_datastore, shadow_vm_id)

            try:
                self.download_file(disk_url, tmp_path, read_lease)
            finally:
                read_lease.Complete()

            if destination_image_id is None:
                destination_image_id = image_id
            spec = self._create_import_vm_spec(
                destination_image_id, destination_datastore)

            imported_vm_name = self._send_image(
                tmp_path, manifest, metadata, spec, destination_image_id,
                destination_datastore, host, port)

            return imported_vm_name

        finally:
            try:
                os.unlink(tmp_path)
            except OSError:
                pass
            self._delete_shadow_vm(shadow_vm_id)
            rm_rf(os_datastore_path(self._get_shadow_vm_datastore(),
                                    compond_path_join(VM_FOLDER_NAME_PREFIX, shadow_vm_id)))
 def _gc_image_dir(self, datastore_id, image_id):
     """
     Moves the current image directory into the GC image folder and
     relies on the later GC call for cleanup.
     Exception is thrown if the move fails.
     Assumes the ref files contained in the image director are locked, so
     there can only be one move happening at any point in time.
     """
     src_path = os.path.dirname(os_vmdk_path(datastore_id, image_id, IMAGE_FOLDER_NAME))
     # Verify locking held.
     assert os.path.exists(src_path)
     # Generate a random suffix, this is to address the following.
     # a - Image disk say i1 was moved to the gc_dir
     # b - Cleanup of i1 failed for whatever reason.
     # c - New copy of i1 was uploaded to the datastore.
     # d - The new copy is now being deleted.
     # The location of the move in d needs to be different from the
     # location of the move from b, hence use a random uuid.
     rnd_uuid = str(uuid.uuid4())
     gc_dir = os_datastore_path(datastore_id, GC_IMAGE_FOLDER)
     dst_dir = os.path.join(gc_dir, rnd_uuid)
     os.makedirs(dst_dir)
     shutil.move(src_path, dst_dir)
 def _gc_image_dir(self, datastore_id, image_id):
     """
     Moves the current image directory into the GC image folder and
     relies on the later GC call for cleanup.
     Exception is thrown if the move fails.
     Assumes the ref files contained in the image director are locked, so
     there can only be one move happening at any point in time.
     """
     src_path = os.path.dirname(
         os_vmdk_path(datastore_id, image_id, IMAGE_FOLDER_NAME))
     # Verify locking held.
     assert (os.path.exists(src_path))
     # Generate a random suffix, this is to address the following.
     # a - Image disk say i1 was moved to the gc_dir
     # b - Cleanup of i1 failed for whatever reason.
     # c - New copy of i1 was uploaded to the datastore.
     # d - The new copy is now being deleted.
     # The location of the move in d needs to be different from the
     # location of the move from b, hence use a random uuid.
     rnd_uuid = str(uuid.uuid4())
     gc_dir = os_datastore_path(datastore_id, GC_IMAGE_FOLDER)
     dst_dir = os.path.join(gc_dir, rnd_uuid)
     os.makedirs(dst_dir)
     shutil.move(src_path, dst_dir)
    def _do_delete_single_image(self, image_sweeper,
                                curdir, image_id,
                                modify):
        self._logger.info("IMAGE SCANNER: Starting to "
                          "delete image: %s, %s"
                          % (curdir, image_id))
        # Read content of marker file
        try:
            marker_pathname = os.path.join(curdir,
                                           self.IMAGE_MARKER_FILE_NAME)
            marker_time = self._read_marker_file(marker_pathname)
        except Exception as ex:
            self._logger.warning("Cannot read marker file: %s, %s"
                                 % (curdir, ex))
            return False

        self._logger.info("IMAGE SCANNER: Marker time: %s"
                          % marker_time)

        # Subtract grace time to avoid
        # errors due to small difference in clock
        # values on different hosts. Pretend the scan
        # started 60 seconds earlier.
        marker_time -= image_sweeper.get_grace_period()

        self._logger.info(
            "IMAGE SCANNER: Marker time after grace: %s"
            % marker_time)

        timestamp_pathname = \
            os.path.join(curdir,
                         self.IMAGE_TIMESTAMP_FILE_NAME)
        renamed_timestamp_pathname = \
            timestamp_pathname + \
            self.IMAGE_TIMESTAMP_FILE_RENAME_SUFFIX

        # Lock image
        datastore_id = image_sweeper.datastore_id
        ds_type = self._get_datastore_type(datastore_id)

        with FileBackedLock(curdir, ds_type):
            # Get mod time of the timestamp file,
            # the method returns None if the file doesn't
            # exists, throws exception if there are
            # other errors
            timestamp_exists, mod_time = \
                self._get_mod_time(
                    timestamp_pathname)

            if timestamp_exists:
                # Marker time is out of date
                # skip this image
                self._logger.info(
                    "IMAGE SCANNER: mod time: %s"
                    % mod_time)
                if mod_time >= marker_time:
                    # Remove marker file
                    self._logger.info(
                        "IMAGE SCANNER: mod time too recent")
                    self._image_sweeper_unlink(marker_pathname)
                    return False

                # Move timestamp file to a new name
                if modify:
                    self._image_sweeper_rename(
                        timestamp_pathname,
                        renamed_timestamp_pathname)

            else:
                # If we could not find the timestamp file
                # it may mean that this was a partially
                # removed image, log message and continue
                self._logger.info("Cannot find timestamp file: %s"
                                  "continuing with image removal"
                                  % timestamp_pathname)

            if modify:
                # Get mod time of the renamed timestamp file
                renamed_timestamp_exists, renamed_mod_time = \
                    self._get_mod_time(
                        renamed_timestamp_pathname)
            else:
                renamed_timestamp_exists = True
                renamed_mod_time = mod_time

            self._logger.info(
                "IMAGE SCANNER: rename timestamp exists: %s, "
                "renamed mod time: %s" %
                (renamed_timestamp_exists, renamed_mod_time))

            # If there was timestamp file but there
            # is no renamed-timestamp file something
            # bad might have happened, skip this image
            if timestamp_exists and \
                    not renamed_timestamp_exists:
                self._logger.warning("Error, missing renamed "
                                     "timestamp file: %s"
                                     % renamed_timestamp_pathname)
                return False

            # Normal case both timestamp and renamed
            # timestamp exist
            if timestamp_exists and renamed_timestamp_exists:
                # Normal case: both timestamp and renamed
                # timestamp files exist. If the mod time on the
                # renamed-timestamp has changed skip this image.
                if renamed_mod_time != mod_time:
                    self._logger.info("mod time changed on renamed "
                                      "timestamp file, %s: %d -> %d" %
                                      (renamed_timestamp_pathname,
                                       mod_time, renamed_mod_time))
                    self._image_sweeper_unlink(marker_pathname)
                    return False
            elif renamed_timestamp_exists:
                # Only the renamed timestamp file exists
                # Check the mod time of the renamed-timestamp
                # file against the marker time
                if renamed_mod_time >= marker_time:
                    self._image_sweeper_unlink(marker_pathname)
                    return False

            # Move directory
            self._logger.info("IMAGE SCANNER: removing image: %s"
                              % curdir)
            if modify:
                trash_dir = os.path.join(
                    os_datastore_path(datastore_id, GC_IMAGE_FOLDER),
                    image_id)
                self._image_sweeper_rename(curdir, trash_dir)
            # Unlock

        # Delete image
        if modify:
            self._image_sweeper_rm_rf(trash_dir)
        return True
    def _do_delete_single_image(self, image_sweeper, curdir, image_id, modify):
        self._logger.info("IMAGE SCANNER: Starting to "
                          "delete image: %s, %s" % (curdir, image_id))
        # Read content of marker file
        try:
            marker_pathname = os.path.join(curdir, self.IMAGE_MARKER_FILE_NAME)
            marker_time = self._read_marker_file(marker_pathname)
        except Exception as ex:
            self._logger.warning("Cannot read marker file: %s, %s" %
                                 (curdir, ex))
            return False

        self._logger.info("IMAGE SCANNER: Marker time: %s" % marker_time)

        # Subtract grace time to avoid
        # errors due to small difference in clock
        # values on different hosts. Pretend the scan
        # started 60 seconds earlier.
        marker_time -= image_sweeper.get_grace_period()

        self._logger.info("IMAGE SCANNER: Marker time after grace: %s" %
                          marker_time)

        timestamp_pathname = \
            os.path.join(curdir,
                         self.IMAGE_TIMESTAMP_FILE_NAME)
        renamed_timestamp_pathname = \
            timestamp_pathname + \
            self.IMAGE_TIMESTAMP_FILE_RENAME_SUFFIX

        # Lock image
        datastore_id = image_sweeper.datastore_id
        ds_type = self._get_datastore_type(datastore_id)

        with FileBackedLock(curdir, ds_type):
            # Get mod time of the timestamp file,
            # the method returns None if the file doesn't
            # exists, throws exception if there are
            # other errors
            timestamp_exists, mod_time = \
                self._get_mod_time(
                    timestamp_pathname)

            if timestamp_exists:
                # Marker time is out of date
                # skip this image
                self._logger.info("IMAGE SCANNER: mod time: %s" % mod_time)
                if mod_time >= marker_time:
                    # Remove marker file
                    self._logger.info("IMAGE SCANNER: mod time too recent")
                    self._image_sweeper_unlink(marker_pathname)
                    return False

                # Move timestamp file to a new name
                if modify:
                    self._image_sweeper_rename(timestamp_pathname,
                                               renamed_timestamp_pathname)

            else:
                # If we could not find the timestamp file
                # it may mean that this was a partially
                # removed image, log message and continue
                self._logger.info("Cannot find timestamp file: %s"
                                  "continuing with image removal" %
                                  timestamp_pathname)

            if modify:
                # Get mod time of the renamed timestamp file
                renamed_timestamp_exists, renamed_mod_time = \
                    self._get_mod_time(
                        renamed_timestamp_pathname)
            else:
                renamed_timestamp_exists = True
                renamed_mod_time = mod_time

            self._logger.info("IMAGE SCANNER: rename timestamp exists: %s, "
                              "renamed mod time: %s" %
                              (renamed_timestamp_exists, renamed_mod_time))

            # If there was timestamp file but there
            # is no renamed-timestamp file something
            # bad might have happened, skip this image
            if timestamp_exists and \
                    not renamed_timestamp_exists:
                self._logger.warning("Error, missing renamed "
                                     "timestamp file: %s" %
                                     renamed_timestamp_pathname)
                return False

            # Normal case both timestamp and renamed
            # timestamp exist
            if timestamp_exists and renamed_timestamp_exists:
                # Normal case: both timestamp and renamed
                # timestamp files exist. If the mod time on the
                # renamed-timestamp has changed skip this image.
                if renamed_mod_time != mod_time:
                    self._logger.info("mod time changed on renamed "
                                      "timestamp file, %s: %d -> %d" %
                                      (renamed_timestamp_pathname, mod_time,
                                       renamed_mod_time))
                    self._image_sweeper_unlink(marker_pathname)
                    return False
            elif renamed_timestamp_exists:
                # Only the renamed timestamp file exists
                # Check the mod time of the renamed-timestamp
                # file against the marker time
                if renamed_mod_time >= marker_time:
                    self._image_sweeper_unlink(marker_pathname)
                    return False

            # Move directory
            self._logger.info("IMAGE SCANNER: removing image: %s" % curdir)
            if modify:
                trash_dir = os.path.join(
                    os_datastore_path(datastore_id, GC_IMAGE_FOLDER), image_id)
                self._image_sweeper_rename(curdir, trash_dir)
            # Unlock

        # Delete image
        if modify:
            self._image_sweeper_rm_rf(trash_dir)
        return True