def test_optimal_size_cow_leaf_empty(self): # verify optimal size equals to actual size + one chunk. with self.make_volume(size=GIB, format=sc.COW_FORMAT) as vol: chunk_size = 1024 * constants.MEGAB check = qemuimg.check(vol.getVolumePath(), qemuimg.FORMAT.QCOW2) actual_size = check['offset'] + chunk_size self.assertEqual(vol.optimal_size(), actual_size)
def test_check(self): with namedTemporaryDir() as tmpdir: path = os.path.join(tmpdir, 'test.qcow2') qemuimg.create(path, size=1048576, format=qemuimg.FORMAT.QCOW2) info = qemuimg.check(path) # The exact value depends on qcow2 internals self.assertEqual(int, type(info['offset']))
def test_offset_without_stats(self): def call(cmd, **kw): out = ["No errors were found on the image.", "Image end offset: 4271243264"] return 0, out, [] with MonkeyPatchScope([(utils, "execCmd", call)]): check = qemuimg.check('unused') self.assertEquals(4271243264, check['offset'])
def test_commit(self, qcow2_compat, base, top, use_base): size = 1048576 with namedTemporaryDir() as tmpdir: chain = [] parent = None # Create a chain of 4 volumes. for i in range(4): vol = os.path.join(tmpdir, "vol%d.img" % i) format = (qemuimg.FORMAT.RAW if i == 0 else qemuimg.FORMAT.QCOW2) make_image(vol, size, format, i, qcow2_compat, parent) orig_offset = qemuimg.check(vol)["offset"] if i > 0 else None chain.append((vol, orig_offset)) parent = vol base_vol = chain[base][0] top_vol = chain[top][0] op = qemuimg.commit(top_vol, topFormat=qemuimg.FORMAT.QCOW2, base=base_vol if use_base else None) with utils.closing(op): op.wait_for_completion() base_fmt = (qemuimg.FORMAT.RAW if base == 0 else qemuimg.FORMAT.QCOW2) for i in range(base, top + 1): offset = i * 1024 pattern = 0xf0 + i # The base volume must have the data from all the volumes # merged into it. qemu_pattern_verify(base_vol, base_fmt, offset=offset, len=1024, pattern=pattern) if i > base: # internal and top volumes should keep the data, we # may want to wipe this data when deleting the volumes # later. vol, orig_offset = chain[i] actual_offset = qemuimg.check(vol)["offset"] self.assertEqual(actual_offset, orig_offset)
def test_offset_with_stats(self): def call(cmd, **kw): out = ["No errors were found on the image.", "65157/98304 = 66.28% allocated, 0.00% fragmented, 0.00% " "compressed clusters", "Image end offset: 4271243264"] return 0, out, [] with MonkeyPatchScope([(utils, "execCmd", call)]): check = qemuimg.check('unused') self.assertEquals(4271243264, check['offset'])
def test_offset_without_stats(self): def call(cmd, **kw): out = [ "No errors were found on the image.", "Image end offset: 4271243264" ] return 0, out, [] with MonkeyPatchScope([(commands, "execCmd", call)]): check = qemuimg.check('unused') self.assertEquals(4271243264, check['offset'])
def test_offset_with_stats(self): def call(cmd, **kw): out = [ "No errors were found on the image.", "65157/98304 = 66.28% allocated, 0.00% fragmented, 0.00% " "compressed clusters", "Image end offset: 4271243264" ] return 0, out, [] with MonkeyPatchScope([(commands, "execCmd", call)]): check = qemuimg.check('unused') self.assertEquals(4271243264, check['offset'])
def optimal_size(self): """ Return the optimal size of the volume. Returns: optimal size is the minimum of the volume maximum size and the volume actual size plus padding. For leaf volumes, the padding is one chunk, and for internal volumes the padding is `MIN_PADDING`. Size is returned in bytes. Note: the volume must be prepared when calling this helper. """ if self.getFormat() == sc.RAW_FORMAT: virtual_size = self.getSize() * sc.BLOCK_SIZE self.log.debug("RAW format, using virtual size: %s", virtual_size) return virtual_size # Read actual size. check = qemuimg.check(self.getVolumePath(), qemuimg.FORMAT.QCOW2) actual_size = check['offset'] # Add padding. if self.isLeaf(): # For leaf volumes, the padding is one chunk. chnuk_size_mb = int(config.get("irs", "volume_utilization_chunk_mb")) padding = chnuk_size_mb * constants.MEGAB self.log.debug("Leaf volume, using padding: %s", padding) potential_optimal_size = actual_size + padding else: # For internal volumes, using minimal padding. padding = MIN_PADDING self.log.debug("Internal volume, using padding: %s", padding) potential_optimal_size = actual_size + padding # Limit optimal size to the minimal volume size. potential_optimal_size = max(sc.MIN_CHUNK, potential_optimal_size) # Limit optimal size by maximum size. max_size = self.max_size(self.getSize() * sc.BLOCK_SIZE, self.getFormat()) optimal_size = min(potential_optimal_size, max_size) self.log.debug("COW format, actual_size: %s, max_size: %s, " "optimal_size: %s", actual_size, max_size, optimal_size) return optimal_size
def optimal_size(self): """ Return the optimal size of the volume. Returns: optimal size is the minimum of the volume maximum size and the volume actual size plus padding. For leaf volumes, the padding is one chunk, and for internal volumes the padding is `MIN_PADDING`. Size is returned in bytes. Note: the volume must be prepared when calling this helper. """ if self.getFormat() == sc.RAW_FORMAT: virtual_size = self.getSize() * sc.BLOCK_SIZE self.log.debug("RAW format, using virtual size: %s", virtual_size) return virtual_size # Read actual size. check = qemuimg.check(self.getVolumePath(), qemuimg.FORMAT.QCOW2) actual_size = check['offset'] # Add padding. if self.isLeaf(): # For leaf volumes, the padding is one chunk. chnuk_size_mb = int( config.get("irs", "volume_utilization_chunk_mb")) padding = chnuk_size_mb * constants.MEGAB self.log.debug("Leaf volume, using padding: %s", padding) potential_optimal_size = actual_size + padding else: # For internal volumes, using minimal padding. padding = MIN_PADDING self.log.debug("Internal volume, using padding: %s", padding) potential_optimal_size = actual_size + padding # Limit optimal size to the minimal volume size. potential_optimal_size = max(sc.MIN_CHUNK, potential_optimal_size) # Limit optimal size by maximum size. max_size = self.max_size(self.getSize() * sc.BLOCK_SIZE, self.getFormat()) optimal_size = min(potential_optimal_size, max_size) self.log.debug( "COW format, actual_size: %s, max_size: %s, " "optimal_size: %s", actual_size, max_size, optimal_size) return optimal_size
def shrinkToOptimalSize(self): """ Reduce a logical volume to the actual disk size, adding round up to next closest volume utilization chunk """ volParams = self.getVolumeParams() if volParams['volFormat'] == volume.COW_FORMAT: self.prepare() try: check = qemuimg.check(self.getVolumePath(), qemuimg.FORMAT.QCOW2) finally: self.teardown(self.sdUUID, self.volUUID) volActualSize = check['offset'] volExtendSizeMB = int( config.get("irs", "volume_utilization_chunk_mb")) volExtendSize = volExtendSizeMB * constants.MEGAB volUtil = int(config.get("irs", "volume_utilization_percent")) finalSize = (volActualSize + volExtendSize * volUtil * 0.01) finalSize += volExtendSize - (finalSize % volExtendSize) self.log.debug('Shrink qcow volume: %s to : %s bytes', self.volUUID, finalSize) self.reduce((finalSize + BLOCK_SIZE - 1) / BLOCK_SIZE)
def shrinkToOptimalSize(self): """ Reduce a logical volume to the actual disk size, adding round up to next closest volume utilization chunk """ volParams = self.getVolumeParams() if volParams['volFormat'] == sc.COW_FORMAT: self.prepare() try: check = qemuimg.check(self.getVolumePath(), qemuimg.FORMAT.QCOW2) finally: self.teardown(self.sdUUID, self.volUUID) volActualSize = check['offset'] volExtendSizeMB = int(config.get( "irs", "volume_utilization_chunk_mb")) volExtendSize = volExtendSizeMB * constants.MEGAB volUtil = int(config.get("irs", "volume_utilization_percent")) finalSize = (volActualSize + volExtendSize * volUtil * 0.01) finalSize += volExtendSize - (finalSize % volExtendSize) self.log.debug('Shrink qcow volume: %s to : %s bytes', self.volUUID, finalSize) self.reduce((finalSize + BLOCK_SIZE - 1) / BLOCK_SIZE)