A lot of this work heavily copies from the existing snapshot APIs.
The test driver doesn't really have to do anything more than just
expose an interface into libvirt metadata, making it possible to test
saving and restoring XML, and tracking relations between multiple
checkpoints.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/test/test_driver.c | 427 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 427 insertions(+)
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index b5c3ec05e2..dc6513f435 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -39,6 +39,7 @@
#include "viralloc.h"
#include "virnetworkobj.h"
#include "interface_conf.h"
+#include "checkpoint_conf.h"
#include "domain_conf.h"
#include "domain_event.h"
#include "network_event.h"
@@ -63,6 +64,7 @@
#include "virdomainobjlist.h"
#include "virinterfaceobj.h"
#include "virhostcpu.h"
+#include "virdomaincheckpointobjlist.h"
#include "virdomainsnapshotobjlist.h"
#include "virkeycode.h"
@@ -7109,7 +7111,422 @@ testDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
return ret;
}
+/*
+ * Checkpoint APIs
+ */
+static int
+testDomainCheckpointDiscardAll(void *payload,
+ const void *name ATTRIBUTE_UNUSED,
+ void *data)
+{
+ virDomainMomentObjPtr chk = payload;
+ testMomentRemoveDataPtr curr = data;
+
+ curr->current |= virDomainCheckpointObjListRemove(curr->vm->checkpoints,
+ chk);
+ return 0;
+}
+
+static virDomainObjPtr
+testDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint)
+{
+ return testDomObjFromDomain(checkpoint->domain);
+}
+
+static virDomainMomentObjPtr
+testCheckObjFromName(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;
+}
+
+static virDomainMomentObjPtr
+testCheckObjFromCheckpoint(virDomainObjPtr vm,
+ virDomainCheckpointPtr checkpoint)
+{
+ return testCheckObjFromName(vm, checkpoint->name);
+}
+
+static virDomainCheckpointPtr
+testDomainCheckpointCreateXML(virDomainPtr domain,
+ const char *xmlDesc,
+ unsigned int flags)
+{
+ testDriverPtr privconn = domain->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ char *xml = NULL;
+ virDomainMomentObjPtr chk = NULL;
+ virDomainCheckpointPtr checkpoint = NULL;
+ virDomainMomentObjPtr current = NULL;
+ bool update_current = true;
+ bool redefine = flags & VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE;
+ unsigned int parse_flags = 0;
+ virDomainMomentObjPtr other = NULL;
+ VIR_AUTOUNREF(virDomainCheckpointDefPtr) def = NULL;
+
+ virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE |
+ VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT |
+ VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA |
+ VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE, NULL);
+
+ if (redefine)
+ parse_flags |= VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE;
+ if ((redefine && !(flags & VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT)) ||
+ (flags & VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA))
+ update_current = false;
+
+ if (!(vm = testDomObjFromDomain(domain)))
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("cannot create checkpoint for inactive domain"));
+ goto cleanup;
+ }
+
+ if (!(def = virDomainCheckpointDefParseString(xmlDesc, privconn->caps,
+ privconn->xmlopt, NULL,
+ parse_flags)))
+ goto cleanup;
+
+ if (redefine) {
+ if (virDomainCheckpointRedefinePrep(domain, vm, &def, &chk,
+ privconn->xmlopt,
+ &update_current) < 0)
+ goto cleanup;
+ } else {
+ if (!(def->parent.dom = virDomainDefCopy(vm->def,
+ privconn->caps,
+ privconn->xmlopt,
+ NULL,
+ true)))
+ goto cleanup;
+
+ if (virDomainCheckpointAlignDisks(def) < 0)
+ goto cleanup;
+ }
+
+ if (!chk) {
+ if (!(chk = virDomainCheckpointAssignDef(vm->checkpoints, def)))
+ goto cleanup;
+
+ def = NULL;
+ }
+
+ current = virDomainCheckpointGetCurrent(vm->checkpoints);
+ if (current) {
+ if (!redefine &&
+ VIR_STRDUP(chk->def->parent_name, current->def->name) < 0)
+ goto cleanup;
+ if (update_current)
+ virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
+ }
+
+ /* actually do the checkpoint - except the test driver has nothing
+ * to actually do here */
+
+ /* 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);
+
+ cleanup:
+ if (checkpoint && !(flags & VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA)) {
+ if (update_current)
+ virDomainCheckpointSetCurrent(vm->checkpoints, chk);
+ other = virDomainCheckpointFindByName(vm->checkpoints,
+ chk->def->parent_name);
+ virDomainMomentSetParent(chk, other);
+ } else if (chk) {
+ virDomainCheckpointObjListRemove(vm->checkpoints, chk);
+ }
+
+ virDomainObjEndAPI(&vm);
+ VIR_FREE(xml);
+ return checkpoint;
+}
+
+
+static int
+testDomainListAllCheckpoints(virDomainPtr domain,
+ virDomainCheckpointPtr **chks,
+ unsigned int flags)
+{
+ virDomainObjPtr vm = NULL;
+ int n = -1;
+
+ virCheckFlags(VIR_DOMAIN_CHECKPOINT_LIST_ROOTS |
+ VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL |
+ VIR_DOMAIN_CHECKPOINT_FILTERS_ALL, -1);
+
+ if (!(vm = testDomObjFromDomain(domain)))
+ return -1;
+
+ n = virDomainListCheckpoints(vm->checkpoints, NULL, domain, chks, flags);
+
+ virDomainObjEndAPI(&vm);
+ return n;
+}
+
+
+static int
+testDomainCheckpointListAllChildren(virDomainCheckpointPtr checkpoint,
+ virDomainCheckpointPtr **chks,
+ unsigned int flags)
+{
+ virDomainObjPtr vm = NULL;
+ virDomainMomentObjPtr chk = NULL;
+ int n = -1;
+
+ virCheckFlags(VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS |
+ VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL |
+ VIR_DOMAIN_CHECKPOINT_FILTERS_ALL, -1);
+
+ if (!(vm = testDomObjFromCheckpoint(checkpoint)))
+ return -1;
+
+ if (!(chk = testCheckObjFromCheckpoint(vm, checkpoint)))
+ goto cleanup;
+
+ n = virDomainListCheckpoints(vm->checkpoints, chk, checkpoint->domain,
+ chks, flags);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return n;
+}
+
+
+static virDomainCheckpointPtr
+testDomainCheckpointLookupByName(virDomainPtr domain,
+ const char *name,
+ unsigned int flags)
+{
+ virDomainObjPtr vm;
+ virDomainMomentObjPtr chk = NULL;
+ virDomainCheckpointPtr checkpoint = NULL;
+
+ virCheckFlags(0, NULL);
+
+ if (!(vm = testDomObjFromDomain(domain)))
+ return NULL;
+
+ if (!(chk = testCheckObjFromName(vm, name)))
+ goto cleanup;
+
+ checkpoint = virGetDomainCheckpoint(domain, chk->def->name);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return checkpoint;
+}
+
+
+static virDomainCheckpointPtr
+testDomainCheckpointGetParent(virDomainCheckpointPtr checkpoint,
+ unsigned int flags)
+{
+ virDomainObjPtr vm;
+ virDomainMomentObjPtr chk = NULL;
+ virDomainCheckpointPtr parent = NULL;
+
+ virCheckFlags(0, NULL);
+
+ if (!(vm = testDomObjFromCheckpoint(checkpoint)))
+ return NULL;
+
+ if (!(chk = testCheckObjFromCheckpoint(vm, checkpoint)))
+ goto cleanup;
+
+ if (!chk->def->parent_name) {
+ virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT,
+ _("checkpoint '%s' does not have a parent"),
+ chk->def->name);
+ goto cleanup;
+ }
+
+ parent = virGetDomainCheckpoint(checkpoint->domain, chk->def->parent_name);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return parent;
+}
+
+
+static virDomainCheckpointPtr
+testDomainCheckpointCurrent(virDomainPtr domain,
+ unsigned int flags)
+{
+ virDomainObjPtr vm;
+ virDomainCheckpointPtr checkpoint = NULL;
+ const char *name;
+
+ virCheckFlags(0, NULL);
+
+ if (!(vm = testDomObjFromDomain(domain)))
+ return NULL;
+
+ name = virDomainCheckpointGetCurrentName(vm->checkpoints);
+ if (!name) {
+ virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT, "%s",
+ _("the domain does not have a current checkpoint"));
+ goto cleanup;
+ }
+
+ checkpoint = virGetDomainCheckpoint(domain, name);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return checkpoint;
+}
+
+
+static char *
+testDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint,
+ unsigned int flags)
+{
+ testDriverPtr privconn = checkpoint->domain->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ char *xml = NULL;
+ virDomainMomentObjPtr chk = NULL;
+ size_t i;
+ virDomainCheckpointDefPtr chkdef;
+
+ virCheckFlags(VIR_DOMAIN_CHECKPOINT_XML_SECURE |
+ VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN |
+ VIR_DOMAIN_CHECKPOINT_XML_SIZE, NULL);
+
+ if (!(vm = testDomObjFromCheckpoint(checkpoint)))
+ return NULL;
+
+ if (!(chk = testCheckObjFromCheckpoint(vm, checkpoint)))
+ goto cleanup;
+ chkdef = virDomainCheckpointObjGetDef(chk);
+
+ if (flags & VIR_DOMAIN_CHECKPOINT_XML_SIZE) {
+ if (virDomainObjCheckActive(vm) < 0)
+ goto cleanup;
+
+ for (i = 0; i < chkdef->ndisks; i++) {
+ virDomainCheckpointDiskDefPtr disk = &chkdef->disks[i];
+
+ if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
+ continue;
+ disk->size = 1024; /* Any number will do... */
+ }
+ }
+
+ xml = virDomainCheckpointDefFormat(chkdef, privconn->caps,
+ privconn->xmlopt, flags);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return xml;
+}
+
+
+static int
+testDomainCheckpointIsCurrent(virDomainCheckpointPtr checkpoint,
+ unsigned int flags)
+{
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ virDomainMomentObjPtr chk = NULL;
+
+ virCheckFlags(0, -1);
+
+ if (!(vm = testDomObjFromCheckpoint(checkpoint)))
+ return -1;
+
+ if (!(chk = testCheckObjFromCheckpoint(vm, checkpoint)))
+ goto cleanup;
+
+ ret = chk == virDomainCheckpointGetCurrent(vm->checkpoints);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+
+static int
+testDomainCheckpointDelete(virDomainCheckpointPtr checkpoint,
+ unsigned int flags)
+{
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ virDomainMomentObjPtr chk = NULL;
+ virDomainMomentObjPtr parentchk = NULL;
+
+ virCheckFlags(VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN |
+ VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY |
+ VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY, -1);
+
+ if (!(vm = testDomObjFromCheckpoint(checkpoint)))
+ return -1;
+
+ if (!(chk = testCheckObjFromCheckpoint(vm, checkpoint)))
+ goto cleanup;
+
+ if (flags & (VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN |
+ VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY)) {
+ testMomentRemoveData rem;
+
+ rem.vm = vm;
+ rem.current = false;
+ virDomainMomentForEachDescendant(chk, testDomainCheckpointDiscardAll,
+ &rem);
+ if (rem.current)
+ virDomainCheckpointSetCurrent(vm->checkpoints, chk);
+ } else if (chk->nchildren) {
+ testMomentReparentData rep;
+
+ rep.parent = chk->parent;
+ rep.vm = vm;
+ rep.err = 0;
+ virDomainMomentForEachChild(chk, testDomainMomentReparentChildren,
+ &rep);
+ if (rep.err < 0)
+ goto cleanup;
+ virDomainMomentMoveChildren(chk, chk->parent);
+ }
+
+ if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) {
+ virDomainMomentDropChildren(chk);
+ } else {
+ virDomainMomentDropParent(chk);
+ if (chk == virDomainCheckpointGetCurrent(vm->checkpoints)) {
+ if (chk->def->parent_name) {
+ parentchk = virDomainCheckpointFindByName(vm->checkpoints,
+ chk->def->parent_name);
+ if (!parentchk)
+ VIR_WARN("missing parent checkpoint matching name
'%s'",
+ chk->def->parent_name);
+ }
+ virDomainCheckpointSetCurrent(vm->checkpoints, parentchk);
+ }
+ virDomainCheckpointObjListRemove(vm->checkpoints, chk);
+ }
+
+ ret = 0;
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+/*
+ * Test driver
+ */
static virHypervisorDriver testHypervisorDriver = {
.name = "Test",
.connectOpen = testConnectOpen, /* 0.1.1 */
@@ -7223,6 +7640,16 @@ static virHypervisorDriver testHypervisorDriver = {
.domainSnapshotDelete = testDomainSnapshotDelete, /* 1.1.4 */
.connectBaselineCPU = testConnectBaselineCPU, /* 1.2.0 */
+ .domainCheckpointCreateXML = testDomainCheckpointCreateXML, /* 5.5.0 */
+ .domainCheckpointGetXMLDesc = testDomainCheckpointGetXMLDesc, /* 5.5.0 */
+
+ .domainListAllCheckpoints = testDomainListAllCheckpoints, /* 5.5.0 */
+ .domainCheckpointListAllChildren = testDomainCheckpointListAllChildren, /* 5.5.0 */
+ .domainCheckpointLookupByName = testDomainCheckpointLookupByName, /* 5.5.0 */
+ .domainCheckpointGetParent = testDomainCheckpointGetParent, /* 5.5.0 */
+ .domainCheckpointCurrent = testDomainCheckpointCurrent, /* 5.5.0 */
+ .domainCheckpointIsCurrent = testDomainCheckpointIsCurrent, /* 5.5.0 */
+ .domainCheckpointDelete = testDomainCheckpointDelete, /* 5.5.0 */
};
static virNetworkDriver testNetworkDriver = {
--
2.20.1