def test_roundtrip(nbd_env, format, allocation, discard): # Volume served by qemu-nd. img_id = str(uuid.uuid4()) vol_id = str(uuid.uuid4()) nbd_env.make_volume( nbd_env.virtual_size, img_id, vol_id, vol_format=format, prealloc=allocation) # Server configuration. config = { "sd_id": nbd_env.sd_manifest.sdUUID, "img_id": img_id, "vol_id": vol_id, "discard": discard, } with nbd_server(config) as nbd_url: # Copy data from source to NBD server, and from NBD server to dst. # Both files should match byte for byte after the operation. op = qemuimg.convert( nbd_env.src, nbd_url, srcFormat="raw", create=False) op.run() op = qemuimg.convert(nbd_url, nbd_env.dst, dstFormat="raw") op.run() with io.open(nbd_env.src) as s, io.open(nbd_env.dst) as d: assert s.read() == d.read() # Now the server should not be accessible. with pytest.raises(cmdutils.Error): qemuimg.info(nbd_url)
def test_roundtrip(nbd_env, format, allocation, discard): # Volume served by qemu-nd. img_id = str(uuid.uuid4()) vol_id = str(uuid.uuid4()) nbd_env.make_volume(nbd_env.virtual_size, img_id, vol_id, vol_format=format, prealloc=allocation) # Server configuration. config = { "sd_id": nbd_env.sd_manifest.sdUUID, "img_id": img_id, "vol_id": vol_id, "discard": discard, } with nbd_server(config) as nbd_url: # Copy data from source to NBD server, and from NBD server to dst. # Both files should match byte for byte after the operation. op = qemuimg.convert(nbd_env.src, nbd_url, srcFormat="raw", create=False) op.run() op = qemuimg.convert(nbd_url, nbd_env.dst, dstFormat="raw") op.run() with io.open(nbd_env.src) as s, io.open(nbd_env.dst) as d: assert s.read() == d.read() # Now the server should not be accessible. with pytest.raises(cmdutils.Error): qemuimg.info(nbd_url)
def test_no_create(self): def convert(cmd, **kw): expected = [QEMU_IMG, 'convert', '-p', '-t', 'none', '-T', 'none', '-n', 'src', 'dst'] assert cmd == expected with MonkeyPatchScope([(qemuimg, 'ProgressCommand', convert)]): qemuimg.convert('src', 'dst', create=False)
def test_qcow2_compat_invalid(self): with self.assertRaises(ValueError): qemuimg.convert('image', 'dst', dstFormat='qcow2', backing='bak', backingFormat='qcow2', dstQcow2Compat='1.11')
def test_qcow2_no_backing_file(self): def convert(cmd, **kw): expected = [QEMU_IMG, 'convert', '-p', '-t', 'none', '-T', 'none', 'src', '-O', 'qcow2', '-o', 'compat=0.10', 'dst'] assert cmd == expected with MonkeyPatchScope([(qemuimg, 'config', CONFIG), (qemuimg, 'ProgressCommand', convert)]): qemuimg.convert('src', 'dst', dstFormat='qcow2')
def test_no_format(self): def convert(cmd, **kw): expected = [ QEMU_IMG, 'convert', '-p', '-t', 'none', '-T', 'none', 'src', 'dst' ] self.assertEqual(cmd, expected) with MonkeyPatchScope([(qemuimg, 'ProgressCommand', convert)]): qemuimg.convert('src', 'dst')
def test_qcow2_compat_version3(self): def convert(cmd, **kw): expected = [ QEMU_IMG, 'convert', '-p', '-t', 'none', '-T', 'none', 'src', '-O', 'qcow2', '-o', 'compat=1.1', 'dst' ] self.assertEqual(cmd, expected) with MonkeyPatchScope([(qemuimg, 'config', CONFIG), (qemuimg, 'ProgressCommand', convert)]): qemuimg.convert('src', 'dst', dstFormat='qcow2', dstQcow2Compat='1.1')
def _run(self): with guarded.context(self._source.locks + self._dest.locks): with self._source.prepare(), self._dest.prepare(): # Do not start copying if we have already been aborted if self._status == jobs.STATUS.ABORTED: return # Workaround for volumes containing VM configuration info that # were created with invalid vdsm metadata. if self._source.is_invalid_vm_conf_disk(): src_format = dst_format = qemuimg.FORMAT.RAW else: src_format = self._source.qemu_format dst_format = self._dest.qemu_format with self._dest.volume_operation(): self._operation = qemuimg.convert( self._source.path, self._dest.path, srcFormat=src_format, dstFormat=dst_format, dstQcow2Compat=self._dest.qcow2_compat, backing=self._dest.backing_path, backingFormat=self._dest.backing_qemu_format, preallocation=self._dest.preallocation, unordered_writes=self._dest .recommends_unordered_writes) self._operation.run()
def _run(self): with guarded.context(self._source.locks + self._dest.locks): with self._source.prepare(), self._dest.prepare(): # Do not start copying if we have already been aborted if self._status == jobs.STATUS.ABORTED: return # Workaround for volumes containing VM configuration info that # were created with invalid vdsm metadata. if self._source.is_invalid_vm_conf_disk(): src_format = dst_format = qemuimg.FORMAT.RAW else: src_format = self._source.qemu_format dst_format = self._dest.qemu_format with self._dest.volume_operation(): self._operation = qemuimg.convert( self._source.path, self._dest.path, srcFormat=src_format, dstFormat=dst_format, dstQcow2Compat=self._dest.qcow2_compat, backing=self._dest.backing_path, backingFormat=self._dest.backing_qemu_format, unordered_writes=self._dest. recommends_unordered_writes, create=self._dest.requires_create) with utils.stopwatch("Copy volume {}".format( self._source.path), level=logging.INFO, log=self.log): self._operation.run()
def test_qcow2_backing_file_and_format(self): def convert(cmd, **kw): expected = [ QEMU_IMG, 'convert', '-p', '-t', 'none', '-T', 'none', 'src', '-O', 'qcow2', '-o', 'compat=0.10,backing_file=bak,backing_fmt=qcow2', 'dst' ] self.assertEqual(cmd, expected) with MonkeyPatchScope([(qemuimg, 'config', CONFIG), (qemuimg, 'ProgressCommand', convert)]): qemuimg.convert('src', 'dst', dstFormat='qcow2', backing='bak', backingFormat='qcow2')
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 upload_to_nbd(filename, nbd_url): op = qemuimg.convert(filename, nbd_url, srcFormat="qcow2", create=False, target_is_zero=True) op.run()
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 test_readonly(nbd_env, format, allocation): vol = create_volume(nbd_env, format, allocation) op = qemuimg.convert(nbd_env.src, vol.getVolumePath(), srcFormat="qcow2", dstFormat=format, dstQcow2Compat="1.1", preallocation=PREALLOCATION.get(format)) op.run() config = { "sd_id": vol.sdUUID, "img_id": vol.imgUUID, "vol_id": vol.volUUID, "readonly": True, } with nbd_server(config) as nbd_url: # Writing to read-only NBD server must fail. with pytest.raises(cmdutils.Error): upload_to_nbd(nbd_env.src, nbd_url) # Download must not fail. download_from_nbd(nbd_url, nbd_env.dst) compare_images(nbd_env.src, nbd_env.dst, strict=True) # Now the server should not be accessible. with pytest.raises(cmdutils.Error): qemuimg.info(nbd_url)
def _run(self): with guarded.context(self._source.locks + self._dest.locks): with self._source.prepare(), self._dest.prepare(): # Do not start copying if we have already been aborted if self._status == jobs.STATUS.ABORTED: return # Workaround for volumes containing VM configuration info that # were created with invalid vdsm metadata. if self._source.is_invalid_vm_conf_disk(): src_format = dst_format = qemuimg.FORMAT.RAW else: src_format = self._source.qemu_format dst_format = self._dest.qemu_format with self._dest.volume_operation(): self._operation = qemuimg.convert( self._source.path, self._dest.path, srcFormat=src_format, dstFormat=dst_format, dstQcow2Compat=self._dest.qcow2_compat, backing=self._dest.backing_path, backingFormat=self._dest.backing_qemu_format, preallocation=self._dest.preallocation) self._operation.run()
def test_readonly(nbd_env, format, allocation): # Volume served by qemu-nd. img_id = str(uuid.uuid4()) vol_id = str(uuid.uuid4()) nbd_env.make_volume(nbd_env.virtual_size, img_id, vol_id, vol_format=format, prealloc=allocation) # Fill volume with data before starting the server. vol = nbd_env.sd_manifest.produceVolume(img_id, vol_id) op = qemuimg.convert(nbd_env.src, vol.getVolumePath(), dstFormat=sc.fmt2str(format), preallocation=PREALLOCATION.get(format)) op.run() # Server configuration. config = { "sd_id": nbd_env.sd_manifest.sdUUID, "img_id": img_id, "vol_id": vol_id, "readonly": True, } with nbd_server(config) as nbd_url: # Writing to NBD server must fail. with pytest.raises(cmdutils.Error): op = qemuimg.convert(nbd_env.src, nbd_url, srcFormat="raw", create=False) op.run() # Copy data from NBD server to dst. Both files should match byte # for byte after the operation. op = qemuimg.convert(nbd_url, nbd_env.dst, dstFormat="raw") op.run() with io.open(nbd_env.src) as s, io.open(nbd_env.dst) as d: assert s.read() == d.read() # Now the server should not be accessible. with pytest.raises(cmdutils.Error): qemuimg.info(nbd_url)
def test_readonly(nbd_env, format, allocation): # Volume served by qemu-nd. img_id = str(uuid.uuid4()) vol_id = str(uuid.uuid4()) nbd_env.make_volume( nbd_env.virtual_size, img_id, vol_id, vol_format=format, prealloc=allocation) # Fill volume with data before starting the server. vol = nbd_env.sd_manifest.produceVolume(img_id, vol_id) op = qemuimg.convert( nbd_env.src, vol.getVolumePath(), dstFormat=sc.fmt2str(format), preallocation=PREALLOCATION.get(format)) op.run() # Server configuration. config = { "sd_id": nbd_env.sd_manifest.sdUUID, "img_id": img_id, "vol_id": vol_id, "readonly": True, } with nbd_server(config) as nbd_url: # Writing to NBD server must fail. with pytest.raises(cmdutils.Error): op = qemuimg.convert( nbd_env.src, nbd_url, srcFormat="raw", create=False) op.run() # Copy data from NBD server to dst. Both files should match byte # for byte after the operation. op = qemuimg.convert(nbd_url, nbd_env.dst, dstFormat="raw") op.run() with io.open(nbd_env.src) as s, io.open(nbd_env.dst) as d: assert s.read() == d.read() # Now the server should not be accessible. with pytest.raises(cmdutils.Error): qemuimg.info(nbd_url)
def converted_size(filename, compat): converted = filename + ".qcow2" operation = qemuimg.convert(filename, converted, srcFormat=qemuimg.FORMAT.RAW, dstFormat=qemuimg.FORMAT.QCOW2, dstQcow2Compat=compat) operation.run() return os.stat(converted).st_size
def convert_to_qcow2(src, compressed=False, compat="1.1"): dst = src + ".qcow2" convert_cmd = qemuimg.convert(src, dst, dstFormat=qemuimg.FORMAT.QCOW2, dstQcow2Compat=compat, compressed=compressed) convert_cmd.run() os.remove(src) return dst
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 convert_to_qcow2(src, compressed=False, compat="1.1"): dst = src + ".qcow2" convert_cmd = qemuimg.convert( src, dst, dstFormat=qemuimg.FORMAT.QCOW2, dstQcow2Compat=compat, compressed=compressed) convert_cmd.run() os.remove(src) return dst
def test_qcow2_to_raw_sparse(self, preallocation): 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="qcow2") op.run() op = qemuimg.convert(src, dst, srcFormat="qcow2", dstFormat="raw", preallocation=preallocation) op.run() check_raw_sparse_image(dst, virtual_size)
def test_raw_to_raw_preallocation_off(self, preallocation): virtual_size = 10 * 1024**2 with namedTemporaryDir() as tmpdir: src = os.path.join(tmpdir, 'src') dst = os.path.join(tmpdir, 'dst') with io.open(src, "wb") as f: f.truncate(virtual_size) op = qemuimg.convert(src, dst, srcFormat="raw", dstFormat="raw", preallocation=preallocation) op.run() check_raw_sparse_image(dst, virtual_size)
def test_raw_to_raw(self, preallocation, virtual_size, actual_size): with namedTemporaryDir() as tmpdir: src = os.path.join(tmpdir, 'src') dst = os.path.join(tmpdir, 'dst') with io.open(src, "wb") as f: f.truncate(virtual_size) op = qemuimg.convert(src, dst, srcFormat="raw", 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_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_compressed_qcow2(self, tmpdir): src_file = str(tmpdir.join("test.raw")) dst_file = str(tmpdir.join("test.qcow2")) with io.open(src_file, "wb") as f: f.truncate(1 * GIB) f.write(b"x" * MEGAB) src_file_size = qemuimg.info(src_file)["actualsize"] op = qemuimg.convert(src_file, dst_file, srcFormat=qemuimg.FORMAT.RAW, 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_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_raw_to_compressed_qcow2(self, tmpdir): src_file = str(tmpdir.join("test.raw")) dst_file = str(tmpdir.join("test.qcow2")) with io.open(src_file, "wb") as f: f.truncate(1 * GIB) f.write(b"x" * MEGAB) src_file_size = qemuimg.info(src_file)["actualsize"] op = qemuimg.convert( src_file, dst_file, srcFormat=qemuimg.FORMAT.RAW, 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_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_raw_invalid_preallocation(self): with pytest.raises(ValueError): qemuimg.convert( 'src', 'dst', dstFormat="raw", preallocation=qemuimg.PREALLOCATION.METADATA)
def download_from_nbd(nbd_url, filename): op = qemuimg.convert(nbd_url, filename, dstFormat="qcow2", dstQcow2Compat="1.1") op.run()
def test_qcow2_compat_invalid(self): with pytest.raises(ValueError): qemuimg.convert('image', 'dst', dstFormat='qcow2', backing='bak', backingFormat='qcow2', dstQcow2Compat='1.11')
def _interImagesCopy(self, destDom, srcSdUUID, imgUUID, chains): srcLeafVol = chains['srcChain'][-1] dstLeafVol = chains['dstChain'][-1] try: # Prepare the whole chains before the copy srcLeafVol.prepare(rw=False) dstLeafVol.prepare(rw=True, chainrw=True, setrw=True) except Exception: self.log.error("Unexpected error", exc_info=True) # teardown volumes self.__cleanupMove(srcLeafVol, dstLeafVol) raise try: for srcVol in chains['srcChain']: # Do the actual copy try: dstVol = destDom.produceVolume(imgUUID=imgUUID, volUUID=srcVol.volUUID) if workarounds.invalid_vm_conf_disk(srcVol): srcFormat = dstFormat = qemuimg.FORMAT.RAW else: srcFormat = sc.fmt2str(srcVol.getFormat()) dstFormat = sc.fmt2str(dstVol.getFormat()) parentVol = dstVol.getParentVolume() if parentVol is not None: backing = volume.getBackingVolumePath( imgUUID, parentVol.volUUID) backingFormat = sc.fmt2str(parentVol.getFormat()) else: backing = None backingFormat = None if (destDom.supportsSparseness and dstVol.getType() == sc.PREALLOCATED_VOL): preallocation = qemuimg.PREALLOCATION.FALLOC else: preallocation = None operation = qemuimg.convert( srcVol.getVolumePath(), dstVol.getVolumePath(), srcFormat=srcFormat, dstFormat=dstFormat, dstQcow2Compat=destDom.qcow2_compat(), backing=backing, backingFormat=backingFormat, preallocation=preallocation, unordered_writes=destDom.recommends_unordered_writes( dstVol.getFormat()), create=not destDom.is_block(), ) with utils.stopwatch("Copy volume %s" % srcVol.volUUID): self._run_qemuimg_operation(operation) except ActionStopped: raise except se.StorageException: self.log.error("Unexpected error", exc_info=True) raise except Exception: self.log.error( "Copy image error: image=%s, src domain=%s," " dst domain=%s", imgUUID, srcSdUUID, destDom.sdUUID, exc_info=True) raise se.CopyImageError() finally: # teardown volumes self.__cleanupMove(srcLeafVol, dstLeafVol)
def test_qcow2_backing_file_without_creation(self): virtual_size = MiB with namedTemporaryDir() as tmpdir: # Create source chain. src_base = os.path.join(tmpdir, '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(tmpdir, '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 dest chain dst_base = os.path.join(tmpdir, 'dst_base.img') op = qemuimg.create( dst_base, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1' ) op.run() dst_top = os.path.join(tmpdir, 'dst_top.img') op = qemuimg.create( dst_top, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat='1.1', backing=dst_base, backingFormat='qcow2' ) op.run() # Write data to the source chain. cluster_size = 64 * KiB for i, path in enumerate([src_base, src_top]): qemuio.write_pattern( path, "qcow2", offset=i * cluster_size, len=cluster_size, pattern=0xf0 + i) # Copy base to base. op = qemuimg.convert( src_base, dst_base, srcFormat='qcow2', dstFormat='qcow2', dstQcow2Compat='1.1', create=False ) op.run() # Copy top to top. op = qemuimg.convert( src_top, dst_top, srcFormat='qcow2', dstFormat='qcow2', backing=dst_base, dstQcow2Compat='1.1', create=False ) op.run() # Run comparisons, if there is a mismatch in content or size # op.run() will raise and fail the test. op = qemuimg.compare( src_base, dst_base, img1_format='qcow2', img2_format='qcow2', strict=True ) op.run() op = qemuimg.compare( src_top, dst_top, img1_format='qcow2', img2_format='qcow2', strict=True ) op.run()
def test_qcow2(self, user_mount, dst_compat, create): virtual_size = MiB # Create 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 dest chain dst_base = os.path.join(user_mount.path, 'dst_base.img') op = qemuimg.create( dst_base, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat=dst_compat, ) op.run() dst_top = os.path.join(user_mount.path, 'dst_top.img') op = qemuimg.create( dst_top, size=virtual_size, format=qemuimg.FORMAT.QCOW2, qcow2Compat=dst_compat, backing=dst_base, backingFormat='qcow2' ) op.run() # Write data to the source chain. cluster_size = 64 * KiB for i, path in enumerate([src_base, src_top]): qemuio.write_pattern( path, "qcow2", offset=i * cluster_size, len=cluster_size, pattern=0xf0 + i) # Copy base to base. op = qemuimg.convert( src_base, dst_base, srcFormat='qcow2', dstFormat='qcow2', dstQcow2Compat=dst_compat, create=create ) op.run() # Copy top to top. op = qemuimg.convert( src_top, dst_top, srcFormat='qcow2', dstFormat='qcow2', backing=dst_base, backingFormat='qcow2', dstQcow2Compat=dst_compat, # With a backing we can always use False. create=False ) op.run() # Run comparisons, if there is a mismatch in content or size # op.run() will raise and fail the test. op = qemuimg.compare( src_base, dst_base, img1_format='qcow2', img2_format='qcow2', strict=True ) op.run() op = qemuimg.compare( src_top, dst_top, img1_format='qcow2', img2_format='qcow2', strict=True ) op.run()
def copyCollapsed(self, sdUUID, vmUUID, srcImgUUID, srcVolUUID, dstImgUUID, dstVolUUID, descr, dstSdUUID, volType, volFormat, preallocate, postZero, force, discard): """ Create new template/volume from VM. Do it by collapse and copy the whole chain (baseVolUUID->srcVolUUID) """ self.log.info( "sdUUID=%s vmUUID=%s srcImgUUID=%s srcVolUUID=%s " "dstImgUUID=%s dstVolUUID=%s dstSdUUID=%s volType=%s " "volFormat=%s preallocate=%s force=%s postZero=%s " "discard=%s", sdUUID, vmUUID, srcImgUUID, srcVolUUID, dstImgUUID, dstVolUUID, dstSdUUID, volType, sc.type2name(volFormat), sc.type2name(preallocate), str(force), str(postZero), discard) try: srcVol = dstVol = None # Find out dest sdUUID if dstSdUUID == sd.BLANK_UUID: dstSdUUID = sdUUID volclass = sdCache.produce(sdUUID).getVolumeClass() destDom = sdCache.produce(dstSdUUID) # find src volume try: srcVol = volclass(self.repoPath, sdUUID, srcImgUUID, srcVolUUID) except se.StorageException: raise except Exception as e: self.log.error(e, exc_info=True) raise se.SourceImageActionError(srcImgUUID, sdUUID, str(e)) # Create dst volume try: # Before reading source volume parameters from volume metadata, # prepare the volume. This ensure that the volume capacity will # match the actual virtual size, see # https://bugzilla.redhat.com/1700623. srcVol.prepare(rw=False) volParams = srcVol.getVolumeParams() if volFormat in [sc.COW_FORMAT, sc.RAW_FORMAT]: dstVolFormat = volFormat else: dstVolFormat = volParams['volFormat'] # TODO: This is needed only when copying to qcow2-thin volume # on block storage. Move into calculate_initial_size. dst_vol_allocation = self.calculate_vol_alloc( sdUUID, volParams, dstSdUUID, dstVolFormat) # Find out dest volume parameters if preallocate in [sc.PREALLOCATED_VOL, sc.SPARSE_VOL]: volParams['prealloc'] = preallocate initial_size = self.calculate_initial_size( destDom.supportsSparseness, dstVolFormat, volParams['prealloc'], dst_vol_allocation) self.log.info( "Copy source %s:%s:%s to destination %s:%s:%s " "capacity=%s, initial size=%s", sdUUID, srcImgUUID, srcVolUUID, dstSdUUID, dstImgUUID, dstVolUUID, volParams['capacity'], initial_size) # If image already exists check whether it illegal/fake, # overwrite it if not self.isLegal(dstSdUUID, dstImgUUID): force = True # We must first remove the previous instance of image (if # exists) in destination domain, if we got the overwrite # command if force: self.log.info( "delete image %s on domain %s before " "overwriting", dstImgUUID, dstSdUUID) _deleteImage(destDom, dstImgUUID, postZero, discard) destDom.createVolume(imgUUID=dstImgUUID, capacity=volParams['capacity'], volFormat=dstVolFormat, preallocate=volParams['prealloc'], diskType=volParams['disktype'], volUUID=dstVolUUID, desc=descr, srcImgUUID=sc.BLANK_UUID, srcVolUUID=sc.BLANK_UUID, initial_size=initial_size) dstVol = sdCache.produce(dstSdUUID).produceVolume( imgUUID=dstImgUUID, volUUID=dstVolUUID) except se.StorageException: self.log.error("Unexpected error", exc_info=True) raise except Exception as e: self.log.error("Unexpected error", exc_info=True) raise se.CopyImageError("Destination volume %s error: %s" % (dstVolUUID, str(e))) try: # Start the actual copy image procedure dstVol.prepare(rw=True, setrw=True) if (destDom.supportsSparseness and dstVol.getType() == sc.PREALLOCATED_VOL): preallocation = qemuimg.PREALLOCATION.FALLOC else: preallocation = None try: operation = qemuimg.convert( volParams['path'], dstVol.getVolumePath(), srcFormat=sc.fmt2str(volParams['volFormat']), dstFormat=sc.fmt2str(dstVolFormat), dstQcow2Compat=destDom.qcow2_compat(), preallocation=preallocation, unordered_writes=destDom.recommends_unordered_writes( dstVolFormat), create=not destDom.is_block(), ) with utils.stopwatch("Copy volume %s" % srcVol.volUUID): self._run_qemuimg_operation(operation) except ActionStopped: raise except cmdutils.Error as e: self.log.exception('conversion failure for volume %s', srcVol.volUUID) raise se.CopyImageError(str(e)) # Mark volume as SHARED if volType == sc.SHARED_VOL: dstVol.setShared() dstVol.setLegality(sc.LEGAL_VOL) if force: # Now we should re-link all deleted hardlinks, if exists destDom.templateRelink(dstImgUUID, dstVolUUID) except se.StorageException: self.log.error("Unexpected error", exc_info=True) raise except Exception as e: self.log.error("Unexpected error", exc_info=True) raise se.CopyImageError("src image=%s, dst image=%s: msg=%s" % (srcImgUUID, dstImgUUID, str(e))) self.log.info("Finished copying %s:%s -> %s:%s", sdUUID, srcVolUUID, dstSdUUID, dstVolUUID) # TODO: handle return status return dstVolUUID finally: self.__cleanupCopy(srcVol=srcVol, dstVol=dstVol)