As well as nbdkit 1.24 being released on Thursday, its sister project libnbd 1.6 was released at the same time. This comes with an enhanced copying tool called nbdcopy designed to replace some uses of qemu-img convert (note: it’s not a general replacement).
nbdcopy lets you copy from and to NBD servers (nbdkit, qemu-nbd, qemu-storage-daemon, nbd-server), local files, local block devices, pipes/sockets, and stdin/stdout. For example to stream the content of an NBD server:
$ nbdcopy nbd://localhost - | hexdump -C
-” character streams to stdout.
nbd://localhost is an NBD URI referring to an NBD server that is already running. What if you don’t have an already running server? nbdcopy lets you run one from the command line (and cleans up after). For example this is one way to convert a qcow2 file to raw:
$ nbdcopy -- [ qemu-nbd -f qcow2 disk.qcow ] disk.raw
[ ... ] section starts qemu-nbd as a captive NBD server, exposing privately an NBD endpoint, and nbdcopy copies this to local file
--” is needed to stop nbdcopy trying to interpret qemu-nbd’s own command line arguments.)
However this post is really about the nbdkit release. How did I test and benchmark nbdcopy? Of course I wrote an nbdkit plugin called nbdkit-sparse-random-plugin. This plugin has two clever features for testing copying tools. Firstly it creates random disks which have the same “shape” as virtual machine disk images (but without the overhead of needing to bother with an actual VM). Secondly it can act as both a source and target for testing copies.
Let’s unpack those two things a bit further.
Virtual machine disk images (especially mostly empty ones) are mostly sparse. Here’s part of the sparse map from a Fedora 32 disk image:
$ virt-builder fedora-32 $ filefrag -e fedora-32.img Filesystem type is: 58465342 File size of fedora-32.img is 6442450944 (1572864 blocks of 4096 bytes) ext: logical_offset: physical_offset: length: expected: flags: 0: 0.. 0: 2038672.. 2038672: 1: 1: 1.. 15: 2176040.. 2176054: 15: 2038673: 2: 256.. 271: 2188819.. 2188834: 16: 2176295: 3: 512.. 3135: 3650850.. 3653473: 2624: 2189075: 4: 3168.. 4463: 3781763.. 3783058: 1296: 3653506: [...]
The new sparse-random plugin generates a disk image which has a similar shape — islands of random data in a sea of sparseness. The algorithm for doing this is quite neat. Because the plugin doesn’t need to store the data, unlike a real disk image, it can generate huge disk images (eg. a terabyte) while using almost no memory. We use a low-overhead, high-quality random number generator and are smart about seeds so that every run of sparse-random with the same seed produces identical output.
The other part of this plugin is how we can use it to test copying tools like nbdcopy and qemu-img convert. My idea was that the plugin could be used both as the source and the target of the copy:
$ nbdkit -U - sparse-random 1T --run ' nbdcopy "$uri" "$uri" '
Here we create a terabyte-sized sparse-random disk, and get nbdcopy to copy from the plugin to the plugin. On reads sparse-random supplies the sparseness and random data. On writes it checks if what is being written matches the content of the plugin, throwing
-EIO errors if not. Assuming the copying tool is correctly handling errors, we can both validate the copying tool and benchmark it. And it works with qemu-img convert too:
$ nbdkit -U - sparse-random 1T --run ' qemu-img convert "$uri" "$uri" '
And now we can see which one is faster.
Try it, you may be surprised.