When uploading to qcow2 disks on block storage you must set the disk initial_size
correctly so the system allocates big enough disk. If the initial size is too small, the
upload will fail when trying to write after the end of the device.

This info can be useful for people working on a backup solution for oVirt.

The easiest case is upload of existing qcow2 image using the SDK. In this case we
create a new disk with:

    initial_size=image_size,
    provisioned_size=virtual_size,

image_size is the size of the file:

    os.stat('image').st_size

Note that "qemu-img info" return "actual-size" - this is the allocated size on storage
which is not helpful for uploading images.

Example:

A more tricky case is when a backup system keeps raw guest data, but know how
to generate qcow2 image stream, without creating a temporary image.

In this case the required size can be calculated by counting the number of 
clusters that need to be allocated in the final image. This depends on the
location of the data in the image.

For example this creates 1G image with only one cluster:

$ python -c 'with open("one-cluster.raw", "wb") as f:
    f.truncate(1024**3)
    f.write("x")'

$ ls -lhs one-cluster.raw 
4.0K -rw-rw-r--. 1 nsoffer nsoffer 1.0G Nov 15 18:24 one-cluster.raw

$ qemu-img measure -f raw -O qcow2 one-cluster.raw 
required size: 458752
fully allocated size: 1074135040

$ qemu-img convert -f raw -O qcow2 one-cluster.raw one-cluster.qcow2

$ ls -lhs one-cluster.qcow2 
324K -rw-r--r--. 1 nsoffer nsoffer 384K Nov 15 18:25 one-cluster.qcow2


But this creates a fully allocated 1G image:

$ python -c 'with open("fully-allocated.raw", "wb") as f:
    f.truncate(1024**3)
    for i in range(0, 1024**3, 64 * 1024):
        f.seek(i)
        f.write("x")'

$ ls -lhs fully-allocated.raw 
64M -rw-rw-r--. 1 nsoffer nsoffer 1.0G Nov 15 18:30 fully-allocated.raw

$ qemu-img measure -f raw -O qcow2 fully-allocated.raw 
required size: 1074135040
fully allocated size: 1074135040

$ qemu-img convert -f raw -O qcow2 fully-allocated.raw fully-allocated.qcow2

$ ls -lhs fully-allocated.qcow2 
1.1G -rw-r--r--. 1 nsoffer nsoffer 1.1G Nov 15 18:31 fully-allocated.qcow2


We had code in vdsm that does exactly this, and it was removed since qemu-img
support a new "measure" command in RHEL 7.5 providing this info. But this works
only for existing images.

You can find the code in this vdsm commit:

The module implementing estimation:

The tests for this module:

If you know the data ranges that will be written to the qcow2 image, you can count
the clusters like this:

Then you can use the cluster count to estimate qcow2 file size:

Nir