def test_base_allocation_many_extents(nbd_server, user_file): # Tested only with raw since qcow2 minimal extent is cluster size (64K), # and writing 1000 extents (62.5 MiB) will be too slow in the CI. # Extents must be multiple of file system block size. extent_length = os.statvfs(user_file.path).f_bsize # Use number which is not a multiple of our buffer capacity (1024 extents) # to ensure we read partial buffers correctly. extents_count = 2000 size = extents_count * extent_length create_image(user_file.path, "raw", size) nbd_server.image = user_file.path nbd_server.fmt = "raw" nbd_server.start() with nbd.open(nbd_server.url) as c: # Write data to all even extents. data = b"x" * extent_length for i in range(0, size, extent_length * 2): c.write(i, data) extents = list(nbdutil.extents(c)) assert len(extents) == extents_count for i, ext in enumerate(extents): assert ext.length == extent_length assert ext.zero == bool(i % 2)
def test_base_allocation_some_data(nbd_server, user_file, fmt, zero_flags): size = 1024**3 create_image(user_file.path, fmt, size) nbd_server.image = user_file.path nbd_server.fmt = fmt nbd_server.start() # Use qcow2 cluster size to avoid inconsistent results on CentOS and # Fedora. data_length = 64 * 1024 zero_length = size // 2 - data_length with nbd.open(nbd_server.url) as c: # Create 4 extents: data, zero, data, zero. c.write(0, b"x" * data_length) c.write(size // 2, b"x" * data_length) extents = list(nbdutil.extents(c)) assert extents == [ nbd.Extent(length=data_length, flags=0), nbd.Extent(length=zero_length, flags=zero_flags), nbd.Extent(length=data_length, flags=0), nbd.Extent(length=zero_length, flags=zero_flags), ]
def copy_disk(nbd_url, backup_disk): log.info("Backing up data extents from %s to %s", nbd_url, backup_disk) backup_url = urlparse(nbd_url) with nbd.open(backup_url) as src_client, \ qemu_nbd.open(backup_disk, "qcow2") as dst_client: nbdutil.copy(src_client, dst_client)
def test_open_tcp(tmpdir, url_template, export): image = str(tmpdir.join("image")) with open(image, "wb") as f: f.truncate(1024**3) sock = nbd.TCPAddress("localhost", 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_raw_readinto(nbd_server): offset = 1024**2 data = b"can read from raw" with io.open(nbd_server.image, "wb") as f: f.truncate(2 * 1024**2) f.seek(1024**2) f.write(data) nbd_server.start() with nbd.open(nbd_server.url) as c: buf = bytearray(len(data)) c.readinto(offset, buf) assert buf == data
def test_extents_reply_error(nbd_server, user_file): """ The server SHOULD return NBD_EINVAL if it receives a NBD_CMD_BLOCK_STATUS request including one or more sectors beyond the size of the device. """ size = 1024**2 create_image(user_file.path, "raw", size) nbd_server.image = user_file.path nbd_server.fmt = "raw" nbd_server.start() with nbd.open(nbd_server.url) as c: with pytest.raises(nbd.ReplyError) as e: c.extents(0, size + 1) # Server should return this, qemu does. assert e.value.code == errno.EINVAL # The next request should succeed. assert c.read(4096, 1) == b"\0"
def copy_dirty(nbd_url, backup_disk): log.info("Backing up dirty extents from %s to %s", nbd_url, backup_disk) backup_url = urlparse(nbd_url) with nbd.open(backup_url, dirty=True) as src_client, \ qemu_nbd.open(backup_disk, "qcow2") as dst_client: buf = bytearray(4 * 1024**2) offset = 0 for ext in nbdutil.extents(src_client, dirty=True): if ext.dirty: todo = ext.length while todo: step = min(todo, len(buf)) view = memoryview(buf)[:step] src_client.readinto(offset, view) dst_client.write(offset, view) offset += step todo -= step else: offset += ext.length
def test_base_allocation_full(nbd_server, user_file, fmt): size = 1024**2 create_image(user_file.path, fmt, size) nbd_server.image = user_file.path nbd_server.fmt = fmt nbd_server.start() with nbd.open(nbd_server.url) as c: c.write(0, b"x" * size) # Entire image. extents = c.extents(0, size)["base:allocation"] assert extents == [nbd.Extent(length=size, flags=0)] # First block. extents = c.extents(0, 4096)["base:allocation"] assert extents == [nbd.Extent(length=4096, flags=0)] # Last block. extents = c.extents(size - 4096, 4096)["base:allocation"] assert extents == [nbd.Extent(length=4096, flags=0)] # Some block. extents = c.extents(4096, 4096)["base:allocation"] assert extents == [nbd.Extent(length=4096, flags=0)] # Unaligned start. extents = c.extents(4096 - 1, 4096 + 1)["base:allocation"] assert extents == [nbd.Extent(length=4096 + 1, flags=0)] # Unaligned end. extents = c.extents(4096, 4096 + 1)["base:allocation"] assert extents == [nbd.Extent(length=4096 + 1, flags=0)] # Unaligned start and end. extents = c.extents(4096 - 1, 4096 + 2)["base:allocation"] assert extents == [nbd.Extent(length=4096 + 2, flags=0)]
def test_base_allocation_some_data_unaligned(nbd_server, user_file, fmt, zero_flags): size = 1024**2 create_image(user_file.path, fmt, size) nbd_server.image = user_file.path nbd_server.fmt = fmt nbd_server.start() data_length = 64 * 1024 data_offset = 2 * data_length with nbd.open(nbd_server.url) as c: # Create 3 extents: zero, data, zero. c.write(data_offset, b"x" * data_length) # Unaligned part from first extent and last extent. extents = list(nbdutil.extents(c, data_offset - 1, data_length + 2)) assert extents == [ nbd.Extent(length=1, flags=zero_flags), nbd.Extent(length=data_length, flags=0), nbd.Extent(length=1, flags=zero_flags), ] # Unaligned part from second extent. extents = list(nbdutil.extents(c, data_offset + 1, data_length - 2)) assert extents == [ nbd.Extent(length=data_length - 2, flags=0), ] # Unaligned part from second and last extents. extents = list(nbdutil.extents(c, data_offset + 1, data_length)) assert extents == [ nbd.Extent(length=data_length - 1, flags=0), nbd.Extent(length=1, flags=zero_flags), ]
def test_base_allocation_empty(nbd_server, user_file, fmt, zero_flags): size = nbd.MAX_LENGTH create_image(user_file.path, fmt, size) nbd_server.image = user_file.path nbd_server.fmt = fmt nbd_server.start() with nbd.open(nbd_server.url) as c: # Entire image. extents = c.extents(0, size)["base:allocation"] assert extents == [nbd.Extent(length=size, flags=zero_flags)] # First block. extents = c.extents(0, 4096)["base:allocation"] assert extents == [nbd.Extent(length=4096, flags=zero_flags)] # Last block. extents = c.extents(size - 4096, 4096)["base:allocation"] assert extents == [nbd.Extent(length=4096, flags=zero_flags)] # Some block. extents = c.extents(4096, 4096)["base:allocation"] assert extents == [nbd.Extent(length=4096, flags=zero_flags)] # Unaligned start. extents = c.extents(4096 - 1, 4096 + 1)["base:allocation"] assert extents == [nbd.Extent(length=4096 + 1, flags=zero_flags)] # Unaligned end. extents = c.extents(4096, 4096 + 1)["base:allocation"] assert extents == [nbd.Extent(length=4096 + 1, flags=zero_flags)] # Unaligned start and end. extents = c.extents(4096 - 1, 4096 + 2)["base:allocation"] assert extents == [nbd.Extent(length=4096 + 2, flags=zero_flags)]