From a0efa678123ab20ce977531962077a181a565c6e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 10 Apr 2019 10:42:11 -0500 Subject: [PATCH] backup: test: Implement metadata tracking for checkpoint APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Daniel P. Berrangé --- src/test/test_driver.c | 369 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index f701a2447b..ab0f8b06d6 100755 --- 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" @@ -8084,7 +8086,366 @@ 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 +testCheckpointObjFromName(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 +testCheckpointObjFromCheckpoint(virDomainObjPtr vm, + virDomainCheckpointPtr checkpoint) +{ + return testCheckpointObjFromName(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; + VIR_AUTOUNREF(virDomainCheckpointDefPtr) def = NULL; + + virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE | + VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE, NULL); + + if (redefine) { + parse_flags |= VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE; + 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, + 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) { + if (update_current) + virDomainCheckpointSetCurrent(vm->checkpoints, chk); + virDomainCheckpointLinkParent(vm->checkpoints, chk); + } 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 = testCheckpointObjFromCheckpoint(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 = testCheckpointObjFromName(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 = testCheckpointObjFromCheckpoint(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 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; + unsigned int format_flags; + + 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 = testCheckpointObjFromCheckpoint(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... */ + } + } + + format_flags = virDomainCheckpointFormatConvertXMLFlags(flags); + xml = virDomainCheckpointDefFormat(chkdef, privconn->caps, + privconn->xmlopt, format_flags); + + cleanup: + virDomainObjEndAPI(&vm); + return xml; +} + + +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 = testCheckpointObjFromCheckpoint(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 */ @@ -8216,6 +8577,14 @@ static virHypervisorDriver testHypervisorDriver = { .domainSnapshotDelete = testDomainSnapshotDelete, /* 1.1.4 */ .connectBaselineCPU = testConnectBaselineCPU, /* 1.2.0 */ + .domainCheckpointCreateXML = testDomainCheckpointCreateXML, /* 5.6.0 */ + .domainCheckpointGetXMLDesc = testDomainCheckpointGetXMLDesc, /* 5.6.0 */ + + .domainListAllCheckpoints = testDomainListAllCheckpoints, /* 5.6.0 */ + .domainCheckpointListAllChildren = testDomainCheckpointListAllChildren, /* 5.6.0 */ + .domainCheckpointLookupByName = testDomainCheckpointLookupByName, /* 5.6.0 */ + .domainCheckpointGetParent = testDomainCheckpointGetParent, /* 5.6.0 */ + .domainCheckpointDelete = testDomainCheckpointDelete, /* 5.6.0 */ }; static virNetworkDriver testNetworkDriver = {