[libvirt] [PATCH 00/10] test: Mock snapshot APIs and misc improvements

This series implements snapshot APIs for the test driver, and adds some misc improvements, like specifying domain state in the passed in driver XML. Cole Robinson (10): test: Split object parsing into their own functions test: Simplify args passed to testDomainStartState test: Unify object XML parsing test: Allow specifying object runstate in driver XML test: Allow specifying object transient state in driver XML test: Wire up managed save APIs snapshot_conf: Allow parsing an XML node test: Allow specifying <domainsnapshot> XML test: Implement readonly snapshot APIs test: Implement snapshot create/delete/revert APIs src/conf/domain_conf.c | 3 +- src/conf/snapshot_conf.c | 85 +- src/conf/snapshot_conf.h | 6 + src/test/test_driver.c | 4871 +++++++++++++++++++++++++++++----------------- tests/virshtest.c | 2 +- 5 files changed, 3151 insertions(+), 1816 deletions(-) -- 1.8.3.1

The function that parses custom driver XML was getting pretty unruly, split the object parsing into their own functions. Rename some variables to be consistent across each function. This should be functionally identical. --- src/test/test_driver.c | 463 +++++++++++++++++++++++++++++-------------------- 1 file changed, 276 insertions(+), 187 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index f7eaf06..718f83c 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -689,131 +689,13 @@ static char *testBuildFilename(const char *relativeTo, } } -static int testOpenVolumesForPool(xmlDocPtr xml, - xmlXPathContextPtr ctxt, - const char *file, - virStoragePoolObjPtr pool, - int poolidx) { - char *vol_xpath; - size_t i; - int ret, func_ret = -1; - xmlNodePtr *vols = NULL; - virStorageVolDefPtr def = NULL; - - /* Find storage volumes */ - if (virAsprintf(&vol_xpath, "/node/pool[%d]/volume", poolidx) < 0) - goto error; - - ret = virXPathNodeSet(vol_xpath, ctxt, &vols); - VIR_FREE(vol_xpath); - if (ret < 0) { - goto error; - } - - for (i = 0; i < ret; i++) { - char *relFile = virXMLPropString(vols[i], "file"); - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving volume filename")); - goto error; - } - - def = virStorageVolDefParseFile(pool->def, absFile); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virStorageVolDefParseNode(pool->def, xml, - vols[i])) == NULL) { - goto error; - } - } - - if (VIR_REALLOC_N(pool->volumes.objs, - pool->volumes.count+1) < 0) - goto error; - - if (def->target.path == NULL) { - if (virAsprintf(&def->target.path, "%s/%s", - pool->def->target.path, - def->name) == -1) - goto error; - } - - if (!def->key && VIR_STRDUP(def->key, def->target.path) < 0) - goto error; - - pool->def->allocation += def->allocation; - pool->def->available = (pool->def->capacity - - pool->def->allocation); - - pool->volumes.objs[pool->volumes.count++] = def; - def = NULL; - } - - func_ret = 0; -error: - virStorageVolDefFree(def); - VIR_FREE(vols); - return func_ret; -} - -static int testOpenFromFile(virConnectPtr conn, - const char *file) { - size_t i; - int ret; - long l; +static int +testParseNodeInfo(virNodeInfoPtr nodeInfo, xmlXPathContextPtr ctxt) +{ char *str; - xmlDocPtr xml = NULL; - xmlNodePtr *domains = NULL, *networks = NULL, *ifaces = NULL, - *pools = NULL, *devs = NULL; - xmlXPathContextPtr ctxt = NULL; - virNodeInfoPtr nodeInfo; - virNetworkObjPtr net; - virInterfaceObjPtr iface; - virDomainObjPtr dom; - testConnPtr privconn; - if (VIR_ALLOC(privconn) < 0) - return VIR_DRV_OPEN_ERROR; - if (virMutexInit(&privconn->lock) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot initialize mutex")); - VIR_FREE(privconn); - return VIR_DRV_OPEN_ERROR; - } - - testDriverLock(privconn); - conn->privateData = privconn; - - if (!(privconn->domains = virDomainObjListNew())) - goto error; - - if (!(privconn->caps = testBuildCapabilities(conn))) - goto error; - - if (!(privconn->xmlopt = testBuildXMLConfig())) - goto error; - - if (!(xml = virXMLParseFileCtxt(file, &ctxt))) { - goto error; - } - - if (!xmlStrEqual(ctxt->node->name, BAD_CAST "node")) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Root element is not 'node'")); - goto error; - } - - privconn->nextDomID = 1; - privconn->numCells = 0; - if (VIR_STRDUP(privconn->path, file) < 0) - goto error; - memmove(&privconn->nodeInfo, &defaultNodeInfo, sizeof(defaultNodeInfo)); + long l; + int ret; - nodeInfo = &privconn->nodeInfo; ret = virXPathLong("string(/node/cpu/nodes[1])", ctxt, &l); if (ret == 0) { nodeInfo->nodes = l; @@ -850,7 +732,8 @@ static int testOpenFromFile(virConnectPtr conn, goto error; } - nodeInfo->cpus = nodeInfo->cores * nodeInfo->threads * nodeInfo->sockets * nodeInfo->nodes; + nodeInfo->cpus = (nodeInfo->cores * nodeInfo->threads * + nodeInfo->sockets * nodeInfo->nodes); ret = virXPathLong("string(/node/cpu/active[1])", ctxt, &l); if (ret == 0) { if (l < nodeInfo->cpus) { @@ -890,14 +773,29 @@ static int testOpenFromFile(virConnectPtr conn, goto error; } - ret = virXPathNodeSet("/node/domain", ctxt, &domains); - if (ret < 0) { + return 0; +error: + return -1; +} + +static int +testParseDomains(virConnectPtr conn, + testConnPtr privconn, const char *file, + xmlDocPtr doc, xmlXPathContextPtr ctxt) +{ + int num, ret = -1; + size_t i; + xmlNodePtr *nodes = NULL; + virDomainObjPtr obj; + + num = virXPathNodeSet("/node/domain", ctxt, &nodes); + if (num < 0) { goto error; } - for (i = 0; i < ret; i++) { + for (i = 0; i < num; i++) { virDomainDefPtr def; - char *relFile = virXMLPropString(domains[i], "file"); + char *relFile = virXMLPropString(nodes[i], "file"); if (relFile != NULL) { char *absFile = testBuildFilename(file, relFile); VIR_FREE(relFile); @@ -914,7 +812,7 @@ static int testOpenFromFile(virConnectPtr conn, if (!def) goto error; } else { - if ((def = virDomainDefParseNode(xml, domains[i], + if ((def = virDomainDefParseNode(doc, nodes[i], privconn->caps, privconn->xmlopt, 1 << VIR_DOMAIN_VIRT_TEST, VIR_DOMAIN_XML_INACTIVE)) == NULL) @@ -922,7 +820,7 @@ static int testOpenFromFile(virConnectPtr conn, } if (testDomainGenerateIfnames(def) < 0 || - !(dom = virDomainObjListAdd(privconn->domains, + !(obj = virDomainObjListAdd(privconn->domains, def, privconn->xmlopt, 0, NULL))) { @@ -930,23 +828,38 @@ static int testOpenFromFile(virConnectPtr conn, goto error; } - dom->persistent = 1; - if (testDomainStartState(conn, dom, VIR_DOMAIN_RUNNING_BOOTED) < 0) { - virObjectUnlock(dom); + obj->persistent = 1; + if (testDomainStartState(conn, obj, VIR_DOMAIN_RUNNING_BOOTED) < 0) { + virObjectUnlock(obj); goto error; } - virObjectUnlock(dom); + virObjectUnlock(obj); } - VIR_FREE(domains); - ret = virXPathNodeSet("/node/network", ctxt, &networks); - if (ret < 0) { + ret = 0; +error: + VIR_FREE(nodes); + return ret; +} + +static int +testParseNetworks(testConnPtr privconn, const char *file, + xmlDocPtr doc, xmlXPathContextPtr ctxt) +{ + int num, ret = -1; + size_t i; + xmlNodePtr *nodes = NULL; + virNetworkObjPtr obj; + + num = virXPathNodeSet("/node/network", ctxt, &nodes); + if (num < 0) { goto error; } - for (i = 0; i < ret; i++) { + + for (i = 0; i < num; i++) { virNetworkDefPtr def; - char *relFile = virXMLPropString(networks[i], "file"); + char *relFile = virXMLPropString(nodes[i], "file"); if (relFile != NULL) { char *absFile = testBuildFilename(file, relFile); VIR_FREE(relFile); @@ -961,27 +874,43 @@ static int testOpenFromFile(virConnectPtr conn, if (!def) goto error; } else { - if ((def = virNetworkDefParseNode(xml, networks[i])) == NULL) + if ((def = virNetworkDefParseNode(doc, nodes[i])) == NULL) goto error; } - if (!(net = virNetworkAssignDef(&privconn->networks, def, false))) { + + if (!(obj = virNetworkAssignDef(&privconn->networks, def, false))) { virNetworkDefFree(def); goto error; } - net->persistent = 1; - net->active = 1; - virNetworkObjUnlock(net); + + obj->persistent = 1; + obj->active = 1; + virNetworkObjUnlock(obj); } - VIR_FREE(networks); - /* Parse interface definitions */ - ret = virXPathNodeSet("/node/interface", ctxt, &ifaces); - if (ret < 0) { + ret = 0; +error: + VIR_FREE(nodes); + return ret; +} + +static int +testParseInterfaces(testConnPtr privconn, const char *file, + xmlDocPtr doc, xmlXPathContextPtr ctxt) +{ + int num, ret = -1; + size_t i; + xmlNodePtr *nodes = NULL; + virInterfaceObjPtr obj; + + num = virXPathNodeSet("/node/interface", ctxt, &nodes); + if (num < 0) { goto error; } - for (i = 0; i < ret; i++) { + + for (i = 0; i < num; i++) { virInterfaceDefPtr def; - char *relFile = virXMLPropString(ifaces[i], "file"); + char *relFile = virXMLPropString(nodes[i], "file"); if (relFile != NULL) { char *absFile = testBuildFilename(file, relFile); VIR_FREE(relFile); @@ -996,29 +925,116 @@ static int testOpenFromFile(virConnectPtr conn, if (!def) goto error; } else { - if ((def = virInterfaceDefParseNode(xml, ifaces[i])) == NULL) + if ((def = virInterfaceDefParseNode(doc, nodes[i])) == NULL) goto error; } - if (!(iface = virInterfaceAssignDef(&privconn->ifaces, def))) { + if (!(obj = virInterfaceAssignDef(&privconn->ifaces, def))) { virInterfaceDefFree(def); goto error; } - iface->active = 1; - virInterfaceObjUnlock(iface); + obj->active = 1; + virInterfaceObjUnlock(obj); + } + + ret = 0; +error: + VIR_FREE(nodes); + return ret; +} + +static int +testOpenVolumesForPool(xmlDocPtr xml, + xmlXPathContextPtr ctxt, + const char *file, + virStoragePoolObjPtr pool, + int poolidx) +{ + char *vol_xpath; + size_t i; + int num, ret = -1; + xmlNodePtr *nodes = NULL; + virStorageVolDefPtr def = NULL; + + /* Find storage volumes */ + if (virAsprintf(&vol_xpath, "/node/pool[%d]/volume", poolidx) < 0) + goto error; + + num = virXPathNodeSet(vol_xpath, ctxt, &nodes); + VIR_FREE(vol_xpath); + if (num < 0) { + goto error; } - VIR_FREE(ifaces); - /* Parse Storage Pool list */ - ret = virXPathNodeSet("/node/pool", ctxt, &pools); - if (ret < 0) { + for (i = 0; i < num; i++) { + char *relFile = virXMLPropString(nodes[i], "file"); + if (relFile != NULL) { + char *absFile = testBuildFilename(file, relFile); + VIR_FREE(relFile); + if (!absFile) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("resolving volume filename")); + goto error; + } + + def = virStorageVolDefParseFile(pool->def, absFile); + VIR_FREE(absFile); + if (!def) + goto error; + } else { + if ((def = virStorageVolDefParseNode(pool->def, xml, + nodes[i])) == NULL) { + goto error; + } + } + + if (VIR_REALLOC_N(pool->volumes.objs, + pool->volumes.count+1) < 0) + goto error; + + if (def->target.path == NULL) { + if (virAsprintf(&def->target.path, "%s/%s", + pool->def->target.path, + def->name) == -1) + goto error; + } + + if (!def->key && VIR_STRDUP(def->key, def->target.path) < 0) + goto error; + + pool->def->allocation += def->allocation; + pool->def->available = (pool->def->capacity - + pool->def->allocation); + + pool->volumes.objs[pool->volumes.count++] = def; + def = NULL; + } + + ret = 0; +error: + virStorageVolDefFree(def); + VIR_FREE(nodes); + return ret; +} + +static int +testParseStorage(testConnPtr privconn, const char *file, + xmlDocPtr doc, xmlXPathContextPtr ctxt) +{ + int num, ret = -1; + size_t i; + xmlNodePtr *nodes = NULL; + virStoragePoolObjPtr obj; + + num = virXPathNodeSet("/node/pool", ctxt, &nodes); + if (num < 0) { goto error; } - for (i = 0; i < ret; i++) { + + for (i = 0; i < num; i++) { virStoragePoolDefPtr def; - virStoragePoolObjPtr pool; - char *relFile = virXMLPropString(pools[i], "file"); + char *relFile = virXMLPropString(nodes[i], "file"); if (relFile != NULL) { char *absFile = testBuildFilename(file, relFile); VIR_FREE(relFile); @@ -1033,42 +1049,56 @@ static int testOpenFromFile(virConnectPtr conn, if (!def) goto error; } else { - if ((def = virStoragePoolDefParseNode(xml, - pools[i])) == NULL) { + if ((def = virStoragePoolDefParseNode(doc, + nodes[i])) == NULL) { goto error; } } - if (!(pool = virStoragePoolObjAssignDef(&privconn->pools, + if (!(obj = virStoragePoolObjAssignDef(&privconn->pools, def))) { virStoragePoolDefFree(def); goto error; } - if (testStoragePoolObjSetDefaults(pool) == -1) { - virStoragePoolObjUnlock(pool); + if (testStoragePoolObjSetDefaults(obj) == -1) { + virStoragePoolObjUnlock(obj); goto error; } - pool->active = 1; + obj->active = 1; /* Find storage volumes */ - if (testOpenVolumesForPool(xml, ctxt, file, pool, i+1) < 0) { - virStoragePoolObjUnlock(pool); + if (testOpenVolumesForPool(doc, ctxt, file, obj, i+1) < 0) { + virStoragePoolObjUnlock(obj); goto error; } - virStoragePoolObjUnlock(pool); + virStoragePoolObjUnlock(obj); } - VIR_FREE(pools); - ret = virXPathNodeSet("/node/device", ctxt, &devs); - if (ret < 0) { + ret = 0; +error: + VIR_FREE(nodes); + return ret; +} + +static int +testParseNodedevs(testConnPtr privconn, const char *file, + xmlDocPtr doc, xmlXPathContextPtr ctxt) +{ + int num, ret = -1; + size_t i; + xmlNodePtr *nodes = NULL; + virNodeDeviceObjPtr obj; + + num = virXPathNodeSet("/node/device", ctxt, &nodes); + if (num < 0) { goto error; } - for (i = 0; i < ret; i++) { + + for (i = 0; i < num; i++) { virNodeDeviceDefPtr def; - virNodeDeviceObjPtr dev; - char *relFile = virXMLPropString(devs[i], "file"); + char *relFile = virXMLPropString(nodes[i], "file"); if (relFile != NULL) { char *absFile = testBuildFilename(file, relFile); @@ -1085,32 +1115,91 @@ static int testOpenFromFile(virConnectPtr conn, if (!def) goto error; } else { - if ((def = virNodeDeviceDefParseNode(xml, devs[i], 0, NULL)) == NULL) + if ((def = virNodeDeviceDefParseNode(doc, + nodes[i], 0, NULL)) == NULL) goto error; } - if (!(dev = virNodeDeviceAssignDef(&privconn->devs, def))) { + + if (!(obj = virNodeDeviceAssignDef(&privconn->devs, def))) { virNodeDeviceDefFree(def); goto error; } - virNodeDeviceObjUnlock(dev); + + virNodeDeviceObjUnlock(obj); } - VIR_FREE(devs); + ret = 0; +error: + VIR_FREE(nodes); + return ret; +} + +static int +testOpenFromFile(virConnectPtr conn, const char *file) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + testConnPtr privconn; + + if (VIR_ALLOC(privconn) < 0) + return VIR_DRV_OPEN_ERROR; + if (virMutexInit(&privconn->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(privconn); + return VIR_DRV_OPEN_ERROR; + } + + testDriverLock(privconn); + conn->privateData = privconn; + + if (!(privconn->domains = virDomainObjListNew())) + goto error; + + if (!(privconn->caps = testBuildCapabilities(conn))) + goto error; + + if (!(privconn->xmlopt = testBuildXMLConfig())) + goto error; + + if (!(doc = virXMLParseFileCtxt(file, &ctxt))) { + goto error; + } + + if (!xmlStrEqual(ctxt->node->name, BAD_CAST "node")) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Root element is not 'node'")); + goto error; + } + + privconn->nextDomID = 1; + privconn->numCells = 0; + if (VIR_STRDUP(privconn->path, file) < 0) + goto error; + memmove(&privconn->nodeInfo, &defaultNodeInfo, sizeof(defaultNodeInfo)); + + if (testParseNodeInfo(&privconn->nodeInfo, ctxt) < 0) + goto error; + if (testParseDomains(conn, privconn, file, doc, ctxt) < 0) + goto error; + if (testParseNetworks(privconn, file, doc, ctxt) < 0) + goto error; + if (testParseInterfaces(privconn, file, doc, ctxt) < 0) + goto error; + if (testParseStorage(privconn, file, doc, ctxt) < 0) + goto error; + if (testParseNodedevs(privconn, file, doc, ctxt) < 0) + goto error; xmlXPathFreeContext(ctxt); - xmlFreeDoc(xml); + xmlFreeDoc(doc); testDriverUnlock(privconn); return 0; error: xmlXPathFreeContext(ctxt); - xmlFreeDoc(xml); - VIR_FREE(domains); - VIR_FREE(networks); - VIR_FREE(ifaces); - VIR_FREE(pools); - VIR_FREE(devs); + xmlFreeDoc(doc); virObjectUnref(privconn->domains); virNetworkObjListFree(&privconn->networks); virInterfaceObjListFree(&privconn->ifaces); -- 1.8.3.1

On Wed, Aug 07, 2013 at 07:28:55PM -0400, Cole Robinson wrote:
The function that parses custom driver XML was getting pretty unruly, split the object parsing into their own functions. Rename some variables to be consistent across each function. This should be functionally identical. --- src/test/test_driver.c | 463 +++++++++++++++++++++++++++++-------------------- 1 file changed, 276 insertions(+), 187 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Passing virConnectPtr is redundant, just pass testConnPtr and simplify certain callers. --- src/test/test_driver.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 718f83c..960a58e 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -379,8 +379,7 @@ testDomainGenerateIfnames(virDomainDefPtr domdef) /* Helper to update info for a single VCPU */ static int -testDomainUpdateVCPU(virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainObjPtr dom, +testDomainUpdateVCPU(virDomainObjPtr dom, int vcpu, int maplen, int maxcpu) @@ -430,12 +429,11 @@ testDomainUpdateVCPU(virConnectPtr conn ATTRIBUTE_UNUSED, * @clear_all: If true, rebuild info for ALL vcpus, not just newly added vcpus */ static int -testDomainUpdateVCPUs(virConnectPtr conn, +testDomainUpdateVCPUs(testConnPtr privconn, virDomainObjPtr dom, int nvcpus, unsigned int clear_all) { - testConnPtr privconn = conn->privateData; testDomainObjPrivatePtr privdata = dom->privateData; size_t i; int ret = -1; @@ -453,13 +451,13 @@ testDomainUpdateVCPUs(virConnectPtr conn, /* Set running VCPU and cpumap state */ if (clear_all) { for (i = 0; i < nvcpus; ++i) - if (testDomainUpdateVCPU(conn, dom, i, cpumaplen, maxcpu) < 0) + if (testDomainUpdateVCPU(dom, i, cpumaplen, maxcpu) < 0) goto cleanup; } else if (nvcpus > dom->def->vcpus) { /* VCPU amount has grown, populate info for the new vcpus */ for (i = dom->def->vcpus; i < nvcpus; ++i) - if (testDomainUpdateVCPU(conn, dom, i, cpumaplen, maxcpu) < 0) + if (testDomainUpdateVCPU(dom, i, cpumaplen, maxcpu) < 0) goto cleanup; } @@ -488,14 +486,13 @@ testDomainShutdownState(virDomainPtr domain, /* Set up domain runtime state */ static int -testDomainStartState(virConnectPtr conn, +testDomainStartState(testConnPtr privconn, virDomainObjPtr dom, virDomainRunningReason reason) { - testConnPtr privconn = conn->privateData; int ret = -1; - if (testDomainUpdateVCPUs(conn, dom, dom->def->vcpus, 1) < 0) + if (testDomainUpdateVCPUs(privconn, dom, dom->def->vcpus, 1) < 0) goto cleanup; virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, reason); @@ -587,7 +584,8 @@ static int testOpenDefault(virConnectPtr conn) { domdef = NULL; domobj->persistent = 1; - if (testDomainStartState(conn, domobj, VIR_DOMAIN_RUNNING_BOOTED) < 0) { + if (testDomainStartState(privconn, domobj, + VIR_DOMAIN_RUNNING_BOOTED) < 0) { virObjectUnlock(domobj); goto error; } @@ -779,8 +777,7 @@ error: } static int -testParseDomains(virConnectPtr conn, - testConnPtr privconn, const char *file, +testParseDomains(testConnPtr privconn, const char *file, xmlDocPtr doc, xmlXPathContextPtr ctxt) { int num, ret = -1; @@ -829,7 +826,8 @@ testParseDomains(virConnectPtr conn, } obj->persistent = 1; - if (testDomainStartState(conn, obj, VIR_DOMAIN_RUNNING_BOOTED) < 0) { + if (testDomainStartState(privconn, obj, + VIR_DOMAIN_RUNNING_BOOTED) < 0) { virObjectUnlock(obj); goto error; } @@ -1180,7 +1178,7 @@ testOpenFromFile(virConnectPtr conn, const char *file) if (testParseNodeInfo(&privconn->nodeInfo, ctxt) < 0) goto error; - if (testParseDomains(conn, privconn, file, doc, ctxt) < 0) + if (testParseDomains(privconn, file, doc, ctxt) < 0) goto error; if (testParseNetworks(privconn, file, doc, ctxt) < 0) goto error; @@ -1428,7 +1426,7 @@ testDomainCreateXML(virConnectPtr conn, const char *xml, goto cleanup; def = NULL; - if (testDomainStartState(conn, dom, VIR_DOMAIN_RUNNING_BOOTED) < 0) + if (testDomainStartState(privconn, dom, VIR_DOMAIN_RUNNING_BOOTED) < 0) goto cleanup; event = virDomainEventNewFromObj(dom, @@ -2043,7 +2041,7 @@ testDomainRestoreFlags(virConnectPtr conn, goto cleanup; def = NULL; - if (testDomainStartState(conn, dom, VIR_DOMAIN_RUNNING_RESTORED) < 0) + if (testDomainStartState(privconn, dom, VIR_DOMAIN_RUNNING_RESTORED) < 0) goto cleanup; event = virDomainEventNewFromObj(dom, @@ -2342,11 +2340,11 @@ testDomainSetVcpusFlags(virDomainPtr domain, unsigned int nrCpus, break; case VIR_DOMAIN_AFFECT_LIVE: - ret = testDomainUpdateVCPUs(domain->conn, privdom, nrCpus, 0); + ret = testDomainUpdateVCPUs(privconn, privdom, nrCpus, 0); break; case VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG: - ret = testDomainUpdateVCPUs(domain->conn, privdom, nrCpus, 0); + ret = testDomainUpdateVCPUs(privconn, privdom, nrCpus, 0); if (ret == 0) { persistentDef->vcpus = nrCpus; } @@ -2668,7 +2666,7 @@ static int testDomainCreateWithFlags(virDomainPtr domain, unsigned int flags) { goto cleanup; } - if (testDomainStartState(domain->conn, privdom, + if (testDomainStartState(privconn, privdom, VIR_DOMAIN_RUNNING_BOOTED) < 0) goto cleanup; domain->id = privdom->def->id; -- 1.8.3.1

On Wed, Aug 07, 2013 at 07:28:56PM -0400, Cole Robinson wrote:
Passing virConnectPtr is redundant, just pass testConnPtr and simplify certain callers. --- src/test/test_driver.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Right now things are split a bit between parsing from a relative file path or parsing from inline XML. Unify it. This will simplify upcoming bits. --- src/test/test_driver.c | 236 +++++++++++++++++++++---------------------------- 1 file changed, 103 insertions(+), 133 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 960a58e..d7b2e40 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -687,6 +687,43 @@ static char *testBuildFilename(const char *relativeTo, } } +static xmlNodePtr +testParseXMLDocFromFile(xmlNodePtr node, const char *file, const char *type) +{ + xmlNodePtr ret = NULL; + xmlDocPtr doc = NULL; + char *absFile = NULL; + char *relFile = virXMLPropString(node, "file"); + + if (relFile != NULL) { + absFile = testBuildFilename(file, relFile); + VIR_FREE(relFile); + if (!absFile) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("resolving %s filename"), type); + return NULL; + } + + if (!(doc = virXMLParse(absFile, NULL, type))) + goto error; + + ret = xmlCopyNode(xmlDocGetRootElement(doc), 1); + if (!ret) { + virReportOOMError(); + goto error; + } + xmlReplaceNode(node, ret); + xmlFreeNode(node); + } else { + ret = node; + } + +error: + xmlFreeDoc(doc); + VIR_FREE(absFile); + return ret; +} + static int testParseNodeInfo(virNodeInfoPtr nodeInfo, xmlXPathContextPtr ctxt) { @@ -777,8 +814,9 @@ error: } static int -testParseDomains(testConnPtr privconn, const char *file, - xmlDocPtr doc, xmlXPathContextPtr ctxt) +testParseDomains(testConnPtr privconn, + const char *file, + xmlXPathContextPtr ctxt) { int num, ret = -1; size_t i; @@ -792,29 +830,16 @@ testParseDomains(testConnPtr privconn, const char *file, for (i = 0; i < num; i++) { virDomainDefPtr def; - char *relFile = virXMLPropString(nodes[i], "file"); - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving domain filename")); - goto error; - } - def = virDomainDefParseFile(absFile, privconn->caps, - privconn->xmlopt, - 1 << VIR_DOMAIN_VIRT_TEST, - VIR_DOMAIN_XML_INACTIVE); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virDomainDefParseNode(doc, nodes[i], - privconn->caps, privconn->xmlopt, - 1 << VIR_DOMAIN_VIRT_TEST, - VIR_DOMAIN_XML_INACTIVE)) == NULL) - goto error; - } + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "domain"); + if (!node) + goto error; + + def = virDomainDefParseNode(ctxt->doc, node, + privconn->caps, privconn->xmlopt, + 1 << VIR_DOMAIN_VIRT_TEST, + VIR_DOMAIN_XML_INACTIVE); + if (!def) + goto error; if (testDomainGenerateIfnames(def) < 0 || !(obj = virDomainObjListAdd(privconn->domains, @@ -842,8 +867,9 @@ error: } static int -testParseNetworks(testConnPtr privconn, const char *file, - xmlDocPtr doc, xmlXPathContextPtr ctxt) +testParseNetworks(testConnPtr privconn, + const char *file, + xmlXPathContextPtr ctxt) { int num, ret = -1; size_t i; @@ -857,24 +883,13 @@ testParseNetworks(testConnPtr privconn, const char *file, for (i = 0; i < num; i++) { virNetworkDefPtr def; - char *relFile = virXMLPropString(nodes[i], "file"); - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving network filename")); - goto error; - } + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "network"); + if (!node) + goto error; - def = virNetworkDefParseFile(absFile); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virNetworkDefParseNode(doc, nodes[i])) == NULL) - goto error; - } + def = virNetworkDefParseNode(ctxt->doc, node); + if (!def) + goto error; if (!(obj = virNetworkAssignDef(&privconn->networks, def, false))) { virNetworkDefFree(def); @@ -893,8 +908,9 @@ error: } static int -testParseInterfaces(testConnPtr privconn, const char *file, - xmlDocPtr doc, xmlXPathContextPtr ctxt) +testParseInterfaces(testConnPtr privconn, + const char *file, + xmlXPathContextPtr ctxt) { int num, ret = -1; size_t i; @@ -908,24 +924,14 @@ testParseInterfaces(testConnPtr privconn, const char *file, for (i = 0; i < num; i++) { virInterfaceDefPtr def; - char *relFile = virXMLPropString(nodes[i], "file"); - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving interface filename")); - goto error; - } + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, + "interface"); + if (!node) + goto error; - def = virInterfaceDefParseFile(absFile); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virInterfaceDefParseNode(doc, nodes[i])) == NULL) - goto error; - } + def = virInterfaceDefParseNode(ctxt->doc, node); + if (!def) + goto error; if (!(obj = virInterfaceAssignDef(&privconn->ifaces, def))) { virInterfaceDefFree(def); @@ -943,9 +949,8 @@ error: } static int -testOpenVolumesForPool(xmlDocPtr xml, +testOpenVolumesForPool(const char *file, xmlXPathContextPtr ctxt, - const char *file, virStoragePoolObjPtr pool, int poolidx) { @@ -966,26 +971,14 @@ testOpenVolumesForPool(xmlDocPtr xml, } for (i = 0; i < num; i++) { - char *relFile = virXMLPropString(nodes[i], "file"); - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving volume filename")); - goto error; - } + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, + "volume"); + if (!node) + goto error; - def = virStorageVolDefParseFile(pool->def, absFile); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virStorageVolDefParseNode(pool->def, xml, - nodes[i])) == NULL) { - goto error; - } - } + def = virStorageVolDefParseNode(pool->def, ctxt->doc, node); + if (!def) + goto error; if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) @@ -1017,8 +1010,9 @@ error: } static int -testParseStorage(testConnPtr privconn, const char *file, - xmlDocPtr doc, xmlXPathContextPtr ctxt) +testParseStorage(testConnPtr privconn, + const char *file, + xmlXPathContextPtr ctxt) { int num, ret = -1; size_t i; @@ -1032,26 +1026,14 @@ testParseStorage(testConnPtr privconn, const char *file, for (i = 0; i < num; i++) { virStoragePoolDefPtr def; - char *relFile = virXMLPropString(nodes[i], "file"); - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving pool filename")); - goto error; - } + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, + "pool"); + if (!node) + goto error; - def = virStoragePoolDefParseFile(absFile); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virStoragePoolDefParseNode(doc, - nodes[i])) == NULL) { - goto error; - } - } + def = virStoragePoolDefParseNode(ctxt->doc, node); + if (!def) + goto error; if (!(obj = virStoragePoolObjAssignDef(&privconn->pools, def))) { @@ -1066,7 +1048,7 @@ testParseStorage(testConnPtr privconn, const char *file, obj->active = 1; /* Find storage volumes */ - if (testOpenVolumesForPool(doc, ctxt, file, obj, i+1) < 0) { + if (testOpenVolumesForPool(file, ctxt, obj, i+1) < 0) { virStoragePoolObjUnlock(obj); goto error; } @@ -1081,8 +1063,9 @@ error: } static int -testParseNodedevs(testConnPtr privconn, const char *file, - xmlDocPtr doc, xmlXPathContextPtr ctxt) +testParseNodedevs(testConnPtr privconn, + const char *file, + xmlXPathContextPtr ctxt) { int num, ret = -1; size_t i; @@ -1096,27 +1079,14 @@ testParseNodedevs(testConnPtr privconn, const char *file, for (i = 0; i < num; i++) { virNodeDeviceDefPtr def; - char *relFile = virXMLPropString(nodes[i], "file"); - - if (relFile != NULL) { - char *absFile = testBuildFilename(file, relFile); - VIR_FREE(relFile); - - if (!absFile) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("resolving device filename")); - goto error; - } + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, + "nodedev"); + if (!node) + goto error; - def = virNodeDeviceDefParseFile(absFile, 0, NULL); - VIR_FREE(absFile); - if (!def) - goto error; - } else { - if ((def = virNodeDeviceDefParseNode(doc, - nodes[i], 0, NULL)) == NULL) - goto error; - } + def = virNodeDeviceDefParseNode(ctxt->doc, node, 0, NULL); + if (!def) + goto error; if (!(obj = virNodeDeviceAssignDef(&privconn->devs, def))) { virNodeDeviceDefFree(def); @@ -1178,15 +1148,15 @@ testOpenFromFile(virConnectPtr conn, const char *file) if (testParseNodeInfo(&privconn->nodeInfo, ctxt) < 0) goto error; - if (testParseDomains(privconn, file, doc, ctxt) < 0) + if (testParseDomains(privconn, file, ctxt) < 0) goto error; - if (testParseNetworks(privconn, file, doc, ctxt) < 0) + if (testParseNetworks(privconn, file, ctxt) < 0) goto error; - if (testParseInterfaces(privconn, file, doc, ctxt) < 0) + if (testParseInterfaces(privconn, file, ctxt) < 0) goto error; - if (testParseStorage(privconn, file, doc, ctxt) < 0) + if (testParseStorage(privconn, file, ctxt) < 0) goto error; - if (testParseNodedevs(privconn, file, doc, ctxt) < 0) + if (testParseNodedevs(privconn, file, ctxt) < 0) goto error; xmlXPathFreeContext(ctxt); -- 1.8.3.1

On Wed, Aug 07, 2013 at 07:28:57PM -0400, Cole Robinson wrote:
Right now things are split a bit between parsing from a relative file path or parsing from inline XML. Unify it. This will simplify upcoming bits. --- src/test/test_driver.c | 236 +++++++++++++++++++++---------------------------- 1 file changed, 103 insertions(+), 133 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

When passing in custom driver XML, allow a block like <domain> ... <testdriver> <runstate>3</runstate> </testdriver> </domain> This is only read at initial driver start time, and sets the initial run state of the object. This is handy for UI testing. Wire it up for domains, networks, pools, and interfaces. The latter 3 only have a notion of active and inactive, which map to runstate 1 and 0 respectively. We also take care to unlink this <testdriver> block before passing the XML to the object parse function. Right now nothing complains, but if XML parsing became stricter in the future we don't want it to choke on this custom element. --- src/test/test_driver.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index d7b2e40..4be20b1 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -813,6 +813,80 @@ error: return -1; } +/* + * Parse object runstate from passed in XML, ex: + * <domain> + * ... + * <testdriver> + * <runstate>%d</runstate> + * </testdriver> + * </domain> + */ +static int +testParseXMLObjectRunstate(xmlXPathContextPtr ctxt, + xmlNodePtr node, + unsigned int limit, + unsigned int def, + unsigned int *val) +{ + int tmp, ret = -1; + unsigned int tmpval; + char *xpath = NULL; + + if (virAsprintf(&xpath, "string(%s/testdriver/runstate)", + xmlGetNodePath(node)) < 0) + goto error; + + tmp = virXPathUInt(xpath, ctxt, &tmpval); + if (tmp == 0) { + if (tmpval >= limit) { + virReportError(VIR_ERR_XML_ERROR, + _("runstate '%d' out of range'"), tmpval); + goto error; + } + *val = tmpval; + } else if (tmp == -1) { + *val = def; + } else if (tmp == -2) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid runstate")); + goto error; + } + + ret = 0; +error: + VIR_FREE(xpath); + return ret; +} + +/* + * Unlink custom <testdriver> XML before handing the node off + * to the object parsing handler, since it may complain about unrecognized + * elements (if not now, then in the future) + */ +static int +testNodeUnlinkCustomXML(xmlXPathContextPtr ctxt, xmlNodePtr node) +{ + int ret = -1; + char *xpath = NULL; + xmlNodePtr testnode; + + if (virAsprintf(&xpath, "%s/testdriver", + xmlGetNodePath(node)) < 0) + goto error; + + testnode = virXPathNode(xpath, ctxt); + if (testnode) { + xmlUnlinkNode(testnode); + xmlFreeNode(testnode); + } + + ret = 0; +error: + VIR_FREE(xpath); + return ret; +} + static int testParseDomains(testConnPtr privconn, const char *file, @@ -829,11 +903,20 @@ testParseDomains(testConnPtr privconn, } for (i = 0; i < num; i++) { + unsigned int runstate; virDomainDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "domain"); if (!node) goto error; + if (testParseXMLObjectRunstate(ctxt, node, + VIR_DOMAIN_LAST, VIR_DOMAIN_RUNNING, + &runstate) < 0) + goto error; + + if (testNodeUnlinkCustomXML(ctxt, node) < 0) + goto error; + def = virDomainDefParseNode(ctxt->doc, node, privconn->caps, privconn->xmlopt, 1 << VIR_DOMAIN_VIRT_TEST, @@ -851,11 +934,16 @@ testParseDomains(testConnPtr privconn, } obj->persistent = 1; - if (testDomainStartState(privconn, obj, - VIR_DOMAIN_RUNNING_BOOTED) < 0) { - virObjectUnlock(obj); - goto error; + if (runstate != VIR_DOMAIN_SHUTOFF) { + if (testDomainStartState(privconn, obj, + VIR_DOMAIN_RUNNING_BOOTED) < 0) { + virObjectUnlock(obj); + goto error; + } + } else { + testDomainShutdownState(NULL, obj, 0); } + virDomainObjSetState(obj, runstate, 0); virObjectUnlock(obj); } @@ -882,11 +970,18 @@ testParseNetworks(testConnPtr privconn, } for (i = 0; i < num; i++) { + unsigned int runstate; virNetworkDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "network"); if (!node) goto error; + if (testParseXMLObjectRunstate(ctxt, node, 1, 1, &runstate) < 0) + goto error; + + if (testNodeUnlinkCustomXML(ctxt, node) < 0) + goto error; + def = virNetworkDefParseNode(ctxt->doc, node); if (!def) goto error; @@ -897,7 +992,7 @@ testParseNetworks(testConnPtr privconn, } obj->persistent = 1; - obj->active = 1; + obj->active = runstate; virNetworkObjUnlock(obj); } @@ -923,12 +1018,19 @@ testParseInterfaces(testConnPtr privconn, } for (i = 0; i < num; i++) { + unsigned int runstate; virInterfaceDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "interface"); if (!node) goto error; + if (testParseXMLObjectRunstate(ctxt, node, 1, 1, &runstate) < 0) + goto error; + + if (testNodeUnlinkCustomXML(ctxt, node) < 0) + goto error; + def = virInterfaceDefParseNode(ctxt->doc, node); if (!def) goto error; @@ -938,7 +1040,7 @@ testParseInterfaces(testConnPtr privconn, goto error; } - obj->active = 1; + obj->active = runstate; virInterfaceObjUnlock(obj); } @@ -1025,12 +1127,19 @@ testParseStorage(testConnPtr privconn, } for (i = 0; i < num; i++) { + unsigned int runstate; virStoragePoolDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "pool"); if (!node) goto error; + if (testParseXMLObjectRunstate(ctxt, node, 1, 1, &runstate) < 0) + goto error; + + if (testNodeUnlinkCustomXML(ctxt, node) < 0) + goto error; + def = virStoragePoolDefParseNode(ctxt->doc, node); if (!def) goto error; @@ -1045,7 +1154,7 @@ testParseStorage(testConnPtr privconn, virStoragePoolObjUnlock(obj); goto error; } - obj->active = 1; + obj->active = runstate; /* Find storage volumes */ if (testOpenVolumesForPool(file, ctxt, obj, i+1) < 0) { -- 1.8.3.1

On Wed, Aug 07, 2013 at 07:28:58PM -0400, Cole Robinson wrote:
When passing in custom driver XML, allow a block like
<domain> ... <testdriver> <runstate>3</runstate> </testdriver> </domain>
This is only read at initial driver start time, and sets the initial run state of the object. This is handy for UI testing.
Wire it up for domains, networks, pools, and interfaces. The latter 3 only have a notion of active and inactive, which map to runstate 1 and 0 respectively.
We also take care to unlink this <testdriver> block before passing the XML to the object parse function. Right now nothing complains, but if XML parsing became stricter in the future we don't want it to choke on this custom element.
We have the ability to register custom parser/formatting callbacks for namespaces that we use for QEMU cli arg passthrough. I'm thinking we'd be better off defining a custom namespace for the test driver & using that for this element. Of course we'd have to extend these namespace hooks to the other types of objects, since we only have it for domains currently. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Similar to the runstate commit, allow a block like: <testdriver> <transient/> </testdriver> Wire it up for domains and networks. Generalize the boolean lookup pattern since we are going to use it for other bits as well. --- src/test/test_driver.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 4be20b1..3775906 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -860,6 +860,41 @@ error: } /* + * Parse object bool value from passed in XML, transient example: + * <domain> + * ... + * <testdriver> + * <transient/> + * </testdriver> + * </domain> + */ +static int +testParseXMLObjectBool(xmlXPathContextPtr ctxt, + xmlNodePtr node, + const char *name, + bool *val) +{ + int ret = -1; + char *xpath = NULL; + + if (virAsprintf(&xpath, "boolean(%s/testdriver/%s)", + xmlGetNodePath(node), name) < 0) + goto error; + + ret = virXPathBoolean(xpath, ctxt); + if (ret == -1) { + virReportError(VIR_ERR_XML_ERROR, _("invalid %s"), name); + goto error; + } + *val = ret; + + ret = 0; +error: + VIR_FREE(xpath); + return ret; +} + +/* * Unlink custom <testdriver> XML before handing the node off * to the object parsing handler, since it may complain about unrecognized * elements (if not now, then in the future) @@ -904,6 +939,7 @@ testParseDomains(testConnPtr privconn, for (i = 0; i < num; i++) { unsigned int runstate; + bool transient; virDomainDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "domain"); if (!node) @@ -913,6 +949,8 @@ testParseDomains(testConnPtr privconn, VIR_DOMAIN_LAST, VIR_DOMAIN_RUNNING, &runstate) < 0) goto error; + if (testParseXMLObjectBool(ctxt, node, "transient", &transient) < 0) + goto error; if (testNodeUnlinkCustomXML(ctxt, node) < 0) goto error; @@ -933,7 +971,7 @@ testParseDomains(testConnPtr privconn, goto error; } - obj->persistent = 1; + obj->persistent = !transient; if (runstate != VIR_DOMAIN_SHUTOFF) { if (testDomainStartState(privconn, obj, VIR_DOMAIN_RUNNING_BOOTED) < 0) { @@ -970,6 +1008,7 @@ testParseNetworks(testConnPtr privconn, } for (i = 0; i < num; i++) { + bool transient; unsigned int runstate; virNetworkDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "network"); @@ -978,6 +1017,8 @@ testParseNetworks(testConnPtr privconn, if (testParseXMLObjectRunstate(ctxt, node, 1, 1, &runstate) < 0) goto error; + if (testParseXMLObjectBool(ctxt, node, "transient", &transient) < 0) + goto error; if (testNodeUnlinkCustomXML(ctxt, node) < 0) goto error; @@ -991,7 +1032,7 @@ testParseNetworks(testConnPtr privconn, goto error; } - obj->persistent = 1; + obj->persistent = !transient; obj->active = runstate; virNetworkObjUnlock(obj); } -- 1.8.3.1

Also add a <hasmanagedsave> element to set this data when starting the connection. --- src/test/test_driver.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++- tests/virshtest.c | 2 +- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 3775906..09260ba 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -504,6 +504,7 @@ testDomainStartState(testConnPtr privconn, goto cleanup; } + dom->hasManagedSave = false; ret = 0; cleanup: if (ret < 0) @@ -940,6 +941,7 @@ testParseDomains(testConnPtr privconn, for (i = 0; i < num; i++) { unsigned int runstate; bool transient; + bool hasManagedSave; virDomainDefPtr def; xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "domain"); if (!node) @@ -951,6 +953,9 @@ testParseDomains(testConnPtr privconn, goto error; if (testParseXMLObjectBool(ctxt, node, "transient", &transient) < 0) goto error; + if (testParseXMLObjectBool(ctxt, node, + "hasmanagedsave", &hasManagedSave) < 0) + goto error; if (testNodeUnlinkCustomXML(ctxt, node) < 0) goto error; @@ -982,6 +987,7 @@ testParseDomains(testConnPtr privconn, testDomainShutdownState(NULL, obj, 0); } virDomainObjSetState(obj, runstate, 0); + obj->hasManagedSave = hasManagedSave; virObjectUnlock(obj); } @@ -2817,7 +2823,7 @@ static int testDomainUndefineFlags(virDomainPtr domain, virDomainEventPtr event = NULL; int ret = -1; - virCheckFlags(0, -1); + virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE, -1); testDriverLock(privconn); privdom = virDomainObjListFindByName(privconn->domains, @@ -2828,6 +2834,15 @@ static int testDomainUndefineFlags(virDomainPtr domain, goto cleanup; } + if (privdom->hasManagedSave && + !(flags & VIR_DOMAIN_UNDEFINE_MANAGED_SAVE)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Refusing to undefine while domain managed " + "save image exists")); + goto cleanup; + } + privdom->hasManagedSave = false; + event = virDomainEventNewFromObj(privdom, VIR_DOMAIN_EVENT_UNDEFINED, VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); @@ -5952,6 +5967,111 @@ testDomainScreenshot(virDomainPtr dom ATTRIBUTE_UNUSED, } +static int +testDomainManagedSave(virDomainPtr dom, unsigned int flags) +{ + testConnPtr privconn = dom->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainEventPtr event = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE | + VIR_DOMAIN_SAVE_RUNNING | + VIR_DOMAIN_SAVE_PAUSED, -1); + + testDriverLock(privconn); + vm = virDomainObjListFindByName(privconn->domains, dom->name); + testDriverUnlock(privconn); + + if (vm == NULL) { + virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + if (!vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot do managed save for transient domain")); + goto cleanup; + } + + testDomainShutdownState(dom, vm, VIR_DOMAIN_SHUTOFF_SAVED); + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SAVED); + vm->hasManagedSave = true; + + ret = 0; +cleanup: + if (vm) + virObjectUnlock(vm); + if (event) { + testDriverLock(privconn); + testDomainEventQueue(privconn, event); + testDriverUnlock(privconn); + } + + return ret; +} + + +static int +testDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags) +{ + testConnPtr privconn = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + virCheckFlags(0, -1); + + testDriverLock(privconn); + + vm = virDomainObjListFindByName(privconn->domains, dom->name); + if (vm == NULL) { + virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + ret = vm->hasManagedSave; +cleanup: + if (vm) + virObjectUnlock(vm); + testDriverUnlock(privconn); + return ret; +} + +static int +testDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags) +{ + testConnPtr privconn = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + virCheckFlags(0, -1); + + testDriverLock(privconn); + + vm = virDomainObjListFindByName(privconn->domains, dom->name); + if (vm == NULL) { + virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + vm->hasManagedSave = false; + ret = 0; +cleanup: + if (vm) + virObjectUnlock(vm); + testDriverUnlock(privconn); + return ret; +} + + static virDriver testDriver = { .no = VIR_DRV_TEST, .name = "Test", @@ -6022,6 +6142,9 @@ static virDriver testDriver = { .connectIsAlive = testConnectIsAlive, /* 0.9.8 */ .nodeGetCPUMap = testNodeGetCPUMap, /* 1.0.0 */ .domainScreenshot = testDomainScreenshot, /* 1.0.5 */ + .domainManagedSave = testDomainManagedSave, /* 1.1.2 */ + .domainHasManagedSaveImage = testDomainHasManagedSaveImage, /* 1.1.2 */ + .domainManagedSaveRemove = testDomainManagedSaveRemove, /* 1.1.2 */ }; static virNetworkDriver testNetworkDriver = { diff --git a/tests/virshtest.c b/tests/virshtest.c index ca35bb0..fe255d3 100644 --- a/tests/virshtest.c +++ b/tests/virshtest.c @@ -34,7 +34,7 @@ Max memory: 261072 KiB\n\ Used memory: 131072 KiB\n\ Persistent: yes\n\ Autostart: disable\n\ -Managed save: unknown\n\ +Managed save: no\n\ \n"; static const char *domuuid_fc4 = DOM_UUID "\n\n"; static const char *domid_fc4 = "2\n\n"; -- 1.8.3.1

Similar to how other objects arrange their parse APIs. This will be used by the test driver. --- src/conf/snapshot_conf.c | 85 ++++++++++++++++++++++++++++++++++-------------- src/conf/snapshot_conf.h | 6 ++++ 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index 2a52418..45d6af4 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -166,15 +166,13 @@ cleanup: * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then * caps and expectedVirtTypes are ignored. */ -virDomainSnapshotDefPtr -virDomainSnapshotDefParseString(const char *xmlStr, - virCapsPtr caps, - virDomainXMLOptionPtr xmlopt, - unsigned int expectedVirtTypes, - unsigned int flags) +static virDomainSnapshotDefPtr +virDomainSnapshotDefParse(xmlXPathContextPtr ctxt, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + unsigned int expectedVirtTypes, + unsigned int flags) { - xmlXPathContextPtr ctxt = NULL; - xmlDocPtr xml = NULL; virDomainSnapshotDefPtr def = NULL; virDomainSnapshotDefPtr ret = NULL; xmlNodePtr *nodes = NULL; @@ -184,26 +182,13 @@ virDomainSnapshotDefParseString(const char *xmlStr, struct timeval tv; int active; char *tmp; - int keepBlanksDefault = xmlKeepBlanksDefault(0); char *memorySnapshot = NULL; char *memoryFile = NULL; bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE); - xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt); - if (!xml) { - xmlKeepBlanksDefault(keepBlanksDefault); - return NULL; - } - xmlKeepBlanksDefault(keepBlanksDefault); - if (VIR_ALLOC(def) < 0) goto cleanup; - if (!xmlStrEqual(ctxt->node->name, BAD_CAST "domainsnapshot")) { - virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot")); - goto cleanup; - } - gettimeofday(&tv, NULL); def->name = virXPathString("string(./name)", ctxt); @@ -261,7 +246,8 @@ virDomainSnapshotDefParseString(const char *xmlStr, _("missing domain in snapshot")); goto cleanup; } - def->dom = virDomainDefParseNode(xml, domainNode, caps, xmlopt, + def->dom = virDomainDefParseNode(ctxt->node->doc, domainNode, + caps, xmlopt, expectedVirtTypes, (VIR_DOMAIN_XML_INACTIVE | VIR_DOMAIN_XML_SECURE)); @@ -348,10 +334,61 @@ cleanup: VIR_FREE(nodes); VIR_FREE(memorySnapshot); VIR_FREE(memoryFile); - xmlXPathFreeContext(ctxt); if (ret == NULL) virDomainSnapshotDefFree(def); - xmlFreeDoc(xml); + + return ret; +} + +virDomainSnapshotDefPtr +virDomainSnapshotDefParseNode(xmlDocPtr xml, + xmlNodePtr root, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + unsigned int expectedVirtTypes, + unsigned int flags) +{ + xmlXPathContextPtr ctxt = NULL; + virDomainSnapshotDefPtr def = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "domainsnapshot")) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virDomainSnapshotDefParse(ctxt, caps, xmlopt, + expectedVirtTypes, flags); +cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + +virDomainSnapshotDefPtr +virDomainSnapshotDefParseString(const char *xmlStr, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + unsigned int expectedVirtTypes, + unsigned int flags) +{ + virDomainSnapshotDefPtr ret = NULL; + xmlDocPtr xml; + int keepBlanksDefault = xmlKeepBlanksDefault(0); + + if ((xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)")))) { + xmlKeepBlanksDefault(keepBlanksDefault); + ret = virDomainSnapshotDefParseNode(xml, xmlDocGetRootElement(xml), + caps, xmlopt, + expectedVirtTypes, flags); + xmlFreeDoc(xml); + } + xmlKeepBlanksDefault(keepBlanksDefault); return ret; } diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index 6d625a7..0b8d18a 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -104,6 +104,12 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, virDomainXMLOptionPtr xmlopt, unsigned int expectedVirtTypes, unsigned int flags); +virDomainSnapshotDefPtr virDomainSnapshotDefParseNode(xmlDocPtr xml, + xmlNodePtr root, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + unsigned int expectedVirtTypes, + unsigned int flags); void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def); char *virDomainSnapshotDefFormat(const char *domain_uuid, virDomainSnapshotDefPtr def, -- 1.8.3.1

On Wed, Aug 07, 2013 at 07:29:01PM -0400, Cole Robinson wrote:
Similar to how other objects arrange their parse APIs. This will be used by the test driver. --- src/conf/snapshot_conf.c | 85 ++++++++++++++++++++++++++++++++++-------------- src/conf/snapshot_conf.h | 6 ++++ 2 files changed, 67 insertions(+), 24 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

The user can pass it in as a subelement of <domain> as we already do for storage volumes. --- src/test/test_driver.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 09260ba..67c6d68 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -924,6 +924,68 @@ error: } static int +testParseDomainSnapshots(testConnPtr privconn, + virDomainObjPtr domobj, + const char *file, + xmlXPathContextPtr ctxt, + xmlNodePtr rootnode) +{ + char *xpath; + size_t i; + int num, ret = -1; + xmlNodePtr *nodes = NULL; + + /* Find storage volumes */ + if (virAsprintf(&xpath, "%s/domainsnapshot", xmlGetNodePath(rootnode)) < 0) + goto error; + + num = virXPathNodeSet(xpath, ctxt, &nodes); + VIR_FREE(xpath); + if (num < 0) { + goto error; + } + + for (i = 0; i < num; i++) { + virDomainSnapshotObjPtr snap; + virDomainSnapshotDefPtr def; + xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, + "domainsnapshot"); + if (!node) + goto error; + + def = virDomainSnapshotDefParseNode(ctxt->doc, node, + privconn->caps, + privconn->xmlopt, + 1 << VIR_DOMAIN_VIRT_TEST, + VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | + VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL | + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE); + if (!def) + goto error; + + if (!(snap = virDomainSnapshotAssignDef(domobj->snapshots, def))) { + virDomainSnapshotDefFree(def); + goto error; + } + + if (def->current) { + if (domobj->current_snapshot) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("more than one snapshot claims to be active")); + goto error; + } + + domobj->current_snapshot = snap; + } + } + + ret = 0; +error: + VIR_FREE(nodes); + return ret; +} + +static int testParseDomains(testConnPtr privconn, const char *file, xmlXPathContextPtr ctxt) @@ -976,6 +1038,11 @@ testParseDomains(testConnPtr privconn, goto error; } + if (testParseDomainSnapshots(privconn, obj, file, ctxt, node) < 0) { + virObjectUnlock(obj); + goto error; + } + obj->persistent = !transient; if (runstate != VIR_DOMAIN_SHUTOFF) { if (testDomainStartState(privconn, obj, -- 1.8.3.1

This is just stolen from qemu_driver.c with tweaks to fit the test driver. --- src/test/test_driver.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 67c6d68..0d4cc85 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -44,6 +44,7 @@ #include "interface_conf.h" #include "domain_conf.h" #include "domain_event.h" +#include "snapshot_conf.h" #include "fdstream.h" #include "storage_conf.h" #include "node_device_conf.h" @@ -326,6 +327,27 @@ static const unsigned long long defaultPoolAlloc = 0; static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr pool); static int testNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); +static virDomainObjPtr +testDomObjFromDomain(virDomainPtr domain) +{ + virDomainObjPtr vm; + testConnPtr driver = domain->conn->privateData; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + testDriverLock(driver); + vm = virDomainObjListFindByUUID(driver->domains, domain->uuid); + if (!vm) { + virUUIDFormat(domain->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s' (%s)"), + uuidstr, domain->name); + vm = NULL; + } + + testDriverUnlock(driver); + return vm; +} + static char * testDomainGenerateIfname(virDomainDefPtr domdef) { int maxif = 1024; @@ -6139,6 +6161,362 @@ cleanup: } +/* + * Snapshot APIs + */ + +static virDomainSnapshotObjPtr +testSnapObjFromName(virDomainObjPtr vm, + const char *name) +{ + virDomainSnapshotObjPtr snap = NULL; + snap = virDomainSnapshotFindByName(vm->snapshots, name); + if (!snap) + virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("no domain snapshot with matching name '%s'"), + name); + return snap; +} + +static virDomainSnapshotObjPtr +testSnapObjFromSnapshot(virDomainObjPtr vm, + virDomainSnapshotPtr snapshot) +{ + return testSnapObjFromName(vm, snapshot->name); +} + +static virDomainObjPtr +testDomObjFromSnapshot(virDomainSnapshotPtr snapshot) +{ + return testDomObjFromDomain(snapshot->domain); +} + +static int +testDomainSnapshotNum(virDomainPtr domain, unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int n = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS | + VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + n = virDomainSnapshotObjListNum(vm->snapshots, NULL, flags); + +cleanup: + if (vm) + virObjectUnlock(vm); + return n; +} + +static int +testDomainSnapshotListNames(virDomainPtr domain, + char **names, + int nameslen, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int n = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS | + VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + n = virDomainSnapshotObjListGetNames(vm->snapshots, NULL, names, nameslen, + flags); + +cleanup: + if (vm) + virObjectUnlock(vm); + return n; +} + +static int +testDomainListAllSnapshots(virDomainPtr domain, + virDomainSnapshotPtr **snaps, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int n = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS | + VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + n = virDomainListSnapshots(vm->snapshots, NULL, domain, snaps, flags); + +cleanup: + if (vm) + virObjectUnlock(vm); + return n; +} + +static int +testDomainSnapshotListChildrenNames(virDomainSnapshotPtr snapshot, + char **names, + int nameslen, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + virDomainSnapshotObjPtr snap = NULL; + int n = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS | + VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + n = virDomainSnapshotObjListGetNames(vm->snapshots, snap, names, nameslen, + flags); + +cleanup: + if (vm) + virObjectUnlock(vm); + return n; +} + +static int +testDomainSnapshotNumChildren(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + virDomainSnapshotObjPtr snap = NULL; + int n = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS | + VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + n = virDomainSnapshotObjListNum(vm->snapshots, snap, flags); + +cleanup: + if (vm) + virObjectUnlock(vm); + return n; +} + +static int +testDomainSnapshotListAllChildren(virDomainSnapshotPtr snapshot, + virDomainSnapshotPtr **snaps, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + virDomainSnapshotObjPtr snap = NULL; + int n = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS | + VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + n = virDomainListSnapshots(vm->snapshots, snap, snapshot->domain, snaps, + flags); + +cleanup: + if (vm) + virObjectUnlock(vm); + return n; +} + +static virDomainSnapshotPtr +testDomainSnapshotLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags) +{ + virDomainObjPtr vm; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotPtr snapshot = NULL; + + virCheckFlags(0, NULL); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + if (!(snap = testSnapObjFromName(vm, name))) + goto cleanup; + + snapshot = virGetDomainSnapshot(domain, snap->def->name); + +cleanup: + if (vm) + virObjectUnlock(vm); + return snapshot; +} + +static int +testDomainHasCurrentSnapshot(virDomainPtr domain, + unsigned int flags) +{ + virDomainObjPtr vm; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + ret = (vm->current_snapshot != NULL); + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static virDomainSnapshotPtr +testDomainSnapshotGetParent(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainObjPtr vm; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotPtr parent = NULL; + + virCheckFlags(0, NULL); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + if (!snap->def->parent) { + virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, + _("snapshot '%s' does not have a parent"), + snap->def->name); + goto cleanup; + } + + parent = virGetDomainSnapshot(snapshot->domain, snap->def->parent); + +cleanup: + if (vm) + virObjectUnlock(vm); + return parent; +} + +static virDomainSnapshotPtr +testDomainSnapshotCurrent(virDomainPtr domain, + unsigned int flags) +{ + virDomainObjPtr vm; + virDomainSnapshotPtr snapshot = NULL; + + virCheckFlags(0, NULL); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + if (!vm->current_snapshot) { + virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, "%s", + _("the domain does not have a current snapshot")); + goto cleanup; + } + + snapshot = virGetDomainSnapshot(domain, vm->current_snapshot->def->name); + +cleanup: + if (vm) + virObjectUnlock(vm); + return snapshot; +} + +static char * +testDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + char *xml = NULL; + virDomainSnapshotObjPtr snap = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + virUUIDFormat(snapshot->domain->uuid, uuidstr); + + xml = virDomainSnapshotDefFormat(uuidstr, snap->def, flags, 0); + +cleanup: + if (vm) + virObjectUnlock(vm); + return xml; +} + +static int +testDomainSnapshotIsCurrent(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int ret = -1; + virDomainSnapshotObjPtr snap = NULL; + + virCheckFlags(0, -1); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + ret = (vm->current_snapshot && + STREQ(snapshot->name, vm->current_snapshot->def->name)); + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + + +static int +testDomainSnapshotHasMetadata(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int ret = -1; + virDomainSnapshotObjPtr snap = NULL; + + virCheckFlags(0, -1); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + goto cleanup; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + ret = 1; + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + + static virDriver testDriver = { .no = VIR_DRV_TEST, .name = "Test", @@ -6212,6 +6590,20 @@ static virDriver testDriver = { .domainManagedSave = testDomainManagedSave, /* 1.1.2 */ .domainHasManagedSaveImage = testDomainHasManagedSaveImage, /* 1.1.2 */ .domainManagedSaveRemove = testDomainManagedSaveRemove, /* 1.1.2 */ + + .domainSnapshotNum = testDomainSnapshotNum, /* 1.1.2 */ + .domainSnapshotListNames = testDomainSnapshotListNames, /* 1.1.2 */ + .domainListAllSnapshots = testDomainListAllSnapshots, /* 1.1.2 */ + .domainSnapshotGetXMLDesc = testDomainSnapshotGetXMLDesc, /* 1.1.2 */ + .domainSnapshotNumChildren = testDomainSnapshotNumChildren, /* 1.1.2 */ + .domainSnapshotListChildrenNames = testDomainSnapshotListChildrenNames, /* 1.1.2 */ + .domainSnapshotListAllChildren = testDomainSnapshotListAllChildren, /* 1.1.2 */ + .domainSnapshotLookupByName = testDomainSnapshotLookupByName, /* 1.1.2 */ + .domainHasCurrentSnapshot = testDomainHasCurrentSnapshot, /* 1.1.2 */ + .domainSnapshotGetParent = testDomainSnapshotGetParent, /* 1.1.2 */ + .domainSnapshotCurrent = testDomainSnapshotCurrent, /* 1.1.2 */ + .domainSnapshotIsCurrent = testDomainSnapshotIsCurrent, /* 1.1.2 */ + .domainSnapshotHasMetadata = testDomainSnapshotHasMetadata, /* 1.1.2 */ }; static virNetworkDriver testNetworkDriver = { -- 1.8.3.1

Again stolen from qemu_driver.c, but dropping all the unneeded bits. This aims to copy all the current qemu validation checks since that's the most commonly used real driver, but some of the checks are completely artificial in the test driver. This only supports creation of internal snapshots for initial simplicity. --- src/conf/domain_conf.c | 3 +- src/test/test_driver.c | 504 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 505 insertions(+), 2 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7309877..debac4b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -13371,7 +13371,8 @@ virDomainDefCheckABIStability(virDomainDefPtr src, virArchToString(src->os.arch)); return false; } - if (STRNEQ(src->os.machine, dst->os.machine)) { + if (src->os.machine && dst->os.machine && + STRNEQ(src->os.machine, dst->os.machine)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain OS type %s does not match source %s"), dst->os.machine, src->os.machine); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 0d4cc85..6b73e5b 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -2910,9 +2910,11 @@ static int testDomainUndefineFlags(virDomainPtr domain, testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; virDomainEventPtr event = NULL; + int nsnapshots; int ret = -1; - virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE, -1); + virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE | + VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, -1); testDriverLock(privconn); privdom = virDomainObjListFindByName(privconn->domains, @@ -2932,6 +2934,24 @@ static int testDomainUndefineFlags(virDomainPtr domain, } privdom->hasManagedSave = false; + /* Requiring an inactive VM is part of the documented API for + * UNDEFINE_SNAPSHOTS_METADATA + */ + if (!virDomainObjIsActive(privdom) && + (nsnapshots = virDomainSnapshotObjListNum(privdom->snapshots, + NULL, 0))) { + if (!(flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("cannot delete inactive domain with %d " + "snapshots"), + nsnapshots); + goto cleanup; + } + + /* There isn't actually anything to do, we are just emulating qemu + * behavior here. */ + } + event = virDomainEventNewFromObj(privdom, VIR_DOMAIN_EVENT_UNDEFINED, VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); @@ -6516,6 +6536,485 @@ cleanup: return ret; } +static int +testDomainSnapshotAlignDisks(virDomainObjPtr vm, + virDomainSnapshotDefPtr def, + unsigned int flags) +{ + int align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL; + int align_match = true; + + if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) { + align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL; + align_match = false; + if (virDomainObjIsActive(vm)) + def->state = VIR_DOMAIN_DISK_SNAPSHOT; + else + def->state = VIR_DOMAIN_SHUTOFF; + def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE; + } else if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { + def->state = virDomainObjGetState(vm, NULL); + align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL; + align_match = false; + } else { + def->state = virDomainObjGetState(vm, NULL); + def->memory = (def->state == VIR_DOMAIN_SHUTOFF ? + VIR_DOMAIN_SNAPSHOT_LOCATION_NONE : + VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL); + } + + return virDomainSnapshotAlignDisks(def, align_location, align_match); +} + +static virDomainSnapshotPtr +testDomainSnapshotCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags) +{ + testConnPtr privconn = domain->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainSnapshotDefPtr def = NULL; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotPtr snapshot = NULL; + virDomainEventPtr event = NULL; + char *xml = NULL; + unsigned int parse_flags = VIR_DOMAIN_SNAPSHOT_PARSE_DISKS; + + /* + * REDEFINE + CURRENT: Not implemented yet + * DISK_ONLY: Not implemented yet + * REUSE_EXT: Not implemented yet + * + * NO_METADATA: Explicitly not implemented + * + * HALT: Implemented + * QUIESCE: Nothing to do + * ATOMIC: Nothing to do + * LIVE: Nothing to do + */ + virCheckFlags( + VIR_DOMAIN_SNAPSHOT_CREATE_HALT | + VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE | + VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC | + VIR_DOMAIN_SNAPSHOT_CREATE_LIVE, NULL); + + if (!(vm = testDomObjFromDomain(domain))) + goto cleanup; + + testDriverLock(privconn); + + if (!vm->persistent && (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot halt after transient domain snapshot")); + goto cleanup; + } + + if (!(def = virDomainSnapshotDefParseString(xmlDesc, + privconn->caps, + privconn->xmlopt, + 1 << VIR_DOMAIN_VIRT_TEST, + parse_flags))) + goto cleanup; + + if (!(xml = virDomainDefFormat(vm->def, VIR_DOMAIN_XML_INACTIVE)) || + !(def->dom = virDomainDefParseString(xml, + privconn->caps, + privconn->xmlopt, + 1 << VIR_DOMAIN_VIRT_TEST, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (testDomainSnapshotAlignDisks(vm, def, flags) < 0) + goto cleanup; + + if (!(snap = virDomainSnapshotAssignDef(vm->snapshots, def))) + goto cleanup; + def = NULL; + + if (vm->current_snapshot) { + if (!(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) && + VIR_STRDUP(snap->def->parent, vm->current_snapshot->def->name) < 0) + goto cleanup; + } + + if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT) && + virDomainObjIsActive(vm)) { + testDomainShutdownState(domain, vm, VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT); + event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT); + } + + + snapshot = virGetDomainSnapshot(domain, snap->def->name); +cleanup: + VIR_FREE(xml); + if (vm) { + if (snapshot) { + virDomainSnapshotObjPtr other; + vm->current_snapshot = snap; + other = virDomainSnapshotFindByName(vm->snapshots, + snap->def->parent); + snap->parent = other; + other->nchildren++; + snap->sibling = other->first_child; + other->first_child = snap; + } + virObjectUnlock(vm); + } + if (event) { + testDomainEventQueue(privconn, event); + } + testDriverUnlock(privconn); + virDomainSnapshotDefFree(def); + return snapshot; +} + + +typedef struct _testSnapRemoveData testSnapRemoveData; +typedef testSnapRemoveData *testSnapRemoveDataPtr; +struct _testSnapRemoveData { + virDomainObjPtr vm; + bool current; +}; + +static void +testDomainSnapshotDiscard(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainSnapshotObjPtr snap = payload; + testSnapRemoveDataPtr curr = data; + + if (snap->def->current) + curr->current = true; + virDomainSnapshotObjListRemove(curr->vm->snapshots, snap); +} + +typedef struct _testSnapReparentData testSnapReparentData; +typedef testSnapReparentData *testSnapReparentDataPtr; +struct _testSnapReparentData { + virDomainSnapshotObjPtr parent; + virDomainObjPtr vm; + int err; + virDomainSnapshotObjPtr last; +}; + +static void +testDomainSnapshotReparentChildren(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainSnapshotObjPtr snap = payload; + testSnapReparentDataPtr rep = data; + + if (rep->err < 0) { + return; + } + + VIR_FREE(snap->def->parent); + snap->parent = rep->parent; + + if (rep->parent->def && + VIR_STRDUP(snap->def->parent, rep->parent->def->name) < 0) { + rep->err = -1; + return; + } + + if (!snap->sibling) + rep->last = snap; +} + +static int +testDomainSnapshotDelete(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotObjPtr parentsnap = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | + VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY, -1); + + if (!(vm = testDomObjFromSnapshot(snapshot))) + return -1; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + if (flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | + VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY)) { + testSnapRemoveData rem; + rem.vm = vm; + rem.current = false; + virDomainSnapshotForEachDescendant(snap, + testDomainSnapshotDiscard, + &rem); + if (rem.current) { + if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) { + snap->def->current = true; + } + vm->current_snapshot = snap; + } + } else if (snap->nchildren) { + testSnapReparentData rep; + rep.parent = snap->parent; + rep.vm = vm; + rep.err = 0; + rep.last = NULL; + virDomainSnapshotForEachChild(snap, + testDomainSnapshotReparentChildren, + &rep); + if (rep.err < 0) + goto cleanup; + + /* Can't modify siblings during ForEachChild, so do it now. */ + snap->parent->nchildren += snap->nchildren; + rep.last->sibling = snap->parent->first_child; + snap->parent->first_child = snap->first_child; + } + + if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) { + snap->nchildren = 0; + snap->first_child = NULL; + } else { + virDomainSnapshotDropParent(snap); + if (snap == vm->current_snapshot) { + if (snap->def->parent) { + parentsnap = virDomainSnapshotFindByName(vm->snapshots, + snap->def->parent); + if (!parentsnap) { + VIR_WARN("missing parent snapshot matching name '%s'", + snap->def->parent); + } else { + parentsnap->def->current = true; + } + } + vm->current_snapshot = parentsnap; + } + virDomainSnapshotObjListRemove(vm->snapshots, snap); + } + + ret = 0; +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +testDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, + unsigned int flags) +{ + testConnPtr privconn = snapshot->domain->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainSnapshotObjPtr snap = NULL; + virDomainEventPtr event = NULL; + virDomainEventPtr event2 = NULL; + virDomainDefPtr config = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING | + VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED | + VIR_DOMAIN_SNAPSHOT_REVERT_FORCE, -1); + + /* We have the following transitions, which create the following events: + * 1. inactive -> inactive: none + * 2. inactive -> running: EVENT_STARTED + * 3. inactive -> paused: EVENT_STARTED, EVENT_PAUSED + * 4. running -> inactive: EVENT_STOPPED + * 5. running -> running: none + * 6. running -> paused: EVENT_PAUSED + * 7. paused -> inactive: EVENT_STOPPED + * 8. paused -> running: EVENT_RESUMED + * 9. paused -> paused: none + * Also, several transitions occur even if we fail partway through, + * and use of FORCE can cause multiple transitions. + */ + + if (!(vm = testDomObjFromSnapshot(snapshot))) + return -1; + + if (!(snap = testSnapObjFromSnapshot(vm, snapshot))) + goto cleanup; + + testDriverLock(privconn); + + if (!vm->persistent && + snap->def->state != VIR_DOMAIN_RUNNING && + snap->def->state != VIR_DOMAIN_PAUSED && + (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING | + VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED)) == 0) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("transient domain needs to request run or pause " + "to revert to inactive snapshot")); + goto cleanup; + } + + if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_FORCE)) { + if (!snap->def->dom) { + virReportError(VIR_ERR_SNAPSHOT_REVERT_RISKY, + _("snapshot '%s' lacks domain '%s' rollback info"), + snap->def->name, vm->def->name); + goto cleanup; + } + if (virDomainObjIsActive(vm) && + !(snap->def->state == VIR_DOMAIN_RUNNING + || snap->def->state == VIR_DOMAIN_PAUSED) && + (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING | + VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED))) { + virReportError(VIR_ERR_SNAPSHOT_REVERT_RISKY, "%s", + _("must respawn guest to start inactive snapshot")); + goto cleanup; + } + } + + + if (vm->current_snapshot) { + vm->current_snapshot->def->current = false; + vm->current_snapshot = NULL; + } + + snap->def->current = true; + config = virDomainDefCopy(snap->def->dom, + privconn->caps, privconn->xmlopt, true); + if (!config) + goto cleanup; + + if (snap->def->state == VIR_DOMAIN_RUNNING || + snap->def->state == VIR_DOMAIN_PAUSED) { + /* Transitions 2, 3, 5, 6, 8, 9 */ + bool was_running = false; + bool was_stopped = false; + + if (virDomainObjIsActive(vm)) { + /* Transitions 5, 6, 8, 9 */ + /* Check for ABI compatibility. */ + if (!virDomainDefCheckABIStability(vm->def, config)) { + virErrorPtr err = virGetLastError(); + + if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_FORCE)) { + /* Re-spawn error using correct category. */ + if (err->code == VIR_ERR_CONFIG_UNSUPPORTED) + virReportError(VIR_ERR_SNAPSHOT_REVERT_RISKY, "%s", + err->str2); + goto cleanup; + } + + virResetError(err); + testDomainShutdownState(snapshot->domain, vm, + VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT); + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT); + if (event) + testDomainEventQueue(privconn, event); + goto load; + } + + if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) { + /* Transitions 5, 6 */ + was_running = true; + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, + VIR_DOMAIN_PAUSED_FROM_SNAPSHOT); + /* Create an event now in case the restore fails, so + * that user will be alerted that they are now paused. + * If restore later succeeds, we might replace this. */ + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT); + } + virDomainObjAssignDef(vm, config, false, NULL); + + } else { + /* Transitions 2, 3 */ + load: + was_stopped = true; + virDomainObjAssignDef(vm, config, false, NULL); + if (testDomainStartState(privconn, vm, + VIR_DOMAIN_RUNNING_FROM_SNAPSHOT) < 0) + goto cleanup; + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT); + } + + /* Touch up domain state. */ + if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING) && + (snap->def->state == VIR_DOMAIN_PAUSED || + (flags & VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED))) { + /* Transitions 3, 6, 9 */ + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, + VIR_DOMAIN_PAUSED_FROM_SNAPSHOT); + if (was_stopped) { + /* Transition 3, use event as-is and add event2 */ + event2 = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT); + } /* else transition 6 and 9 use event as-is */ + } else { + /* Transitions 2, 5, 8 */ + virDomainEventFree(event); + event = NULL; + + if (was_stopped) { + /* Transition 2 */ + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT); + } else if (was_running) { + /* Transition 8 */ + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_RESUMED); + } + } + } else { + /* Transitions 1, 4, 7 */ + virDomainObjAssignDef(vm, config, false, NULL); + + if (virDomainObjIsActive(vm)) { + /* Transitions 4, 7 */ + testDomainShutdownState(snapshot->domain, vm, + VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT); + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT); + } + + if (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING | + VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED)) { + /* Flush first event, now do transition 2 or 3 */ + bool paused = (flags & VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED) != 0; + + if (event) + testDomainEventQueue(privconn, event); + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT); + if (paused) { + event2 = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT); + } + } + } + + vm->current_snapshot = snap; + ret = 0; +cleanup: + if (event) { + testDomainEventQueue(privconn, event); + if (event2) + testDomainEventQueue(privconn, event2); + } + virObjectUnlock(vm); + testDriverUnlock(privconn); + + return ret; +} + + static virDriver testDriver = { .no = VIR_DRV_TEST, @@ -6604,6 +7103,9 @@ static virDriver testDriver = { .domainSnapshotCurrent = testDomainSnapshotCurrent, /* 1.1.2 */ .domainSnapshotIsCurrent = testDomainSnapshotIsCurrent, /* 1.1.2 */ .domainSnapshotHasMetadata = testDomainSnapshotHasMetadata, /* 1.1.2 */ + .domainSnapshotCreateXML = testDomainSnapshotCreateXML, /* 1.1.2 */ + .domainRevertToSnapshot = testDomainRevertToSnapshot, /* 1.1.2 */ + .domainSnapshotDelete = testDomainSnapshotDelete, /* 1.1.2 */ }; static virNetworkDriver testNetworkDriver = { -- 1.8.3.1

On Wed, Aug 07, 2013 at 07:28:54PM -0400, Cole Robinson wrote:
This series implements snapshot APIs for the test driver, and adds some misc improvements, like specifying domain state in the passed in driver XML.
Good addition, the test driver would be usefully put to work in a unit test to validate operation of all virsh commands too, so we will want to make sure it covers every API eventually. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 08/09/2013 01:03 PM, Daniel P. Berrange wrote:
On Wed, Aug 07, 2013 at 07:28:54PM -0400, Cole Robinson wrote:
This series implements snapshot APIs for the test driver, and adds some misc improvements, like specifying domain state in the passed in driver XML.
Good addition, the test driver would be usefully put to work in a unit test to validate operation of all virsh commands too, so we will want to make sure it covers every API eventually.
Thanks, I pushed the ACK'd patches 1-3 and 7. Will rework the rest to use XML namespaces. - Cole
participants (2)
-
Cole Robinson
-
Daniel P. Berrange