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 == volume.RAW_FORMAT else 0 try: oop.getProcessPool(dom.sdUUID).truncateFile( volPath, truncSize, mode=VOLUME_PERMISSIONS, creatExcl=True) except OSError as e: if e.errno == errno.EEXIST: raise se.VolumeAlreadyExists(volUUID) raise if preallocate == volume.PREALLOCATED_VOL: try: # ddWatchCopy expects size to be in bytes misc.ddWatchCopy("/dev/zero", volPath, vars.task.aborting, sizeBytes) except 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", volume.type2name(volFormat), volPath, size) if volFormat == volume.COW_FORMAT: qemuimg.create(volPath, sizeBytes, volume.fmt2str(volFormat)) 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, VOLUME_PERMISSIONS) return (volPath, )
def create(cls, repoPath, sdUUID, imgUUID, size, volFormat, preallocate, diskType, volUUID, desc, srcImgUUID, srcVolUUID): """ Create a new volume with given size or snapshot 'size' - in sectors 'volFormat' - volume format COW / RAW 'preallocate' - Prealocate / Sparse 'diskType' - string that describes disk type System|Data|Shared|Swap|Temp 'srcImgUUID' - source image UUID 'srcVolUUID' - source volume UUID """ if not volUUID: volUUID = str(uuid.uuid4()) if volUUID == volume.BLANK_UUID: raise se.InvalidParameterException("volUUID", volUUID) # Validate volume parameters should be checked here for all # internal flows using volume creation. cls.validateCreateVolumeParams(volFormat, preallocate, srcVolUUID) imageDir = image.Image(repoPath).create(sdUUID, imgUUID) vol_path = os.path.join(imageDir, volUUID) voltype = "LEAF" pvol = None # Check if volume already exists if oop.getProcessPool(sdUUID).fileUtils.pathExists(vol_path): raise se.VolumeAlreadyExists(vol_path) # Check if snapshot creation required if srcVolUUID != volume.BLANK_UUID: if srcImgUUID == volume.BLANK_UUID: srcImgUUID = imgUUID pvol = FileVolume(repoPath, sdUUID, srcImgUUID, srcVolUUID) # Cannot create snapshot for ILLEGAL volume if not pvol.isLegal(): raise se.createIllegalVolumeSnapshotError(pvol.volUUID) # create volume rollback vars.task.pushRecovery( task.Recovery("halfbaked volume rollback", "fileVolume", "FileVolume", "halfbakedVolumeRollback", [vol_path])) if preallocate == volume.PREALLOCATED_VOL: try: # ddWatchCopy expects size to be in bytes misc.ddWatchCopy("/dev/zero", vol_path, vars.task.aborting, (int(size) * 512)) except se.ActionStopped, e: raise e except Exception, e: cls.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(vol_path)
def _create(cls, dom, imgUUID, volUUID, size, volFormat, preallocate, volParent, srcImgUUID, srcVolUUID, imgPath, volPath): """ Class specific implementation of volumeCreate. All the exceptions are properly handled and logged in volume.create() """ sizeBytes = int(size) * BLOCK_SIZE if preallocate == volume.SPARSE_VOL: # Sparse = regular file oop.getProcessPool(dom.sdUUID).truncateFile(volPath, sizeBytes) else: try: # ddWatchCopy expects size to be in bytes misc.ddWatchCopy("/dev/zero", volPath, vars.task.aborting, sizeBytes) except 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", volume.type2name(volFormat), volPath, size) if volFormat == volume.COW_FORMAT: volume.createVolume(None, None, volPath, size, volFormat, preallocate) 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(imgPath, volUUID, volFormat, preallocate) # By definition the volume is a leaf cls.file_setrw(volPath, rw=True) return (volPath, )
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() if not force: self.validateDelete() # Mark volume as illegal before deleting self.setLegality(volume.ILLEGAL_VOL) if postZero: self.prepare(justme=True, rw=True, chainrw=force, setrw=True, force=True) try: misc.ddWatchCopy( "/dev/zero", vol_path, vars.task.aborting, int(size), recoveryCallback=volume.baseAsyncTasksRollback) except se.ActionStopped, e: raise e except Exception, e: self.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(volume.ILLEGAL_VOL) if postZero: self.prepare(justme=True, rw=True, chainrw=force, setrw=True, force=True) try: misc.ddWatchCopy("/dev/zero", vol_path, vars.task.aborting, int(size)) except utils.ActionStopped: raise except Exception: self.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(vol_path) 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(volume.BLANK_UUID) if puuid and puuid != volume.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
def create(cls, sdUUID, domainName, domClass, vgUUID, storageType, version): """ Create new storage domain 'sdUUID' - Storage Domain UUID 'domainName' - storage domain name 'domClass' - Data/Iso 'vgUUID' - volume group UUID 'storageType' - NFS_DOMAIN, LOCALFS_DOMAIN, &etc. 'version' - DOMAIN_VERSIONS """ cls.log.info("sdUUID=%s domainName=%s domClass=%s vgUUID=%s " "storageType=%s version=%s", sdUUID, domainName, domClass, vgUUID, storageType, version) if not misc.isAscii(domainName) and not sd.supportsUnicode(version): raise se.UnicodeArgumentException() if len(domainName) > sd.MAX_DOMAIN_DESCRIPTION_SIZE: raise se.StorageDomainDescriptionTooLongError() sd.validateDomainVersion(version) vg = lvm.getVGbyUUID(vgUUID) vgName = vg.name if set((STORAGE_UNREADY_DOMAIN_TAG,)) != set(vg.tags): raise se.VolumeGroupHasDomainTag(vgUUID) try: lvm.getLV(vgName) raise se.StorageDomainNotEmpty(vgUUID) except se.LogicalVolumeDoesNotExistError: pass numOfPVs = len(lvm.listPVNames(vgName)) if version in VERS_METADATA_LV and numOfPVs > MAX_PVS: cls.log.debug("%d > %d", numOfPVs, MAX_PVS) raise se.StorageDomainIsMadeFromTooManyPVs() # Create metadata service volume metasize = cls.metaSize(vgName) lvm.createLV(vgName, sd.METADATA, "%s" % (metasize)) # Create the mapping right now so the index 0 is guaranteed # to belong to the metadata volume. Since the metadata is at # least SDMETADATA/METASIZE units, we know we can use the first # SDMETADATA bytes of the metadata volume for the SD metadata. # pass metadata's dev to ensure it is the first mapping mapping = cls.getMetaDataMapping(vgName) # Create the rest of the BlockSD internal volumes for metaFile, metaSizeMb in sd.SPECIAL_VOLUME_SIZES_MIB.iteritems(): lvm.createLV(vgName, metaFile, metaSizeMb) lvm.createLV(vgName, MASTERLV, MASTERLV_SIZE) # Create VMS file system _createVMSfs(os.path.join("/dev", vgName, MASTERLV)) lvm.deactivateLVs(vgName, MASTERLV) path = lvm.lvPath(vgName, sd.METADATA) # Zero out the metadata and special volumes before use try: misc.ddCopy("/dev/zero", path, RESERVED_METADATA_SIZE) path = lvm.lvPath(vgName, sd.INBOX) misc.ddCopy("/dev/zero", path, RESERVED_MAILBOX_SIZE) path = lvm.lvPath(vgName, sd.OUTBOX) misc.ddCopy("/dev/zero", path, RESERVED_MAILBOX_SIZE) except utils.ActionStopped: raise except se.StorageException: raise se.VolumesZeroingError(path) if version in VERS_METADATA_LV: md = LvBasedSDMetadata(vgName, sd.METADATA) elif version in VERS_METADATA_TAG: md = TagBasedSDMetadata(vgName) logBlkSize, phyBlkSize = lvm.getVGBlockSizes(vgName) # create domain metadata # FIXME : This is 99% like the metadata in file SD # Do we really need to keep the VGUUID? # no one reads it from here anyway initialMetadata = { sd.DMDK_VERSION: version, sd.DMDK_SDUUID: sdUUID, sd.DMDK_TYPE: storageType, sd.DMDK_CLASS: domClass, sd.DMDK_DESCRIPTION: domainName, sd.DMDK_ROLE: sd.REGULAR_DOMAIN, sd.DMDK_POOLS: [], sd.DMDK_LOCK_POLICY: '', sd.DMDK_LOCK_RENEWAL_INTERVAL_SEC: sd.DEFAULT_LEASE_PARAMS[ sd.DMDK_LOCK_RENEWAL_INTERVAL_SEC], sd.DMDK_LEASE_TIME_SEC: sd.DEFAULT_LEASE_PARAMS[ sd.DMDK_LEASE_TIME_SEC], sd.DMDK_IO_OP_TIMEOUT_SEC: sd.DEFAULT_LEASE_PARAMS[ sd.DMDK_IO_OP_TIMEOUT_SEC], sd.DMDK_LEASE_RETRIES: sd.DEFAULT_LEASE_PARAMS[ sd.DMDK_LEASE_RETRIES], DMDK_VGUUID: vgUUID, DMDK_LOGBLKSIZE: logBlkSize, DMDK_PHYBLKSIZE: phyBlkSize, } initialMetadata.update(mapping) md.update(initialMetadata) # Mark VG with Storage Domain Tag try: lvm.replaceVGTag(vgName, STORAGE_UNREADY_DOMAIN_TAG, STORAGE_DOMAIN_TAG) except se.StorageException: raise se.VolumeGroupUninitialized(vgName) bsd = BlockStorageDomain(sdUUID) bsd.initSPMlease() return bsd
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() if not force: self.validateDelete() # Mark volume as illegal before deleting self.setLegality(volume.ILLEGAL_VOL) if postZero: self.prepare(justme=True, rw=True, chainrw=force, setrw=True, force=True) try: misc.ddWatchCopy("/dev/zero", vol_path, vars.task.aborting, int(size)) except utils.ActionStopped: raise except Exception: self.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(vol_path) 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(volume.BLANK_UUID) if puuid and puuid != volume.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
#See http://gerrit.usersys.redhat.com/771 #Assert volumes are writable. (Don't do this at home.) lvNames = (vol.volUUID for vol in volumes) try: lvm.changelv(sdUUID, lvNames, "--permission", "rw") except se.StorageException, e: #Hope this only means that some volumes were already writable pass for lv in lvm.getLV(sdUUID): if lv.name in lvNames: # wipe out the whole volume try: misc.ddWatchCopy( "/dev/zero", lvm.lvPath(sdUUID, lv.name), vars.task.aborting, int(lv.size), recoveryCallback=volume.baseAsyncTasksRollback) except se.ActionStopped, e: raise e except Exception, e: raise se.VolumesZeroingError(lv.name) def deleteMultipleVolumes(sdUUID, volumes, postZero): "Delete multiple volumes (LVs) in the same domain (VG)." "" if postZero: _postZero(sdUUID, volumes) lvNames = [vol.volUUID for vol in volumes] lvm.removeLVs(sdUUID, lvNames)
class BlockStorageDomain(sd.StorageDomain): mountpoint = os.path.join(sd.StorageDomain.storage_repository, sd.DOMAIN_MNT_POINT, sd.BLOCKSD_DIR) def __init__(self, sdUUID): domaindir = os.path.join(self.mountpoint, sdUUID) metadata = selectMetadata(sdUUID) sd.StorageDomain.__init__(self, sdUUID, domaindir, metadata) lvm.activateLVs(self.sdUUID, SPECIAL_LVS) self.metavol = lvm.lvPath(self.sdUUID, sd.METADATA) try: self.logBlkSize = self.getMetaParam(DMDK_LOGBLKSIZE) self.phyBlkSize = self.getMetaParam(DMDK_PHYBLKSIZE) except KeyError: # Initialize the block sizes metadata if not defined self.logBlkSize, self.phyBlkSize = lvm.getVGBlockSizes(sdUUID) self.setMetaParam(DMDK_LOGBLKSIZE, self.logBlkSize) self.setMetaParam(DMDK_PHYBLKSIZE, self.phyBlkSize) # Check that all devices in the VG have the same logical and physical # block sizes. lvm.checkVGBlockSizes(sdUUID, (self.logBlkSize, self.phyBlkSize)) # _extendlock is used to prevent race between # VG extend and LV extend. self._extendlock = threading.Lock() self.imageGarbageCollector() self._registerResourceNamespaces() self._lastUncachedSelftest = 0 def _registerResourceNamespaces(self): """ Register resources namespaces and create factories for it. """ sd.StorageDomain._registerResourceNamespaces(self) rmanager = rm.ResourceManager.getInstance() # Register lvm activation resource namespace for the underlying VG lvmActivationFactory = resourceFactories.LvmActivationFactory( self.sdUUID) lvmActivationNamespace = sd.getNamespace(self.sdUUID, LVM_ACTIVATION_NAMESPACE) try: rmanager.registerNamespace(lvmActivationNamespace, lvmActivationFactory) except Exception: self.log.warn("Resource namespace %s already registered", lvmActivationNamespace) @classmethod def metaSize(cls, vgroup): ''' Calc the minimal meta volume size in MB''' # In any case the metadata volume cannot be less than 512MB for the # case of 512 bytes per volume metadata, 2K for domain metadata and # extent size of 128MB. In any case we compute the right size on line. vg = lvm.getVG(vgroup) minmetasize = (SD_METADATA_SIZE / sd.METASIZE * int(vg.extent_size) + (1024 * 1024 - 1)) / (1024 * 1024) metaratio = int(vg.extent_size) / sd.METASIZE metasize = (int(vg.extent_count) * sd.METASIZE + (1024 * 1024 - 1)) / (1024 * 1024) metasize = max(minmetasize, metasize) if metasize > int(vg.free) / (1024 * 1024): raise se.VolumeGroupSizeError( "volume group has not enough extents %s (Minimum %s), VG may be too small" % (vg.extent_count, (1024 * 1024) / sd.METASIZE)) cls.log.info("size %s MB (metaratio %s)" % (metasize, metaratio)) return metasize @classmethod def create(cls, sdUUID, domainName, domClass, vgUUID, storageType, version): """ Create new storage domain 'sdUUID' - Storage Domain UUID 'domainName' - storage domain name 'vgUUID' - volume group UUID 'domClass' - Data/Iso """ cls.log.info( "sdUUID=%s domainName=%s domClass=%s vgUUID=%s " "storageType=%s version=%s", sdUUID, domainName, domClass, vgUUID, storageType, version) if len(domainName) > sd.MAX_DOMAIN_DESCRIPTION_SIZE: raise se.StorageDomainDescriptionTooLongError() sd.validateDomainVersion(version) vg = lvm.getVGbyUUID(vgUUID) vgName = vg.name if set((STORAGE_UNREADY_DOMAIN_TAG, )) != set(vg.tags): raise se.VolumeGroupHasDomainTag(vgUUID) try: lvm.getLV(vgName) raise se.StorageDomainNotEmpty(vgUUID) except se.LogicalVolumeDoesNotExistError: pass numOfPVs = len(lvm.listPVNames(vgName)) if version in VERS_METADATA_LV and numOfPVs > MAX_PVS: cls.log.debug("%d > %d", numOfPVs, MAX_PVS) raise se.StorageDomainIsMadeFromTooManyPVs() # Set the name of the VG to be the same as sdUUID if vgName != sdUUID: lvm.renameVG(vgName, sdUUID) vgName = sdUUID # Create metadata service volume metasize = cls.metaSize(vgName) lvm.createLV(vgName, sd.METADATA, "%s" % (metasize)) # Create the mapping right now so the index 0 is guaranteed # to belong to the metadata volume. Since the metadata is at # least SDMETADATA/METASIZE units, we know we can use the first # SDMETADATA bytes of the metadata volume for the SD metadata. # pass metadata's dev to ensure it is the first mapping mapping = cls.getMetaDataMapping(vgName) # Create the rest of the BlockSD internal volumes lvm.createLV(vgName, sd.LEASES, sd.LEASES_SIZE) lvm.createLV(vgName, sd.IDS, sd.IDS_SIZE) lvm.createLV(vgName, sd.INBOX, sd.INBOX_SIZE) lvm.createLV(vgName, sd.OUTBOX, sd.OUTBOX_SIZE) lvm.createLV(vgName, MASTERLV, MASTERLV_SIZE) # Create VMS file system _createVMSfs(os.path.join("/dev", vgName, MASTERLV)) lvm.deactivateLVs(vgName, MASTERLV) path = lvm.lvPath(vgName, sd.METADATA) # Zero out the metadata and special volumes before use try: misc.ddCopy("/dev/zero", path, RESERVED_METADATA_SIZE) path = lvm.lvPath(vgName, sd.INBOX) misc.ddCopy("/dev/zero", path, RESERVED_MAILBOX_SIZE) path = lvm.lvPath(vgName, sd.OUTBOX) misc.ddCopy("/dev/zero", path, RESERVED_MAILBOX_SIZE) except se.ActionStopped, e: raise e except se.StorageException: raise se.VolumesZeroingError(path)