Skip to content

Commit 0ea268b

Browse files
committed
Custom parameters for QCOW2 snapshot used for mirror in migration
It's needed for migration because the mirror parent is empty, meaning that if the access size is bigger than 512, it will try to read the parent to complete with empty parent, corrupting part of the cluster when we will coalesce it in the real parent. The cluster size of the snapshot is the only one changed so that when it's coalesced in its parent, we find ourselves with the original cluster size again. It limits the max size of images to 64TiB otherwise it would not work with migration. Signed-off-by: Damien Thenot <damien.thenot@vates.tech>
1 parent 8e53ec5 commit 0ea268b

7 files changed

Lines changed: 45 additions & 21 deletions

File tree

drivers/FileSR.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ def reset_leaf(self, sr_uuid, vdi_uuid):
767767

768768
@override
769769
def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
770-
cloneOp=False, secondary=None, cbtlog=None) -> str:
770+
cloneOp=False, secondary=None, cbtlog=None, is_mirror_destination=False) -> str:
771771
# If cbt enabled, save file consistency state
772772
if cbtlog is not None:
773773
if blktap2.VDI.tap_status(self.session, vdi_uuid):
@@ -785,7 +785,7 @@ def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
785785
if not blktap2.VDI.tap_pause(self.session, sr_uuid, vdi_uuid):
786786
raise util.SMException("failed to pause VDI %s" % vdi_uuid)
787787
try:
788-
return self._snapshot(snapType, cbtlog, consistency_state)
788+
return self._snapshot(snapType, cbtlog, consistency_state, is_mirror_destination)
789789
finally:
790790
self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary)
791791
blktap2.VDI.tap_unpause(self.session, sr_uuid, vdi_uuid, secondary)
@@ -812,7 +812,7 @@ def _create_new_parent(self, src, newsrc):
812812
def __fist_enospace(self):
813813
raise util.CommandException(28, "cowutil snapshot", reason="No space")
814814

815-
def _snapshot(self, snap_type, cbtlog=None, cbt_consistency=None):
815+
def _snapshot(self, snap_type, cbtlog=None, cbt_consistency=None, is_mirror_destination=False):
816816
util.SMlog("FileVDI._snapshot for %s (type %s)" % (self.uuid, snap_type))
817817

818818
args = []
@@ -864,7 +864,8 @@ def _snapshot(self, snap_type, cbtlog=None, cbt_consistency=None):
864864
util.fistpoint.activate_custom_fn(
865865
"FileSR_fail_snap1",
866866
self.__fist_enospace)
867-
util.ioretry(lambda: self._snap(tmpsrc, newsrcname))
867+
868+
util.ioretry(lambda: self._snap(tmpsrc, newsrcname, is_mirror_destination))
868869
# SMB3 can return EACCES if we attempt to rename over the
869870
# hardlink leaf too quickly after creating it.
870871
util.ioretry(lambda: self._rename(tmpsrc, src),
@@ -983,8 +984,8 @@ def get_params(self) -> str:
983984
opterr='VDI %s unavailable %s' % (self.uuid, self.path))
984985
return super(FileVDI, self).get_params()
985986

986-
def _snap(self, child, parent):
987-
self.cowutil.snapshot(child, parent, self.vdi_type == VdiType.RAW)
987+
def _snap(self, child, parent, is_mirror_destination=False):
988+
self.cowutil.snapshot(child, parent, self.vdi_type == VdiType.RAW, is_mirror_image=is_mirror_destination)
988989

989990
def _clonecleanup(self, src, dst, newsrc):
990991
try:

drivers/LVMSR.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,7 +1689,7 @@ def _detach(self):
16891689

16901690
@override
16911691
def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
1692-
cloneOp=False, secondary=None, cbtlog=None) -> str:
1692+
cloneOp=False, secondary=None, cbtlog=None, is_mirror_destination=False) -> str:
16931693
# If cbt enabled, save file consistency state
16941694
if cbtlog is not None:
16951695
if blktap2.VDI.tap_status(self.session, vdi_uuid):
@@ -1707,7 +1707,7 @@ def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
17071707

17081708
snapResult = None
17091709
try:
1710-
snapResult = self._snapshot(snapType, cloneOp, cbtlog, consistency_state)
1710+
snapResult = self._snapshot(snapType, cloneOp, cbtlog, consistency_state, is_mirror_destination)
17111711
except Exception as e1:
17121712
try:
17131713
blktap2.VDI.tap_unpause(self.session, sr_uuid, vdi_uuid,
@@ -1724,7 +1724,7 @@ def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
17241724
(unpause_time - pause_time))
17251725
return snapResult
17261726

1727-
def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None):
1727+
def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None, is_mirror_destination=False):
17281728
util.SMlog("LVMVDI._snapshot for %s (type %s)" % (self.uuid, snapType))
17291729

17301730
if not self.sr.isMaster:
@@ -1829,7 +1829,7 @@ def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None):
18291829
self.utilisation = lvSizeBase
18301830
util.fistpoint.activate("LVHDRT_clone_vdi_after_shrink_parent", self.sr.uuid)
18311831

1832-
snapVDI = self._createSnap(origUuid, snapVdiType, lvSizeOrig, False)
1832+
snapVDI = self._createSnap(origUuid, snapVdiType, lvSizeOrig, False, is_mirror_destination)
18331833
util.fistpoint.activate("LVHDRT_clone_vdi_after_first_snap", self.sr.uuid)
18341834
snapVDI2 = None
18351835
if snapType == VDI.SNAPSHOT_DOUBLE:
@@ -1881,7 +1881,7 @@ def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None):
18811881

18821882
return self._finishSnapshot(snapVDI, snapVDI2, hostRefs, cloneOp, snapType)
18831883

1884-
def _createSnap(self, snapUuid, snapVdiType, snapSizeLV, isNew):
1884+
def _createSnap(self, snapUuid, snapVdiType, snapSizeLV, isNew, is_mirror_destination=False):
18851885
"""Snapshot self and return the snapshot VDI object"""
18861886

18871887
snapLV = LV_PREFIX[snapVdiType] + snapUuid
@@ -1893,7 +1893,7 @@ def _createSnap(self, snapUuid, snapVdiType, snapSizeLV, isNew):
18931893
self.sr.lvActivator.add(snapUuid, snapLV, False)
18941894
parentRaw = (self.vdi_type == VdiType.RAW)
18951895
self.cowutil.snapshot(
1896-
snapPath, self.path, parentRaw, max(self.size, self.cowutil.getDefaultPreallocationSizeVirt())
1896+
snapPath, self.path, parentRaw, max(self.size, self.cowutil.getDefaultPreallocationSizeVirt()), is_mirror_image=is_mirror_destination
18971897
)
18981898
snapParent = self.cowutil.getParent(snapPath, LvmCowUtil.extractUuid)
18991899

drivers/VDI.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def attach_from_config(self, sr_uuid, vdi_uuid) -> str:
214214
raise xs_errors.XenError('Unimplemented')
215215

216216
def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
217-
cloneOp=False, secondary=None, cbtlog=None) -> str:
217+
cloneOp=False, secondary=None, cbtlog=None, is_mirror_destination=False) -> str:
218218
raise xs_errors.XenError('Unimplemented')
219219

220220
def _delete_cbt_log(self) -> None:
@@ -379,12 +379,14 @@ def snapshot(self, sr_uuid, vdi_uuid) -> str:
379379
if self.sr.srcmd.params['driver_params'].get("mirror"):
380380
secondary = self.sr.srcmd.params['driver_params']["mirror"]
381381

382+
is_mirror_destination = bool(self.sr.srcmd.params['driver_params'].get("base_mirror")) and not secondary
383+
382384
if self._get_blocktracking_status():
383385
cbtlog = self._get_cbt_logpath(self.uuid)
384386
else:
385387
cbtlog = None
386388
return self._do_snapshot(sr_uuid, vdi_uuid, snapType,
387-
secondary=secondary, cbtlog=cbtlog)
389+
secondary=secondary, cbtlog=cbtlog, is_mirror_destination=is_mirror_destination)
388390

389391
def activate(self, sr_uuid, vdi_uuid) -> Optional[Dict[str, str]]:
390392
"""Activate VDI - called pre tapdisk open"""

drivers/cowutil.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ def snapshot(
229229
parent: str,
230230
parentRaw: bool,
231231
msize: int = 0,
232-
checkEmpty: bool = True
232+
checkEmpty: bool = True,
233+
is_mirror_image: bool = False
233234
) -> None:
234235
pass
235236

drivers/qcow2util.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -790,16 +790,34 @@ def snapshot(
790790
parent: str,
791791
parentRaw: bool,
792792
msize: int = 0,
793-
checkEmpty: bool = True
793+
checkEmpty: bool = True,
794+
is_mirror_image: bool = False
794795
) -> None:
796+
# TODO: msize, it's use to preallocate metadata, could we honor this too?
797+
# TODO: checkEmpty? If it is False, then the parent could be empty and should still be used for snapshot
798+
# But if True, if the parent is empty, we do what? vhd would just use the parent of parent as base, should we emulate this behavior?
799+
800+
cmd = [QEMU_IMG, "create"]
801+
795802
if parentRaw:
796803
parent_type = RAW_TYPE
797804
parent_cluster_size = QCOW2_DEFAULT_CLUSTER_SIZE
798805
else:
799806
parent_type = QCOW2_TYPE
800807
parent_cluster_size = self.getBlockSize(parent)
808+
args = ["-f", QCOW2_TYPE, "-F", parent_type, "-b", parent]
809+
810+
if is_mirror_image:
811+
# is_mirror_image override the cluster size to ensure that we have a write of 512 to avoid having to read the parent during a migration.
812+
# It also enable extended_l2 for this purpose, this is only done in the snapshot used for the mirror, this configuration will be lost when coalesced in its parent
813+
# Ensuring we go back to a better cluster_size for performance reasons. This limit our images max size to 64TiB.
814+
parent_cluster_size = 16 * 1024 # 16KiB
815+
args.extend(["-o", "extended_l2=on"])
816+
817+
args.extend(["-o", f"cluster_size={parent_cluster_size}"])
818+
cmd.extend(args)
819+
cmd.append(path)
801820

802-
cmd = [QEMU_IMG, "create", "-f", QCOW2_TYPE, "-b", parent, "-F", parent_type, "-o", f"cluster_size={parent_cluster_size}", path]
803821
self._ioretry(cmd)
804822
self.setHidden(path, False) #We add hidden header at creation
805823

drivers/vhdutil.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,8 @@ def snapshot(
356356
parent: str,
357357
parentRaw: bool,
358358
msize: int = 0,
359-
checkEmpty: bool = True
359+
checkEmpty: bool = True,
360+
is_mirror_image: bool = False
360361
) -> None:
361362
cmd = [VHD_UTIL, "snapshot", OPT_LOG_ERR, "-n", path, "-p", parent]
362363
if parentRaw:

tests/test_cbt.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ def _rename(self, from_path, to_path) -> None:
6161

6262
@override
6363
def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
64-
cloneOp=False, secondary=None, cbtlog=None) -> str:
64+
cloneOp=False, secondary=None, cbtlog=None, is_mirror_destination=False) -> str:
6565
return self.state_mock._do_snapshot(
66-
sr_uuid, vdi_uuid, snapType, cloneOp, secondary, cbtlog
66+
sr_uuid, vdi_uuid, snapType, cloneOp, secondary, cbtlog, is_mirror_destination
6767
)
6868

6969
@override
@@ -366,7 +366,8 @@ def test_snapshot_success_with_CBT_disable(self, context):
366366
mock.ANY,
367367
mock.ANY,
368368
mock.ANY,
369-
None)
369+
None,
370+
False)
370371

371372
@testlib.with_context
372373
@mock.patch('VDI.cbtutil', autospec=True)

0 commit comments

Comments
 (0)