Example #1
0
    def _allocate_volume(cls, vol_path, size, preallocate):
        try:
            # Always create sparse image, since qemu-img create uses
            # posix_fallocate() which is inefficient and harmful.
            op = qemuimg.create(vol_path, size=size, format=qemuimg.FORMAT.RAW)

            # This is fast but it can get stuck if storage is inaccessible.
            with vars.task.abort_callback(op.abort):
                with utils.stopwatch("Creating image {}".format(vol_path),
                                     level=logging.INFO,
                                     log=cls.log):
                    op.run()

            # If the image is preallocated, allocate the rest of the image
            # using fallocate helper. qemu-img create always writes zeroes to
            # the first block so we should skip it during preallocation.
            if preallocate == sc.PREALLOCATED_VOL:
                op = fallocate.allocate(vol_path, size - 4096, offset=4096)

                # This is fast on NFS 4.2, GlusterFS, XFS and ext4, but can be
                # extremely slow on NFS < 4.2, writing zeroes to entire image.
                with vars.task.abort_callback(op.abort):
                    with utils.stopwatch(
                            "Preallocating volume {}".format(vol_path),
                            level=logging.INFO,
                            log=cls.log):
                        op.run()
        except exception.ActionStopped:
            raise
        except Exception:
            cls.log.error("Unexpected error", exc_info=True)
            raise se.VolumesZeroingError(vol_path)
Example #2
0
def zero(device_path, size=None, task=_NullTask()):
    """
    Zero a block device.

    Arguments:
        device_path (str): Path to block device to wipe
        size (int): Number of bytes to write. If not specified, use the device
            size. Size must be aligned to `vdsm.storage.constants.BLOCK_SIZE`.
        task (`storage.task.Task`): Task running this operation. If specified,
            the zero operation will be aborted if the task is aborted.

    Raises:
        `vdsm.common.exception.ActionStopped` if the wipe was aborted
        `vdsm.storage.exception.VolumesZeroingError` if writing to storage
            failed.
        `vdsm.storage.exception.InvalidParameterException` if size is not
            aligned to `vdsm.storage.constants.BLOCK_SIZE`.
    """
    if size is None:
        # Always aligned to LVM extent size (128MiB).
        size = fsutils.size(device_path)
    elif size % sc.BLOCK_SIZE:
        raise se.InvalidParameterException("size", size)

    log.info("Zeroing device %s (size=%d)", device_path, size)
    with utils.stopwatch("Zero device %s" % device_path,
                         level=logging.INFO,
                         log=log):
        try:
            op = blkdiscard.zeroout_operation(device_path, size)
            with task.abort_callback(op.abort):
                op.run()
        except se.StorageException as e:
            raise se.VolumesZeroingError("Zeroing device %s failed: %s" %
                                         (device_path, e))
Example #3
0
    def _create(cls, dom, imgUUID, volUUID, size, volFormat, preallocate,
                volParent, srcImgUUID, srcVolUUID, volPath,
                initialSize=None):
        """
        Class specific implementation of volumeCreate. All the exceptions are
        properly handled and logged in volume.create()
        """
        if initialSize:
            cls.log.error("initialSize is not supported for file-based "
                          "volumes")
            raise se.InvalidParameterException("initial size",
                                               initialSize)

        sizeBytes = size * BLOCK_SIZE
        truncSize = sizeBytes if volFormat == sc.RAW_FORMAT else 0

        try:
            oop.getProcessPool(dom.sdUUID).truncateFile(
                volPath, truncSize, mode=sc.FILE_VOLUME_PERMISSIONS,
                creatExcl=True)
        except OSError as e:
            if e.errno == errno.EEXIST:
                raise se.VolumeAlreadyExists(volUUID)
            raise

        if preallocate == sc.PREALLOCATED_VOL:
            try:
                operation = fallocate.allocate(volPath,
                                               sizeBytes)
                with vars.task.abort_callback(operation.abort):
                    with utils.stopwatch("Preallocating volume %s" % volPath):
                        operation.run()
            except exception.ActionStopped:
                raise
            except Exception:
                cls.log.error("Unexpected error", exc_info=True)
                raise se.VolumesZeroingError(volPath)

        if not volParent:
            cls.log.info("Request to create %s volume %s with size = %s "
                         "sectors", sc.type2name(volFormat), volPath,
                         size)
            if volFormat == sc.COW_FORMAT:
                qemuimg.create(volPath,
                               size=sizeBytes,
                               format=sc.fmt2str(volFormat),
                               qcow2Compat=dom.qcow2_compat())
        else:
            # Create hardlink to template and its meta file
            cls.log.info("Request to create snapshot %s/%s of volume %s/%s",
                         imgUUID, volUUID, srcImgUUID, srcVolUUID)
            volParent.clone(volPath, volFormat)

        # Forcing the volume permissions in case one of the tools we use
        # (dd, qemu-img, etc.) will mistakenly change the file permissiosn.
        dom.oop.os.chmod(volPath, sc.FILE_VOLUME_PERMISSIONS)

        return (volPath,)
Example #4
0
 def _fallocate_volume(cls, vol_path, size):
     try:
         operation = fallocate.allocate(vol_path, size)
         with vars.task.abort_callback(operation.abort):
             with utils.stopwatch("Preallocating volume %s" % vol_path):
                 operation.run()
     except exception.ActionStopped:
         raise
     except Exception:
         cls.log.error("Unexpected error", exc_info=True)
         raise se.VolumesZeroingError(vol_path)
Example #5
0
def zero(device_path, size=None, task=None):
    """
    Zero a block device.

    Arguments:
        device_path (str): Path to block device to wipe
        size (int): Number of bytes to write. If not specified, use the device
            size. Size must be aligned to `vdsm.storage.constants.BLOCK_SIZE`.
        task (`storage.task.Task`): Task running this operation. If specified,
            the zero operation will be aborted if the task is aborted.

    Raises:
        `vdsm.common.exception.ActionStopped` if the wipe was aborted
        `vdsm.storage.exception.VolumesZeroingError` if writing to storage
            failed.
        `vdsm.storage.exception.InvalidParameterException` if size is not
            aligned to `vdsm.storage.constants.BLOCK_SIZE`.
    """
    if size is None:
        # Always aligned to LVM extent size (128MiB).
        size = fsutils.size(device_path)
    elif size % sc.BLOCK_SIZE:
        raise se.InvalidParameterException("size", size)

    log.info("Zeroing device %s (size=%d)", device_path, size)
    with utils.stopwatch("Zero device %s" % device_path,
                         level=logging.INFO,
                         log=log):
        try:
            # Write optimal size blocks. Images are always aligned to
            # optimal size blocks, so we typically have only one call.
            blocks = size // OPTIMAL_BLOCK_SIZE
            if blocks > 0:
                _zero(device_path, 0, OPTIMAL_BLOCK_SIZE, blocks, task=task)

            # When zeroing special volumes size may not be aligned to
            # optimal block size, so we need to write the last block.
            rest = size % OPTIMAL_BLOCK_SIZE
            if rest > 0:
                offset = blocks * OPTIMAL_BLOCK_SIZE
                _zero(device_path, offset, rest, 1, task=task)
        except se.StorageException as e:
            raise se.VolumesZeroingError("Zeroing device %s failed: %s" %
                                         (device_path, e))
Example #6
0
def zero(device_path, size=None, task=_NullTask()):
    """
    Zero a block device.

    Arguments:
        device_path (str): Path to block device to wipe
        size (int): Number of bytes to write. If not specified, use the device
            size. Size must be aligned to `vdsm.storage.constants.BLOCK_SIZE`.
        task (`storage.task.Task`): Task running this operation. If specified,
            the zero operation will be aborted if the task is aborted.

    Raises:
        `vdsm.common.exception.ActionStopped` if the wipe was aborted
        `vdsm.storage.exception.VolumesZeroingError` if writing to storage
            failed.
        `vdsm.storage.exception.InvalidParameterException` if size is not
            aligned to `vdsm.storage.constants.BLOCK_SIZE`.
    """
    if size is None:
        # Always aligned to LVM extent size (128MiB).
        size = fsutils.size(device_path)
    elif size % sc.BLOCK_SIZE:
        raise se.InvalidParameterException("size", size)

    log.info("Zeroing device %s (size=%d)", device_path, size)
    with utils.stopwatch("Zero device %s" % device_path,
                         level=logging.INFO,
                         log=log):
        zero_method = config.get('irs', 'zero_method')
        try:
            if zero_method == "blkdiscard":
                _zero_blkdiscard(device_path, size, task)
            elif zero_method == "dd":
                _zero_dd(device_path, size, task)
            else:
                raise exception.InvalidConfiguration(
                    reason="Unsupported value for irs:zero_method",
                    zero_method=zero_method)
        except se.StorageException as e:
            raise se.VolumesZeroingError("Zeroing device %s failed: %s" %
                                         (device_path, e))
Example #7
0
    def _allocate_volume(cls, vol_path, size, preallocate):
        if preallocate == sc.PREALLOCATED_VOL:
            preallocation = qemuimg.PREALLOCATION.FALLOC
        else:
            preallocation = qemuimg.PREALLOCATION.OFF

        try:
            operation = qemuimg.create(
                vol_path,
                size=size,
                format=qemuimg.FORMAT.RAW,
                preallocation=preallocation)

            with vars.task.abort_callback(operation.abort):
                with utils.stopwatch("Preallocating volume %s" % vol_path):
                    operation.run()
        except exception.ActionStopped:
            raise
        except Exception:
            cls.log.error("Unexpected error", exc_info=True)
            raise se.VolumesZeroingError(vol_path)
Example #8
0
    def delete(self, postZero, force):
        """ Delete volume
            'postZero' - zeroing file before deletion
            'force' is required to remove shared and internal volumes
        """
        self.log.info("Request to delete LV %s of image %s in VG %s ",
                      self.volUUID, self.imgUUID, self.sdUUID)

        vol_path = self.getVolumePath()
        size = self.getVolumeSize(bs=1)
        offs = self.getMetaOffset()

        # On block storage domains we store a volume's parent UUID in two
        # places: 1) in the domain's metadata LV, and 2) in a LV tag attached
        # to the volume LV itself.  The LV tag is more efficient to access
        # than the domain metadata but it may only be updated by the SPM.
        #
        # This means that after a live merge completes the domain metadata LV
        # will be updated but the LV tag will not.  We can detect this case
        # here and fix the LV tag since this is an SPM verb.
        #
        # File domains do not have this complexity because the metadata is
        # stored in only one place and that metadata is updated by the HSM
        # host when the live merge finishes.
        sync = False
        for childID in self.getChildren():
            child = BlockVolume(self.repoPath, self.sdUUID, self.imgUUID,
                                childID)
            metaParent = child.getParentMeta()
            tagParent = child.getParentTag()
            if metaParent != tagParent:
                self.log.debug(
                    "Updating stale PUUID LV tag from %s to %s for "
                    "volume %s", tagParent, metaParent, child.volUUID)
                child.setParentTag(metaParent)
                sync = True
        if sync:
            self.recheckIfLeaf()

        if not force:
            self.validateDelete()

        # Mark volume as illegal before deleting
        self.setLegality(sc.ILLEGAL_VOL)

        discard = config.getboolean('irs', 'discard_enable')
        if postZero or discard:
            self.prepare(justme=True,
                         rw=True,
                         chainrw=force,
                         setrw=True,
                         force=True)
            try:
                if postZero:
                    try:
                        misc.ddWatchCopy("/dev/zero", vol_path,
                                         vars.task.aborting, int(size))
                    except exception.ActionStopped:
                        raise
                    except Exception:
                        self.log.error("Unexpected error", exc_info=True)
                        raise se.VolumesZeroingError(vol_path)
                if discard:
                    try:
                        blkdiscard.blkdiscard(vol_path)
                    except cmdutils.Error as e:
                        log.warning('Discarding %s failed: %s', vol_path, e)
            finally:
                self.teardown(self.sdUUID, self.volUUID, justme=True)

        # try to cleanup as much as possible
        eFound = se.CannotDeleteVolume(self.volUUID)
        puuid = None
        try:
            # We need to blank parent record in our metadata
            # for parent to become leaf successfully.
            puuid = self.getParent()
            self.setParent(sc.BLANK_UUID)
            if puuid and puuid != sc.BLANK_UUID:
                pvol = BlockVolume(self.repoPath, self.sdUUID, self.imgUUID,
                                   puuid)
                pvol.recheckIfLeaf()
        except Exception as e:
            eFound = e
            self.log.warning("cannot finalize parent volume %s",
                             puuid,
                             exc_info=True)

        try:
            try:
                lvm.removeLVs(self.sdUUID, self.volUUID)
            except se.CannotRemoveLogicalVolume:
                # At this point LV is already marked as illegal, we will
                # try to cleanup whatever we can...
                pass

            self.removeMetadata([self.sdUUID, offs])
        except Exception as e:
            eFound = e
            self.log.error("cannot remove volume %s/%s",
                           self.sdUUID,
                           self.volUUID,
                           exc_info=True)

        try:
            self.log.debug("Unlinking %s", vol_path)
            os.unlink(vol_path)
            return True
        except Exception as e:
            eFound = e
            self.log.error("cannot delete volume's %s/%s link path: %s",
                           self.sdUUID,
                           self.volUUID,
                           vol_path,
                           exc_info=True)

        raise eFound