Skip to content

Commit

Permalink
Add bitmap info to getQemuImageInfo
Browse files Browse the repository at this point in the history
We add bitmap info the the getQemuImageInfo output so this can be used
within ovirt-engine.

Signed-off-by: Jean-Louis Dupond <[email protected]>
  • Loading branch information
dupondje committed Jun 30, 2023
1 parent 07d3089 commit 7c41bd5
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 0 deletions.
43 changes: 43 additions & 0 deletions lib/vdsm/api/vdsm-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,46 @@ types:
1.1: qemu versions starting with 1.1 which are supported by
QCOW2 version 3.

Qcow2BitmapInfoFlags: &Qcow2BitmapInfoFlags
added: '4.5.5'
description: An enumeration of qcow compat version.
name: Qcow2BitmapInfoFlags
type: enum
values:
'in-use': This flag is set by any process actively modifying the
qcow2 file, and cleared when the updated bitmap is flushed
to the qcow2 image. The presence of this flag in an
offline image means that the bitmap was not saved correctly
after its last usage, and may contain inconsistent data.
'auto': The bitmap must reflect all changes of the virtual disk by
any application that would write to this qcow2 file.

Qcow2BitmapInfo: &Qcow2BitmapInfo
added: '4.5.5'
description: Information about a Bitmap
name: Qcow2BitmapInfo
properties:
- description: The UUID of bitmap
name: name
type: *UUID

- description: Granularity of the bitmap
name: granularity
type: uint

- description: Bitmap flags
name: flags
defaultvalue: '[]'
type: *Qcow2BitmapInfoFlags
type: object

Qcow2Bitmaps: &Qcow2Bitmaps
added: '4.5.5'
description: A list of qcow2 bitmaps
name: Qcow2Bitmaps
defaultvalue: '[]'
type: *Qcow2BitmapInfo

Qcow2Attributes: &Qcow2Attributes
added: '4.1'
description: Possible QCOW2 attributes which are allowed
Expand Down Expand Up @@ -8131,6 +8171,9 @@ types:
- description: The compat version.
name: compat
type: *Qcow2Compat
- description: Volume bitmaps
name: bitmaps
type: *Qcow2Bitmaps
type: object

VolumeSizeInfo: &VolumeSizeInfo
Expand Down
1 change: 1 addition & 0 deletions lib/vdsm/host/caps.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def get():
caps['measure_active'] = True
caps['mailbox_events'] = config.getboolean("mailbox", "events_enable")
caps['zerocopy_migrations'] = hasattr(libvirt, 'VIR_MIGRATE_ZEROCOPY')
caps['qemu_image_info_bitmaps'] = True

return caps

Expand Down
10 changes: 10 additions & 0 deletions lib/vdsm/storage/volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os.path
import logging
from collections import namedtuple
from contextlib import contextmanager

from vdsm import utils
Expand All @@ -27,6 +28,8 @@

log = logging.getLogger('storage.volume')

Qcow2BitmapInfo = namedtuple("Qcow2BitmapInfo", "name, granularity, flags")


def getBackingVolumePath(imgUUID, volUUID):
# We used to return a relative path ../<imgUUID>/<volUUID> but this caused
Expand Down Expand Up @@ -289,6 +292,13 @@ def getQemuImageInfo(self):
result["backingfile"] = info["backing-filename"]
if "format-specific" in info:
result["compat"] = info["format-specific"]["data"]["compat"]
if "bitmaps" in info["format-specific"]["data"]:
result["bitmaps"] = [
Qcow2BitmapInfo(bitmap["name"],
bitmap["granularity"],
bitmap["flags"])
for bitmap in info["format-specific"]["data"]["bitmaps"]
]

return result

Expand Down
45 changes: 45 additions & 0 deletions tests/storage/bitmaps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ def test_add_only_valid_bitmaps(vol_chain):
},
]

qemuInfo = vol_chain.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmap, 65536),
]


def test_no_bitmaps_to_add(vol_chain):
# Add bitmaps from base volume to top volume
Expand All @@ -90,6 +95,9 @@ def test_no_bitmaps_to_add(vol_chain):
info = qemuimg.info(vol_chain.top_vol)
assert 'bitmaps' not in info['format-specific']['data']

qemuInfo = vol_chain.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


def test_add_bitmap_failed(monkeypatch, vol_chain):
# Add new bitmap to base volume
Expand Down Expand Up @@ -134,6 +142,11 @@ def test_merge_only_valid_bitmaps(vol_chain):
},
]

qemuInfo = vol_chain.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmap, 65536),
]


def test_no_bitmaps_to_merge(vol_chain):
# Merge bitmaps from base volume to top volume
Expand All @@ -142,6 +155,9 @@ def test_no_bitmaps_to_merge(vol_chain):
info = qemuimg.info(vol_chain.base_vol)
assert 'bitmaps' not in info['format-specific']['data']

qemuInfo = vol_chain.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


def test_merge_bitmaps_failed(monkeypatch, vol_chain):
bitmap = 'bitmap'
Expand All @@ -164,6 +180,11 @@ def test_merge_bitmaps_failed(monkeypatch, vol_chain):
},
]

qemuInfo = vol_chain.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmap, 65536),
]


def test_merge_bitmaps_failed_to_add_bitmap(
monkeypatch, vol_chain):
Expand All @@ -180,6 +201,9 @@ def test_merge_bitmaps_failed_to_add_bitmap(
info = qemuimg.info(vol_chain.base_vol)
assert 'bitmaps' not in info['format-specific']['data']

qemuInfo = vol_chain.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


def test_skip_holes_during_merge_bitmaps(tmp_mount, vol_chain):
virtual_size = MiB
Expand Down Expand Up @@ -219,6 +243,9 @@ def test_skip_holes_during_merge_bitmaps(tmp_mount, vol_chain):
info = qemuimg.info(vol_chain.base_vol)
assert 'bitmaps' not in info['format-specific']['data']

qemuInfo = vol_chain.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


def test_remove_bitmap_failed(monkeypatch, tmp_mount, vol_chain):
bitmap = 'bitmap'
Expand Down Expand Up @@ -257,6 +284,11 @@ def test_prune_stale_bitmaps(tmp_mount, vol_chain):
},
]

qemuInfo = vol_chain.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmap, 65536),
]


def test_prune_disabled_bitmaps(tmp_mount, vol_chain):
# Add valid bitmap to volumes
Expand All @@ -282,6 +314,11 @@ def test_prune_disabled_bitmaps(tmp_mount, vol_chain):
},
]

qemuInfo = vol_chain.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmap, 65536),
]


def test_prune_in_use_bitmaps(tmp_mount, vol_chain):
# Add inconsistent "in-use" bitmaps to volumes
Expand All @@ -307,6 +344,11 @@ def test_prune_in_use_bitmaps(tmp_mount, vol_chain):
},
]

qemuInfo = vol_chain.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmap, 65536),
]


def test_clear_bitmaps(tmp_mount, vol_chain):
bitmap_1 = 'bitmap_1'
Expand All @@ -324,6 +366,9 @@ def test_clear_bitmaps(tmp_mount, vol_chain):
vol_bitmaps = info["format-specific"]["data"].get("bitmaps", [])
assert not vol_bitmaps

qemuInfo = vol_chain.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


def test_clear_bitmaps_failed(monkeypatch, tmp_mount, vol_chain):
# Add new bitmap to top volume
Expand Down
18 changes: 18 additions & 0 deletions tests/storage/blocksd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from vdsm.storage import sanlock_direct
from vdsm.storage import sd
from vdsm.storage import sp
from vdsm.storage.volume import Qcow2BitmapInfo
from vdsm.storage.sdc import sdCache
from vdsm.storage.sdm.api import merge as api_merge
from vdsm.storage.spbackends import StoragePoolDiskBackend
Expand Down Expand Up @@ -1109,6 +1110,12 @@ def test_create_snapshot_cloning_bitmaps(
},
]

qemuInfo = top_vol.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo(bitmap_names[0], 65536, ["auto"]),
Qcow2BitmapInfo(bitmap_names[1], 65536, ["auto"]),
]


@requires_root
@pytest.mark.root
Expand Down Expand Up @@ -1193,6 +1200,12 @@ def test_create_snapshot_with_new_bitmap(
},
]

qemuInfo = top.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo("old-bitmap", 65536, ["auto"]),
Qcow2BitmapInfo("new-bitmap", 65536, ["auto"]),
]


@requires_root
@pytest.mark.root
Expand Down Expand Up @@ -1250,6 +1263,11 @@ def test_create_volume_with_new_bitmap(
},
]

qemuInfo = vol.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo("new-bitmap", 65536, ["auto"]),
]


@requires_root
@pytest.mark.root
Expand Down
18 changes: 18 additions & 0 deletions tests/storage/localfssd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from vdsm.storage import localFsSD
from vdsm.storage import qemuimg
from vdsm.storage import sd
from vdsm.storage.volume import Qcow2BitmapInfo

from . import qemuio
from . marks import requires_unprivileged_user
Expand Down Expand Up @@ -1316,6 +1317,12 @@ def test_create_snapshot_cloning_bitmaps(user_domain, local_fallocate):
},
]

qemuInfo = vol.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo(bitmap_names[0], 65536, ["auto"]),
Qcow2BitmapInfo(bitmap_names[1], 65536, ["auto"]),
]


def test_create_snapshot_with_new_bitmap(user_domain, local_fallocate):
if user_domain.getVersion() == 3:
Expand Down Expand Up @@ -1374,6 +1381,12 @@ def test_create_snapshot_with_new_bitmap(user_domain, local_fallocate):
},
]

qemuInfo = top.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo("old-bitmap", 65536, ["auto"]),
Qcow2BitmapInfo("new-bitmap", 65536, ["auto"]),
]


def test_create_volume_with_new_bitmap(user_domain, local_fallocate):
if user_domain.getVersion() == 3:
Expand Down Expand Up @@ -1408,6 +1421,11 @@ def test_create_volume_with_new_bitmap(user_domain, local_fallocate):
},
]

qemuInfo = vol.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo("new-bitmap", 65536, ["auto"]),
]


def test_fail_add_bitmaps_to_v3_domain(user_domain, local_fallocate):
if user_domain.getVersion() != 3:
Expand Down
6 changes: 6 additions & 0 deletions tests/storage/qemuimg_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,12 @@ def test_copy_bitmaps(self, tmp_mount):
},
]

qemuInfo = vol.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
(["auto"], bitmaps[0], 65536),
(["auto"], bitmaps[1], 65536),
]

def test_convert_without_copy_bitmaps(self, tmp_mount):
virtual_size = MiB

Expand Down
6 changes: 6 additions & 0 deletions tests/storage/sdm_add_bitmap_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from vdsm.storage import exception as se
from vdsm.storage import guarded
from vdsm.storage import qemuimg
from vdsm.storage.volume import Qcow2BitmapInfo
from vdsm.storage.sdm import volume_info
from vdsm.storage.sdm.api import add_bitmap

Expand Down Expand Up @@ -76,6 +77,11 @@ def test_add_bitmap(fake_scheduler, env_type):
assert qcow2_data["bitmaps"][0]["name"] == bitmap
assert env_vol.getMetaParam(sc.GENERATION) == generation + 1

qemuInfo = env_vol.getQemuImageInfo()
assert qemuInfo["bitmaps"] == [
Qcow2BitmapInfo(bitmap, 65536, ["auto"]),
]


@pytest.mark.parametrize("env_type", ["file", "block"])
def test_vol_type_not_qcow(fake_scheduler, env_type):
Expand Down
15 changes: 15 additions & 0 deletions tests/storage/sdm_clear_bitmaps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ def test_clear_bitmaps(fake_scheduler, env_type):
assert "bitmaps" not in vol_info["format-specific"]["data"]
assert top_vol.getMetaParam(sc.GENERATION) == generation + 1

qemuInfo = top_vol.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


@pytest.mark.parametrize("env_type", ["file", "block"])
def test_clear_invalid_bitmaps(fake_scheduler, env_type):
Expand Down Expand Up @@ -110,6 +113,9 @@ def test_clear_invalid_bitmaps(fake_scheduler, env_type):
assert "bitmaps" not in vol_info["format-specific"]["data"]
assert top_vol.getMetaParam(sc.GENERATION) == generation + 1

qemuInfo = top_vol.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


@pytest.mark.parametrize("env_type", ["file", "block"])
def test_vol_type_not_qcow(fake_scheduler, env_type):
Expand Down Expand Up @@ -179,6 +185,9 @@ def test_clear_missing_bitmaps(fake_scheduler, env_type):
assert not bitmaps
assert top_vol.getMetaParam(sc.GENERATION) == generation + 1

qemuInfo = top_vol.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo


@pytest.mark.parametrize("env_type", ["file", "block"])
def test_clear_bitmaps_from_vol_chain(fake_scheduler, env_type):
Expand Down Expand Up @@ -214,6 +223,9 @@ def test_clear_bitmaps_from_vol_chain(fake_scheduler, env_type):
assert not bitmaps
assert leaf_vol.getMetaParam(sc.GENERATION) == generation + 1

qemuInfo = leaf_vol.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo

# Clear all the bitmaps from an internal volume
internal_vol = env.chain[1]
generation = internal_vol.getMetaParam(sc.GENERATION)
Expand All @@ -234,3 +246,6 @@ def test_clear_bitmaps_from_vol_chain(fake_scheduler, env_type):
bitmaps = vol_info["format-specific"]["data"].get("bitmaps", [])
assert not bitmaps
assert internal_vol.getMetaParam(sc.GENERATION) == generation + 1

qemuInfo = internal_vol.getQemuImageInfo()
assert 'bitmaps' not in qemuInfo
Loading

0 comments on commit 7c41bd5

Please sign in to comment.