def createVolume(parent, parent_format, volume, size, format, prealloc): """ --- Create new volume. 'parent' - backing volume name 'parent_format' - backing volume format 'volume' - new volume name 'format' - volume format [ 'COW' or 'RAW' ] 'size' - in sectors, always multiple of the grain size (64KB) 'preallocate' - flag PREALLOCATED_VOL/SPARSE_VOL, defines actual storage device type. PREALLOCATED_VOL = preallocated storage using non-sparse format (+ DD for file, use raw LV for SAN) # SAN Prealloc/RAW = Normal LV (if snapshot => create copy of LV) Sparse/RAW = if snapshot create LVM snapshot (in the future, use storage backend thin provisioning), else create Normal LV <== Not supported Prealloc/COW = build qcow2 image within a preallocated space - used only for COPY Sparse/COW = QCOW2 over LV # File Prealloc/RAW = Normal file + DD (implicit pre-zero) Sparse/RAW = Normal file (touch) Prealloc/COW = QCOW2 + DD <== Not supported Sparse/COW = QCOW2 """ # TODO: accept size only in bytes and convert before call to qemu-img cmd = [constants.EXT_QEMUIMG, "create", "-f", fmt2str(format)] cwd = None if format == COW_FORMAT and parent: # cmd += ["-b", parent, volume] # cwd = os.path.split(os.path.split(volume)[0])[0] # Temporary fix for qemu-img creation problem cmd += ["-F", parent_format, "-b", os.path.join("..", parent), volume] cwd = os.path.split(volume)[0] else: size = int(size) if size < 1: raise se.createVolumeSizeError() # qemu-img expects size to be in kilobytes by default, # can also accept size in M or G with appropriate suffix # +1 is so that odd numbers will round upwards. cmd += [volume, "%uK" % ((size + 1) / 2)] (rc, out, err) = misc.execCmd(cmd, sudo=False, cwd=cwd) if rc: raise se.VolumeCreationError(out) return True
def create(cls, repoPath, sdUUID, imgUUID, size, volFormat, preallocate, diskType, volUUID, desc, srcImgUUID, srcVolUUID, initialSize=None): """ Create a new volume with given size or snapshot 'size' - in sectors 'volFormat' - volume format COW / RAW 'preallocate' - Preallocate / Sparse 'diskType' - enum (API.Image.DiskTypes) 'srcImgUUID' - source image UUID 'srcVolUUID' - source volume UUID 'initialSize' - initial volume size in sectors, in case of thin provisioning """ dom = sdCache.produce(sdUUID) dom.validateCreateVolumeParams(volFormat, srcVolUUID, preallocate=preallocate) imgPath = image.Image(repoPath).create(sdUUID, imgUUID) volPath = os.path.join(imgPath, volUUID) volParent = None volType = type2name(LEAF_VOL) # Get the specific class name and class module to be used in the # Recovery tasks. clsModule, clsName = cls._getModuleAndClass() try: if srcVolUUID != BLANK_UUID: # When the srcImgUUID isn't specified we assume it's the same # as the imgUUID if srcImgUUID == BLANK_UUID: srcImgUUID = imgUUID volParent = cls(repoPath, sdUUID, srcImgUUID, srcVolUUID) if not volParent.isLegal(): raise se.createIllegalVolumeSnapshotError( volParent.volUUID) if imgUUID != srcImgUUID: volParent.share(imgPath) volParent = cls(repoPath, sdUUID, imgUUID, srcVolUUID) # Override the size with the size of the parent size = volParent.getSize() except se.StorageException: cls.log.error("Unexpected error", exc_info=True) raise except Exception as e: cls.log.error("Unexpected error", exc_info=True) raise se.VolumeCannotGetParent( "Couldn't get parent %s for volume %s: %s" % (srcVolUUID, volUUID, e)) try: cls.log.info("Creating volume %s", volUUID) # Rollback sentinel to mark the start of the task vars.task.pushRecovery( task.Recovery(task.ROLLBACK_SENTINEL, clsModule, clsName, "startCreateVolumeRollback", [sdUUID, imgUUID, volUUID])) # Create volume rollback vars.task.pushRecovery( task.Recovery("Halfbaked volume rollback", clsModule, clsName, "halfbakedVolumeRollback", [sdUUID, volUUID, volPath])) # Specific volume creation (block, file, etc...) try: metaId = cls._create(dom, imgUUID, volUUID, size, volFormat, preallocate, volParent, srcImgUUID, srcVolUUID, volPath, initialSize=initialSize) except (se.VolumeAlreadyExists, se.CannotCreateLogicalVolume, se.VolumeCreationError, se.InvalidParameterException) as e: cls.log.error("Failed to create volume %s: %s", volPath, e) vars.task.popRecovery() raise # When the volume format is raw what the guest sees is the apparent # size of the file/device therefore if the requested size doesn't # match the apparent size (eg: physical extent granularity in LVM) # we need to update the size value so that the metadata reflects # the correct state. if volFormat == RAW_FORMAT: apparentSize = int(dom.getVSize(imgUUID, volUUID) / BLOCK_SIZE) if apparentSize < size: cls.log.error( "The volume %s apparent size %s is smaller " "than the requested size %s", volUUID, apparentSize, size) raise se.VolumeCreationError() if apparentSize > size: cls.log.info( "The requested size for volume %s doesn't " "match the granularity on domain %s, " "updating the volume size from %s to %s", volUUID, sdUUID, size, apparentSize) size = apparentSize vars.task.pushRecovery( task.Recovery("Create volume metadata rollback", clsModule, clsName, "createVolumeMetadataRollback", map(str, metaId))) cls.newMetadata(metaId, sdUUID, imgUUID, srcVolUUID, size, type2name(volFormat), type2name(preallocate), volType, diskType, desc, LEGAL_VOL) if dom.hasVolumeLeases(): cls.newVolumeLease(metaId, sdUUID, volUUID) except se.StorageException: cls.log.error("Unexpected error", exc_info=True) raise except Exception as e: cls.log.error("Unexpected error", exc_info=True) raise se.VolumeCreationError("Volume creation %s failed: %s" % (volUUID, e)) # Remove the rollback for the halfbaked volume vars.task.replaceRecoveries( task.Recovery("Create volume rollback", clsModule, clsName, "createVolumeRollback", [repoPath, sdUUID, imgUUID, volUUID, imgPath])) return volUUID
# should stay %d and size should be int(size) volsize = "%s" % (size / 2 / 1024) vars.task.pushRecovery( task.Recovery("halfbaked volume rollback", "blockVolume", "BlockVolume", "halfbakedVolumeRollback", [sdUUID, volUUID, vol_path])) lvm.createLV(sdUUID, volUUID, volsize, activate=True) if os.path.exists(vol_path): os.unlink(vol_path) os.symlink(lvm.lvPath(sdUUID, volUUID), vol_path) except se.StorageException: cls.log.error("Unexpected error", exc_info=True) raise except Exception, e: cls.log.error("Unexpected error", exc_info=True) raise se.VolumeCreationError( "blockVolume create/link lv %s failed: %s" % (volUUID, str(e))) # By definition volume is now a leaf and should be writeable. # Default permission for lvcreate is read and write. No need to set permission. try: cls.log.info( "blockVolume: create: volUUID %s srcImg %s srvVol %s" % (volUUID, srcImgUUID, srcVolUUID)) if not pvol: cls.log.info( "Request to create %s volume %s with size = %s sectors", volume.type2name(volFormat), vol_path, size) # Create 'raw' volume via qemu-img actually redundant if volFormat == volume.COW_FORMAT: volume.createVolume(None, None, vol_path, size, volFormat,