mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-10 04:06:47 +00:00
feat(instances): external snapshots can freeze the guest file systems
A disk-only snapshot of a running VM is crash-consistent: data the guest has not written yet is missing from it. With a connected QEMU guest agent, the snapshot form now offers to freeze the guest's file systems while the disks are snapshotted (libvirt's QUIESCE flag); the box is checked by default and can be cleared. It is offered only for a running VM whose agent is connected, and not together with memory: libvirt accepts quiesce only for disk-only snapshots, and a memory snapshot keeps the whole guest state anyway. A request that does not fit is refused before libvirt is called.
This commit is contained in:
parent
64ea12b0ce
commit
f766890825
7 changed files with 68 additions and 10 deletions
|
|
@ -34,6 +34,7 @@ try:
|
|||
VIR_MIGRATE_UNSAFE,
|
||||
VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC,
|
||||
VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY,
|
||||
VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE,
|
||||
VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED,
|
||||
VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING,
|
||||
VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
|
|
@ -1575,14 +1576,23 @@ class wvmInstance(wvmConnect):
|
|||
</domainsnapshot>"""
|
||||
self._snapshotCreateXML(xml, 0)
|
||||
|
||||
def create_external_snapshot(self, name, desc="", memory=False):
|
||||
def create_external_snapshot(self, name, desc="", memory=False, quiesce=False):
|
||||
"""A disk-only snapshot, or with memory too (running VMs only). Every
|
||||
disk gets a libvirt-named overlay next to it; the memory file goes
|
||||
next to the first disk."""
|
||||
next to the first disk. quiesce: the guest agent freezes the guest's
|
||||
file systems for a disk-only snapshot of a running VM (libvirt
|
||||
accepts it only without memory, which keeps the whole state anyway)."""
|
||||
if not SNAPSHOT_NAME.fullmatch(name):
|
||||
raise util.OperationError(
|
||||
"Snapshot names are 1-64 letters, digits, '.', '_' or '-' and do not start with '.'"
|
||||
)
|
||||
if quiesce:
|
||||
if memory:
|
||||
raise util.OperationError("A snapshot with memory is not quiesced; it keeps the whole guest state")
|
||||
if self.get_status() != 1:
|
||||
raise util.OperationError("Only a running VM's file systems can be frozen")
|
||||
if not self.is_agent_ready():
|
||||
raise util.OperationError("The QEMU guest agent is not connected; it freezes the file systems")
|
||||
self._check_snapshot_state()
|
||||
if self.instance.snapshotListNames(VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL):
|
||||
raise util.OperationError("The VM has internal snapshots; external snapshots cannot be mixed with them")
|
||||
|
|
@ -1610,6 +1620,8 @@ class wvmInstance(wvmConnect):
|
|||
flags = VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC
|
||||
if not memory:
|
||||
flags |= VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY
|
||||
if quiesce:
|
||||
flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE
|
||||
self._snapshotCreateXML(xml, flags)
|
||||
self._refresh_pools_after_snapshot()
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,24 @@ class CreateTestCase(unittest.TestCase):
|
|||
"<memory snapshot='external' file='/pool/11111111-2222-3333-4444-555555555555.m.mem'/>", xml
|
||||
)
|
||||
|
||||
def test_a_quiesced_snapshot_freezes_the_guest_file_systems(self):
|
||||
p = proxy()
|
||||
p.is_agent_ready = lambda: True
|
||||
p.create_external_snapshot("q", quiesce=True)
|
||||
_, flags = created(p)
|
||||
self.assertEqual(flags, DISK_ONLY_ATOMIC | libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE)
|
||||
|
||||
def test_quiesce_needs_a_running_vm_with_its_agent_and_no_memory(self):
|
||||
# libvirt accepts quiesce only for disk-only snapshots, through the agent
|
||||
for status, ready, memory, pattern in (
|
||||
(5, True, False, "running VM"), (3, True, False, "running VM"),
|
||||
(1, False, False, "guest agent is not connected"), (1, True, True, "memory"),
|
||||
):
|
||||
with self.subTest(status=status, ready=ready, memory=memory):
|
||||
p = proxy(status=status)
|
||||
p.is_agent_ready = lambda ready=ready: ready
|
||||
self.refused(p, pattern, quiesce=True, memory=memory)
|
||||
|
||||
def refused(self, p, pattern, **kwargs):
|
||||
with self.assertRaisesRegex(util.OperationError, pattern):
|
||||
p.create_external_snapshot(kwargs.pop("name", "s"), **kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue