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)
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))
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,)
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)
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))
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))
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)
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