def test_backing_chain(tmpdir): size = 128 * 1024 base = str(tmpdir.join("base.raw")) top = str(tmpdir.join("top.qcow2")) base_data = b"data from base".ljust(32, b"\0") # Add base image with some data. qemu_img.create(base, "raw", size=size) with qemu_nbd.open(base, "raw") as c: c.write(0, base_data) c.flush() # Add empty overlay. qemu_img.create(top, "qcow2", backing_file=base, backing_format="raw") top_addr = nbd.UnixAddress(str(tmpdir.join("sock"))) # By default, we see data from base. with qemu_nbd.run(top, "qcow2", top_addr), \ nbd.Client(top_addr) as c: assert c.read(0, 32) == base_data # With backing chain disabled, we see data only from top. with qemu_nbd.run(top, "qcow2", top_addr, backing_chain=False), \ nbd.Client(top_addr) as c: assert c.read(0, 32) == b"\0" * 32
def test_options(tmpdir, fmt, options): size = 4 * 1024**2 chunk_size = 128 * 1024 src = str(tmpdir.join("src." + fmt)) qemu_img.create(src, fmt, size=size) with qemu_nbd.open(src, fmt) as c: for offset in range(0, size, chunk_size): c.write(offset, struct.pack(">Q", offset)) c.flush() dst = str(tmpdir.join("dst." + fmt)) qemu_img.create(dst, fmt, size=size) src_addr = nbd.UnixAddress(str(tmpdir.join("src.sock"))) dst_addr = nbd.UnixAddress(str(tmpdir.join("dst.sock"))) with qemu_nbd.run( src, fmt, src_addr, read_only=True, **options), \ qemu_nbd.run( dst, fmt, dst_addr, **options), \ nbd.Client(src_addr) as src_client, \ nbd.Client(dst_addr) as dst_client: nbdutil.copy(src_client, dst_client) qemu_img.compare(src, dst)
def test_copy_nbd_to_nbd(tmpdir, src_fmt, dst_fmt, zero, hole): # Default cluser size with qcow2 format. cluster_size = 64 * 1024 extents = [ ("data", cluster_size), ("zero", cluster_size), ("data", cluster_size), ("hole", cluster_size + io.MAX_ZERO_SIZE), ("data", cluster_size + io.BUFFER_SIZE), ("hole", cluster_size), ("data", cluster_size), ] size = sum(length for _, length in extents) src = str(tmpdir.join("src." + src_fmt)) qemu_img.create(src, src_fmt, size=size) populate_image(src, src_fmt, extents) src_sock = UnixAddress(tmpdir.join("src.sock")) src_url = urlparse(src_sock.url()) dst = str(tmpdir.join("dst." + dst_fmt)) qemu_img.create(dst, dst_fmt, size=size) dst_sock = UnixAddress(tmpdir.join("dst.sock")) dst_url = urlparse(dst_sock.url()) # Note: We need extra worker for reading extents for source. max_workers = 2 with qemu_nbd.run( src, src_fmt, src_sock, read_only=True, shared=max_workers + 1), \ qemu_nbd.run( dst, dst_fmt, dst_sock, shared=max_workers), \ nbd.open(src_url, "r") as src_backend, \ nbd.open(dst_url, "r+", sparse=True) as dst_backend: # Because we copy to new image, we can always use zero=False, but we # test both to verify that the result is the same. io.copy(src_backend, dst_backend, max_workers=max_workers, zero=zero, hole=hole) # Compare image content - must match. qemu_img.compare(src, dst) # Allocation can be compared only with qcow2 images when we write zeroes to # zero extents and skip holes. if src_fmt == "qcow2" and dst_fmt == "qcow2" and zero and not hole: qemu_img.compare(src, dst, strict=True)
def test_shared(tmpdir, fmt): size = 1024**2 chunk_size = size // 2 src = str(tmpdir.join("src." + fmt)) qemu_img.create(src, fmt, size=size) with qemu_nbd.open(src, fmt) as c: c.write(0, b"a" * chunk_size) c.write(0, b"b" * chunk_size) c.flush() dst = str(tmpdir.join("dst." + fmt)) qemu_img.create(dst, fmt, size=size) src_addr = nbd.UnixAddress(str(tmpdir.join("src.sock"))) dst_addr = nbd.UnixAddress(str(tmpdir.join("dst.sock"))) # Start 2 qemu-nbd servers, each with 2 clients that can read and write in # parallel for higher throughput. with qemu_nbd.run(src, fmt, src_addr, read_only=True, shared=2), \ qemu_nbd.run(dst, fmt, dst_addr, shared=2), \ nbd.Client(src_addr) as src_client1, \ nbd.Client(src_addr) as src_client2, \ nbd.Client(dst_addr) as dst_client1, \ nbd.Client(dst_addr) as dst_client2: # Copy first half of the image with src_client1 and dst_client2 and # second half with src_client2 and dst_client2. In a real application # we would have more clients, running in multiple threads. chunk1 = src_client1.read(0, chunk_size) dst_client1.write(0, chunk1) chunk2 = src_client2.read(chunk_size, chunk_size) dst_client2.write(chunk_size, chunk2) dst_client1.flush() dst_client2.flush() qemu_img.compare(src, dst)
def test_run_tcp(tmpfile): with io.open(tmpfile, "r+b") as f: f.truncate(1024**2) addr = nbd.TCPAddress("localhost", testutil.random_tcp_port()) with qemu_nbd.run(tmpfile, "raw", addr): # The helper already waited for the NBD socket, not wait needed. assert sockutil.wait_for_socket(addr, 0.0) # The socket must be closed, no wait needed. assert not sockutil.wait_for_socket(addr, 0.0)
def test_open_tcp(tmpdir, url_template, host, export): image = str(tmpdir.join("image")) with open(image, "wb") as f: f.truncate(1024**3) sock = nbd.TCPAddress(host, testutil.random_tcp_port()) url = url_template.format(port=sock.port) log.debug("Trying url=%r export=%r", url, export) with qemu_nbd.run(image, "raw", sock, export_name=export): with nbd.open(urlparse(url)) as c: assert c.export_size == 1024**3
def test_open_unix(tmpdir, url, export): image = str(tmpdir.join("image")) with open(image, "wb") as f: f.truncate(1024**3) sock = nbd.UnixAddress(tmpdir.join("sock")) url = url.replace("/path", sock) log.debug("Trying url=%r export=%r", url, export) with qemu_nbd.run(image, "raw", sock, export_name=export): with nbd.open(urlparse(url)) as c: assert c.export_size == 1024**3
def test_qcow2_write_read(tmpdir): image = str(tmpdir.join("image")) sock = nbd.UnixAddress(tmpdir.join("sock")) offset = 1024**2 data = b"can read and write qcow2" create_image(image, "qcow2", 1024**3) with qemu_nbd.run(image, "qcow2", sock): with nbd.Client(sock) as c: c.write(offset, data) c.flush() with nbd.Client(sock) as c: assert c.read(offset, len(data)) == data
def test_raw_read(tmpdir): image = str(tmpdir.join("image")) sock = nbd.UnixAddress(tmpdir.join("sock")) offset = 1024**2 data = b"can read from raw" with open(image, "wb") as f: f.truncate(1024**3) f.seek(offset) f.write(data) with qemu_nbd.run(image, "raw", sock): with nbd.Client(sock) as c: assert c.read(offset, len(data)) == data
def test_run_unix(tmpdir): image = str(tmpdir.join("image")) sock = str(tmpdir.join("sock")) with io.open(image, "wb") as f: f.truncate(1024**2) addr = nbd.UnixAddress(sock) with qemu_nbd.run(image, "raw", addr): # The helper already waited for the NBD socket, not wait needed. assert sockutil.wait_for_socket(addr, 0.0) # The socket must be closed, no wait needed. assert not sockutil.wait_for_socket(addr, 0.0)
def test_raw_write(tmpdir): image = str(tmpdir.join("image")) with open(image, "wb") as f: f.truncate(1024**3) sock = nbd.UnixAddress(tmpdir.join("sock")) offset = 1024**2 data = b"can write to raw" with qemu_nbd.run(image, "raw", sock): with nbd.Client(sock) as c: c.write(offset, data) c.flush() with open(image, "rb") as f: f.seek(offset) assert f.read(len(data)) == data
def test_handshake(tmpdir, export, fmt): image = str(tmpdir.join("image")) create_image(image, fmt, 1024**3) sock = nbd.UnixAddress(tmpdir.join("sock")) with qemu_nbd.run(image, fmt, sock, export_name=export): if export: c = nbd.Client(sock, export) else: c = nbd.Client(sock) with c: # TODO: test transmission_flags? assert c.export_size == 1024**3 assert c.minimum_block_size == 1 assert c.preferred_block_size == 4096 assert c.maximum_block_size == 32 * 1024**2 assert c.base_allocation
def test_handshake(tmpdir, export, fmt): image = str(tmpdir.join("image")) create_image(image, fmt, 1024**3) sock = nbd.UnixAddress(tmpdir.join("sock")) with qemu_nbd.run(image, fmt, sock, export_name=export): if export: c = nbd.Client(sock, export) else: c = nbd.Client(sock) with c: # TODO: test transmission_flags? assert c.export_size == 1024**3 assert c.minimum_block_size == 1 assert c.preferred_block_size == 4096 assert c.maximum_block_size == 32 * 1024**2 # Both available in in current qemu-nbd (>= 5.2.0). assert c.has_base_allocation assert c.has_allocation_depth == (fmt != "raw")
def test_zero_min_block_size(tmpdir, fmt): offset = 1024**2 image = str(tmpdir.join("image")) sock = nbd.UnixAddress(tmpdir.join("sock")) create_image(image, fmt, 1024**3) with qemu_nbd.run(image, fmt, sock): # Fill range with data with nbd.Client(sock) as c: size = c.minimum_block_size c.write(offset, b"x" * size) c.flush() # Zero range using minimum block size with nbd.Client(sock) as c: c.zero(offset, size) c.flush() with nbd.Client(sock) as c: assert c.read(offset, size) == b"\0" * size
def test_zero(tmpdir, fmt): size = 2 * 1024**2 offset = 1024**2 image = str(tmpdir.join("image")) sock = nbd.UnixAddress(tmpdir.join("sock")) create_image(image, fmt, size) with qemu_nbd.run(image, fmt, sock): # Fill image with data with nbd.Client(sock) as c: c.write(0, b"x" * size) c.flush() # Zero a range with nbd.Client(sock) as c: c.zero(offset, 4096) c.flush() with nbd.Client(sock) as c: assert c.read(offset, 4096) == b"\0" * 4096