Ejemplo n.º 1
0
    def syncVolumeChain(self, sdUUID, imgUUID, volUUID, actualChain):
        """
        Fix volume metadata to reflect the given actual chain.  This function
        is used to correct the volume chain linkage after a live merge.
        """
        curChain = self.getChain(sdUUID, imgUUID, volUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in curChain)
        self.log.info("Current chain=%s ", log_str)

        subChain = []
        for vol in curChain:
            if vol.volUUID not in actualChain:
                subChain.insert(0, vol.volUUID)
            elif len(subChain) > 0:
                break
        if len(subChain) == 0:
            return
        self.log.info("Unlinking subchain: %s", subChain)

        sdDom = sdCache.produce(sdUUID=sdUUID)
        dstParent = sdDom.produceVolume(imgUUID, subChain[0]).getParent()
        subChainTailVol = sdDom.produceVolume(imgUUID, subChain[-1])
        if subChainTailVol.isLeaf():
            self.log.info(
                "Leaf volume %s is being removed from the chain. "
                "Marking it ILLEGAL to prevent data corruption",
                subChainTailVol.volUUID)
            subChainTailVol.setLegality(sc.ILLEGAL_VOL)
        else:
            for childID in subChainTailVol.getChildren():
                self.log.info("Setting parent of volume %s to %s", childID,
                              dstParent)
                sdDom.produceVolume(imgUUID, childID). \
                    setParentMeta(dstParent)
Ejemplo n.º 2
0
    def syncData(self, sdUUID, imgUUID, dstSdUUID, syncType):
        srcChain = self.getChain(sdUUID, imgUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in srcChain)
        self.log.info("Source chain=%s ", log_str)

        dstChain = self.getChain(dstSdUUID, imgUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in dstChain)
        self.log.info("Dest chain=%s ", log_str)

        if syncType == SYNC_VOLUMES_INTERNAL:
            try:
                # Removing the leaf volumes
                del srcChain[-1], dstChain[-1]
            except IndexError:
                raise se.ImageIsNotLegalChain()
        elif syncType == SYNC_VOLUMES_LEAF:
            try:
                # Removing all the internal volumes
                del srcChain[:-1], dstChain[:-1]
            except IndexError:
                raise se.ImageIsNotLegalChain()
        elif syncType != SYNC_VOLUMES_ALL:
            raise se.MiscNotImplementedException()

        if len(srcChain) != len(dstChain):
            raise se.DestImageActionError(imgUUID, dstSdUUID)

        # Checking the volume uuids (after removing the leaves to allow
        # different uuids for the current top layer, see previous check).
        for i, v in enumerate(srcChain):
            if v.volUUID != dstChain[i].volUUID:
                raise se.DestImageActionError(imgUUID, dstSdUUID)

        dstDom = sdCache.produce(dstSdUUID)

        self._interImagesCopy(dstDom, sdUUID, imgUUID, {
            'srcChain': srcChain,
            'dstChain': dstChain
        })
        self._finalizeDestinationImage(dstDom, imgUUID, {
            'srcChain': srcChain,
            'dstChain': dstChain
        }, False)
Ejemplo n.º 3
0
    def _activateVolumeForImportExport(self, domain, imgUUID, volUUID=None):
        chain = self.getChain(domain.sdUUID, imgUUID, volUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in chain)
        self.log.info("chain=%s ", log_str)

        template = chain[0].getParentVolume()
        if template or len(chain) > 1:
            self.log.error("Importing and exporting an image with more "
                           "than one volume is not supported")
            raise se.CopyImageError()

        domain.activateVolumes(imgUUID, volUUIDs=[chain[0].volUUID])
        return chain[0]
Ejemplo n.º 4
0
    def validateVolumeChain(self, sdUUID, imgUUID):
        """
        Check correctness of the whole chain (including template if exists)
        """
        if not self.isLegal(sdUUID, imgUUID):
            raise se.ImageIsNotLegalChain(imgUUID)
        chain = self.getChain(sdUUID, imgUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in chain)
        self.log.info("Current chain=%s ", log_str)

        # check if the chain is build above a template, or it is a standalone
        pvol = chain[0].getParentVolume()
        if pvol:
            if not pvol.isLegal() or pvol.isFake():
                raise se.ImageIsNotLegalChain(imgUUID)
Ejemplo n.º 5
0
    def estimateChainSize(self, sdUUID, imgUUID, volUUID, capacity):
        """
        Compute an estimate of the whole chain size
        using the sum of the actual size of the chain's volumes
        """
        chain = self.getChain(sdUUID, imgUUID, volUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in chain)
        self.log.info("chain=%s ", log_str)

        chain_allocation = 0
        template = chain[0].getParentVolume()
        if template:
            chain_allocation = template.getVolumeSize()
        for vol in chain:
            chain_allocation += vol.getVolumeSize()
        if chain_allocation > capacity:
            chain_allocation = capacity
        # allocate %10 more for cow metadata
        chain_allocation = int(chain_allocation * sc.COW_OVERHEAD)
        return chain_allocation
Ejemplo n.º 6
0
    def _createTargetImage(self, destDom, srcSdUUID, imgUUID):
        # Before actual data copying we need perform several operation
        # such as: create all volumes, create fake template if needed, ...
        try:
            # Find all volumes of source image
            srcChain = self.getChain(srcSdUUID, imgUUID)
            log_str = logutils.volume_chain_to_str(vol.volUUID
                                                   for vol in srcChain)
            self.log.info("Source chain=%s ", log_str)
        except se.StorageException:
            self.log.error("Unexpected error", exc_info=True)
            raise
        except Exception as e:
            self.log.error("Unexpected error", exc_info=True)
            raise se.SourceImageActionError(imgUUID, srcSdUUID, str(e))

        fakeTemplate = False
        pimg = sc.BLANK_UUID  # standalone chain
        # check if the chain is build above a template, or it is a standalone
        pvol = srcChain[0].getParentVolume()
        if pvol:
            # find out parent volume parameters
            volParams = pvol.getVolumeParams()
            pimg = volParams['imgUUID']  # pimg == template image
            if destDom.isBackup():
                # FIXME: This workaround help as copy VM to the backup domain
                #        without its template. We will create fake template
                #        for future VM creation and mark it as FAKE volume.
                #        This situation is relevant for backup domain only.
                fakeTemplate = True

        @contextmanager
        def justLogIt(img):
            self.log.debug("You don't really need lock parent of image %s",
                           img)
            yield

        dstImageResourcesNamespace = rm.getNamespace(sc.IMAGE_NAMESPACE,
                                                     destDom.sdUUID)
        # In destination domain we need to lock image's template if exists
        with rm.acquireResource(dstImageResourcesNamespace, pimg, rm.SHARED) \
                if pimg != sc.BLANK_UUID else justLogIt(imgUUID):
            if fakeTemplate:
                self.createFakeTemplate(destDom.sdUUID, volParams)

            dstChain = []
            for srcVol in srcChain:
                # Create the dst volume
                try:
                    # find out src volume parameters
                    volParams = srcVol.getVolumeParams()

                    # To avoid prezeroing preallocated volumes on NFS domains
                    # we create the target as a sparse volume (since it will be
                    # soon filled with the data coming from the copy) and then
                    # we change its metadata back to the original value.
                    if (destDom.supportsSparseness
                            or volParams['volFormat'] != sc.RAW_FORMAT):
                        tmpVolPreallocation = sc.SPARSE_VOL
                    else:
                        tmpVolPreallocation = sc.PREALLOCATED_VOL

                    destDom.createVolume(imgUUID=imgUUID,
                                         capacity=volParams['capacity'],
                                         volFormat=volParams['volFormat'],
                                         preallocate=tmpVolPreallocation,
                                         diskType=volParams['disktype'],
                                         volUUID=srcVol.volUUID,
                                         desc=volParams['descr'],
                                         srcImgUUID=pimg,
                                         srcVolUUID=volParams['parent'])

                    dstVol = destDom.produceVolume(imgUUID=imgUUID,
                                                   volUUID=srcVol.volUUID)

                    # Extend volume (for LV only) size to the actual size
                    dstVol.extend(volParams['apparentsize'])

                    # Change destination volume metadata to preallocated in
                    # case we've used a sparse volume to accelerate the
                    # volume creation
                    if volParams['prealloc'] == sc.PREALLOCATED_VOL \
                            and tmpVolPreallocation != sc.PREALLOCATED_VOL:
                        dstVol.setType(sc.PREALLOCATED_VOL)

                    dstChain.append(dstVol)
                except se.StorageException:
                    self.log.error("Unexpected error", exc_info=True)
                    raise
                except Exception as e:
                    self.log.error("Unexpected error", exc_info=True)
                    raise se.DestImageActionError(imgUUID, destDom.sdUUID,
                                                  str(e))

                # only base may have a different parent image
                pimg = imgUUID

        return {'srcChain': srcChain, 'dstChain': dstChain}
Ejemplo n.º 7
0
    def _start_commit(self, drive, job):
        """
        Start libvirt blockCommit block job.

        Must be called under self._lock.
        """
        # Persist the job before starting the commit, to ensure that vdsm will
        # know about the commit if it was killed after the block job was
        # started.
        job.state = Job.COMMIT
        self._persist_jobs()

        # Check that libvirt exposes full volume chain information
        actual_chain = self._vm.query_drive_volume_chain(drive)
        if actual_chain is None:
            self._untrack_job(job.id)
            raise exception.MergeFailed("Cannot get drive actual volume chain",
                                        drive=drive.name,
                                        alias=drive.alias,
                                        job=job.id)

        try:
            base_target = drive.volume_target(job.base, actual_chain)
            top_target = drive.volume_target(job.top, actual_chain)
        except VolumeNotFound as e:
            self._untrack_job(job.id)
            raise exception.MergeFailed(str(e),
                                        top=job.top,
                                        base=job.base,
                                        chain=actual_chain,
                                        job=job.id)

        # Indicate that we expect libvirt to maintain the relative paths of
        # backing files. This is necessary to ensure that a volume chain is
        # visible from any host even if the mountpoint is different.
        flags = libvirt.VIR_DOMAIN_BLOCK_COMMIT_RELATIVE

        if job.top == drive.volumeID:
            # Pass a flag to libvirt to indicate that we expect a two phase
            # block job. In the first phase, data is copied to base. Once
            # completed, an event is raised to indicate that the job has
            # transitioned to the second phase. We must then tell libvirt to
            # pivot to the new active layer (base).
            flags |= libvirt.VIR_DOMAIN_BLOCK_COMMIT_ACTIVE

        orig_chain = [entry.uuid for entry in actual_chain]
        chain_str = logutils.volume_chain_to_str(orig_chain)
        log.info(
            "Starting merge with job_id=%r, original chain=%s, "
            "disk=%r, base=%r, top=%r, bandwidth=%d, flags=%d", job.id,
            chain_str, drive.name, base_target, top_target, job.bandwidth,
            flags)

        try:
            # pylint: disable=no-member
            self._dom.blockCommit(drive.name,
                                  base_target,
                                  top_target,
                                  job.bandwidth,
                                  flags=flags)
        except libvirt.libvirtError as e:
            self._untrack_job(job.id)
            raise exception.MergeFailed(str(e), job=job.id)
Ejemplo n.º 8
0
    def syncVolumeChain(self, sdUUID, imgUUID, volUUID, actualChain):
        """
        Fix volume metadata to reflect the given actual chain. This function
        is used to correct the volume chain linkage after a live merge or for
        recovery after live merge failure.

        There are multiple cases for the usage of this function:

        1. Marking leaf volume ILLEGAL before we complete a live merge of the
           leaf volume. In this case actual_chain will not contain the leaf
           volume id.

           actual_chain: ["base-vol", "internal-vol"]
           current_chain: ["base-vol", "internal-vol", "leaf-vol"]
           action: mark "leaf-vol" as illegal.

        2. Removal of internal volume after internal live merge was completed
           successfully. In this case actual_chain will not contain one of the
           internal volumes ids.

           actual_chain: ["base-vol", "leaf-vol"]
           current_chain: ["base-vol", "internal-vol", "leaf-vol"]
           action: set "leaf-vol" parent to "base-vol"

        3. Fixing the chain after completing live merge of leaf volume has
           failed. In this case actual_chain will contain all volumes ids. This
           reverts the change done in case 1.

           actual_chain: ["base-vol", "internal-vol", "leaf-vol"]
           current_chain: ["base-vol", "internal-vol", "leaf-vol"]
           action: if "leaf-vol" is ILLEGAL, mark it to LEGAL

        4. Do nothing if actual and current chain matches (no subChain) and
           leaf volume is marked legal. I this case no change needs to be done
           in the current_chain.

           actual_chain: ["base-vol", "internal-vol", "leaf-vol"]
           current_chain: ["base-vol", "internal-vol", "leaf-vol"]
           action: if "leaf-vol" is LEGAL, do nothing
        """
        curChain = self.getChain(sdUUID, imgUUID, volUUID)
        log_str = logutils.volume_chain_to_str(vol.volUUID for vol in curChain)
        self.log.info("Current chain=%s ", log_str)
        sdDom = sdCache.produce(sdUUID)

        subChain = []
        for vol in curChain:
            if vol.volUUID not in actualChain:
                subChain.insert(0, vol.volUUID)
            elif len(subChain) > 0:
                break

        if len(subChain) == 0:
            tailVol = sdDom.produceVolume(imgUUID, volUUID)
            if not tailVol.isLegal():
                # Case 3 - fixing the chain.
                self.log.info(
                    "Leaf volume %s is ILLEGAL but is part of the actual chain"
                    " - marking it LEGAL so it can be used again.",
                    tailVol.volUUID)
                tailVol.setLegality(sc.LEGAL_VOL)
            # Case 4 - do nothing.
            return

        dstParent = sdDom.produceVolume(imgUUID, subChain[0]).getParent()
        subChainTailVol = sdDom.produceVolume(imgUUID, subChain[-1])
        if subChainTailVol.isLeaf():
            # Case 1 - mark leaf ILLEGAL.
            self.log.info(
                "Leaf volume %s is being removed from the actual chain. "
                "Marking it ILLEGAL to prevent data corruption",
                subChainTailVol.volUUID)
            subChainTailVol.setLegality(sc.ILLEGAL_VOL)
        else:
            # Case 2 - remove internal volume.
            for childID in subChainTailVol.getChildren():
                self.log.info(
                    "Internal volume %s removed from actual chain, linking "
                    "child volume %s to parent volume %s", subChainTailVol,
                    childID, dstParent)
                sdDom.produceVolume(imgUUID, childID). \
                    setParentMeta(dstParent)