def test_chain(self, tmpdir): base = str(tmpdir.join("base")) top = str(tmpdir.join("top")) dst = str(tmpdir.join("dst")) base_offset = 4 * 64 * 1024 top_offset = 5 * 64 * 1024 # Create base image with pattern. op = qemuimg.create( base, size=10 * 64 * 1024, format=qemuimg.FORMAT.RAW) op.run() qemuio.write_pattern(base, qemuimg.FORMAT.RAW, offset=base_offset) # Create top image with pattern. op = qemuimg.create( top, format=qemuimg.FORMAT.QCOW2, qcow2Compat="1.1", backing=base) op.run() qemuio.write_pattern(top, qemuimg.FORMAT.QCOW2, offset=top_offset) # Convert, collpasing top and base into dst. op = qemuimg.convert( top, dst, srcFormat=qemuimg.FORMAT.QCOW2, dstFormat=qemuimg.FORMAT.RAW, unordered_writes=True) op.run() # Verify patterns qemuio.verify_pattern(dst, qemuimg.FORMAT.RAW, offset=base_offset) qemuio.verify_pattern(dst, qemuimg.FORMAT.RAW, offset=top_offset)
def make_volume(env, size, md_fmt, real_fmt): img_id = make_uuid() vol_id = make_uuid() env.make_volume(size, img_id, vol_id, vol_format=md_formats[md_fmt]) vol = env.sd_manifest.produceVolume(img_id, vol_id) qemuimg.create(vol.getVolumePath(), size, qemu_formats[real_fmt]) return vol
def test_unsafe_create_volume(self): with namedTemporaryDir() as tmpdir: path = os.path.join(tmpdir, 'test.qcow2') # Using unsafe=True to verify that it is possible to create an # image based on a non-existing backing file, like an inactive LV. qemuimg.create(path, size=1048576, format=qemuimg.FORMAT.QCOW2, backing='no-such-file', unsafe=True)
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 vol_chain(tmp_mount): virtual_size = MiB # Create base volume base_vol = os.path.join(tmp_mount.path, 'base.img') op = qemuimg.create( base_vol, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1' ) op.run() # Create top volume top_vol = os.path.join(tmp_mount.path, 'top.img') op = qemuimg.create( top_vol, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1', backing=base_vol, backingFormat='qcow2' ) op.run() return Volumes(base_vol, top_vol)
def make_qemu_chain(env, size, base_vol_fmt, chain_len, qcow2_compat='0.10'): vol_list = [] img_id = make_uuid() parent_vol_id = sc.BLANK_UUID vol_fmt = base_vol_fmt for i in range(chain_len): vol_id = make_uuid() if parent_vol_id != sc.BLANK_UUID: vol_fmt = sc.COW_FORMAT vol_type = sc.LEAF_VOL if i == chain_len - 1 else sc.INTERNAL_VOL env.make_volume(size, img_id, vol_id, parent_vol_id=parent_vol_id, vol_format=vol_fmt, vol_type=vol_type) vol = env.sd_manifest.produceVolume(img_id, vol_id) if vol_fmt == sc.COW_FORMAT: backing = parent_vol_id if parent_vol_id != sc.BLANK_UUID else None qemuimg.create(vol.volumePath, size=size, format=qemuimg.FORMAT.QCOW2, qcow2Compat=qcow2_compat, backing=backing) vol_list.append(vol) parent_vol_id = vol_id return vol_list
def test_ok(self, vol_fmt): with self.fake_volume(vol_fmt) as vol: qemu_fmt = sc.FMT2STR[vol_fmt] qemuimg.create(vol.volumePath, size=self.SIZE, format=qemu_fmt) h = FakeHSM() self.assertNotRaises(h.verify_untrusted_volume, 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_wrong_format_raises(self, vol_fmt, qemu_fmt): with self.fake_volume(vol_fmt) as vol: qemuimg.create(vol.volumePath, size=self.SIZE, format=qemu_fmt) h = FakeHSM() self.assertRaises(se.ImageVerificationError, h.verify_untrusted_volume, 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_read_bad_chain_raises(tmpdir): # Create a good chain. base_qcow2 = str(tmpdir.join("base.qcow2")) op = qemuimg.create(base_qcow2, "1m", qemuimg.FORMAT.QCOW2) op.run() top = str(tmpdir.join("top.qcow2")) op = qemuimg.create(top, "1m", qemuimg.FORMAT.QCOW2, backing=base_qcow2, backingFormat=qemuimg.FORMAT.QCOW2) op.run() # Create a broken chain using unsafe rebase with the wrong backing # format. base_raw = str(tmpdir.join("base.raw")) op = qemuimg.create(base_raw, "1m", qemuimg.FORMAT.RAW) op.run() operation = qemuimg.rebase(top, backing=base_raw, format=qemuimg.FORMAT.QCOW2, backingFormat=qemuimg.FORMAT.QCOW2, unsafe=True) operation.run() with pytest.raises(cmdutils.Error): qemuio.verify_pattern(top, qemuimg.FORMAT.QCOW2)
def test_zero_size(self): def create(cmd, **kw): expected = [QEMU_IMG, 'create', 'image', '0'] self.assertEqual(cmd, expected) return 0, '', '' with MonkeyPatchScope([(commands, "execCmd", create)]): qemuimg.create('image', size=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: operation = fallocate.allocate(volPath, sizeBytes) with vars.task.abort_callback(operation.abort): with utils.stopwatch("Preallocating volume %s" % volPath): operation.run() 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,)
def make_file_volume(sd_manifest, size, imguuid, voluuid, parent_vol_id=sc.BLANK_UUID, vol_format=sc.RAW_FORMAT, vol_type=sc.LEAF_VOL, prealloc=sc.SPARSE_VOL, disk_type=sc.DATA_DISKTYPE, desc='fake volume', qcow2_compat='0.10', legal=True): volpath = os.path.join(sd_manifest.domaindir, "images", imguuid, voluuid) # Create needed path components. make_file(volpath, size) # Create the image. if vol_format == sc.COW_FORMAT: backing = parent_vol_id if parent_vol_id != sc.BLANK_UUID else None if backing: backing_path = os.path.join(sd_manifest.domaindir, "images", imguuid, backing) backing_format = qemuimg.info(backing_path)["format"] else: backing_format = None op = qemuimg.create(volpath, size=size, format=qemuimg.FORMAT.QCOW2, qcow2Compat=qcow2_compat, backing=backing, backingFormat=backing_format) op.run() else: # TODO: Use fallocate helper like the real code. if prealloc == sc.PREALLOCATED_VOL: preallocation = qemuimg.PREALLOCATION.FALLOC else: preallocation = None op = qemuimg.create(volpath, size=size, format=qemuimg.FORMAT.RAW, preallocation=preallocation) op.run() # Create meta files. mdfiles = [volpath + '.meta', volpath + '.lease'] for mdfile in mdfiles: make_file(mdfile) vol_class = sd_manifest.getVolumeClass() vol_class.newMetadata((volpath, ), sd_manifest.sdUUID, imguuid, parent_vol_id, size, sc.type2name(vol_format), sc.type2name(prealloc), sc.type2name(vol_type), disk_type, desc, sc.LEGAL_VOL if legal else sc.ILLEGAL_VOL)
def test_no_match(self, img_format): with namedTemporaryDir() as tmpdir: path = os.path.join(tmpdir, 'test') qemuimg.create(path, '1m', img_format) qemu_pattern_write(path, img_format, pattern=2) self.assertRaises(ChainVerificationError, qemu_pattern_verify, path, img_format, pattern=4)
def test_qcow2_compat_version3(self): def create(cmd, **kw): expected = [ QEMU_IMG, 'create', '-f', 'qcow2', '-o', 'compat=1.1', 'image' ] self.assertEqual(cmd, expected) return 0, '', '' with MonkeyPatchScope([(qemuimg, 'config', CONFIG), (commands, 'execCmd', create)]): qemuimg.create('image', format='qcow2', qcow2Compat='1.1')
def test_missing_backing_file(self): with fake_file_env() as env: vol = make_qemu_chain(env, self.SIZE, sc.COW_FORMAT, 2)[1] # Simulate upload of image without backing file to a a snapshot qemuimg.create(vol.volumePath, size=self.SIZE, format=qemuimg.FORMAT.QCOW2) h = FakeHSM() self.assertRaises(se.ImageVerificationError, h.verify_untrusted_volume, 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_qcow2_collapsed(self, user_mount, dst_compat, create): virtual_size = MiB # Create empty source chain. src_base = os.path.join(user_mount.path, 'src_base.img') op = qemuimg.create( src_base, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1' ) op.run() src_top = os.path.join(user_mount.path, 'src_top.img') op = qemuimg.create( src_top, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1', backing=src_base, backingFormat='qcow2' ) op.run() # Create destination image. dst = os.path.join(user_mount.path, 'dst.img') op = qemuimg.create( dst, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat=dst_compat, ) op.run() # Copy src chain to dst. op = qemuimg.convert( src_top, dst, srcFormat='qcow2', dstFormat='qcow2', dstQcow2Compat=dst_compat, create=create ) op.run() # Since source is empty strict compare should work on both source # chain and destination. op = qemuimg.compare( src_top, dst, img1_format='qcow2', img2_format='qcow2', strict=True ) op.run()
def test_backingfile_raises(self): with self.fake_volume(sc.COW_FORMAT) as vol: qemu_fmt = qemuimg.FORMAT.QCOW2 qemuimg.create(vol.volumePath, size=self.SIZE, format=qemu_fmt, backing='foo') h = FakeHSM() self.assertRaises(se.ImageVerificationError, h.verify_untrusted_volume, 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_unexpected_backing_file(self): with self.fake_volume(sc.COW_FORMAT) as vol: # Simulate upload of qcow2 with backing file to base image qemuimg.create(vol.volumePath, size=self.SIZE, format=qemuimg.FORMAT.QCOW2, backing='unexpected') h = FakeHSM() self.assertRaises(se.ImageVerificationError, h.verify_untrusted_volume, 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_empty_image(self, qcow2_compat, desired_qcow2_compat): with namedTemporaryDir() as tmpdir: base_path = os.path.join(tmpdir, 'base.img') leaf_path = os.path.join(tmpdir, 'leaf.img') size = 1048576 qemuimg.create(base_path, size=size, format=qemuimg.FORMAT.RAW) qemuimg.create(leaf_path, format=qemuimg.FORMAT.QCOW2, backing=base_path) qemuimg.amend(leaf_path, desired_qcow2_compat) self.assertEqual( qemuimg.info(leaf_path)['compat'], desired_qcow2_compat)
def make_image(path, size, format, index, qcow2_compat, backing=None): qemuimg.create(path, size=size, format=format, qcow2Compat=qcow2_compat, backing=backing) offset = index * 1024 qemu_pattern_write(path, format, offset=offset, len=1024, pattern=0xf0 + index)
def test_match_custom_offset_and_len(self, offset, len): with namedTemporaryDir() as tmpdir: path = os.path.join(tmpdir, 'test') qemuimg.create(path, '1m', qemuimg.FORMAT.QCOW2) qemu_pattern_write(path, qemuimg.FORMAT.QCOW2, offset=offset, len=len) qemu_pattern_verify(path, qemuimg.FORMAT.QCOW2, offset=offset, len=len)
def test_valid_qcow2_compat(self, hsm_compat, config_compat, sd_version): with self.fake_volume(vol_fmt=sc.COW_FORMAT, sd_version=sd_version) as vol: create_conf = make_config([('irs', 'qcow2_compat', config_compat)]) info = {"format": qemuimg.FORMAT.QCOW2, "compat": hsm_compat} with MonkeyPatchScope([(qemuimg, 'config', create_conf), (qemuimg, 'info', lambda unused: info)]): qemuimg.create(vol.volumePath, size=self.SIZE, format=qemuimg.FORMAT.QCOW2) h = FakeHSM() self.assertNotRaises(h.verify_untrusted_volume, 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_empty_image(self, monkeypatch, qcow2_compat, desired_compat): monkeypatch.setattr(qemuimg, 'config', CONFIG) with namedTemporaryDir() as tmpdir: base_path = os.path.join(tmpdir, 'base.img') leaf_path = os.path.join(tmpdir, 'leaf.img') size = 1048576 op_base = qemuimg.create(base_path, size=size, format=qemuimg.FORMAT.RAW) op_base.run() op_leaf = qemuimg.create(leaf_path, format=qemuimg.FORMAT.QCOW2, backing=base_path) op_leaf.run() qemuimg.amend(leaf_path, desired_compat) assert qemuimg.info(leaf_path)['compat'] == desired_compat
def test_qcow2_to_compressed_qcow2(self, tmpdir): src_file = str(tmpdir.join("test_src.qcow2")) dst_file = str(tmpdir.join("test_dst.qcow2")) op = qemuimg.create( src_file, size=1 * GIB, format=qemuimg.FORMAT.QCOW2) op.run() qemuio.write_pattern( src_file, qemuimg.FORMAT.QCOW2, len=1 * MEGAB, pattern=0xf0) src_file_size = qemuimg.info(src_file)["actualsize"] op = qemuimg.convert( src_file, dst_file, srcFormat=qemuimg.FORMAT.QCOW2, dstFormat=qemuimg.FORMAT.QCOW2, compressed=True) op.run() dst_file_size = qemuimg.info(dst_file)["actualsize"] assert src_file_size > dst_file_size
def test_no_match(tmpdir, image_format): path = str(tmpdir.join('test.' + image_format)) op = qemuimg.create(path, '1m', image_format) op.run() qemuio.write_pattern(path, image_format, pattern=2) with pytest.raises(qemuio.VerificationError): qemuio.verify_pattern(path, image_format, pattern=4)
def _create_cow_volume( cls, dom, vol_id, capacity, vol_path, initial_size, vol_parent, img_id, src_img_id, src_vol_id): """ specific implementation of _create() for COW volumes. All the exceptions are properly handled and logged in volume.create() """ if initial_size: cls.log.error("initial size is not supported " "for file-based volumes") raise se.InvalidParameterException("initial size", initial_size) cls._truncate_volume(vol_path, 0, vol_id, dom) if not vol_parent: cls.log.info("Request to create COW volume %s with capacity = %s", vol_path, capacity) operation = qemuimg.create(vol_path, size=capacity, format=sc.fmt2str(sc.COW_FORMAT), 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", img_id, vol_id, src_img_id, src_vol_id, capacity) vol_parent.clone(vol_path, sc.COW_FORMAT, capacity) # Forcing the volume permissions in case one of the tools we use # (dd, qemu-img, etc.) will mistakenly change the file permissions. cls._set_permissions(vol_path, dom) return (vol_path,)
def _allocate_volume(cls, vol_path, size, preallocate): try: # Always create sparse image, since qemu-img create uses # posix_fallocate() which is inefficient and harmful. op = qemuimg.create(vol_path, size=size, format=qemuimg.FORMAT.RAW) # This is fast but it can get stuck if storage is inaccessible. with vars.task.abort_callback(op.abort): with utils.stopwatch("Creating image {}".format(vol_path), level=logging.INFO, log=cls.log): op.run() # If the image is preallocated, allocate the rest of the image # using fallocate helper. qemu-img create always writes zeroes to # the first block so we should skip it during preallocation. if preallocate == sc.PREALLOCATED_VOL: op = fallocate.allocate(vol_path, size - 4096, offset=4096) # This is fast on NFS 4.2, GlusterFS, XFS and ext4, but can be # extremely slow on NFS < 4.2, writing zeroes to entire image. with vars.task.abort_callback(op.abort): with utils.stopwatch( "Preallocating volume {}".format(vol_path), level=logging.INFO, log=cls.log): op.run() except exception.ActionStopped: raise except Exception: cls.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(vol_path)
def test_info(self): with namedTemporaryDir() as tmpdir: base_path = os.path.join(tmpdir, 'base.img') leaf_path = os.path.join(tmpdir, 'leaf.img') size = 1048576 leaf_fmt = qemuimg.FORMAT.QCOW2 with MonkeyPatchScope([(qemuimg, 'config', CONFIG)]): qemuimg.create(base_path, size=size, format=qemuimg.FORMAT.RAW) qemuimg.create(leaf_path, format=leaf_fmt, backing=base_path) info = qemuimg.info(leaf_path) self.assertEqual(leaf_fmt, info['format']) self.assertEqual(size, info['virtualsize']) self.assertEqual(self.CLUSTER_SIZE, info['clustersize']) self.assertEqual(base_path, info['backingfile']) self.assertEqual('0.10', info['compat'])
def test_open_write_mode(tmpdir, fmt): image = str(tmpdir.join("disk." + fmt)) op = qemuimg.create(image, "1m", fmt) op.run() with qemuio.open(image, fmt): with pytest.raises(cmdutils.Error): qemuimg.info(image, fmt)
def test_skip_holes_during_merge_bitmaps(tmp_mount, vol_chain): virtual_size = MiB bitmap = 'bitmap' # Create base parent volume base_parent_vol = os.path.join(tmp_mount.path, 'base_parent.img') op = qemuimg.create( base_parent_vol, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1' ) op.run() # Rebase the volume chain on top of base parent volume op = qemuimg.rebase( vol_chain.base_vol, base_parent_vol, unsafe=True) op.run() # Add new bitmap to base parent volume op = qemuimg.bitmap_add(base_parent_vol, bitmap) op.run() # Add new bitmap to top volume, base volume is missing that # bitmap so there is a hole op = qemuimg.bitmap_add(vol_chain.top_vol, bitmap) op.run() bitmaps.merge_bitmaps( vol_chain.base_vol, vol_chain.top_vol, base_parent_path=base_parent_vol) info = qemuimg.info(vol_chain.base_vol) assert 'bitmaps' not in info
def make_volume(env, size, md_fmt, real_fmt): img_id = make_uuid() vol_id = make_uuid() env.make_volume(size, img_id, vol_id, vol_format=md_formats[md_fmt]) vol = env.sd_manifest.produceVolume(img_id, vol_id) op = qemuimg.create(vol.getVolumePath(), size, qemu_formats[real_fmt]) op.run() return vol
def test_untrusted_image(self): with namedTemporaryDir() as tmpdir: img = os.path.join(tmpdir, 'untrusted.img') size = 500 * 1024**3 op = qemuimg.create(img, size=size, format=qemuimg.FORMAT.QCOW2) op.run() info = qemuimg.info(img, trusted_image=False) assert size == info['virtualsize']
def test_unsafe_info(self, unsafe): with namedTemporaryDir() as tmpdir: img = os.path.join(tmpdir, 'img.img') size = 1048576 op = qemuimg.create(img, size=size, format=qemuimg.FORMAT.QCOW2) op.run() info = qemuimg.info(img, unsafe=unsafe) assert size == info['virtualsize']
def test_zero_size(self): with namedTemporaryDir() as tmpdir: image = os.path.join(tmpdir, "image") op = qemuimg.create(image, size=0) op.run() info = qemuimg.info(image) assert info['format'] == qemuimg.FORMAT.RAW assert info['virtualsize'] == 0
def test_ok(self, vol_fmt): with self.fake_volume(vol_fmt) as vol: qemu_fmt = sc.FMT2STR[vol_fmt] op = qemuimg.create(vol.volumePath, size=self.SIZE, format=qemu_fmt) op.run() h = FakeHSM() h.verify_untrusted_volume( 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_wrong_format_raises(self, vol_fmt, qemu_fmt): with self.fake_volume(vol_fmt) as vol: op = qemuimg.create(vol.volumePath, size=self.SIZE, format=qemu_fmt) op.run() h = FakeHSM() with pytest.raises(se.ImageVerificationError): h.verify_untrusted_volume( 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
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) size_blk = (size + sc.BLOCK_SIZE - 1) // sc.BLOCK_SIZE lv_size = sd_manifest.getVolumeClass().calculate_volume_alloc_size( prealloc, size_blk, None) lvm.createLV(sduuid, voluuid, lv_size) # LVM may create the volume with a larger size due to extent granularity lv_size_blk = int(lvm.getLV(sduuid, voluuid).size) // sc.BLOCK_SIZE if lv_size_blk > size_blk: size_blk = lv_size_blk 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: 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, size_blk, sc.type2name(vol_format), sc.type2name(prealloc), sc.type2name(vol_type), disk_type, desc, sc.LEGAL_VOL)
def test_allocate(self, image_format, allocation_mode, allocated_bytes): size = 16 * 1024 * 1024 with temporaryPath() as image: op = qemuimg.create(image, size=size, format=image_format, preallocation=allocation_mode) op.run() allocated = os.stat(image).st_blocks * 512 assert allocated == allocated_bytes
def test_check(self): with namedTemporaryDir() as tmpdir: path = os.path.join(tmpdir, 'test.qcow2') op = qemuimg.create(path, size=1048576, format=qemuimg.FORMAT.QCOW2) op.run() info = qemuimg.check(path) # The exact value depends on qcow2 internals assert isinstance(info['offset'], int)
def test_unexpected_backing_file(self): with self.fake_volume(sc.COW_FORMAT) as vol: # Simulate upload of qcow2 with backing file to base image op = qemuimg.create(vol.volumePath, size=self.SIZE, format=qemuimg.FORMAT.QCOW2, backing='unexpected') op.run() h = FakeHSM() with pytest.raises(se.ImageVerificationError): h.verify_untrusted_volume( 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_missing_backing_file(self): with fake_file_env() as env: vol = make_qemu_chain(env, self.SIZE, sc.COW_FORMAT, 2)[1] # Simulate upload of image without backing file to a a snapshot op = qemuimg.create(vol.volumePath, size=self.SIZE, format=qemuimg.FORMAT.QCOW2) op.run() h = FakeHSM() with pytest.raises(se.ImageVerificationError): h.verify_untrusted_volume( 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_qcow2_compat(self): with namedTemporaryDir() as tmpdir: image = os.path.join(tmpdir, "image") size = 1024 * 1024 * 1024 * 10 # 10 GB op = qemuimg.create(image, format='qcow2', size=size) op.run() info = qemuimg.info(image) assert info['format'] == qemuimg.FORMAT.QCOW2 assert info['compat'] == "0.10" assert info['virtualsize'] == size
def test_valid_qcow2_compat(self, hsm_compat, config_compat, sd_version): with self.fake_volume(vol_fmt=sc.COW_FORMAT, sd_version=sd_version) as vol: create_conf = make_config([('irs', 'qcow2_compat', config_compat)]) with MonkeyPatchScope([(qemuimg, 'config', create_conf)]): op = qemuimg.create(vol.volumePath, size=self.SIZE, format=qemuimg.FORMAT.QCOW2, qcow2Compat=hsm_compat) op.run() h = FakeHSM() h.verify_untrusted_volume( 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def make_image(path, size, format, index, qcow2_compat, backing=None): op = qemuimg.create(path, size=size, format=format, qcow2Compat=qcow2_compat, backing=backing) op.run() offset = index * 1024 qemuio.write_pattern( path, format, offset=offset, len=1024, pattern=0xf0 + index)
def test_smaller_size_ok(self, vol_fmt, qemu_fmt): # Engine < 4.2.6 rounds disk size to a multiple of 1G, creating disks # with incorrect virtual size. To be compatible with old engines we # cannot fail verification in this case. with self.fake_volume(vol_fmt) as vol: op = qemuimg.create( vol.volumePath, size=self.SIZE - sc.BLOCK_SIZE, format=qemu_fmt) op.run() h = FakeHSM() h.verify_untrusted_volume( 'sp', vol.sdUUID, vol.imgUUID, vol.volUUID)
def test_info(self): with namedTemporaryDir() as tmpdir: base_path = os.path.join(tmpdir, 'base.img') leaf_path = os.path.join(tmpdir, 'leaf.img') size = 1048576 leaf_fmt = qemuimg.FORMAT.QCOW2 with MonkeyPatchScope([(qemuimg, 'config', CONFIG)]): op = qemuimg.create(base_path, size=size, format=qemuimg.FORMAT.RAW) op.run() op = qemuimg.create(leaf_path, format=leaf_fmt, backing=base_path) op.run() info = qemuimg.info(leaf_path) assert leaf_fmt == info['format'] assert size == info['virtualsize'] assert self.CLUSTER_SIZE == info['clustersize'] assert base_path == info['backingfile'] assert '0.10' == info['compat']
def create_image(self, img_path, img_format): if img_format == 'raw': with open(img_path, 'w') as f: f.truncate(VIRTUAL_SIZE) elif img_format == 'cow': op = qemuimg.create( img_path, size=VIRTUAL_SIZE, format=qemuimg.FORMAT.QCOW2, qcow2Compat=QCOW2_COMPAT) op.run() else: raise AssertionError("invalid format: %s" % img_format)
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() """ lvSize = cls.calculate_volume_alloc_size(preallocate, size, initialSize) lvm.createLV(dom.sdUUID, volUUID, lvSize, 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 size = %s " "blocks", sc.type2name(volFormat), volPath, size) if volFormat == sc.COW_FORMAT: operation = qemuimg.create(volPath, size=size * BLOCK_SIZE, 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 size %s (blocks)", imgUUID, volUUID, srcImgUUID, srcVolUUID, size) volParent.clone(volPath, volFormat, size) 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.changeLVTags(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)
def test_qcow2_to_raw(self, preallocation, virtual_size, actual_size): with namedTemporaryDir() as tmpdir: src = os.path.join(tmpdir, 'src') dst = os.path.join(tmpdir, 'dst') op = qemuimg.create(src, size=virtual_size, format="qcow2") op.run() op = qemuimg.convert(src, dst, srcFormat="qcow2", dstFormat="raw", preallocation=preallocation) op.run() stat = os.stat(dst) assert stat.st_size == virtual_size assert stat.st_blocks * 512 == actual_size
def test_raw_to_qcow2_metadata_prealloc(self): virtual_size = 10 * 1024**2 with namedTemporaryDir() as tmpdir: src = os.path.join(tmpdir, 'src') dst = os.path.join(tmpdir, 'dst') op = qemuimg.create(src, size=virtual_size, format="raw") op.run() op = qemuimg.convert(src, dst, srcFormat="raw", dstFormat="qcow2", preallocation=qemuimg.PREALLOCATION.METADATA) op.run() actual_size = os.stat(dst).st_size disk_size = qemuimg.info(dst, format="qcow2")["actualsize"] assert actual_size > virtual_size assert disk_size < virtual_size
def test_single(self, tmpdir, format): src = str(tmpdir.join("src")) dst = str(tmpdir.join("dst")) offset = 4 * 64 * 1024 op = qemuimg.create( src, size=10 * 64 * 1024, format=format, qcow2Compat="1.1") op.run() qemuio.write_pattern(src, format, offset=offset) op = qemuimg.convert( src, dst, srcFormat=format, dstFormat=qemuimg.FORMAT.RAW, unordered_writes=True) op.run() qemuio.verify_pattern(dst, qemuimg.FORMAT.RAW, offset=offset)
def test_empty_image(self, qcow2_compat): with namedTemporaryDir() as tmpdir: size = 1048576 image = os.path.join(tmpdir, "base.img") op = qemuimg.create(image, size=size, format=self.FORMAT, qcow2Compat=qcow2_compat) op.run() expected = [ # single run - empty { "start": 0, "length": size, "data": False, "zero": True, }, ] self.check_map(qemuimg.map(image), expected)
def make_file_volume(sd_manifest, size, imguuid, voluuid, parent_vol_id=sc.BLANK_UUID, vol_format=sc.RAW_FORMAT, vol_type=sc.LEAF_VOL, prealloc=sc.SPARSE_VOL, disk_type=sc.DATA_DISKTYPE, desc='fake volume', qcow2_compat='0.10'): volpath = os.path.join(sd_manifest.domaindir, "images", imguuid, voluuid) # Create needed path components. make_file(volpath, size) # Create qcow2 file if needed. if vol_format == sc.COW_FORMAT: backing = parent_vol_id if parent_vol_id != sc.BLANK_UUID else None op = qemuimg.create( volpath, size=size, format=qemuimg.FORMAT.QCOW2, qcow2Compat=qcow2_compat, backing=backing) op.run() # Create meta files. mdfiles = [volpath + '.meta', volpath + '.lease'] for mdfile in mdfiles: make_file(mdfile) size_blk = size // sc.BLOCK_SIZE vol_class = sd_manifest.getVolumeClass() vol_class.newMetadata( (volpath,), sd_manifest.sdUUID, imguuid, parent_vol_id, size_blk, sc.type2name(vol_format), sc.type2name(prealloc), sc.type2name(vol_type), disk_type, desc, sc.LEGAL_VOL)
def test_one_block(self, offset, length, expected_length, qcow2_compat): with namedTemporaryDir() as tmpdir: size = 1048576 image = os.path.join(tmpdir, "base.img") op = qemuimg.create(image, size=size, format=self.FORMAT, qcow2Compat=qcow2_compat) op.run() qemuio.write_pattern( image, self.FORMAT, offset=offset, len=length, pattern=0xf0) expected = [ # run 1 - empty { "start": 0, "length": offset, "data": False, "zero": True, }, # run 2 - data { "start": offset, "length": expected_length, "data": True, "zero": False, }, # run 3 - empty { "start": offset + expected_length, "length": size - offset - expected_length, "data": False, "zero": True, }, ] self.check_map(qemuimg.map(image), expected)