Exemplo n.º 1
0
    def calculate_volume_alloc_size(cls, preallocate, capacity, initial_size):
        """ Calculate the allocation size in mb of the volume
        'preallocate' - Sparse or Preallocated
        'capacity' - the volume size in blocks
        'initial_size' - optional, if provided the initial allocated
                         size in blocks for sparse volumes
         """
        if initial_size and preallocate == sc.PREALLOCATED_VOL:
            log.error("Initial size is not supported for preallocated volumes")
            raise se.InvalidParameterException("initial size",
                                               initial_size)

        if initial_size:
            capacity_bytes = capacity * sc.BLOCK_SIZE
            initial_size_bytes = initial_size * sc.BLOCK_SIZE
            max_size = cls.max_size(capacity_bytes, sc.COW_FORMAT)
            if initial_size_bytes > max_size:
                log.error("The requested initial %s is bigger "
                          "than the max size %s", initial_size_bytes, max_size)
                raise se.InvalidParameterException("initial size",
                                                   initial_size)

        if preallocate == sc.SPARSE_VOL:
            if initial_size:
                initial_size = int(initial_size * QCOW_OVERHEAD_FACTOR)
                alloc_size = (utils.round(initial_size, BLOCKS_TO_MB) //
                              BLOCKS_TO_MB)
            else:
                alloc_size = config.getint("irs",
                                           "volume_utilization_chunk_mb")
        else:
            alloc_size = utils.round(capacity, BLOCKS_TO_MB) // BLOCKS_TO_MB

        return alloc_size
Exemplo n.º 2
0
    def calculate_volume_alloc_size(cls, preallocate, capacity, initial_size):
        """ Calculate the allocation size in mb of the volume
        'preallocate' - Sparse or Preallocated
        'capacity' - the volume size in blocks
        'initial_size' - optional, if provided the initial allocated
                         size in blocks for sparse volumes
         """
        if initial_size and preallocate == sc.PREALLOCATED_VOL:
            log.error("Initial size is not supported for preallocated volumes")
            raise se.InvalidParameterException("initial size",
                                               initial_size)

        if initial_size:
            capacity_bytes = capacity * sc.BLOCK_SIZE
            initial_size_bytes = initial_size * sc.BLOCK_SIZE
            max_size = cls.max_size(capacity_bytes, sc.COW_FORMAT)
            if initial_size_bytes > max_size:
                log.error("The requested initial %s is bigger "
                          "than the max size %s", initial_size_bytes, max_size)
                raise se.InvalidParameterException("initial size",
                                                   initial_size)

        if preallocate == sc.SPARSE_VOL:
            if initial_size:
                initial_size = int(initial_size * QCOW_OVERHEAD_FACTOR)
                alloc_size = (utils.round(initial_size, BLOCKS_TO_MB) //
                              BLOCKS_TO_MB)
            else:
                alloc_size = config.getint("irs",
                                           "volume_utilization_chunk_mb")
        else:
            alloc_size = utils.round(capacity, BLOCKS_TO_MB) // BLOCKS_TO_MB

        return alloc_size
Exemplo n.º 3
0
def make_block_volume(lvm,
                      sd_manifest,
                      size,
                      imguuid,
                      voluuid,
                      parent_vol_id=sc.BLANK_UUID,
                      vol_format=sc.RAW_FORMAT,
                      prealloc=sc.PREALLOCATED_VOL,
                      disk_type=image.UNKNOWN_DISK_TYPE,
                      desc='fake volume'):
    sduuid = sd_manifest.sdUUID
    image_manifest = image.ImageManifest(sd_manifest.getRepoPath())
    imagedir = image_manifest.getImageDir(sduuid, imguuid)
    if not os.path.exists(imagedir):
        os.makedirs(imagedir)

    size_mb = utils.round(size, MB) / MB
    lvm.createLV(sduuid, voluuid, size_mb)
    lv_size = int(lvm.getLV(sduuid, voluuid).size)
    lv_size_blk = lv_size / sc.BLOCK_SIZE

    with sd_manifest.acquireVolumeMetadataSlot(voluuid,
                                               sc.VOLUME_MDNUMBLKS) as slot:
        lvm.addtag(sduuid, voluuid, "%s%s" % (sc.TAG_PREFIX_MD, slot))
        lvm.addtag(sduuid, voluuid,
                   "%s%s" % (sc.TAG_PREFIX_PARENT, parent_vol_id))
        lvm.addtag(sduuid, voluuid, "%s%s" % (sc.TAG_PREFIX_IMAGE, imguuid))

    vol_class = sd_manifest.getVolumeClass()
    vol_class.newMetadata(
        (sduuid, slot), sduuid, imguuid, parent_vol_id, lv_size_blk,
        sc.type2name(vol_format), sc.type2name(prealloc),
        sc.type2name(sc.LEAF_VOL), disk_type, desc, sc.LEGAL_VOL)
Exemplo n.º 4
0
 def _extendSizeRaw(self, new_capacity):
     # Since this method relies on lvm.extendLV (lvextend) when the
     # requested size is equal or smaller than the current size, the
     # request is siliently ignored.
     new_capacity_mb = (utils.round(new_capacity, constants.MEGAB) //
                        constants.MEGAB)
     lvm.extendLV(self.sdUUID, self.volUUID, new_capacity_mb)
Exemplo n.º 5
0
Arquivo: merge.py Projeto: minqf/vdsm
def _extend_base_allocation(base_vol, top_vol):
    if not (base_vol.is_block() and base_vol.getFormat() == sc.COW_FORMAT):
        return

    base_alloc = base_vol.getVolumeSize()
    top_alloc = top_vol.getVolumeSize()
    vol_chunk_size = config.getint('irs', 'volume_utilization_chunk_mb') * MiB
    potential_alloc = base_alloc + top_alloc + vol_chunk_size
    # TODO: add chunk_size only if top is leaf.
    capacity = base_vol.getCapacity()
    max_alloc = utils.round(capacity * sc.COW_OVERHEAD, MiB)
    actual_alloc = min(potential_alloc, max_alloc)
    actual_alloc = utils.round(actual_alloc, MiB)
    actual_alloc_mb = actual_alloc // MiB
    dom = sdCache.produce(base_vol.sdUUID)
    dom.extendVolume(base_vol.volUUID, actual_alloc_mb)
Exemplo n.º 6
0
    def optimal_cow_size(cls, required_size, capacity, is_leaf):
        """
        Return the optimal size for a volume based on the required allocation,
        volume capacity, and volume type.

        This is a class method so we can use this calculation for the base
        volume during merge, using the mesured size for the merged instead of
        the actual volume size.
        """
        # Add free space.
        if is_leaf:
            # For leaf volumes, add one chunk of free space so the VM
            # will not pause quickly when it is started.
            free_space = config.getint("irs",
                                       "volume_utilization_chunk_mb") * MiB
            optimal_size = required_size + free_space
        else:
            # For internal volumes, use the required size. It cannot be zero
            # since it includes qcow2 metadata.
            assert required_size > 0
            optimal_size = required_size

        # Align to align_size (lvm extent size) so callers can compare optimal
        # size with the actual size of the logical volume.
        optimal_size = utils.round(optimal_size, cls.align_size)

        # Limit by maximum size.
        max_size = cls.max_size(capacity, sc.COW_FORMAT)
        optimal_size = min(optimal_size, max_size)

        cls.log.debug(
            "COW format, required_size=%s max_size=%s optimal_size=%s",
            required_size, max_size, optimal_size)

        return optimal_size
Exemplo n.º 7
0
    def createLV(self,
                 vgName,
                 lvName,
                 size,
                 activate=True,
                 contiguous=False,
                 initialTags=()):
        try:
            vg_md = self.vgmd[vgName]
        except KeyError:
            raise se.CannotCreateLogicalVolume(vgName, lvName)

        # Size is received as a string in MB.  We need to convert it to bytes
        # and round it up to a multiple of the VG extent size.
        try:
            size = int(size) << 20
        except ValueError:
            raise se.CannotCreateLogicalVolume(vgName, lvName)
        size = utils.round(size, int(vg_md['extent_size']))

        # devices is hard to emulate properly (must have a PE allocator that
        # works the same as for LVM).  Since we don't need this value, use None
        devices = None

        state = 'a' if activate else '-'
        lv_attr = dict(voltype='-',
                       permission='w',
                       allocations='i',
                       fixedminor='-',
                       state=state,
                       devopen='-',
                       target='-',
                       zero='-')
        lv_md = dict(uuid=fake_lvm_uuid(),
                     name=lvName,
                     vg_name=vgName,
                     attr=lv_attr,
                     size=str(size),
                     seg_start_pe='0',
                     devices=devices,
                     tags=initialTags,
                     writeable=True,
                     opened=False,
                     active=bool(activate))

        lv_count = int(vg_md['lv_count']) + 1

        if (vgName, lvName) in self.lvmd:
            raise se.CannotCreateLogicalVolume(vgName, lvName)

        self.lvmd[(vgName, lvName)] = lv_md
        self.vgmd[vgName]['lv_count'] = str(lv_count)

        # Create an LV as a regular file so we have a place to write data
        if activate:
            lv_path = self.lvPath(vgName, lvName)
        else:
            lv_path = self._lvPathInactive(vgName, lvName)
        make_file(lv_path, size)
Exemplo n.º 8
0
 def getMaxVolumeSize(self, capacity):
     """
     Returns the maximum volume size in bytes. This value is larger than
     drive capacity since we must allocate extra space for cow internal
     data. The actual lv size may be larger due to rounding to next lvm
     extent.
     """
     return utils.round(capacity * self.VOLWM_COW_OVERHEAD, constants.MEGAB)
Exemplo n.º 9
0
 def test_lv_create_round_up_size(self):
     with self.base_config() as lvm:
         vg = lvm.getVG(self.VG_NAME)
         extent_size_mb = int(vg.extent_size) // MB
         odd_size_mb = extent_size_mb - 1
         lvm.createLV(self.VG_NAME, self.LV_NAME, odd_size_mb)
         rounded_up_size_mb = utils.round(odd_size_mb, extent_size_mb)
         lv = lvm.getLV(self.VG_NAME, self.LV_NAME)
         self.assertEqual(int(lv.size), rounded_up_size_mb * MB)
Exemplo n.º 10
0
def make_block_volume(lvm,
                      sd_manifest,
                      size,
                      imguuid,
                      voluuid,
                      parent_vol_id=sc.BLANK_UUID,
                      vol_format=sc.RAW_FORMAT,
                      vol_type=sc.LEAF_VOL,
                      prealloc=sc.PREALLOCATED_VOL,
                      disk_type=sc.DATA_DISKTYPE,
                      desc='fake volume',
                      qcow2_compat='0.10'):
    sduuid = sd_manifest.sdUUID
    imagedir = sd_manifest.getImageDir(imguuid)
    if not os.path.exists(imagedir):
        os.makedirs(imagedir)

    lv_size = sd_manifest.getVolumeClass().calculate_volume_alloc_size(
        prealloc, vol_format, size, None)
    lv_size_mb = (utils.round(lv_size, MiB) // MiB)
    lvm.createLV(sduuid, voluuid, lv_size_mb)

    # LVM may create the volume with a larger size due to extent granularity
    if vol_format == sc.RAW_FORMAT:
        lv_size = int(lvm.getLV(sduuid, voluuid).size)
        if lv_size > size:
            size = lv_size

    if vol_format == sc.COW_FORMAT:
        volpath = lvm.lvPath(sduuid, voluuid)
        backing = parent_vol_id if parent_vol_id != sc.BLANK_UUID else None

        # Write qcow2 image to the fake block device - truncating the file.
        op = qemuimg.create(volpath,
                            size=size,
                            format=qemuimg.FORMAT.QCOW2,
                            qcow2Compat=qcow2_compat,
                            backing=backing)
        op.run()

        # Truncate fake block device back ot the proper size.
        with open(volpath, "r+") as f:
            f.truncate(int(lvm.getLV(sduuid, voluuid).size))

    with sd_manifest.acquireVolumeMetadataSlot(voluuid) as slot:
        add_tags = [
            "%s%s" % (sc.TAG_PREFIX_MD, slot),
            "%s%s" % (sc.TAG_PREFIX_PARENT, parent_vol_id),
            "%s%s" % (sc.TAG_PREFIX_IMAGE, imguuid),
        ]
        lvm.changeLVsTags(sduuid, (voluuid, ), addTags=add_tags)

    vol_class = sd_manifest.getVolumeClass()
    vol_class.newMetadata((sduuid, slot), sduuid, imguuid, parent_vol_id, size,
                          sc.type2name(vol_format), sc.type2name(prealloc),
                          sc.type2name(vol_type), disk_type, desc,
                          sc.LEGAL_VOL)
Exemplo n.º 11
0
 def test_lv_create_round_up_size(self):
     with self.base_config() as lvm:
         vg = lvm.getVG(self.VG_NAME)
         extent_size_mb = int(vg.extent_size) // MB
         odd_size_mb = extent_size_mb - 1
         lvm.createLV(self.VG_NAME, self.LV_NAME, odd_size_mb)
         rounded_up_size_mb = utils.round(odd_size_mb, extent_size_mb)
         lv = lvm.getLV(self.VG_NAME, self.LV_NAME)
         self.assertEqual(int(lv.size), rounded_up_size_mb * MB)
Exemplo n.º 12
0
 def getMaxVolumeSize(self, capacity):
     """
     Returns the maximum volume size in bytes. This value is larger than
     drive capacity since we must allocate extra space for cow internal
     data. The actual lv size may be larger due to rounding to next lvm
     extent.
     """
     return utils.round(capacity * self.VOLWM_COW_OVERHEAD,
                        constants.MEGAB)
Exemplo n.º 13
0
 def extend(self, newSize):
     """Extend a logical volume
         'newSize' - new size in blocks
     """
     self.log.info("Request to extend LV %s of image %s in VG %s with "
                   "size = %s", self.volUUID, self.imgUUID, self.sdUUID,
                   newSize)
     # we should return: Success/Failure
     # Backend APIs:
     sizemb = utils.round(newSize, BLOCKS_TO_MB) // BLOCKS_TO_MB
     lvm.extendLV(self.sdUUID, self.volUUID, sizemb)
Exemplo n.º 14
0
 def test_optimal_size_cow_leaf(self):
     # Optimal size calculated using actual size and chunk size.
     with self.make_volume(size=2 * GiB, format=sc.COW_FORMAT) as vol:
         chunk_size = GiB
         check = qemuimg.check(vol.getVolumePath(), qemuimg.FORMAT.QCOW2)
         optimal_size = utils.round(check['offset'] + chunk_size,
                                    vol.align_size)
         self.assertEqual(vol.optimal_size(), optimal_size)
         self.assertEqual(
             vol.optimal_cow_size(check["offset"], 2 * GiB, vol.isLeaf()),
             optimal_size)
Exemplo n.º 15
0
 def extend(self, new_size):
     """Extend a logical volume
         'new_size' - new size in bytes
     """
     self.log.info(
         "Request to extend LV %s of image %s in VG %s with "
         "size = %s", self.volUUID, self.imgUUID, self.sdUUID, new_size)
     # we should return: Success/Failure
     # Backend APIs:
     sizemb = utils.round(new_size, MiB) // MiB
     lvm.extendLV(self.sdUUID, self.volUUID, sizemb)
Exemplo n.º 16
0
    def createLV(self, vgName, lvName, size, activate=True, contiguous=False,
                 initialTags=()):
        try:
            vg_md = self.vgmd[vgName]
        except KeyError:
            raise se.CannotCreateLogicalVolume(vgName, lvName)

        # Size is received as a string in MB.  We need to convert it to bytes
        # and round it up to a multiple of the VG extent size.
        try:
            size = int(size) << 20
        except ValueError:
            raise se.CannotCreateLogicalVolume(vgName, lvName)
        size = utils.round(size, int(vg_md['extent_size']))

        # devices is hard to emulate properly (must have a PE allocator that
        # works the same as for LVM).  Since we don't need this value, use None
        devices = None

        state = 'a' if activate else '-'
        lv_attr = dict(voltype='-',
                       permission='w',
                       allocations='i',
                       fixedminor='-',
                       state=state,
                       devopen='-',
                       target='-',
                       zero='-')
        lv_md = dict(uuid=fake_lvm_uuid(),
                     name=lvName,
                     vg_name=vgName,
                     attr=lv_attr,
                     size=str(size),
                     seg_start_pe='0',
                     devices=devices,
                     tags=initialTags,
                     writeable=True,
                     opened=False,
                     active=bool(activate))

        lv_count = int(vg_md['lv_count']) + 1

        if (vgName, lvName) in self.lvmd:
            raise se.CannotCreateLogicalVolume(vgName, lvName)

        self.lvmd[(vgName, lvName)] = lv_md
        self.vgmd[vgName]['lv_count'] = str(lv_count)

        # Create an LV as a regular file so we have a place to write data
        if activate:
            lv_path = self.lvPath(vgName, lvName)
        else:
            lv_path = self._lvPathInactive(vgName, lvName)
        make_file(lv_path, size)
Exemplo n.º 17
0
 def extend(self, new_size_blk):
     """Extend a logical volume
         'new_size_blk' - new size in blocks
     """
     self.log.info(
         "Request to extend LV %s of image %s in VG %s with "
         "size = %s blocks", self.volUUID, self.imgUUID, self.sdUUID,
         new_size_blk)
     # we should return: Success/Failure
     # Backend APIs:
     sizemb = utils.round(new_size_blk, BLOCKS_TO_MB) // BLOCKS_TO_MB
     lvm.extendLV(self.sdUUID, self.volUUID, sizemb)
Exemplo n.º 18
0
    def _create(cls, dom, imgUUID, volUUID, capacity, volFormat, preallocate,
                volParent, srcImgUUID, srcVolUUID, volPath, initial_size=None,
                add_bitmaps=False):
        """
        Class specific implementation of volumeCreate. All the exceptions are
        properly handled and logged in volume.create()
        """

        lv_size = cls.calculate_volume_alloc_size(
            preallocate, volFormat, capacity, initial_size)
        lv_size_mb = utils.round(lv_size, MiB) // MiB

        lvm.createLV(dom.sdUUID, volUUID, lv_size_mb, activate=True,
                     initialTags=(sc.TAG_VOL_UNINIT,))

        fileutils.rm_file(volPath)
        lvPath = lvm.lvPath(dom.sdUUID, volUUID)
        cls.log.info("Creating volume symlink from %r to %r", lvPath, volPath)
        os.symlink(lvPath, volPath)

        if not volParent:
            cls.log.info("Request to create %s volume %s with capacity = %s",
                         sc.type2name(volFormat), volPath, capacity)
            if volFormat == sc.COW_FORMAT:
                operation = qemuimg.create(volPath,
                                           size=capacity,
                                           format=sc.fmt2str(volFormat),
                                           qcow2Compat=dom.qcow2_compat())
                operation.run()
        else:
            # Create hardlink to template and its meta file
            cls.log.info("Request to create snapshot %s/%s of volume %s/%s "
                         "with capacity %s",
                         imgUUID, volUUID, srcImgUUID, srcVolUUID, capacity)
            volParent.clone(volPath, volFormat, capacity)

        with dom.acquireVolumeMetadataSlot(volUUID) as slot:
            mdTags = ["%s%s" % (sc.TAG_PREFIX_MD, slot),
                      "%s%s" % (sc.TAG_PREFIX_PARENT, srcVolUUID),
                      "%s%s" % (sc.TAG_PREFIX_IMAGE, imgUUID)]
            lvm.changeLVsTags(
                dom.sdUUID,
                (volUUID,),
                delTags=[sc.TAG_VOL_UNINIT],
                addTags=mdTags)

        try:
            lvm.deactivateLVs(dom.sdUUID, [volUUID])
        except se.CannotDeactivateLogicalVolume:
            cls.log.warn("Cannot deactivate new created volume %s/%s",
                         dom.sdUUID, volUUID, exc_info=True)

        return (dom.sdUUID, slot)
Exemplo n.º 19
0
 def getNextVolumeSize(self, curSize, capacity):
     """
     Returns the next volume size in bytes. This value is based on the
     volExtensionChunk property and it's the size that should be requested
     for the next LV extension.  curSize is the current size of the volume
     to be extended.  For the leaf volume curSize == self.apparentsize.
     For internal volumes it is discovered by calling irs.getVolumeSize().
     capacity is the maximum size of the volume. It can be discovered using
     libvirt.virDomain.blockInfo() or qemuimg.info().
     """
     nextSize = utils.round(curSize + self.volExtensionChunk, MiB)
     return min(nextSize, self.getMaxVolumeSize(capacity))
Exemplo n.º 20
0
def test_volume_size_alignment(size_param):
    with fake_block_env() as env:
        sd_id = env.sd_manifest.sdUUID
        img_id = make_uuid()
        vol_id = make_uuid()
        make_block_volume(env.lvm, env.sd_manifest, size_param, img_id, vol_id)
        vol = env.sd_manifest.produceVolume(img_id, vol_id)

        expected_size = utils.round(size_param, sc.VG_EXTENT_SIZE)
        assert expected_size == vol.getCapacity()
        assert expected_size == int(env.lvm.getLV(sd_id, vol_id).size)
        lv_file_size = os.stat(env.lvm.lvPath(sd_id, vol_id)).st_size
        assert expected_size == lv_file_size
Exemplo n.º 21
0
    def reduce(self, newSize, allowActive=False):
        """
        Reduce the size of the logical volume.

        Arguments:
            newSize (int) - new size in blocks
            allowActive (boolean) - indicates whether the LV is active
        """
        self.log.info("Request to reduce LV %s of image %s in VG %s with "
                      "size = %s allowActive = %s", self.volUUID, self.imgUUID,
                      self.sdUUID, newSize, allowActive)
        sizemb = utils.round(newSize, BLOCKS_TO_MB) // BLOCKS_TO_MB
        lvm.reduceLV(self.sdUUID, self.volUUID, sizemb, force=allowActive)
Exemplo n.º 22
0
    def reduce(self, newSize, allowActive=False):
        """
        Reduce the size of the logical volume.

        Arguments:
            newSize (int) - new size in blocks
            allowActive (boolean) - indicates whether the LV is active
        """
        self.log.info("Request to reduce LV %s of image %s in VG %s with "
                      "size = %s allowActive = %s", self.volUUID, self.imgUUID,
                      self.sdUUID, newSize, allowActive)
        sizemb = utils.round(newSize, BLOCKS_TO_MB) // BLOCKS_TO_MB
        lvm.reduceLV(self.sdUUID, self.volUUID, sizemb, force=allowActive)
Exemplo n.º 23
0
 def getNextVolumeSize(self, curSize, capacity):
     """
     Returns the next volume size in bytes. This value is based on the
     volExtensionChunk property and it's the size that should be requested
     for the next LV extension.  curSize is the current size of the volume
     to be extended.  For the leaf volume curSize == self.apparentsize.
     For internal volumes it is discovered by calling irs.getVolumeSize().
     capacity is the maximum size of the volume. It can be discovered using
     libvirt.virDomain.blockInfo() or qemuimg.info().
     """
     nextSize = utils.round(curSize + self.volExtensionChunk,
                            constants.MEGAB)
     return min(nextSize, self.getMaxVolumeSize(capacity))
Exemplo n.º 24
0
    def test_volume_size_alignment(self, size_param):
        with fake_block_env() as env:
            sd_id = env.sd_manifest.sdUUID
            img_id = make_uuid()
            vol_id = make_uuid()
            make_block_volume(env.lvm, env.sd_manifest, size_param,
                              img_id, vol_id)
            vol = env.sd_manifest.produceVolume(img_id, vol_id)

            extent_size = sc.VG_EXTENT_SIZE_MB * MB
            expected_size = utils.round(size_param, extent_size)
            self.assertEqual(expected_size / sc.BLOCK_SIZE, vol.getSize())
            self.assertEqual(expected_size,
                             int(env.lvm.getLV(sd_id, vol_id).size))
            lv_file_size = os.stat(env.lvm.lvPath(sd_id, vol_id)).st_size
            self.assertEqual(expected_size, lv_file_size)
Exemplo n.º 25
0
def _extend_base_allocation(base_vol, top_vol):
    if not (base_vol.is_block() and base_vol.getFormat() == sc.COW_FORMAT):
        return

    base_alloc = base_vol.getVolumeSize(bs=1)
    top_alloc = top_vol.getVolumeSize(bs=1)
    vol_chunk_size = (config.getint('irs', 'volume_utilization_chunk_mb') *
                      constants.MEGAB)
    potential_alloc = base_alloc + top_alloc + vol_chunk_size
    # TODO: add chunk_size only if top is leaf.
    capacity = base_vol.getSize() * sc.BLOCK_SIZE
    max_alloc = utils.round(capacity * sc.COW_OVERHEAD, constants.MEGAB)
    actual_alloc = min(potential_alloc, max_alloc)
    actual_alloc_mb = (actual_alloc + constants.MEGAB - 1) / constants.MEGAB
    dom = sdCache.produce_manifest(base_vol.sdUUID)
    dom.extendVolume(base_vol.volUUID, actual_alloc_mb)
Exemplo n.º 26
0
Arquivo: merge.py Projeto: k0ste/vdsm
def _extend_base_allocation(base_vol, top_vol):
    if not (base_vol.is_block() and base_vol.getFormat() == sc.COW_FORMAT):
        return

    base_alloc = base_vol.getVolumeSize(bs=1)
    top_alloc = top_vol.getVolumeSize(bs=1)
    vol_chunk_size = (config.getint('irs', 'volume_utilization_chunk_mb') *
                      constants.MEGAB)
    potential_alloc = base_alloc + top_alloc + vol_chunk_size
    # TODO: add chunk_size only if top is leaf.
    capacity = base_vol.getSizeBlk() * sc.BLOCK_SIZE
    max_alloc = utils.round(capacity * sc.COW_OVERHEAD, constants.MEGAB)
    actual_alloc = min(potential_alloc, max_alloc)
    actual_alloc_mb = (actual_alloc + constants.MEGAB - 1) / constants.MEGAB
    dom = sdCache.produce(base_vol.sdUUID)
    dom.extendVolume(base_vol.volUUID, actual_alloc_mb)
Exemplo n.º 27
0
    def test_volume_size_alignment(self, size_param):
        with fake_block_env() as env:
            sd_id = env.sd_manifest.sdUUID
            img_id = make_uuid()
            vol_id = make_uuid()
            make_block_volume(env.lvm, env.sd_manifest, size_param, img_id,
                              vol_id)
            vol = env.sd_manifest.produceVolume(img_id, vol_id)

            extent_size = sc.VG_EXTENT_SIZE_MB * MB
            expected_size = utils.round(size_param, extent_size)
            self.assertEqual(expected_size / sc.BLOCK_SIZE, vol.getSize())
            self.assertEqual(expected_size,
                             int(env.lvm.getLV(sd_id, vol_id).size))
            lv_file_size = os.stat(env.lvm.lvPath(sd_id, vol_id)).st_size
            self.assertEqual(expected_size, lv_file_size)
Exemplo n.º 28
0
def make_block_volume(lvm, sd_manifest, size, imguuid, voluuid,
                      parent_vol_id=sc.BLANK_UUID,
                      vol_format=sc.RAW_FORMAT,
                      prealloc=sc.PREALLOCATED_VOL,
                      disk_type=image.UNKNOWN_DISK_TYPE,
                      desc='fake volume'):
    sduuid = sd_manifest.sdUUID
    image_manifest = image.ImageManifest(sd_manifest.getRepoPath())
    imagedir = image_manifest.getImageDir(sduuid, imguuid)
    if not os.path.exists(imagedir):
        os.makedirs(imagedir)

    size_mb = utils.round(size, MB) / MB
    lvm.createLV(sduuid, voluuid, size_mb)
    lv_size = int(lvm.getLV(sduuid, voluuid).size)
    lv_size_blk = lv_size / sc.BLOCK_SIZE

    with sd_manifest.acquireVolumeMetadataSlot(
            voluuid, sc.VOLUME_MDNUMBLKS) as slot:
        lvm.addtag(sduuid, voluuid, "%s%s" % (sc.TAG_PREFIX_MD, slot))
        lvm.addtag(sduuid, voluuid, "%s%s" % (sc.TAG_PREFIX_PARENT,
                                              sc.BLANK_UUID))
        lvm.addtag(sduuid, voluuid, "%s%s" % (sc.TAG_PREFIX_IMAGE, imguuid))

    vol_class = sd_manifest.getVolumeClass()
    vol_class.newMetadata(
        (sduuid, slot),
        sduuid,
        imguuid,
        parent_vol_id,
        lv_size_blk,
        sc.type2name(vol_format),
        sc.type2name(prealloc),
        sc.type2name(sc.LEAF_VOL),
        disk_type,
        desc,
        sc.LEGAL_VOL)
Exemplo n.º 29
0
 def _size_param_to_bytes(self, size):
     # Size is received as a string in MB.  We need to convert it to bytes
     # and round it up to a multiple of the VG extent size.
     extent_size = sc.VG_EXTENT_SIZE_MB << 20
     size = int(size) << 20
     return utils.round(size, extent_size)
Exemplo n.º 30
0
 def _size_param_to_bytes(self, size):
     # Size is received as a string in MB.  We need to convert it to bytes
     # and round it up to a multiple of the VG extent size.
     extent_size = sc.VG_EXTENT_SIZE_MB << 20
     size = int(size) << 20
     return utils.round(size, extent_size)
Exemplo n.º 31
0
 def test_round(self, n, size, result):
     self.assertEqual(utils.round(n, size), result)
Exemplo n.º 32
0
 def test_max_size(self):
     conf = drive_config(format='cow')
     drive = Drive({}, self.log, **conf)
     size = utils.round(self.CAPACITY * drive.VOLWM_COW_OVERHEAD,
                        constants.MEGAB)
     self.assertEqual(drive.getMaxVolumeSize(self.CAPACITY), size)
Exemplo n.º 33
0
 def test_max_size(self):
     conf = drive_config(format='cow', diskType=DISK_TYPE.BLOCK)
     drive = Drive(self.log, **conf)
     size = utils.round(self.CAPACITY * drive.VOLWM_COW_OVERHEAD, MiB)
     assert drive.getMaxVolumeSize(self.CAPACITY) == size
Exemplo n.º 34
0
 def test_round(self, n, size, result):
     self.assertEqual(utils.round(n, size), result)
Exemplo n.º 35
0
 def padToBlockSize(self, path):
     size = _IOProcessOs(self._iop).stat(path).st_size
     newSize = utils.round(size, sc.BLOCK_SIZE_4K)
     log.debug("Truncating file %s to %d bytes", path, newSize)
     truncateFile(self._iop, path, newSize)
Exemplo n.º 36
0
 def _extendSizeRaw(self, newSize):
     # Since this method relies on lvm.extendLV (lvextend) when the
     # requested size is equal or smaller than the current size, the
     # request is siliently ignored.
     newSizeMb = utils.round(newSize, BLOCKS_TO_MB) // BLOCKS_TO_MB
     lvm.extendLV(self.sdUUID, self.volUUID, newSizeMb)
Exemplo n.º 37
0
 def _extendSizeRaw(self, newSize):
     # Since this method relies on lvm.extendLV (lvextend) when the
     # requested size is equal or smaller than the current size, the
     # request is siliently ignored.
     newSizeMb = utils.round(newSize, BLOCKS_TO_MB) // BLOCKS_TO_MB
     lvm.extendLV(self.sdUUID, self.volUUID, newSizeMb)
Exemplo n.º 38
0
 def _size_param_to_bytes(self, size_mb):
     # Size is received in MiB. We need to convert it to bytes
     # and round it up to a multiple of the VG extent size.
     return utils.round(size_mb * MiB, sc.VG_EXTENT_SIZE)
Exemplo n.º 39
0
    def snapshot(self):
        """Live snapshot command"""
        def norm_snap_drive_params(drive):
            """Normalize snapshot parameters"""

            if "baseVolumeID" in drive:
                base_drv = {
                    "device": "disk",
                    "domainID": drive["domainID"],
                    "imageID": drive["imageID"],
                    "volumeID": drive["baseVolumeID"]
                }
                target_drv = base_drv.copy()
                target_drv["volumeID"] = drive["volumeID"]

            elif "baseGUID" in drive:
                base_drv = {"GUID": drive["baseGUID"]}
                target_drv = {"GUID": drive["GUID"]}

            elif "baseUUID" in drive:
                base_drv = {"UUID": drive["baseUUID"]}
                target_drv = {"UUID": drive["UUID"]}

            else:
                base_drv, target_drv = (None, None)

            return base_drv, target_drv

        def rollback_drives(new_drives):
            """Rollback the prepared volumes for the snapshot"""

            for vm_dev_name, drive in new_drives.items():
                try:
                    self.vm.cif.teardownVolumePath(drive)
                except Exception:
                    self.vm.log.exception("Unable to teardown drive: %s",
                                          vm_dev_name)

        def memory_snapshot(memory_volume_path):
            """Libvirt snapshot XML"""

            return vmxml.Element('memory',
                                 snapshot='external',
                                 file=memory_volume_path)

        def vm_conf_for_memory_snapshot():
            """Returns the needed vm configuration with the memory snapshot"""

            return {
                'restoreFromSnapshot': True,
                '_srcDomXML': self.vm.migratable_domain_xml(),
                'elapsedTimeOffset': time.time() - self.vm.start_time
            }

        snap = vmxml.Element('domainsnapshot')
        disks = vmxml.Element('disks')
        new_drives = {}
        vm_drives = {}

        for drive in self.snap_drives:
            base_drv, tget_drv = norm_snap_drive_params(drive)

            try:
                self.vm.findDriveByUUIDs(tget_drv)
            except LookupError:
                # The vm is not already using the requested volume for the
                # snapshot, continuing.
                pass
            else:
                # The snapshot volume is the current one, skipping
                self.vm.log.debug("The volume is already in use: %s", tget_drv)
                continue  # Next drive

            try:
                vm_drive = self.vm.findDriveByUUIDs(base_drv)
            except LookupError:
                # The volume we want to snapshot doesn't exist
                self.vm.log.error("The base volume doesn't exist: %s",
                                  base_drv)
                return response.error('snapshotErr')

            if vm_drive.hasVolumeLeases:
                self.vm.log.error('disk %s has volume leases', vm_drive.name)
                return response.error('noimpl')

            if vm_drive.transientDisk:
                self.vm.log.error('disk %s is a transient disk', vm_drive.name)
                return response.error('transientErr')

            vm_dev_name = vm_drive.name

            new_drives[vm_dev_name] = tget_drv.copy()
            new_drives[vm_dev_name]["type"] = "disk"
            new_drives[vm_dev_name]["diskType"] = vm_drive.diskType
            new_drives[vm_dev_name]["poolID"] = vm_drive.poolID
            new_drives[vm_dev_name]["name"] = vm_dev_name
            new_drives[vm_dev_name]["format"] = "cow"

            # We need to keep track of the drive object because
            # it keeps original data and used to generate snapshot element.
            # We keep the old volume ID so we can clear the block threshold.
            vm_drives[vm_dev_name] = (vm_drive, base_drv["volumeID"])

        prepared_drives = {}

        for vm_dev_name, vm_device in new_drives.items():
            # Adding the device before requesting to prepare it as we want
            # to be sure to teardown it down even when prepareVolumePath
            # failed for some unknown issue that left the volume active.
            prepared_drives[vm_dev_name] = vm_device
            try:
                new_drives[vm_dev_name]["path"] = \
                    self.vm.cif.prepareVolumePath(new_drives[vm_dev_name])
            except Exception:
                self.vm.log.exception(
                    'unable to prepare the volume path for '
                    'disk %s', vm_dev_name)
                rollback_drives(prepared_drives)
                return response.error('snapshotErr')

            drive, _ = vm_drives[vm_dev_name]
            snapelem = drive.get_snapshot_xml(vm_device)
            disks.appendChild(snapelem)

        snap.appendChild(disks)

        snap_flags = (libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT
                      | libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA)

        if self.memory_params:
            # Save the needed vm configuration
            # TODO: this, as other places that use pickle.dump
            # directly to files, should be done with outOfProcess
            vm_conf_vol = self.memory_params['dstparams']
            vm_conf_vol_path = self.vm.cif.prepareVolumePath(vm_conf_vol)
            try:
                with open(vm_conf_vol_path, "rb+") as f:
                    vm_conf = vm_conf_for_memory_snapshot()
                    # protocol=2 is needed for clusters < 4.4
                    # (for Python 2 host compatibility)
                    data = pickle.dumps(vm_conf, protocol=2)

                    # Ensure that the volume is aligned; qemu-img may segfault
                    # when converting unligned images.
                    # https://bugzilla.redhat.com/1649788
                    aligned_length = utils.round(len(data), 4096)
                    data = data.ljust(aligned_length, b"\0")

                    f.write(data)
                    f.flush()
                    os.fsync(f.fileno())
            finally:
                self.vm.cif.teardownVolumePath(vm_conf_vol)

            # Adding the memory volume to the snapshot xml
            memory_vol = self.memory_params['dst']
            memory_vol_path = self.vm.cif.prepareVolumePath(memory_vol)
            snap.appendChild(memory_snapshot(memory_vol_path))
        else:
            memory_vol = memory_vol_path = None
            snap_flags |= libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY

        snapxml = xmlutils.tostring(snap)
        # TODO: this is debug information. For 3.6.x we still need to
        # see the XML even with 'info' as default level.
        self.vm.log.info("%s", snapxml)

        self._snapshot_job['memoryVolPath'] = memory_vol_path
        self._snapshot_job['memoryVol'] = memory_vol
        self._snapshot_job['newDrives'] = new_drives
        vm_drives_serialized = {}
        for k, v in vm_drives.items():
            vm_drives_serialized[k] = [xmlutils.tostring(v[0].getXML()), v[1]]
        self._snapshot_job['vmDrives'] = vm_drives_serialized
        self.vm.update_snapshot_metadata(self._snapshot_job)

        # We need to stop the drive monitoring for two reasons, one is to
        # prevent spurious libvirt errors about missing drive paths (since
        # we're changing them), and also to prevent to trigger a drive
        # extension for the new volume with the apparent size of the old one
        # (the apparentsize is updated as last step in updateDriveParameters)
        self.vm.drive_monitor.disable()

        try:
            if self.should_freeze:
                self.vm.freeze()
            try:
                self.vm.log.info(
                    "Taking a live snapshot (drives=%s,"
                    "memory=%s)",
                    ', '.join(drive["name"] for drive in new_drives.values()),
                    self.memory_params is not None)
                self.vm.run_dom_snapshot(snapxml, snap_flags)
                self.vm.log.info("Completed live snapshot")
            except libvirt.libvirtError:
                self.vm.log.exception("Unable to take snapshot")
                if self.should_freeze:
                    self.vm.thaw()
                return response.error('snapshotErr')
        except:
            # In case the VM was shutdown in the middle of the snapshot
            # operation we keep doing the finalizing and reporting the failure.
            self._finalize_vm(memory_vol)
            res = False
        else:
            res = self.teardown(memory_vol_path, memory_vol, new_drives,
                                vm_drives)
        if not res:
            raise RuntimeError("Failed to execute snapshot, "
                               "considering the operation as failure")
Exemplo n.º 40
0
 def test_max_size(self):
     conf = drive_config(format='cow')
     drive = Drive({}, self.log, **conf)
     size = utils.round(self.CAPACITY * drive.VOLWM_COW_OVERHEAD,
                        constants.MEGAB)
     self.assertEqual(drive.getMaxVolumeSize(self.CAPACITY), size)
Exemplo n.º 41
0
def padToBlockSize(path):
    with open(path, 'a') as f:
        size = os.fstat(f.fileno()).st_size
        newSize = utils.round(size, sc.BLOCK_SIZE)
        log.info("Truncating file %s to %d bytes", path, newSize)
        os.ftruncate(f.fileno(), newSize)