diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am index 8040bf936699574a49cade79661781f6670c4d22..e0e13fb1c36f7a3c791d9ba61859c4ccf3eb6a70 100644 --- a/src/qemu/Makefile.inc.am +++ b/src/qemu/Makefile.inc.am @@ -68,6 +68,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_vhost_user.h \ qemu/qemu_vhost_user_gpu.c \ qemu/qemu_vhost_user_gpu.h \ + qemu/qemu_checkpoint.c \ + qemu/qemu_checkpoint.h \ $(NULL) diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c new file mode 100644 index 0000000000000000000000000000000000000000..95ddff4c97ade4bad30ad81915820267fa35f996 --- /dev/null +++ b/src/qemu/qemu_checkpoint.c @@ -0,0 +1,483 @@ +/* + * qemu_checkpoint.c: checkpoint related implementation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include + +#include "qemu_checkpoint.h" +#include "qemu_capabilities.h" +#include "qemu_monitor.h" +#include "qemu_monitor_json.h" +#include "qemu_domain.h" + +#include "virerror.h" +#include "virlog.h" +#include "datatypes.h" +#include "viralloc.h" +#include "domain_conf.h" +#include "libvirt_internal.h" +#include "virxml.h" +#include "virstring.h" +#include "virdomaincheckpointobjlist.h" +#include "virdomainsnapshotobjlist.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_checkpoint"); + +/* Looks up the domain object from checkpoint and unlocks the + * driver. The returned domain object is locked and ref'd and the + * caller must call virDomainObjEndAPI() on it. */ +virDomainObjPtr +qemuDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint) +{ + return qemuDomainObjFromDomain(checkpoint->domain); +} + + +/* Looks up checkpoint object from VM and name */ +virDomainMomentObjPtr +qemuCheckpointObjFromName(virDomainObjPtr vm, + const char *name) +{ + virDomainMomentObjPtr chk = NULL; + chk = virDomainCheckpointFindByName(vm->checkpoints, name); + if (!chk) + virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT, + _("no domain checkpoint with matching name '%s'"), + name); + + return chk; +} + + +/* Looks up checkpoint object from VM and checkpointPtr */ +virDomainMomentObjPtr +qemuCheckpointObjFromCheckpoint(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint) +{ + return qemuCheckpointObjFromName(vm, checkpoint->name); +} + + +/* Called inside job lock */ +static int +qemuCheckpointPrepare(virQEMUDriverPtr driver, + virCapsPtr caps, + virDomainObjPtr vm, + virDomainCheckpointDefPtr def) +{ + int ret = -1; + size_t i; + char *xml = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + + /* Easiest way to clone inactive portion of vm->def is via + * conversion in and back out of xml. */ + if (!(xml = qemuDomainDefFormatLive(driver, priv->qemuCaps, + vm->def, priv->origCPU, + true, true)) || + !(def->parent.dom = virDomainDefParseString(xml, caps, driver->xmlopt, + priv->qemuCaps, + VIR_DOMAIN_DEF_PARSE_INACTIVE | + VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) + goto cleanup; + + if (virDomainCheckpointAlignDisks(def) < 0) + goto cleanup; + + for (i = 0; i < def->ndisks; i++) { + virDomainCheckpointDiskDefPtr disk = &def->disks[i]; + + if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) + continue; + + if (vm->def->disks[i]->src->format != VIR_STORAGE_FILE_QCOW2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("checkpoint for disk %s unsupported " + "for storage type %s"), + disk->name, + virStorageFileFormatTypeToString( + vm->def->disks[i]->src->format)); + goto cleanup; + } + } + + ret = 0; + + cleanup: + VIR_FREE(xml); + return ret; +} + +static int +qemuCheckpointAddActions(virDomainObjPtr vm, + virJSONValuePtr actions, + virDomainMomentObjPtr old_current, + virDomainCheckpointDefPtr def) +{ + size_t i, j; + virDomainCheckpointDefPtr olddef; + virDomainMomentObjPtr parent; + bool search_parents; + + for (i = 0; i < def->ndisks; i++) { + virDomainCheckpointDiskDef *disk = &def->disks[i]; + const char *node; + + if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) + continue; + node = qemuDomainDiskNodeFormatLookup(vm, disk->name); + if (qemuMonitorJSONTransactionAdd(actions, + "block-dirty-bitmap-add", + "s:node", node, + "s:name", disk->bitmap, + "b:persistent", true, + NULL) < 0) + return -1; + + /* We only want one active bitmap for a disk along the + * checkpoint chain, then later differential backups will + * merge the bitmaps (only one active) between the bounding + * checkpoint and the leaf checkpoint. If the same disks are + * involved in each checkpoint, this search terminates in one + * iteration; but it is also possible to have to search + * further than the immediate parent to find another + * checkpoint with a bitmap on the same disk. */ + search_parents = true; + for (parent = old_current; search_parents && parent; + parent = virDomainCheckpointFindByName(vm->checkpoints, + olddef->parent.parent_name)) { + olddef = virDomainCheckpointObjGetDef(parent); + for (j = 0; j < olddef->ndisks; j++) { + virDomainCheckpointDiskDef *disk2; + + disk2 = &olddef->disks[j]; + if (STRNEQ(disk->name, disk2->name) || + disk2->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) + continue; + if (qemuMonitorJSONTransactionAdd(actions, + "block-dirty-bitmap-disable", + "s:node", node, + "s:name", disk2->bitmap, + NULL) < 0) + return -1; + search_parents = false; + break; + } + } + } + return 0; +} + + +virDomainCheckpointPtr +qemuCheckpointCreateXML(virDomainPtr domain, + virDomainObjPtr vm, + const char *xmlDesc, + unsigned int flags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virDomainMomentObjPtr chk = NULL; + virDomainCheckpointPtr checkpoint = NULL; + bool update_current = true; + bool redefine = flags & VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE; + unsigned int parse_flags = 0; + virDomainMomentObjPtr other = NULL; + virQEMUDriverConfigPtr cfg = NULL; + virCapsPtr caps = NULL; + virJSONValuePtr actions = NULL; + int ret; + VIR_AUTOUNREF(virDomainCheckpointDefPtr) def = NULL; + + virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE, NULL); + /* TODO: VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE */ + + if (redefine) { + parse_flags |= VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE; + update_current = false; + } + + if (virDomainSnapshotObjListNum(vm->snapshots, NULL, 0) > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cannot create checkpoint while snapshot exists")); + goto cleanup; + } + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu binary lacks persistent bitmaps support")); + goto cleanup; + } + + if (!(caps = virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cannot create checkpoint for inactive domain")); + goto cleanup; + } + + if (!(def = virDomainCheckpointDefParseString(xmlDesc, caps, driver->xmlopt, + priv->qemuCaps, parse_flags))) + goto cleanup; + /* Unlike snapshots, the RNG schema already ensured a sane filename. */ + + /* We are going to modify the domain below. */ + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (redefine) { + if (virDomainCheckpointRedefinePrep(vm, &def, &chk, + driver->xmlopt, + &update_current) < 0) + goto endjob; + } else if (qemuCheckpointPrepare(driver, caps, vm, def) < 0) { + goto endjob; + } + + if (!chk) { + if (!(chk = virDomainCheckpointAssignDef(vm->checkpoints, def))) + goto endjob; + + def = NULL; + } + + other = virDomainCheckpointGetCurrent(vm->checkpoints); + if (other) { + if (!redefine && + VIR_STRDUP(chk->def->parent_name, other->def->name) < 0) + goto endjob; + if (update_current) { + virDomainCheckpointSetCurrent(vm->checkpoints, NULL); + if (qemuDomainCheckpointWriteMetadata(vm, other, + driver->caps, driver->xmlopt, + cfg->checkpointDir) < 0) + goto endjob; + } + } + + /* actually do the checkpoint */ + if (redefine) { + /* XXX Should we validate that the redefined checkpoint even + * makes sense, such as checking that qemu-img recognizes the + * checkpoint bitmap name in at least one of the domain's disks? */ + } else { + if (!(actions = virJSONValueNewArray())) + goto endjob; + if (qemuCheckpointAddActions(vm, actions, other, + virDomainCheckpointObjGetDef(chk)) < 0) + goto endjob; + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorTransaction(priv->mon, &actions); + if (qemuDomainObjExitMonitor(driver, vm) < 0 || ret < 0) + goto endjob; + } + + /* If we fail after this point, there's not a whole lot we can do; + * we've successfully created the checkpoint, so we have to go + * forward the best we can. + */ + checkpoint = virGetDomainCheckpoint(domain, chk->def->name); + + endjob: + if (checkpoint) { + if (update_current) + virDomainCheckpointSetCurrent(vm->checkpoints, chk); + if (qemuDomainCheckpointWriteMetadata(vm, chk, driver->caps, + driver->xmlopt, + cfg->checkpointDir) < 0) { + /* if writing of metadata fails, error out rather than trying + * to silently carry on without completing the checkpoint */ + virObjectUnref(checkpoint); + checkpoint = NULL; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to save metadata for checkpoint %s"), + chk->def->name); + virDomainCheckpointObjListRemove(vm->checkpoints, chk); + } else { + virDomainCheckpointLinkParent(vm->checkpoints, chk); + } + } else if (chk) { + virDomainCheckpointObjListRemove(vm->checkpoints, chk); + } + + qemuDomainObjEndJob(driver, vm); + + cleanup: + virJSONValueFree(actions); + virObjectUnref(caps); + virObjectUnref(cfg); + return checkpoint; +} + + +char * +qemuCheckpointGetXMLDesc(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint, + unsigned int flags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virDomainMomentObjPtr chk = NULL; + virDomainCheckpointDefPtr chkdef; + unsigned int format_flags; + + virCheckFlags(VIR_DOMAIN_CHECKPOINT_XML_SECURE | + VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN, NULL); + + if (!(chk = qemuCheckpointObjFromCheckpoint(vm, checkpoint))) + return NULL; + + chkdef = virDomainCheckpointObjGetDef(chk); + + format_flags = virDomainCheckpointFormatConvertXMLFlags(flags); + return virDomainCheckpointDefFormat(chkdef, driver->caps, driver->xmlopt, + format_flags); +} + + +struct virQEMUCheckpointReparent { + const char *dir; + virDomainMomentObjPtr parent; + virDomainObjPtr vm; + virCapsPtr caps; + virDomainXMLOptionPtr xmlopt; + int err; +}; + + +static int +qemuCheckpointReparentChildren(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainMomentObjPtr moment = payload; + struct virQEMUCheckpointReparent *rep = data; + + if (rep->err < 0) + return 0; + + VIR_FREE(moment->def->parent_name); + + if (rep->parent->def && + VIR_STRDUP(moment->def->parent_name, rep->parent->def->name) < 0) { + rep->err = -1; + return 0; + } + + rep->err = qemuDomainCheckpointWriteMetadata(rep->vm, moment, rep->caps, + rep->xmlopt, rep->dir); + return 0; +} + + +int +qemuCheckpointDelete(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint, + unsigned int flags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + VIR_AUTOUNREF(virQEMUDriverConfigPtr) cfg = virQEMUDriverGetConfig(driver); + int ret = -1; + virDomainMomentObjPtr chk = NULL; + virQEMUMomentRemove rem; + struct virQEMUCheckpointReparent rep; + bool metadata_only = !!(flags & VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY); + + virCheckFlags(VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN | + VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY | + VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY, -1); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + return -1; + + if (!metadata_only) { + /* Until qemu-img supports offline bitmap deletion, we are stuck + * with requiring a running guest */ + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cannot delete checkpoint for inactive domain")); + goto endjob; + } + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu binary lacks persistent bitmaps support")); + goto endjob; + } + } + + if (!(chk = qemuCheckpointObjFromCheckpoint(vm, checkpoint))) + goto endjob; + + if (flags & (VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN | + VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY)) { + rem.driver = driver; + rem.vm = vm; + rem.metadata_only = metadata_only; + rem.err = 0; + rem.current = virDomainCheckpointGetCurrent(vm->checkpoints); + rem.found = false; + rem.momentDiscard = qemuDomainCheckpointDiscard; + virDomainMomentForEachDescendant(chk, qemuDomainMomentDiscardAll, + &rem); + if (rem.err < 0) + goto endjob; + if (rem.found) { + virDomainCheckpointSetCurrent(vm->checkpoints, chk); + if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) { + if (qemuDomainCheckpointWriteMetadata(vm, chk, driver->caps, + driver->xmlopt, + cfg->checkpointDir) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to set checkpoint '%s' as current"), + chk->def->name); + virDomainCheckpointSetCurrent(vm->checkpoints, NULL); + goto endjob; + } + } + } + } else if (chk->nchildren) { + rep.dir = cfg->checkpointDir; + rep.parent = chk->parent; + rep.vm = vm; + rep.err = 0; + rep.caps = driver->caps; + rep.xmlopt = driver->xmlopt; + virDomainMomentForEachChild(chk, qemuCheckpointReparentChildren, + &rep); + if (rep.err < 0) + goto endjob; + virDomainMomentMoveChildren(chk, chk->parent); + } + + if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) { + virDomainMomentDropChildren(chk); + ret = 0; + } else { + ret = qemuDomainCheckpointDiscard(driver, vm, chk, true, metadata_only); + } + + endjob: + qemuDomainObjEndJob(driver, vm); + return ret; +} diff --git a/src/qemu/qemu_checkpoint.h b/src/qemu/qemu_checkpoint.h new file mode 100644 index 0000000000000000000000000000000000000000..0d36d718d64603432c08222e9a373148993e2e56 --- /dev/null +++ b/src/qemu/qemu_checkpoint.h @@ -0,0 +1,50 @@ +/* + * qemu_checkpoint.h: Implementation and handling of checkpoint + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "virconftypes.h" +#include "datatypes.h" + +virDomainObjPtr +qemuDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint); + +virDomainMomentObjPtr +qemuCheckpointObjFromCheckpoint(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint); + +virDomainMomentObjPtr +qemuCheckpointObjFromName(virDomainObjPtr vm, + const char *name); + +virDomainCheckpointPtr +qemuCheckpointCreateXML(virDomainPtr domain, + virDomainObjPtr vm, + const char *xmlDesc, + unsigned int flags); + + +char * +qemuCheckpointGetXMLDesc(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint, + unsigned int flags); + +int +qemuCheckpointDelete(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint, + unsigned int flags); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index facfb412e651c66df13d08dde2b5ce256b1bab74..3eb10e939f02a00b5e47bc18946a7ef866ec7db7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -53,6 +53,7 @@ #include "qemu_migration_params.h" #include "qemu_blockjob.h" #include "qemu_security.h" +#include "qemu_checkpoint.h" #include "virerror.h" #include "virlog.h" @@ -195,39 +196,6 @@ qemuSnapObjFromSnapshot(virDomainObjPtr vm, return qemuSnapObjFromName(vm, snapshot->name); } -/* Looks up the domain object from checkpoint and unlocks the - * driver. The returned domain object is locked and ref'd and the - * caller must call virDomainObjEndAPI() on it. */ -static virDomainObjPtr -qemuDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint) -{ - return qemuDomainObjFromDomain(checkpoint->domain); -} - - -/* Looks up checkpoint object from VM and name */ -static virDomainMomentObjPtr -qemuCheckpointObjFromName(virDomainObjPtr vm, - const char *name) -{ - virDomainMomentObjPtr chk = NULL; - chk = virDomainCheckpointFindByName(vm->checkpoints, name); - if (!chk) - virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT, - _("no domain checkpoint with matching name '%s'"), - name); - - return chk; -} - - -/* Looks up checkpoint object from VM and checkpointPtr */ -static virDomainMomentObjPtr -qemuCheckpointObjFromCheckpoint(virDomainObjPtr vm, - virDomainCheckpointPtr checkpoint) -{ - return qemuCheckpointObjFromName(vm, checkpoint->name); -} static int qemuAutostartDomain(virDomainObjPtr vm, @@ -17113,266 +17081,24 @@ qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, } -/* Called inside job lock */ -static int -qemuDomainCheckpointPrepare(virQEMUDriverPtr driver, virCapsPtr caps, - virDomainObjPtr vm, - virDomainCheckpointDefPtr def) -{ - int ret = -1; - size_t i; - char *xml = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - - /* Easiest way to clone inactive portion of vm->def is via - * conversion in and back out of xml. */ - if (!(xml = qemuDomainDefFormatLive(driver, priv->qemuCaps, - vm->def, priv->origCPU, - true, true)) || - !(def->parent.dom = virDomainDefParseString(xml, caps, driver->xmlopt, - priv->qemuCaps, - VIR_DOMAIN_DEF_PARSE_INACTIVE | - VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) - goto cleanup; - - if (virDomainCheckpointAlignDisks(def) < 0) - goto cleanup; - - for (i = 0; i < def->ndisks; i++) { - virDomainCheckpointDiskDefPtr disk = &def->disks[i]; - - if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) - continue; - - if (vm->def->disks[i]->src->format != VIR_STORAGE_FILE_QCOW2) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("checkpoint for disk %s unsupported " - "for storage type %s"), - disk->name, - virStorageFileFormatTypeToString( - vm->def->disks[i]->src->format)); - goto cleanup; - } - } - - ret = 0; - - cleanup: - VIR_FREE(xml); - return ret; -} - -static int -qemuDomainCheckpointAddActions(virDomainObjPtr vm, - virJSONValuePtr actions, - virDomainMomentObjPtr old_current, - virDomainCheckpointDefPtr def) -{ - size_t i, j; - virDomainCheckpointDefPtr olddef; - virDomainMomentObjPtr parent; - bool search_parents; - - for (i = 0; i < def->ndisks; i++) { - virDomainCheckpointDiskDef *disk = &def->disks[i]; - const char *node; - - if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) - continue; - node = qemuDomainDiskNodeFormatLookup(vm, disk->name); - if (qemuMonitorJSONTransactionAdd(actions, - "block-dirty-bitmap-add", - "s:node", node, - "s:name", disk->bitmap, - "b:persistent", true, - NULL) < 0) - return -1; - - /* We only want one active bitmap for a disk along the - * checkpoint chain, then later differential backups will - * merge the bitmaps (only one active) between the bounding - * checkpoint and the leaf checkpoint. If the same disks are - * involved in each checkpoint, this search terminates in one - * iteration; but it is also possible to have to search - * further than the immediate parent to find another - * checkpoint with a bitmap on the same disk. */ - search_parents = true; - for (parent = old_current; search_parents && parent; - parent = virDomainCheckpointFindByName(vm->checkpoints, - olddef->parent.parent_name)) { - olddef = virDomainCheckpointObjGetDef(parent); - for (j = 0; j < olddef->ndisks; j++) { - virDomainCheckpointDiskDef *disk2; - - disk2 = &olddef->disks[j]; - if (STRNEQ(disk->name, disk2->name) || - disk2->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) - continue; - if (qemuMonitorJSONTransactionAdd(actions, - "block-dirty-bitmap-disable", - "s:node", node, - "s:name", disk2->bitmap, - NULL) < 0) - return -1; - search_parents = false; - break; - } - } - } - return 0; -} - static virDomainCheckpointPtr qemuDomainCheckpointCreateXML(virDomainPtr domain, const char *xmlDesc, unsigned int flags) { - virQEMUDriverPtr driver = domain->conn->privateData; virDomainObjPtr vm = NULL; - char *xml = NULL; - virDomainMomentObjPtr chk = NULL; virDomainCheckpointPtr checkpoint = NULL; - bool update_current = true; - bool redefine = flags & VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE; - unsigned int parse_flags = 0; - virDomainMomentObjPtr other = NULL; - virQEMUDriverConfigPtr cfg = NULL; - virCapsPtr caps = NULL; - qemuDomainObjPrivatePtr priv; - virJSONValuePtr actions = NULL; - int ret; - VIR_AUTOUNREF(virDomainCheckpointDefPtr) def = NULL; - - virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE, NULL); - /* TODO: VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE */ - - if (redefine) { - parse_flags |= VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE; - update_current = false; - } if (!(vm = qemuDomainObjFromDomain(domain))) goto cleanup; - priv = vm->privateData; - cfg = virQEMUDriverGetConfig(driver); - if (virDomainCheckpointCreateXMLEnsureACL(domain->conn, vm->def, flags) < 0) goto cleanup; - if (virDomainSnapshotObjListNum(vm->snapshots, NULL, 0) > 0) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("cannot create checkpoint while snapshot exists")); - goto cleanup; - } - - if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("qemu binary lacks persistent bitmaps support")); - goto cleanup; - } - - if (!(caps = virQEMUDriverGetCapabilities(driver, false))) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("cannot create checkpoint for inactive domain")); - goto cleanup; - } - - if (!(def = virDomainCheckpointDefParseString(xmlDesc, caps, driver->xmlopt, - priv->qemuCaps, parse_flags))) - goto cleanup; - /* Unlike snapshots, the RNG schema already ensured a sane filename. */ - - /* We are going to modify the domain below. */ - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (redefine) { - if (virDomainCheckpointRedefinePrep(vm, &def, &chk, - driver->xmlopt, - &update_current) < 0) - goto endjob; - } else if (qemuDomainCheckpointPrepare(driver, caps, vm, def) < 0) { - goto endjob; - } - - if (!chk) { - if (!(chk = virDomainCheckpointAssignDef(vm->checkpoints, def))) - goto endjob; - - def = NULL; - } - - other = virDomainCheckpointGetCurrent(vm->checkpoints); - if (other) { - if (!redefine && - VIR_STRDUP(chk->def->parent_name, other->def->name) < 0) - goto endjob; - if (update_current) { - virDomainCheckpointSetCurrent(vm->checkpoints, NULL); - if (qemuDomainCheckpointWriteMetadata(vm, other, - driver->caps, driver->xmlopt, - cfg->checkpointDir) < 0) - goto endjob; - } - } - - /* actually do the checkpoint */ - if (redefine) { - /* XXX Should we validate that the redefined checkpoint even - * makes sense, such as checking that qemu-img recognizes the - * checkpoint bitmap name in at least one of the domain's disks? */ - } else { - if (!(actions = virJSONValueNewArray())) - goto endjob; - if (qemuDomainCheckpointAddActions(vm, actions, other, - virDomainCheckpointObjGetDef(chk)) < 0) - goto endjob; - qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorTransaction(priv->mon, &actions); - if (qemuDomainObjExitMonitor(driver, vm) < 0 || ret < 0) - goto endjob; - } - - /* If we fail after this point, there's not a whole lot we can do; - * we've successfully created the checkpoint, so we have to go - * forward the best we can. - */ - checkpoint = virGetDomainCheckpoint(domain, chk->def->name); - - endjob: - if (checkpoint) { - if (update_current) - virDomainCheckpointSetCurrent(vm->checkpoints, chk); - if (qemuDomainCheckpointWriteMetadata(vm, chk, driver->caps, - driver->xmlopt, - cfg->checkpointDir) < 0) { - /* if writing of metadata fails, error out rather than trying - * to silently carry on without completing the checkpoint */ - virObjectUnref(checkpoint); - checkpoint = NULL; - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to save metadata for checkpoint %s"), - chk->def->name); - virDomainCheckpointObjListRemove(vm->checkpoints, chk); - } else { - virDomainCheckpointLinkParent(vm->checkpoints, chk); - } - } else if (chk) { - virDomainCheckpointObjListRemove(vm->checkpoints, chk); - } - - qemuDomainObjEndJob(driver, vm); + checkpoint = qemuCheckpointCreateXML(domain, vm, xmlDesc, flags); cleanup: - virJSONValueFree(actions); virDomainObjEndAPI(&vm); - VIR_FREE(xml); - virObjectUnref(caps); - virObjectUnref(cfg); return checkpoint; } @@ -17501,15 +17227,8 @@ static char * qemuDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint, unsigned int flags) { - virQEMUDriverPtr driver = checkpoint->domain->conn->privateData; virDomainObjPtr vm = NULL; char *xml = NULL; - virDomainMomentObjPtr chk = NULL; - virDomainCheckpointDefPtr chkdef; - unsigned int format_flags; - - virCheckFlags(VIR_DOMAIN_CHECKPOINT_XML_SECURE | - VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN, NULL); if (!(vm = qemuDomObjFromCheckpoint(checkpoint))) return NULL; @@ -17517,13 +17236,7 @@ qemuDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint, if (virDomainCheckpointGetXMLDescEnsureACL(checkpoint->domain->conn, vm->def, flags) < 0) goto cleanup; - if (!(chk = qemuCheckpointObjFromCheckpoint(vm, checkpoint))) - goto cleanup; - chkdef = virDomainCheckpointObjGetDef(chk); - - format_flags = virDomainCheckpointFormatConvertXMLFlags(flags); - xml = virDomainCheckpointDefFormat(chkdef, driver->caps, driver->xmlopt, - format_flags); + xml = qemuCheckpointGetXMLDesc(vm, checkpoint, flags); cleanup: virDomainObjEndAPI(&vm); @@ -17535,108 +17248,23 @@ static int qemuDomainCheckpointDelete(virDomainCheckpointPtr checkpoint, unsigned int flags) { - virQEMUDriverPtr driver = checkpoint->domain->conn->privateData; virDomainObjPtr vm = NULL; - qemuDomainObjPrivatePtr priv; int ret = -1; - virDomainMomentObjPtr chk = NULL; - virQEMUMomentRemove rem; - virQEMUMomentReparent rep; - bool metadata_only = !!(flags & VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY); - virQEMUDriverConfigPtr cfg = NULL; - - virCheckFlags(VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN | - VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY | - VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY, -1); if (!(vm = qemuDomObjFromCheckpoint(checkpoint))) return -1; - cfg = virQEMUDriverGetConfig(driver); - if (virDomainCheckpointDeleteEnsureACL(checkpoint->domain->conn, vm->def) < 0) goto cleanup; - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - priv = vm->privateData; - if (!metadata_only) { - /* Until qemu-img supports offline bitmap deletion, we are stuck - * with requiring a running guest */ - if (!virDomainObjIsActive(vm)) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("cannot delete checkpoint for inactive domain")); - goto endjob; - } - if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("qemu binary lacks persistent bitmaps support")); - goto endjob; - } - } - - if (!(chk = qemuCheckpointObjFromCheckpoint(vm, checkpoint))) - goto endjob; - - if (flags & (VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN | - VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY)) { - rem.driver = driver; - rem.vm = vm; - rem.metadata_only = metadata_only; - rem.err = 0; - rem.current = virDomainCheckpointGetCurrent(vm->checkpoints); - rem.found = false; - rem.momentDiscard = qemuDomainCheckpointDiscard; - virDomainMomentForEachDescendant(chk, qemuDomainMomentDiscardAll, - &rem); - if (rem.err < 0) - goto endjob; - if (rem.found) { - virDomainCheckpointSetCurrent(vm->checkpoints, chk); - if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) { - if (qemuDomainCheckpointWriteMetadata(vm, chk, driver->caps, - driver->xmlopt, - cfg->checkpointDir) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("failed to set checkpoint '%s' as current"), - chk->def->name); - virDomainCheckpointSetCurrent(vm->checkpoints, NULL); - goto endjob; - } - } - } - } else if (chk->nchildren) { - rep.dir = cfg->checkpointDir; - rep.parent = chk->parent; - rep.vm = vm; - rep.err = 0; - rep.caps = driver->caps; - rep.xmlopt = driver->xmlopt; - rep.writeMetadata = qemuDomainCheckpointWriteMetadata; - virDomainMomentForEachChild(chk, qemuDomainMomentReparentChildren, - &rep); - if (rep.err < 0) - goto endjob; - virDomainMomentMoveChildren(chk, chk->parent); - } - - if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) { - virDomainMomentDropChildren(chk); - ret = 0; - } else { - ret = qemuDomainCheckpointDiscard(driver, vm, chk, true, metadata_only); - } - - endjob: - qemuDomainObjEndJob(driver, vm); + ret = qemuCheckpointDelete(vm, checkpoint, flags); cleanup: virDomainObjEndAPI(&vm); - virObjectUnref(cfg); return ret; } + static int qemuDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, char **result, unsigned int flags) {