Exemple #1
0
    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:
                # ddWatchCopy expects size to be in bytes
                misc.ddWatchCopy("/dev/zero", volPath,
                                 vars.task.aborting, sizeBytes)
            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,)
Exemple #2
0
 def testStop(self):
     """
     Test that stop really stops the copying process.
     """
     try:
         with tempfile.NamedTemporaryFile() as src:
             os.unlink(src.name)
             os.mkfifo(src.name)
             with tempfile.NamedTemporaryFile() as dst:
                 misc.ddWatchCopy(src.name, dst.name, lambda: True, 100)
     except exception.ActionStopped:
         self.log.info("Looks like it stopped!")
     else:
         self.fail("Copying didn't stop!")
Exemple #3
0
 def testStop(self):
     """
     Test that stop really stops the copying process.
     """
     try:
         with tempfile.NamedTemporaryFile() as src:
             os.unlink(src.name)
             os.mkfifo(src.name)
             with tempfile.NamedTemporaryFile() as dst:
                 misc.ddWatchCopy(src.name, dst.name, lambda: True, 100)
     except exception.ActionStopped:
         self.log.info("Looks like it stopped!")
     else:
         self.fail("Copying didn't stop!")
Exemple #4
0
    def testCopy(self):
        """
        Test that regular copying works.
        """
        # Prepare source
        data = "Everything starts somewhere, " + \
               "though many physicists disagree." + \
               "But people have always been dimly aware of the " + \
               "problem with the start of things." + \
               "They wonder how the snowplough driver gets to work, or " + \
               "how the makers of dictionaries look up the spelling of words."
        # (C) Terry Pratchet - Small Gods

        # Makes sure we round up to a complete block size
        data *= 512

        with temporaryPath(perms=0o666, data=data) as srcPath:
            with temporaryPath(perms=0o666) as dstPath:
                # Copy
                rc, out, err = misc.ddWatchCopy(srcPath, dstPath,
                                                None, len(data))

                # Get copied data
                with open(dstPath) as f:
                    readData = f.read()

        # Comapre
        self.assertEqual(readData, data)
Exemple #5
0
    def testNonAlignedAppend(self):
        data = "ABCD" * 256  # 1Kb
        add_data = "E"
        repetitions = misc.MEGA / len(data)

        path = self._createDataFile(data, repetitions)
        try:
            with open(path, "a") as f:  # Appending additional data
                f.write(add_data)

            self.assertEqual(os.stat(path).st_size, misc.MEGA + len(add_data))

            # Using os.stat(path).st_size is part of the test, please do not
            # remove or change.
            rc, out, err = misc.ddWatchCopy(
                "/dev/zero", path, None, misc.MEGA, os.stat(path).st_size)

            self.assertEqual(rc, 0)
            self.assertEquals(os.stat(path).st_size,
                              misc.MEGA * 2 + len(add_data))

            with open(path, "r") as f:
                for i in range(repetitions):
                    self.assertEqual(f.read(len(data)), data)
                # Checking the additional data
                self.assertEqual(f.read(len(add_data)), add_data)
        finally:
            os.unlink(path)
Exemple #6
0
    def testNonAlignedCopy(self):
        """
        Test that copying a file with odd length works.
        """

        data = '- "What\'re quantum mechanics?"' + \
               '- "I don\'t know. People who repair quantums, I suppose."'
        # (C) Terry Pratchet - Small Gods

        # Make sure the length is appropriate
        if (len(data) % 512) == 0:
            data += "!"

        with temporaryPath(perms=0o666, data=data) as srcPath:
            with temporaryPath(perms=0o666) as dstPath:
                # Copy
                rc, out, err = misc.ddWatchCopy(srcPath, dstPath,
                                                None, len(data))

                # Get copied data
                with open(dstPath) as dp:
                    readData = dp.read()

        # Compare
        self.assertEqual(readData, data)
Exemple #7
0
    def testCopy(self):
        """
        Test that regular copying works.
        """
        # Prepare source
        data = "Everything starts somewhere, " + \
               "though many physicists disagree." + \
               "But people have always been dimly aware of the " + \
               "problem with the start of things." + \
               "They wonder how the snowplough driver gets to work, or " + \
               "how the makers of dictionaries look up the spelling of words."
        # (C) Terry Pratchet - Small Gods

        # Makes sure we round up to a complete block size
        data *= 512

        with temporaryPath(perms=0o666, data=data) as srcPath:
            with temporaryPath(perms=0o666) as dstPath:
                # Copy
                rc, out, err = misc.ddWatchCopy(srcPath, dstPath,
                                                None, len(data))

                # Get copied data
                with open(dstPath) as f:
                    readData = f.read()

        # Comapre
        self.assertEquals(readData, data)
Exemple #8
0
    def testNonAlignedAppend(self):
        data = "ABCD" * 256  # 1Kb
        add_data = "E"
        repetitions = misc.MEGA / len(data)

        path = self._createDataFile(data, repetitions)
        try:
            with open(path, "a") as f:  # Appending additional data
                f.write(add_data)

            self.assertEquals(os.stat(path).st_size, misc.MEGA + len(add_data))

            # Using os.stat(path).st_size is part of the test, please do not
            # remove or change.
            rc, out, err = misc.ddWatchCopy(
                "/dev/zero", path, None, misc.MEGA, os.stat(path).st_size)

            self.assertEquals(rc, 0)
            self.assertEquals(os.stat(path).st_size,
                              misc.MEGA * 2 + len(add_data))

            with open(path, "r") as f:
                for i in range(repetitions):
                    self.assertEquals(f.read(len(data)), data)
                # Checking the additional data
                self.assertEquals(f.read(len(add_data)), add_data)
        finally:
            os.unlink(path)
Exemple #9
0
    def testNonAlignedCopy(self, sudo=False):
        """
        Test that copying a file with odd length works.
        """

        data = '- "What\'re quantum mechanics?"' + \
               '- "I don\'t know. People who repair quantums, I suppose."'
        # (C) Terry Pratchet - Small Gods

        # Make sure the length is appropriate
        if (len(data) % 512) == 0:
            data += "!"

        with temporaryPath(perms=0o666, data=data) as srcPath:
            with temporaryPath(perms=0o666) as dstPath:
                # Copy
                rc, out, err = misc.ddWatchCopy(srcPath, dstPath,
                                                None, len(data))

                # Get copied data
                with open(dstPath) as dp:
                    readData = dp.read()

        # Compare
        self.assertEquals(readData, data)
Exemple #10
0
    def _extendSizeRaw(self, newSize):
        volPath = self.getVolumePath()
        curSizeBytes = self.oop.os.stat(volPath).st_size
        newSizeBytes = newSize * BLOCK_SIZE

        # No real sanity checks here, they should be included in the calling
        # function/method. We just validate the sizes to be consistent since
        # they're computed and used in the pre-allocated case.
        if newSizeBytes == curSizeBytes:
            return  # Nothing to do
        elif curSizeBytes <= 0:
            raise se.StorageException("Volume size is impossible: %s" %
                                      curSizeBytes)
        elif newSizeBytes < curSizeBytes:
            raise se.VolumeResizeValueError(newSize)

        if self.getType() == sc.PREALLOCATED_VOL:
            # for pre-allocated we need to zero to the file size
            misc.ddWatchCopy("/dev/zero", volPath, vars.task.aborting,
                             newSizeBytes - curSizeBytes, curSizeBytes)
        else:
            # for sparse files we can just truncate to the correct size
            self.oop.truncateFile(volPath, newSizeBytes)
Exemple #11
0
    def _extendSizeRaw(self, newSize):
        volPath = self.getVolumePath()
        curSizeBytes = self.oop.os.stat(volPath).st_size
        newSizeBytes = newSize * BLOCK_SIZE

        # No real sanity checks here, they should be included in the calling
        # function/method. We just validate the sizes to be consistent since
        # they're computed and used in the pre-allocated case.
        if newSizeBytes == curSizeBytes:
            return  # Nothing to do
        elif curSizeBytes <= 0:
            raise se.StorageException(
                "Volume size is impossible: %s" % curSizeBytes)
        elif newSizeBytes < curSizeBytes:
            raise se.VolumeResizeValueError(newSize)

        if self.getType() == sc.PREALLOCATED_VOL:
            # for pre-allocated we need to zero to the file size
            misc.ddWatchCopy("/dev/zero", volPath, vars.task.aborting,
                             newSizeBytes - curSizeBytes, curSizeBytes)
        else:
            # for sparse files we can just truncate to the correct size
            self.oop.truncateFile(volPath, newSizeBytes)
Exemple #12
0
    def testAlignedAppend(self):
        data = "ABCD" * 256  # 1Kb
        repetitions = misc.MEGA / len(data)

        path = self._createDataFile(data, repetitions)
        try:
            # Using os.stat(path).st_size is part of the test, please do not
            # remove or change.
            rc, out, err = misc.ddWatchCopy(
                "/dev/zero", path, None, misc.MEGA, os.stat(path).st_size)

            self.assertEqual(rc, 0)
            self.assertEqual(os.stat(path).st_size, misc.MEGA * 2)

            with open(path, "r") as f:
                for i in range(repetitions):
                    self.assertEqual(f.read(len(data)), data)
        finally:
            os.unlink(path)
Exemple #13
0
    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:
                # ddWatchCopy expects size to be in bytes
                misc.ddWatchCopy("/dev/zero", volPath, vars.task.aborting,
                                 sizeBytes)
            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, sizeBytes, sc.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, sc.FILE_VOLUME_PERMISSIONS)

        return (volPath, )
Exemple #14
0
    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
Exemple #15
0
    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