def _deleteImage(dom, imgUUID, postZero): """This ancillary function will be removed. Replaces Image.delete() in Image.[copy(), move(),multimove()]. """ allVols = dom.getAllVolumes() imgVols = sd.getVolsOfImage(allVols, imgUUID) if not imgVols: log.warning("No volumes found for image %s. %s", imgUUID, allVols) return elif postZero: dom.zeroImage(dom.sdUUID, imgUUID, imgVols) else: dom.deleteImage(dom.sdUUID, imgUUID, imgVols)
def merge(self, sdUUID, vmUUID, imgUUID, ancestor, successor, postZero): """Merge source volume to the destination volume. 'successor' - source volume UUID 'ancestor' - destination volume UUID """ self.log.info("sdUUID=%s vmUUID=%s" " imgUUID=%s ancestor=%s successor=%s postZero=%s", sdUUID, vmUUID, imgUUID, ancestor, successor, str(postZero)) sdDom = sdCache.produce(sdUUID) allVols = sdDom.getAllVolumes() volsImgs = sd.getVolsOfImage(allVols, imgUUID) # Since image namespace should be locked is produce all the volumes is # safe. Producing the (eventual) template is safe also. # TODO: Split for block and file based volumes for efficiency sake. vols = {} for vName in volsImgs.iterkeys(): vols[vName] = sdDom.produceVolume(imgUUID, vName) srcVol = vols[successor] srcVolParams = srcVol.getVolumeParams() srcVolParams['children'] = [] for vName, vol in vols.iteritems(): if vol.getParent() == successor: srcVolParams['children'].append(vol) dstVol = vols[ancestor] dstParentUUID = dstVol.getParent() if dstParentUUID != sd.BLANK_UUID: volParams = vols[dstParentUUID].getVolumeParams() else: volParams = dstVol.getVolumeParams() accSize, chain = self.subChainSizeCalc(ancestor, successor, vols) imageApparentSize = volParams['size'] # allocate %10 more for cow metadata reqSize = min(accSize, imageApparentSize) * 1.1 try: # Start the actual merge image procedure # IMPORTANT NOTE: volumes in the same image chain might have # different capacity since the introduction of the disk resize # feature. This means that when we merge volumes the ancestor # should get the new size from the successor (in order to be # able to contain the additional data that we are collapsing). if dstParentUUID != sd.BLANK_UUID: # The ancestor isn't a base volume of the chain. self.log.info("Internal volume merge: src = %s dst = %s", srcVol.getVolumePath(), dstVol.getVolumePath()) chainToRemove = self._internalVolumeMerge( sdDom, srcVolParams, volParams, reqSize, chain) # The ancestor is actually a base volume of the chain. # We have 2 cases here: # Case 1: ancestor is a COW volume (use 'rebase' workaround) # Case 2: ancestor is a RAW volume (use 'convert + rebase') elif volParams['volFormat'] == volume.RAW_FORMAT: self.log.info("merge with convert: src = %s dst = %s", srcVol.getVolumePath(), dstVol.getVolumePath()) chainToRemove = self._baseRawVolumeMerge( sdDom, srcVolParams, volParams, [vols[vName] for vName in chain]) else: self.log.info("4 steps merge: src = %s dst = %s", srcVol.getVolumePath(), dstVol.getVolumePath()) chainToRemove = self._baseCowVolumeMerge( sdDom, srcVolParams, volParams, reqSize, chain) # This is unrecoverable point, clear all recoveries vars.task.clearRecoveries() # mark all snapshots from 'ancestor' to 'successor' as illegal self.markIllegalSubChain(sdDom, imgUUID, chainToRemove) except ActionStopped: raise except se.StorageException: self.log.error("Unexpected error", exc_info=True) raise except Exception as e: self.log.error(e, exc_info=True) raise se.SourceImageActionError(imgUUID, sdUUID, str(e)) try: # remove all snapshots from 'ancestor' to 'successor' self.removeSubChain(sdDom, imgUUID, chainToRemove, postZero) except Exception: self.log.error("Failure to remove subchain %s -> %s in image %s", ancestor, successor, imgUUID, exc_info=True) newVol = sdDom.produceVolume(imgUUID=srcVolParams['imgUUID'], volUUID=srcVolParams['volUUID']) try: newVol.shrinkToOptimalSize() except qemuImg.QImgError: self.log.warning("Auto shrink after merge failed", exc_info=True) self.log.info("Merge src=%s with dst=%s was successfully finished.", srcVol.getVolumePath(), dstVol.getVolumePath())