libvirt/src/test/test_driver.c

7245 lines
204 KiB
C
Raw Normal View History

/*
* test_driver.c: A "mock" hypervisor for use by application unit tests
*
test: Avoid use-after-free on virDomainSnapshotDelete The following virsh command was triggering a use-after-free: $ virsh -c test:///default ' snapshot-create-as test s1 snapshot-create-as test s2 snapshot-delete --children-only test s1 snapshot-current --name test' Domain snapshot s1 created Domain snapshot s2 created Domain snapshot s1 children deleted error: name in virGetDomainSnapshot must not be NULL I got lucky on that run - although the error message is quite unexpected. On other runs, I was able to get a core dump, and valgrind confirms there is a definitive problem. The culprit? We were inconsistent about whether we set vm->current_snapshot, snap->def->current, or both when updating how the current snapshot was being tracked. As a result, deletion did not see that snapshot s2 was previously current, and failed to update vm->current_snapshot, so that the next API using the current snapshot failed because it referenced stale memory for the now-gone s2 (instead of the intended s1). The test driver code was copied from the qemu code (which DOES track both pieces of state everywhere), but was purposefully simplified because the test driver does not have to write persistent snapshot state to the file system. But when you realize that the only reason snap->def->current needs to exist is when writing out one file per snapshot for qemu, it's just as easy to state that the test driver never has to mess with the field (rather than chasing down which places forgot to set the field), and have vm->current_snapshot be the sole source of truth in the test driver. Ideally, I'd get rid of the 'current' member in virDomainSnapshotDef, as well as the 'current_snapshot' member in virDomainDef, and instead track the current member in virDomainSnapshotObjList, coupled with writing ALL snapshot state for qemu in a single file (where I can use <snapshots current='...'> as a wrapper, rather than VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL to output <current>1</current> XML on a per-snapshot file basis). But that's a bigger change, so for now I'm just patching things to avoid the test driver segfault. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
2019-03-17 03:57:45 +00:00
* Copyright (C) 2006-2019 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
2008-10-30 17:40:57 +00:00
#include <libxml/xmlsave.h>
#include <libxml/xpathInternals.h>
#include "virerror.h"
#include "datatypes.h"
#include "test_driver.h"
#include "virbuffer.h"
2012-12-13 18:01:25 +00:00
#include "viruuid.h"
#include "capabilities.h"
#include "configmake.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
#include "virnetworkobj.h"
#include "interface_conf.h"
#include "domain_conf.h"
2009-01-20 20:23:53 +00:00
#include "domain_event.h"
#include "network_event.h"
#include "snapshot_conf.h"
#include "virfdstream.h"
2008-10-30 17:40:57 +00:00
#include "storage_conf.h"
#include "virstorageobj.h"
#include "storage_event.h"
#include "node_device_conf.h"
#include "virnodedeviceobj.h"
#include "node_device_event.h"
2012-12-13 18:13:21 +00:00
#include "virxml.h"
#include "virthread.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#include "virfile.h"
#include "virtypedparam.h"
#include "virrandom.h"
#include "virstring.h"
#include "cpu/cpu.h"
#include "virauth.h"
#include "viratomic.h"
#include "virdomainobjlist.h"
#include "virinterfaceobj.h"
#include "virhostcpu.h"
#include "virdomainsnapshotobjlist.h"
#include "virkeycode.h"
#define VIR_FROM_THIS VIR_FROM_TEST
VIR_LOG_INIT("test.test_driver");
#define MAX_CPUS 128
struct _testCell {
unsigned long mem;
unsigned long freeMem;
int numCpus;
virCapsHostNUMACellCPU cpus[MAX_CPUS];
};
typedef struct _testCell testCell;
typedef struct _testCell *testCellPtr;
#define MAX_CELLS 128
struct _testAuth {
char *username;
char *password;
};
typedef struct _testAuth testAuth;
typedef struct _testAuth *testAuthPtr;
2015-06-22 14:25:16 +00:00
struct _testDriver {
virObjectLockable parent;
virNodeInfo nodeInfo;
virInterfaceObjListPtr ifaces;
bool transaction_running;
virInterfaceObjListPtr backupIfaces;
virStoragePoolObjListPtr pools;
virNodeDeviceObjListPtr devs;
int numCells;
testCell cells[MAX_CELLS];
size_t numAuths;
testAuthPtr auths;
2009-01-20 20:23:53 +00:00
/* virAtomic access only */
volatile int nextDomID;
/* immutable pointer, immutable object after being initialized with
* testBuildCapabilities */
virCapsPtr caps;
/* immutable pointer, immutable object */
virDomainXMLOptionPtr xmlopt;
/* immutable pointer, self-locking APIs */
virDomainObjListPtr domains;
virNetworkObjListPtr networks;
virObjectEventStatePtr eventState;
};
2015-06-22 14:25:16 +00:00
typedef struct _testDriver testDriver;
typedef testDriver *testDriverPtr;
static testDriverPtr defaultPrivconn;
static virMutex defaultLock = VIR_MUTEX_INITIALIZER;
static virClassPtr testDriverClass;
static void testDriverDispose(void *obj);
static int testDriverOnceInit(void)
{
if (!(VIR_CLASS_NEW(testDriver, virClassForObjectLockable())))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(testDriver);
#define TEST_MODEL "i686"
#define TEST_EMULATOR "/usr/bin/test-hv"
static const virNodeInfo defaultNodeInfo = {
TEST_MODEL,
1024*1024*3, /* 3 GB */
16,
1400,
2,
2,
2,
2,
};
static void
testDriverDispose(void *obj)
{
testDriverPtr driver = obj;
virObjectUnref(driver->caps);
virObjectUnref(driver->xmlopt);
virObjectUnref(driver->domains);
virNodeDeviceObjListFree(driver->devs);
virObjectUnref(driver->networks);
virObjectUnref(driver->ifaces);
virObjectUnref(driver->pools);
virObjectUnref(driver->eventState);
}
#define TEST_NAMESPACE_HREF "http://libvirt.org/schemas/domain/test/1.0"
typedef struct _testDomainNamespaceDef testDomainNamespaceDef;
typedef testDomainNamespaceDef *testDomainNamespaceDefPtr;
struct _testDomainNamespaceDef {
int runstate;
bool transient;
bool hasManagedSave;
unsigned int num_snap_nodes;
xmlNodePtr *snap_nodes;
};
static void
testDomainDefNamespaceFree(void *data)
{
testDomainNamespaceDefPtr nsdata = data;
size_t i;
if (!nsdata)
return;
for (i = 0; i < nsdata->num_snap_nodes; i++)
xmlFreeNode(nsdata->snap_nodes[i]);
VIR_FREE(nsdata->snap_nodes);
VIR_FREE(nsdata);
}
static int
testDomainDefNamespaceParse(xmlDocPtr xml ATTRIBUTE_UNUSED,
xmlNodePtr root ATTRIBUTE_UNUSED,
xmlXPathContextPtr ctxt,
void **data)
{
testDomainNamespaceDefPtr nsdata = NULL;
int tmp, n;
size_t i;
unsigned int tmpuint;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
if (xmlXPathRegisterNs(ctxt, BAD_CAST "test",
BAD_CAST TEST_NAMESPACE_HREF) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to register xml namespace '%s'"),
TEST_NAMESPACE_HREF);
return -1;
}
if (VIR_ALLOC(nsdata) < 0)
return -1;
n = virXPathNodeSet("./test:domainsnapshot", ctxt, &nodes);
if (n < 0)
goto error;
if (n && VIR_ALLOC_N(nsdata->snap_nodes, n) < 0)
goto error;
for (i = 0; i < n; i++) {
xmlNodePtr newnode = xmlCopyNode(nodes[i], 1);
if (!newnode) {
virReportOOMError();
goto error;
}
nsdata->snap_nodes[nsdata->num_snap_nodes] = newnode;
nsdata->num_snap_nodes++;
}
tmp = virXPathBoolean("boolean(./test:transient)", ctxt);
if (tmp == -1) {
virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid transient"));
goto error;
}
nsdata->transient = tmp;
tmp = virXPathBoolean("boolean(./test:hasmanagedsave)", ctxt);
if (tmp == -1) {
virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid hasmanagedsave"));
goto error;
}
nsdata->hasManagedSave = tmp;
tmp = virXPathUInt("string(./test:runstate)", ctxt, &tmpuint);
if (tmp == 0) {
if (tmpuint >= VIR_DOMAIN_LAST) {
virReportError(VIR_ERR_XML_ERROR,
_("runstate '%d' out of range'"), tmpuint);
goto error;
}
nsdata->runstate = tmpuint;
} else if (tmp == -1) {
nsdata->runstate = VIR_DOMAIN_RUNNING;
} else if (tmp == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid runstate"));
goto error;
}
if (nsdata->transient && nsdata->runstate == VIR_DOMAIN_SHUTOFF) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("transient domain cannot have runstate 'shutoff'"));
goto error;
}
if (nsdata->hasManagedSave && nsdata->runstate != VIR_DOMAIN_SHUTOFF) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("domain with managedsave data can only have runstate 'shutoff'"));
goto error;
}
*data = nsdata;
return 0;
error:
testDomainDefNamespaceFree(nsdata);
return -1;
}
static virCapsPtr
testBuildCapabilities(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virCapsPtr caps;
virCapsGuestPtr guest;
int guest_types[] = { VIR_DOMAIN_OSTYPE_HVM,
VIR_DOMAIN_OSTYPE_XEN };
size_t i, j;
capabilities: use bool instead of int While preparing to add a capability for active commit, I noticed that the existing code was abusing int for boolean values. * src/conf/capabilities.h (_virCapsGuestFeature, _virCapsHost) (virCapabilitiesNew, virCapabilitiesAddGuestFeature): Improve types. * src/conf/capabilities.c (virCapabilitiesNew) (virCapabilitiesAddGuestFeature): Adjust signature. * src/bhyve/bhyve_capabilities.c (virBhyveCapsBuild): Update clients. * src/esx/esx_driver.c (esxCapsInit): Likewise. * src/libxl/libxl_conf.c (libxlMakeCapabilities): Likewise. * src/lxc/lxc_conf.c (virLXCDriverCapsInit): Likewise. * src/openvz/openvz_conf.c (openvzCapsInit): Likewise. * src/parallels/parallels_driver.c (parallelsBuildCapabilities): Likewise. * src/phyp/phyp_driver.c (phypCapsInit): Likewise. * src/qemu/qemu_capabilities.c (virQEMUCapsInit) (virQEMUCapsInitGuestFromBinary): Likewise. * src/security/virt-aa-helper.c (get_definition): Likewise. * src/test/test_driver.c (testBuildCapabilities): Likewise. * src/uml/uml_conf.c (umlCapsInit): Likewise. * src/vbox/vbox_tmpl.c (vboxCapsInit): Likewise. * src/vmware/vmware_conf.c (vmwareCapsInit): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorBuildCapabilities): Likewise. * src/xenapi/xenapi_driver.c (getCapsObject): Likewise. * tests/qemucaps2xmltest.c (testGetCaps): Likewise. * tests/testutils.c (virTestGenericCapsInit): Likewise. * tests/testutilslxc.c (testLXCCapsInit): Likewise. * tests/testutilsqemu.c (testQemuCapsInit): Likewise. * tests/testutilsxen.c (testXenCapsInit): Likewise. * tests/vircaps2xmltest.c (buildVirCapabilities): Likewise. * tests/vircapstest.c (buildNUMATopology): Likewise. * tests/vmx2xmltest.c (testCapsInit): Likewise. * tests/xml2vmxtest.c (testCapsInit): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-07-14 12:56:13 +00:00
if ((caps = virCapabilitiesNew(VIR_ARCH_I686, false, false)) == NULL)
goto error;
if (virCapabilitiesAddHostFeature(caps, "pae") < 0)
goto error;
if (virCapabilitiesAddHostFeature(caps, "nonpae") < 0)
goto error;
virCapabilitiesHostInitIOMMU(caps);
if (VIR_ALLOC_N(caps->host.pagesSize, 4) < 0)
goto error;
caps->host.pagesSize[caps->host.nPagesSize++] = 4;
caps->host.pagesSize[caps->host.nPagesSize++] = 8;
caps->host.pagesSize[caps->host.nPagesSize++] = 2048;
caps->host.pagesSize[caps->host.nPagesSize++] = 1024 * 1024;
for (i = 0; i < privconn->numCells; i++) {
virCapsHostNUMACellCPUPtr cpu_cells;
virCapsHostNUMACellPageInfoPtr pages;
size_t nPages = caps->host.nPagesSize - 1;
if (VIR_ALLOC_N(cpu_cells, privconn->cells[i].numCpus) < 0 ||
VIR_ALLOC_N(pages, nPages) < 0) {
VIR_FREE(cpu_cells);
goto error;
}
memcpy(cpu_cells, privconn->cells[i].cpus,
sizeof(*cpu_cells) * privconn->cells[i].numCpus);
if (i == 1)
pages[0].size = caps->host.pagesSize[1];
else
pages[0].size = caps->host.pagesSize[0];
for (j = 1; j < nPages; j++)
pages[j].size = caps->host.pagesSize[j + 1];
pages[0].avail = privconn->cells[i].mem / pages[0].size;
if (virCapabilitiesAddHostNUMACell(caps, i, privconn->cells[i].mem,
privconn->cells[i].numCpus,
cpu_cells, 0, NULL, nPages, pages) < 0)
goto error;
}
for (i = 0; i < ARRAY_CARDINALITY(guest_types); i++) {
if ((guest = virCapabilitiesAddGuest(caps,
guest_types[i],
VIR_ARCH_I686,
TEST_EMULATOR,
NULL,
0,
NULL)) == NULL)
goto error;
if (virCapabilitiesAddGuestDomain(guest,
VIR_DOMAIN_VIRT_TEST,
NULL,
NULL,
0,
NULL) == NULL)
goto error;
capabilities: use bool instead of int While preparing to add a capability for active commit, I noticed that the existing code was abusing int for boolean values. * src/conf/capabilities.h (_virCapsGuestFeature, _virCapsHost) (virCapabilitiesNew, virCapabilitiesAddGuestFeature): Improve types. * src/conf/capabilities.c (virCapabilitiesNew) (virCapabilitiesAddGuestFeature): Adjust signature. * src/bhyve/bhyve_capabilities.c (virBhyveCapsBuild): Update clients. * src/esx/esx_driver.c (esxCapsInit): Likewise. * src/libxl/libxl_conf.c (libxlMakeCapabilities): Likewise. * src/lxc/lxc_conf.c (virLXCDriverCapsInit): Likewise. * src/openvz/openvz_conf.c (openvzCapsInit): Likewise. * src/parallels/parallels_driver.c (parallelsBuildCapabilities): Likewise. * src/phyp/phyp_driver.c (phypCapsInit): Likewise. * src/qemu/qemu_capabilities.c (virQEMUCapsInit) (virQEMUCapsInitGuestFromBinary): Likewise. * src/security/virt-aa-helper.c (get_definition): Likewise. * src/test/test_driver.c (testBuildCapabilities): Likewise. * src/uml/uml_conf.c (umlCapsInit): Likewise. * src/vbox/vbox_tmpl.c (vboxCapsInit): Likewise. * src/vmware/vmware_conf.c (vmwareCapsInit): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorBuildCapabilities): Likewise. * src/xenapi/xenapi_driver.c (getCapsObject): Likewise. * tests/qemucaps2xmltest.c (testGetCaps): Likewise. * tests/testutils.c (virTestGenericCapsInit): Likewise. * tests/testutilslxc.c (testLXCCapsInit): Likewise. * tests/testutilsqemu.c (testQemuCapsInit): Likewise. * tests/testutilsxen.c (testXenCapsInit): Likewise. * tests/vircaps2xmltest.c (buildVirCapabilities): Likewise. * tests/vircapstest.c (buildNUMATopology): Likewise. * tests/vmx2xmltest.c (testCapsInit): Likewise. * tests/xml2vmxtest.c (testCapsInit): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-07-14 12:56:13 +00:00
if (virCapabilitiesAddGuestFeature(guest, "pae", true, true) == NULL)
goto error;
capabilities: use bool instead of int While preparing to add a capability for active commit, I noticed that the existing code was abusing int for boolean values. * src/conf/capabilities.h (_virCapsGuestFeature, _virCapsHost) (virCapabilitiesNew, virCapabilitiesAddGuestFeature): Improve types. * src/conf/capabilities.c (virCapabilitiesNew) (virCapabilitiesAddGuestFeature): Adjust signature. * src/bhyve/bhyve_capabilities.c (virBhyveCapsBuild): Update clients. * src/esx/esx_driver.c (esxCapsInit): Likewise. * src/libxl/libxl_conf.c (libxlMakeCapabilities): Likewise. * src/lxc/lxc_conf.c (virLXCDriverCapsInit): Likewise. * src/openvz/openvz_conf.c (openvzCapsInit): Likewise. * src/parallels/parallels_driver.c (parallelsBuildCapabilities): Likewise. * src/phyp/phyp_driver.c (phypCapsInit): Likewise. * src/qemu/qemu_capabilities.c (virQEMUCapsInit) (virQEMUCapsInitGuestFromBinary): Likewise. * src/security/virt-aa-helper.c (get_definition): Likewise. * src/test/test_driver.c (testBuildCapabilities): Likewise. * src/uml/uml_conf.c (umlCapsInit): Likewise. * src/vbox/vbox_tmpl.c (vboxCapsInit): Likewise. * src/vmware/vmware_conf.c (vmwareCapsInit): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorBuildCapabilities): Likewise. * src/xenapi/xenapi_driver.c (getCapsObject): Likewise. * tests/qemucaps2xmltest.c (testGetCaps): Likewise. * tests/testutils.c (virTestGenericCapsInit): Likewise. * tests/testutilslxc.c (testLXCCapsInit): Likewise. * tests/testutilsqemu.c (testQemuCapsInit): Likewise. * tests/testutilsxen.c (testXenCapsInit): Likewise. * tests/vircaps2xmltest.c (buildVirCapabilities): Likewise. * tests/vircapstest.c (buildNUMATopology): Likewise. * tests/vmx2xmltest.c (testCapsInit): Likewise. * tests/xml2vmxtest.c (testCapsInit): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-07-14 12:56:13 +00:00
if (virCapabilitiesAddGuestFeature(guest, "nonpae", true, true) == NULL)
goto error;
}
caps->host.nsecModels = 1;
if (VIR_ALLOC_N(caps->host.secModels, caps->host.nsecModels) < 0)
goto error;
if (VIR_STRDUP(caps->host.secModels[0].model, "testSecurity") < 0)
goto error;
if (VIR_STRDUP(caps->host.secModels[0].doi, "") < 0)
goto error;
return caps;
error:
virObjectUnref(caps);
return NULL;
}
static testDriverPtr
testDriverNew(void)
{
virDomainXMLNamespace ns = {
.parse = testDomainDefNamespaceParse,
.free = testDomainDefNamespaceFree,
};
virDomainDefParserConfig config = {
.features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG |
VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN |
VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS |
VIR_DOMAIN_DEF_FEATURE_USER_ALIAS |
VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT |
VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING,
};
testDriverPtr ret;
if (testDriverInitialize() < 0)
return NULL;
if (!(ret = virObjectLockableNew(testDriverClass)))
return NULL;
if (!(ret->xmlopt = virDomainXMLOptionNew(&config, NULL, &ns, NULL, NULL)) ||
!(ret->eventState = virObjectEventStateNew()) ||
!(ret->ifaces = virInterfaceObjListNew()) ||
!(ret->domains = virDomainObjListNew()) ||
!(ret->networks = virNetworkObjListNew()) ||
!(ret->devs = virNodeDeviceObjListNew()) ||
!(ret->pools = virStoragePoolObjListNew()))
goto error;
virAtomicIntSet(&ret->nextDomID, 1);
return ret;
error:
virObjectUnref(ret);
return NULL;
}
static const char *defaultConnXML =
"<node>"
"<domain type='test'>"
" <name>test</name>"
" <uuid>6695eb01-f6a4-8304-79aa-97f2502e193f</uuid>"
" <memory>8388608</memory>"
" <currentMemory>2097152</currentMemory>"
" <vcpu>2</vcpu>"
" <os>"
" <type>hvm</type>"
" </os>"
" <devices>"
" <interface type='network'>"
" <mac address='aa:bb:cc:dd:ee:ff'/>"
" <source network='default' bridge='virbr0'/>"
" <address type='pci' domain='0x0000' bus='0x00' slot='0x1' function='0x0'/>"
" </interface>"
" </devices>"
"</domain>"
""
"<network>"
" <name>default</name>"
" <uuid>dd8fe884-6c02-601e-7551-cca97df1c5df</uuid>"
" <bridge name='virbr0'/>"
" <forward/>"
" <ip address='192.168.122.1' netmask='255.255.255.0'>"
" <dhcp>"
" <range start='192.168.122.2' end='192.168.122.254'/>"
" </dhcp>"
" </ip>"
"</network>"
""
"<interface type=\"ethernet\" name=\"eth1\">"
" <start mode=\"onboot\"/>"
" <mac address=\"aa:bb:cc:dd:ee:ff\"/>"
" <mtu size=\"1492\"/>"
" <protocol family=\"ipv4\">"
" <ip address=\"192.168.0.5\" prefix=\"24\"/>"
" <route gateway=\"192.168.0.1\"/>"
" </protocol>"
"</interface>"
""
2008-10-30 17:40:57 +00:00
"<pool type='dir'>"
" <name>default-pool</name>"
" <uuid>dfe224cb-28fb-8dd0-c4b2-64eb3f0f4566</uuid>"
2008-10-30 17:40:57 +00:00
" <target>"
" <path>/default-pool</path>"
" </target>"
"</pool>"
""
"<device>"
" <name>computer</name>"
" <capability type='system'>"
" <hardware>"
" <vendor>Libvirt</vendor>"
" <version>Test driver</version>"
" <serial>123456</serial>"
" <uuid>11111111-2222-3333-4444-555555555555</uuid>"
" </hardware>"
" <firmware>"
" <vendor>Libvirt</vendor>"
" <version>Test Driver</version>"
" <release_date>01/22/2007</release_date>"
" </firmware>"
" </capability>"
"</device>"
"<device>"
" <name>scsi_host1</name>"
" <parent>computer</parent>"
" <capability type='scsi_host'>"
" <host>1</host>"
" <unique_id>0</unique_id>"
" <capability type='fc_host'>"
" <wwnn>2000000012341234</wwnn>"
" <wwpn>1000000012341234</wwpn>"
" <fabric_wwn>2000000043214321</fabric_wwn>"
" </capability>"
" <capability type='vport_ops'>"
" <max_vports>127</max_vports>"
" <vports>1</vports>"
" </capability>"
" </capability>"
"</device>"
"<device>"
" <name>scsi_host2</name>"
" <parent>computer</parent>"
" <capability type='scsi_host'>"
" <host>2</host>"
" <unique_id>1</unique_id>"
" <capability type='fc_host'>"
" <wwnn>2000000056785678</wwnn>"
" <wwpn>1000000056785678</wwpn>"
" <fabric_wwn>2000000087658765</fabric_wwn>"
" </capability>"
" <capability type='vport_ops'>"
" <max_vports>127</max_vports>"
" <vports>0</vports>"
" </capability>"
" </capability>"
"</device>"
"<device>"
" <name>scsi_host11</name>"
" <parent>scsi_host1</parent>"
" <capability type='scsi_host'>"
" <host>11</host>"
" <unique_id>10</unique_id>"
" <capability type='fc_host'>"
" <wwnn>2000000034563456</wwnn>"
" <wwpn>1000000034563456</wwpn>"
" <fabric_wwn>2000000043214321</fabric_wwn>"
" </capability>"
" </capability>"
"</device>"
"</node>";
2008-10-30 17:40:57 +00:00
static const char *defaultPoolSourcesLogicalXML =
"<sources>\n"
" <source>\n"
" <device path='/dev/sda20'/>\n"
" <name>testvg1</name>\n"
" <format type='lvm2'/>\n"
" </source>\n"
" <source>\n"
" <device path='/dev/sda21'/>\n"
" <name>testvg2</name>\n"
" <format type='lvm2'/>\n"
" </source>\n"
"</sources>\n";
static const char *defaultPoolSourcesNetFSXML =
"<sources>\n"
" <source>\n"
" <host name='%s'/>\n"
" <dir path='/testshare'/>\n"
" <format type='nfs'/>\n"
" </source>\n"
"</sources>\n";
2008-12-07 20:45:28 +00:00
static const unsigned long long defaultPoolCap = (100 * 1024 * 1024 * 1024ull);
static const unsigned long long defaultPoolAlloc;
2008-10-30 17:40:57 +00:00
static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr obj);
static int testNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info);
static virDomainObjPtr
testDomObjFromDomain(virDomainPtr domain)
{
virDomainObjPtr vm;
2015-06-22 14:25:16 +00:00
testDriverPtr driver = domain->conn->privateData;
char uuidstr[VIR_UUID_STRING_BUFLEN];
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);
}
return vm;
}
static char *
testDomainGenerateIfname(virDomainDefPtr domdef)
{
int maxif = 1024;
int ifctr;
for (ifctr = 0; ifctr < maxif; ++ifctr) {
virDomainNetDefPtr net = NULL;
char *ifname;
if (virAsprintf(&ifname, "testnet%d", ifctr) < 0)
return NULL;
/* Generate network interface names */
if (!(net = virDomainNetFindByName(domdef, ifname)))
return ifname;
VIR_FREE(ifname);
}
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Exceeded max iface limit %d"), maxif);
return NULL;
}
static int
testDomainGenerateIfnames(virDomainDefPtr domdef)
{
size_t i = 0;
for (i = 0; i < domdef->nnets; i++) {
char *ifname;
if (domdef->nets[i]->ifname)
continue;
ifname = testDomainGenerateIfname(domdef);
if (!ifname)
return -1;
domdef->nets[i]->ifname = ifname;
}
return 0;
}
static void
testDomainShutdownState(virDomainPtr domain,
virDomainObjPtr privdom,
virDomainShutoffReason reason)
{
virDomainObjRemoveTransientDef(privdom);
virDomainObjSetState(privdom, VIR_DOMAIN_SHUTOFF, reason);
if (domain)
domain->id = -1;
}
/* Set up domain runtime state */
static int
2015-06-22 14:25:16 +00:00
testDomainStartState(testDriverPtr privconn,
virDomainObjPtr dom,
virDomainRunningReason reason)
{
int ret = -1;
virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, reason);
dom->def->id = virAtomicIntAdd(&privconn->nextDomID, 1);
if (virDomainObjSetDefTransient(privconn->caps,
privconn->xmlopt,
dom) < 0) {
goto cleanup;
}
dom->hasManagedSave = false;
ret = 0;
cleanup:
if (ret < 0)
testDomainShutdownState(NULL, dom, VIR_DOMAIN_SHUTOFF_FAILED);
return ret;
}
static char *testBuildFilename(const char *relativeTo,
const char *filename)
{
char *offset;
int baseLen;
char *ret;
if (!filename || filename[0] == '\0')
return NULL;
if (filename[0] == '/') {
ignore_value(VIR_STRDUP(ret, filename));
return ret;
}
offset = strrchr(relativeTo, '/');
if ((baseLen = (offset-relativeTo+1))) {
char *absFile;
int totalLen = baseLen + strlen(filename) + 1;
if (VIR_ALLOC_N(absFile, totalLen) < 0)
return NULL;
if (virStrncpy(absFile, relativeTo, baseLen, totalLen) < 0) {
VIR_FREE(absFile);
return NULL;
}
strcat(absFile, filename);
return absFile;
} else {
ignore_value(VIR_STRDUP(ret, filename));
return ret;
}
}
static xmlNodePtr
testParseXMLDocFromFile(xmlNodePtr node, const char *file, const char *type)
{
xmlNodePtr ret = NULL;
xmlDocPtr doc = NULL;
char *absFile = NULL;
VIR_AUTOFREE(char *) relFile = NULL;
if ((relFile = virXMLPropString(node, "file"))) {
absFile = testBuildFilename(file, 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);
return ret;
}
static int
testParseNodeInfo(virNodeInfoPtr nodeInfo, xmlXPathContextPtr ctxt)
{
long l;
int ret;
VIR_AUTOFREE(char *) str = NULL;
ret = virXPathLong("string(/node/cpu/nodes[1])", ctxt, &l);
if (ret == 0) {
nodeInfo->nodes = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node cpu nodes value"));
goto error;
}
ret = virXPathLong("string(/node/cpu/sockets[1])", ctxt, &l);
if (ret == 0) {
nodeInfo->sockets = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node cpu sockets value"));
goto error;
}
ret = virXPathLong("string(/node/cpu/cores[1])", ctxt, &l);
if (ret == 0) {
nodeInfo->cores = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node cpu cores value"));
goto error;
}
ret = virXPathLong("string(/node/cpu/threads[1])", ctxt, &l);
if (ret == 0) {
nodeInfo->threads = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node cpu threads value"));
goto error;
}
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)
nodeInfo->cpus = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node cpu active value"));
goto error;
}
ret = virXPathLong("string(/node/cpu/mhz[1])", ctxt, &l);
if (ret == 0) {
nodeInfo->mhz = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node cpu mhz value"));
goto error;
}
str = virXPathString("string(/node/cpu/model[1])", ctxt);
if (str != NULL) {
if (virStrcpyStatic(nodeInfo->model, str) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Model %s too big for destination"), str);
goto error;
}
}
ret = virXPathLong("string(/node/memory[1])", ctxt, &l);
if (ret == 0) {
nodeInfo->memory = l;
} else if (ret == -2) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("invalid node memory value"));
goto error;
}
return 0;
error:
return -1;
}
static int
2015-06-22 14:25:16 +00:00
testParseDomainSnapshots(testDriverPtr privconn,
virDomainObjPtr domobj,
const char *file,
xmlXPathContextPtr ctxt)
{
size_t i;
int ret = -1;
testDomainNamespaceDefPtr nsdata = domobj->def->namespaceData;
xmlNodePtr *nodes = nsdata->snap_nodes;
snapshot: Drop virDomainSnapshotDef.current The only use for the 'current' member of virDomainSnapshotDef was with the PARSE/FORMAT_INTERNAL flag for controlling an internal-use <active> element marking whether a particular snapshot definition was current, and even then, only by the qemu driver on output, and by qemu and test driver on input. But this duplicates vm->snapshot_current, and gets in the way of potential simplifications to have qemu store a single file for all snapshots rather than one file per snapshot. Get rid of the member by adding a bool* parameter during parse (ignored if the PARSE_INTERNAL flag is not set), and by adding a new flag during format (if FORMAT_INTERNAL is set, the value printed in <active> depends on the new FORMAT_CURRENT). Then update the qemu driver accordingly, which involves hoisting assignments to vm->current_snapshot to occur prior to any point where a snapshot XML file is written (although qemu kept vm->current_snapshot and snapshot->def_current in sync by the end of the function, they were not always identical in the middle of functions, so the shuffling gets a bit interesting). Later patches will clean up some of that confusing churn to vm->current_snapshot. Note: even if later patches refactor qemu to no longer use FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we will always need PARSE_INTERNAL for input (because on upgrade, a new libvirt still has to parse XML left from a previous libvirt). Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-19 03:56:19 +00:00
bool cur;
for (i = 0; i < nsdata->num_snap_nodes; i++) {
virDomainMomentObjPtr snap;
virDomainSnapshotDefPtr def;
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
"domainsnapshot");
if (!node)
goto error;
def = virDomainSnapshotDefParseNode(ctxt->doc, node,
privconn->caps,
privconn->xmlopt,
snapshot: Drop virDomainSnapshotDef.current The only use for the 'current' member of virDomainSnapshotDef was with the PARSE/FORMAT_INTERNAL flag for controlling an internal-use <active> element marking whether a particular snapshot definition was current, and even then, only by the qemu driver on output, and by qemu and test driver on input. But this duplicates vm->snapshot_current, and gets in the way of potential simplifications to have qemu store a single file for all snapshots rather than one file per snapshot. Get rid of the member by adding a bool* parameter during parse (ignored if the PARSE_INTERNAL flag is not set), and by adding a new flag during format (if FORMAT_INTERNAL is set, the value printed in <active> depends on the new FORMAT_CURRENT). Then update the qemu driver accordingly, which involves hoisting assignments to vm->current_snapshot to occur prior to any point where a snapshot XML file is written (although qemu kept vm->current_snapshot and snapshot->def_current in sync by the end of the function, they were not always identical in the middle of functions, so the shuffling gets a bit interesting). Later patches will clean up some of that confusing churn to vm->current_snapshot. Note: even if later patches refactor qemu to no longer use FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we will always need PARSE_INTERNAL for input (because on upgrade, a new libvirt still has to parse XML left from a previous libvirt). Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-19 03:56:19 +00:00
&cur,
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))) {
virObjectUnref(def);
goto error;
}
snapshot: Drop virDomainSnapshotDef.current The only use for the 'current' member of virDomainSnapshotDef was with the PARSE/FORMAT_INTERNAL flag for controlling an internal-use <active> element marking whether a particular snapshot definition was current, and even then, only by the qemu driver on output, and by qemu and test driver on input. But this duplicates vm->snapshot_current, and gets in the way of potential simplifications to have qemu store a single file for all snapshots rather than one file per snapshot. Get rid of the member by adding a bool* parameter during parse (ignored if the PARSE_INTERNAL flag is not set), and by adding a new flag during format (if FORMAT_INTERNAL is set, the value printed in <active> depends on the new FORMAT_CURRENT). Then update the qemu driver accordingly, which involves hoisting assignments to vm->current_snapshot to occur prior to any point where a snapshot XML file is written (although qemu kept vm->current_snapshot and snapshot->def_current in sync by the end of the function, they were not always identical in the middle of functions, so the shuffling gets a bit interesting). Later patches will clean up some of that confusing churn to vm->current_snapshot. Note: even if later patches refactor qemu to no longer use FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we will always need PARSE_INTERNAL for input (because on upgrade, a new libvirt still has to parse XML left from a previous libvirt). Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-19 03:56:19 +00:00
if (cur) {
if (virDomainSnapshotGetCurrent(domobj->snapshots)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("more than one snapshot claims to be active"));
goto error;
}
virDomainSnapshotSetCurrent(domobj->snapshots, snap);
}
}
if (virDomainSnapshotUpdateRelations(domobj->snapshots) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Snapshots have inconsistent relations for "
"domain %s"), domobj->def->name);
goto error;
}
ret = 0;
error:
return ret;
}
static int
2015-06-22 14:25:16 +00:00
testParseDomains(testDriverPtr privconn,
const char *file,
xmlXPathContextPtr ctxt)
{
int num, ret = -1;
size_t i;
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjPtr obj = NULL;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
num = virXPathNodeSet("/node/domain", ctxt, &nodes);
if (num < 0)
return -1;
for (i = 0; i < num; i++) {
virDomainDefPtr def;
testDomainNamespaceDefPtr nsdata;
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "domain");
if (!node)
goto error;
def = virDomainDefParseNode(ctxt->doc, node,
privconn->caps, privconn->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (!def)
goto error;
if (testDomainGenerateIfnames(def) < 0 ||
!(obj = virDomainObjListAdd(privconn->domains,
def,
privconn->xmlopt,
0, NULL))) {
virDomainDefFree(def);
goto error;
}
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (testParseDomainSnapshots(privconn, obj, file, ctxt) < 0)
goto error;
nsdata = def->namespaceData;
obj->persistent = !nsdata->transient;
obj->hasManagedSave = nsdata->hasManagedSave;
if (nsdata->runstate != VIR_DOMAIN_SHUTOFF) {
if (testDomainStartState(privconn, obj,
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
VIR_DOMAIN_RUNNING_BOOTED) < 0)
goto error;
} else {
testDomainShutdownState(NULL, obj, 0);
}
virDomainObjSetState(obj, nsdata->runstate, 0);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&obj);
}
ret = 0;
error:
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&obj);
return ret;
}
static int
2015-06-22 14:25:16 +00:00
testParseNetworks(testDriverPtr privconn,
const char *file,
xmlXPathContextPtr ctxt)
{
int num;
size_t i;
virNetworkObjPtr obj;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
num = virXPathNodeSet("/node/network", ctxt, &nodes);
if (num < 0)
return -1;
for (i = 0; i < num; i++) {
virNetworkDefPtr def;
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "network");
if (!node)
return -1;
def = virNetworkDefParseNode(ctxt->doc, node);
if (!def)
return -1;
if (!(obj = virNetworkObjAssignDef(privconn->networks, def, 0))) {
virNetworkDefFree(def);
return -1;
}
virNetworkObjSetActive(obj, true);
virNetworkObjEndAPI(&obj);
}
return 0;
}
static int
2015-06-22 14:25:16 +00:00
testParseInterfaces(testDriverPtr privconn,
const char *file,
xmlXPathContextPtr ctxt)
{
int num;
size_t i;
virInterfaceObjPtr obj;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
num = virXPathNodeSet("/node/interface", ctxt, &nodes);
if (num < 0)
return -1;
for (i = 0; i < num; i++) {
virInterfaceDefPtr def;
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
"interface");
if (!node)
return -1;
def = virInterfaceDefParseNode(ctxt->doc, node);
if (!def)
return -1;
if (!(obj = virInterfaceObjListAssignDef(privconn->ifaces, def))) {
virInterfaceDefFree(def);
return -1;
}
virInterfaceObjSetActive(obj, true);
virInterfaceObjEndAPI(&obj);
}
return 0;
}
static int
testOpenVolumesForPool(const char *file,
xmlXPathContextPtr ctxt,
virStoragePoolObjPtr obj,
int objidx)
{
virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
size_t i;
int num;
VIR_AUTOFREE(char *) vol_xpath = NULL;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
VIR_AUTOPTR(virStorageVolDef) volDef = NULL;
/* Find storage volumes */
if (virAsprintf(&vol_xpath, "/node/pool[%d]/volume", objidx) < 0)
return -1;
num = virXPathNodeSet(vol_xpath, ctxt, &nodes);
if (num < 0)
return -1;
for (i = 0; i < num; i++) {
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
"volume");
if (!node)
return -1;
if (!(volDef = virStorageVolDefParseNode(def, ctxt->doc, node, 0)))
return -1;
if (!volDef->target.path) {
if (virAsprintf(&volDef->target.path, "%s/%s",
def->target.path, volDef->name) < 0)
return -1;
}
if (!volDef->key && VIR_STRDUP(volDef->key, volDef->target.path) < 0)
return -1;
if (virStoragePoolObjAddVol(obj, volDef) < 0)
return -1;
def->allocation += volDef->target.allocation;
def->available = (def->capacity - def->allocation);
volDef = NULL;
}
return 0;
}
static int
2015-06-22 14:25:16 +00:00
testParseStorage(testDriverPtr privconn,
const char *file,
xmlXPathContextPtr ctxt)
{
int num;
size_t i;
virStoragePoolObjPtr obj;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
num = virXPathNodeSet("/node/pool", ctxt, &nodes);
if (num < 0)
return -1;
for (i = 0; i < num; i++) {
2008-10-30 17:40:57 +00:00
virStoragePoolDefPtr def;
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
"pool");
if (!node)
return -1;
2008-10-30 17:40:57 +00:00
def = virStoragePoolDefParseNode(ctxt->doc, node);
if (!def)
return -1;
2008-10-30 17:40:57 +00:00
if (!(obj = virStoragePoolObjAssignDef(privconn->pools, def, false))) {
2008-10-30 17:40:57 +00:00
virStoragePoolDefFree(def);
return -1;
2008-10-30 17:40:57 +00:00
}
if (testStoragePoolObjSetDefaults(obj) == -1) {
virStoragePoolObjEndAPI(&obj);
return -1;
}
virStoragePoolObjSetActive(obj, true);
/* Find storage volumes */
if (testOpenVolumesForPool(file, ctxt, obj, i+1) < 0) {
virStoragePoolObjEndAPI(&obj);
return -1;
}
virStoragePoolObjEndAPI(&obj);
2008-10-30 17:40:57 +00:00
}
return 0;
}
static int
2015-06-22 14:25:16 +00:00
testParseNodedevs(testDriverPtr privconn,
const char *file,
xmlXPathContextPtr ctxt)
{
int num;
size_t i;
virNodeDeviceObjPtr obj;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
num = virXPathNodeSet("/node/device", ctxt, &nodes);
if (num < 0)
return -1;
for (i = 0; i < num; i++) {
virNodeDeviceDefPtr def;
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
"nodedev");
if (!node)
return -1;
def = virNodeDeviceDefParseNode(ctxt->doc, node, 0, NULL);
if (!def)
return -1;
if (!(obj = virNodeDeviceObjListAssignDef(privconn->devs, def))) {
virNodeDeviceDefFree(def);
return -1;
}
virNodeDeviceObjSetSkipUpdateCaps(obj, true);
virNodeDeviceObjEndAPI(&obj);
}
return 0;
}
static int
2015-06-22 14:25:16 +00:00
testParseAuthUsers(testDriverPtr privconn,
xmlXPathContextPtr ctxt)
{
int num;
size_t i;
VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
num = virXPathNodeSet("/node/auth/user", ctxt, &nodes);
if (num < 0)
return -1;
privconn->numAuths = num;
if (num && VIR_ALLOC_N(privconn->auths, num) < 0)
return -1;
for (i = 0; i < num; i++) {
VIR_AUTOFREE(char *) username = NULL;
ctxt->node = nodes[i];
username = virXPathString("string(.)", ctxt);
if (!username || STREQ(username, "")) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing username in /node/auth/user field"));
return -1;
}
/* This field is optional. */
privconn->auths[i].password = virXMLPropString(nodes[i], "password");
VIR_STEAL_PTR(privconn->auths[i].username, username);
}
return 0;
}
2016-06-27 21:03:07 +00:00
static int
testOpenParse(testDriverPtr privconn,
const char *file,
xmlXPathContextPtr ctxt)
{
if (!virXMLNodeNameEqual(ctxt->node, "node")) {
2016-06-27 21:03:07 +00:00
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Root element is not 'node'"));
goto error;
}
if (testParseNodeInfo(&privconn->nodeInfo, ctxt) < 0)
goto error;
if (testParseDomains(privconn, file, ctxt) < 0)
goto error;
if (testParseNetworks(privconn, file, ctxt) < 0)
goto error;
if (testParseInterfaces(privconn, file, ctxt) < 0)
goto error;
if (testParseStorage(privconn, file, ctxt) < 0)
goto error;
if (testParseNodedevs(privconn, file, ctxt) < 0)
goto error;
if (testParseAuthUsers(privconn, ctxt) < 0)
goto error;
return 0;
error:
return -1;
}
/* No shared state between simultaneous test connections initialized
* from a file. */
static int
testOpenFromFile(virConnectPtr conn, const char *file)
{
xmlDocPtr doc = NULL;
xmlXPathContextPtr ctxt = NULL;
2015-06-22 14:25:16 +00:00
testDriverPtr privconn;
if (!(privconn = testDriverNew()))
return VIR_DRV_OPEN_ERROR;
virObjectLock(privconn);
conn->privateData = privconn;
if (!(privconn->caps = testBuildCapabilities(conn)))
goto error;
if (!(doc = virXMLParseFileCtxt(file, &ctxt)))
goto error;
privconn->numCells = 0;
memmove(&privconn->nodeInfo, &defaultNodeInfo, sizeof(defaultNodeInfo));
2016-06-27 21:03:07 +00:00
if (testOpenParse(privconn, file, ctxt) < 0)
goto error;
xmlXPathFreeContext(ctxt);
xmlFreeDoc(doc);
virObjectUnlock(privconn);
return VIR_DRV_OPEN_SUCCESS;
error:
xmlXPathFreeContext(ctxt);
xmlFreeDoc(doc);
virObjectUnref(privconn);
conn->privateData = NULL;
return VIR_DRV_OPEN_ERROR;
}
/* Simultaneous test:///default connections should share the same
* common state (among other things, this allows testing event
* detection in one connection for an action caused in another). */
static int
testOpenDefault(virConnectPtr conn)
{
int ret = VIR_DRV_OPEN_ERROR;
testDriverPtr privconn = NULL;
xmlDocPtr doc = NULL;
xmlXPathContextPtr ctxt = NULL;
size_t i;
virMutexLock(&defaultLock);
if (defaultPrivconn) {
conn->privateData = virObjectRef(defaultPrivconn);
virMutexUnlock(&defaultLock);
return VIR_DRV_OPEN_SUCCESS;
}
if (!(privconn = testDriverNew()))
goto error;
conn->privateData = privconn;
memmove(&privconn->nodeInfo, &defaultNodeInfo, sizeof(defaultNodeInfo));
/* Numa setup */
privconn->numCells = 2;
for (i = 0; i < privconn->numCells; i++) {
privconn->cells[i].numCpus = 8;
privconn->cells[i].mem = (i + 1) * 2048 * 1024;
privconn->cells[i].freeMem = (i + 1) * 1024 * 1024;
}
for (i = 0; i < 16; i++) {
virBitmapPtr siblings = virBitmapNew(16);
if (!siblings)
goto error;
ignore_value(virBitmapSetBit(siblings, i));
privconn->cells[i / 8].cpus[(i % 8)].id = i;
privconn->cells[i / 8].cpus[(i % 8)].socket_id = i / 8;
privconn->cells[i / 8].cpus[(i % 8)].core_id = i % 8;
privconn->cells[i / 8].cpus[(i % 8)].siblings = siblings;
}
if (!(privconn->caps = testBuildCapabilities(conn)))
goto error;
if (!(doc = virXMLParseStringCtxt(defaultConnXML,
_("(test driver)"), &ctxt)))
goto error;
if (testOpenParse(privconn, NULL, ctxt) < 0)
goto error;
defaultPrivconn = privconn;
ret = VIR_DRV_OPEN_SUCCESS;
cleanup:
virMutexUnlock(&defaultLock);
xmlXPathFreeContext(ctxt);
xmlFreeDoc(doc);
return ret;
error:
virObjectUnref(privconn);
conn->privateData = NULL;
goto cleanup;
}
static int
testConnectAuthenticate(virConnectPtr conn,
virConnectAuthPtr auth)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ret = -1;
ssize_t i;
VIR_AUTOFREE(char *) username = NULL;
VIR_AUTOFREE(char *) password = NULL;
virObjectLock(privconn);
if (privconn->numAuths == 0) {
virObjectUnlock(privconn);
return 0;
}
/* Authentication is required because the test XML contains a
* non-empty <auth/> section. First we must ask for a username.
*/
if (!(username = virAuthGetUsername(conn, auth, "test", NULL,
"localhost"/*?*/)))
goto cleanup;
/* Does the username exist? */
for (i = 0; i < privconn->numAuths; ++i) {
if (STREQ(privconn->auths[i].username, username))
goto found_user;
}
i = -1;
found_user:
/* Even if we didn't find the user, we still ask for a password. */
if (i == -1 || privconn->auths[i].password != NULL) {
if (!(password = virAuthGetPassword(conn, auth, "test", username,
"localhost")))
goto cleanup;
}
if (i == -1 ||
(password && STRNEQ(privconn->auths[i].password, password))) {
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed, see test XML for the correct username/password"));
goto cleanup;
}
ret = 0;
cleanup:
virObjectUnlock(privconn);
return ret;
}
static void
testDriverCloseInternal(testDriverPtr driver)
{
virMutexLock(&defaultLock);
bool disposed = !virObjectUnref(driver);
if (disposed && driver == defaultPrivconn)
defaultPrivconn = NULL;
virMutexUnlock(&defaultLock);
}
static virDrvOpenStatus
testConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth,
virConfPtr conf ATTRIBUTE_UNUSED,
unsigned int flags)
{
int ret;
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (conn->uri->path[0] == '\0' ||
(conn->uri->path[0] == '/' && conn->uri->path[1] == '\0')) {
virReportError(VIR_ERR_INVALID_ARG,
"%s", _("testOpen: supply a path or use test:///default"));
return VIR_DRV_OPEN_ERROR;
}
if (STREQ(conn->uri->path, "/default"))
ret = testOpenDefault(conn);
else
ret = testOpenFromFile(conn,
conn->uri->path);
if (ret != VIR_DRV_OPEN_SUCCESS)
return ret;
/* Fake authentication. */
if (testConnectAuthenticate(conn, auth) < 0) {
testDriverCloseInternal(conn->privateData);
conn->privateData = NULL;
return VIR_DRV_OPEN_ERROR;
}
return VIR_DRV_OPEN_SUCCESS;
}
static int
testConnectClose(virConnectPtr conn)
{
testDriverCloseInternal(conn->privateData);
conn->privateData = NULL;
return 0;
}
static int testConnectGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned long *hvVer)
{
*hvVer = 2;
return 0;
}
static char *testConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return virGetHostname();
}
static int testConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 1;
}
static int testConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 0;
}
static int testConnectIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 1;
}
static int testConnectGetMaxVcpus(virConnectPtr conn ATTRIBUTE_UNUSED,
const char *type ATTRIBUTE_UNUSED)
{
return 32;
}
static char *
testConnectBaselineCPU(virConnectPtr conn ATTRIBUTE_UNUSED,
const char **xmlCPUs,
unsigned int ncpus,
unsigned int flags)
{
virCPUDefPtr *cpus = NULL;
virCPUDefPtr cpu = NULL;
char *cpustr = NULL;
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, NULL);
if (!(cpus = virCPUDefListParse(xmlCPUs, ncpus, VIR_CPU_TYPE_HOST)))
goto cleanup;
if (!(cpu = virCPUBaseline(VIR_ARCH_NONE, cpus, ncpus, NULL, NULL, false)))
goto cleanup;
if ((flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) &&
virCPUExpandFeatures(cpus[0]->arch, cpu) < 0)
goto cleanup;
cpustr = virCPUDefFormat(cpu, NULL);
cleanup:
virCPUDefListFree(cpus);
virCPUDefFree(cpu);
return cpustr;
}
static int testNodeGetInfo(virConnectPtr conn,
virNodeInfoPtr info)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virObjectLock(privconn);
memcpy(info, &privconn->nodeInfo, sizeof(virNodeInfo));
virObjectUnlock(privconn);
return 0;
}
static char *testConnectGetCapabilities(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
char *xml;
virObjectLock(privconn);
xml = virCapabilitiesFormatXML(privconn->caps);
virObjectUnlock(privconn);
return xml;
}
static char *
testConnectGetSysinfo(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned int flags)
{
char *ret;
const char *sysinfo = "<sysinfo type='smbios'>\n"
" <bios>\n"
" <entry name='vendor'>LENOVO</entry>\n"
" <entry name='version'>G4ETA1WW (2.61 )</entry>\n"
" <entry name='date'>05/07/2014</entry>\n"
" <entry name='release'>2.61</entry>\n"
" </bios>\n"
"</sysinfo>\n";
virCheckFlags(0, NULL);
ignore_value(VIR_STRDUP(ret, sysinfo));
return ret;
}
static const char *
testConnectGetType(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return "TEST";
}
static int testConnectNumOfDomains(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
Convert virDomainObjListPtr to use a hash of domain objects The current virDomainObjListPtr object stores domain objects in an array. This means that to find a particular objects requires O(n) time, and more critically acquiring O(n) mutex locks. The new impl replaces the array with a virHashTable, keyed off UUID. Finding a object based on UUID is now O(1) time, and only requires a single mutex lock. Finding by name/id is unchanged in complexity. In changing this, all code which iterates over the array had to be updated to use a hash table iterator function callback. Several of the functions which were identically duplicating across all drivers were pulled into domain_conf.c * src/conf/domain_conf.h, src/conf/domain_conf.c: Change virDomainObjListPtr to use virHashTable. Add a initializer method virDomainObjListInit, and rename virDomainObjListFree to virDomainObjListDeinit, since its not actually freeing the container, only its contents. Also add some convenient methods virDomainObjListGetInactiveNames, virDomainObjListGetActiveIDs and virDomainObjListNumOfDomains which can be used to implement the correspondingly named public API entry points in drivers * src/libvirt_private.syms: Export new methods from domain_conf.h * src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_conf.c, src/openvz/openvz_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/uml/uml_driver.c, src/vbox/vbox_tmpl.c: Update all code to deal with hash tables instead of arrays for domains
2009-10-09 11:33:51 +00:00
int count;
virObjectLock(privconn);
count = virDomainObjListNumOfDomains(privconn->domains, true, NULL, NULL);
virObjectUnlock(privconn);
Convert virDomainObjListPtr to use a hash of domain objects The current virDomainObjListPtr object stores domain objects in an array. This means that to find a particular objects requires O(n) time, and more critically acquiring O(n) mutex locks. The new impl replaces the array with a virHashTable, keyed off UUID. Finding a object based on UUID is now O(1) time, and only requires a single mutex lock. Finding by name/id is unchanged in complexity. In changing this, all code which iterates over the array had to be updated to use a hash table iterator function callback. Several of the functions which were identically duplicating across all drivers were pulled into domain_conf.c * src/conf/domain_conf.h, src/conf/domain_conf.c: Change virDomainObjListPtr to use virHashTable. Add a initializer method virDomainObjListInit, and rename virDomainObjListFree to virDomainObjListDeinit, since its not actually freeing the container, only its contents. Also add some convenient methods virDomainObjListGetInactiveNames, virDomainObjListGetActiveIDs and virDomainObjListNumOfDomains which can be used to implement the correspondingly named public API entry points in drivers * src/libvirt_private.syms: Export new methods from domain_conf.h * src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_conf.c, src/openvz/openvz_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/uml/uml_driver.c, src/vbox/vbox_tmpl.c: Update all code to deal with hash tables instead of arrays for domains
2009-10-09 11:33:51 +00:00
return count;
}
static int testDomainIsActive(virDomainPtr dom)
{
virDomainObjPtr obj;
int ret;
if (!(obj = testDomObjFromDomain(dom)))
return -1;
ret = virDomainObjIsActive(obj);
virDomainObjEndAPI(&obj);
return ret;
}
static int testDomainIsPersistent(virDomainPtr dom)
{
virDomainObjPtr obj;
int ret;
if (!(obj = testDomObjFromDomain(dom)))
return -1;
ret = obj->persistent;
virDomainObjEndAPI(&obj);
return ret;
}
static int testDomainIsUpdated(virDomainPtr dom ATTRIBUTE_UNUSED)
{
return 0;
}
static virDomainPtr
testDomainCreateXML(virConnectPtr conn, const char *xml,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virDomainPtr ret = NULL;
virDomainDefPtr def;
virDomainObjPtr dom = NULL;
virObjectEventPtr event = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL);
if (flags & VIR_DOMAIN_START_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
virObjectLock(privconn);
if ((def = virDomainDefParseString(xml, privconn->caps, privconn->xmlopt,
NULL, parse_flags)) == NULL)
goto cleanup;
if (testDomainGenerateIfnames(def) < 0)
goto cleanup;
if (!(dom = virDomainObjListAdd(privconn->domains,
def,
privconn->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
NULL)))
goto cleanup;
def = NULL;
if (testDomainStartState(privconn, dom, VIR_DOMAIN_RUNNING_BOOTED) < 0) {
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!dom->persistent)
virDomainObjListRemove(privconn->domains, dom);
goto cleanup;
}
event = virDomainEventLifecycleNewFromObj(dom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_BOOTED);
ret = virGetDomain(conn, dom->def->name, dom->def->uuid, dom->def->id);
cleanup:
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&dom);
virObjectEventStateQueue(privconn->eventState, event);
virDomainDefFree(def);
virObjectUnlock(privconn);
return ret;
}
static virDomainPtr testDomainLookupByID(virConnectPtr conn,
int id)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virDomainPtr ret = NULL;
virDomainObjPtr dom;
if (!(dom = virDomainObjListFindByID(privconn->domains, id))) {
virReportError(VIR_ERR_NO_DOMAIN, NULL);
return NULL;
}
ret = virGetDomain(conn, dom->def->name, dom->def->uuid, dom->def->id);
virDomainObjEndAPI(&dom);
return ret;
}
static virDomainPtr testDomainLookupByUUID(virConnectPtr conn,
const unsigned char *uuid)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virDomainPtr ret = NULL;
virDomainObjPtr dom;
if (!(dom = virDomainObjListFindByUUID(privconn->domains, uuid))) {
virReportError(VIR_ERR_NO_DOMAIN, NULL);
return NULL;
}
ret = virGetDomain(conn, dom->def->name, dom->def->uuid, dom->def->id);
virDomainObjEndAPI(&dom);
return ret;
}
static virDomainPtr testDomainLookupByName(virConnectPtr conn,
const char *name)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virDomainPtr ret = NULL;
virDomainObjPtr dom;
if (!(dom = virDomainObjListFindByName(privconn->domains, name))) {
virReportError(VIR_ERR_NO_DOMAIN, NULL);
goto cleanup;
}
ret = virGetDomain(conn, dom->def->name, dom->def->uuid, dom->def->id);
cleanup:
virDomainObjEndAPI(&dom);
return ret;
}
static int testConnectListDomains(virConnectPtr conn,
int *ids,
int maxids)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
return virDomainObjListGetActiveIDs(privconn->domains, ids, maxids,
NULL, NULL);
}
static int testDomainDestroyFlags(virDomainPtr domain,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(VIR_DOMAIN_DESTROY_GRACEFUL, -1);
if (!(privdom = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjCheckActive(privdom) < 0)
goto cleanup;
testDomainShutdownState(domain, privdom, VIR_DOMAIN_SHUTOFF_DESTROYED);
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!privdom->persistent)
virDomainObjListRemove(privconn->domains, privdom);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int testDomainDestroy(virDomainPtr domain)
{
return testDomainDestroyFlags(domain, 0);
}
static int testDomainResume(virDomainPtr domain)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
if (virDomainObjGetState(privdom, NULL) != VIR_DOMAIN_PAUSED) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain '%s' not paused"),
domain->name);
goto cleanup;
}
virDomainObjSetState(privdom, VIR_DOMAIN_RUNNING,
VIR_DOMAIN_RUNNING_UNPAUSED);
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_RESUMED,
VIR_DOMAIN_EVENT_RESUMED_UNPAUSED);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int testDomainSuspend(virDomainPtr domain)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
int state;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
state = virDomainObjGetState(privdom, NULL);
if (state == VIR_DOMAIN_SHUTOFF || state == VIR_DOMAIN_PAUSED) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain '%s' not running"),
domain->name);
goto cleanup;
}
virDomainObjSetState(privdom, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_SUSPENDED,
VIR_DOMAIN_EVENT_SUSPENDED_PAUSED);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int testDomainShutdownFlags(virDomainPtr domain,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(privdom = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjGetState(privdom, NULL) == VIR_DOMAIN_SHUTOFF) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("domain '%s' not running"), domain->name);
goto cleanup;
}
testDomainShutdownState(domain, privdom, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!privdom->persistent)
virDomainObjListRemove(privconn->domains, privdom);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int testDomainShutdown(virDomainPtr domain)
{
return testDomainShutdownFlags(domain, 0);
}
/* Similar behaviour as shutdown */
static int testDomainReboot(virDomainPtr domain,
unsigned int action ATTRIBUTE_UNUSED)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
if (!(privdom = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjCheckActive(privdom) < 0)
goto cleanup;
virDomainObjSetState(privdom, VIR_DOMAIN_SHUTDOWN,
VIR_DOMAIN_SHUTDOWN_USER);
switch (privdom->def->onReboot) {
case VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY:
virDomainObjSetState(privdom, VIR_DOMAIN_SHUTOFF,
VIR_DOMAIN_SHUTOFF_SHUTDOWN);
break;
case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART:
virDomainObjSetState(privdom, VIR_DOMAIN_RUNNING,
VIR_DOMAIN_RUNNING_BOOTED);
break;
case VIR_DOMAIN_LIFECYCLE_ACTION_PRESERVE:
virDomainObjSetState(privdom, VIR_DOMAIN_SHUTOFF,
VIR_DOMAIN_SHUTOFF_SHUTDOWN);
break;
case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART_RENAME:
virDomainObjSetState(privdom, VIR_DOMAIN_RUNNING,
VIR_DOMAIN_RUNNING_BOOTED);
break;
default:
virDomainObjSetState(privdom, VIR_DOMAIN_SHUTOFF,
VIR_DOMAIN_SHUTOFF_SHUTDOWN);
break;
}
if (virDomainObjGetState(privdom, NULL) == VIR_DOMAIN_SHUTOFF) {
testDomainShutdownState(domain, privdom, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!privdom->persistent)
virDomainObjListRemove(privconn->domains, privdom);
}
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static char *
testDomainGetHostname(virDomainPtr domain,
unsigned int flags)
{
char *ret = NULL;
virDomainObjPtr vm = NULL;
virCheckFlags(0, NULL);
if (!(vm = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
ignore_value(virAsprintf(&ret, "%shost", domain->name));
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int testDomainGetInfo(virDomainPtr domain,
virDomainInfoPtr info)
{
struct timeval tv;
virDomainObjPtr privdom;
int ret = -1;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
if (gettimeofday(&tv, NULL) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("getting time of day"));
goto cleanup;
}
info->state = virDomainObjGetState(privdom, NULL);
info->memory = privdom->def->mem.cur_balloon;
info->maxMem = virDomainDefGetMemoryTotal(privdom->def);
info->nrVirtCpu = virDomainDefGetVcpus(privdom->def);
info->cpuTime = ((tv.tv_sec * 1000ll * 1000ll * 1000ll) + (tv.tv_usec * 1000ll));
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
return ret;
}
static int
testDomainGetState(virDomainPtr domain,
int *state,
int *reason,
unsigned int flags)
{
virDomainObjPtr privdom;
virCheckFlags(0, -1);
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
*state = virDomainObjGetState(privdom, reason);
virDomainObjEndAPI(&privdom);
return 0;
}
static int
testDomainGetTime(virDomainPtr dom ATTRIBUTE_UNUSED,
long long *seconds,
unsigned int *nseconds,
unsigned int flags)
{
virCheckFlags(0, -1);
*seconds = 627319920;
*nseconds = 0;
return 0;
}
#define TEST_SAVE_MAGIC "TestGuestMagic"
/**
* testDomainSaveImageWrite:
* @driver: test driver data
* @def: domain definition whose XML will be stored in the image
* @path: path to the saved image
*
* Returns true on success, else false.
*/
static bool
testDomainSaveImageWrite(testDriverPtr driver,
const char *path,
virDomainDefPtr def)
{
int len;
int fd = -1;
VIR_AUTOFREE(char *) xml = NULL;
xml = virDomainDefFormat(def, driver->caps, VIR_DOMAIN_DEF_FORMAT_SECURE);
2009-03-16 17:14:34 +00:00
if (xml == NULL) {
virReportSystemError(errno,
_("saving domain '%s' failed to allocate space for metadata"),
def->name);
goto error;
}
if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) {
virReportSystemError(errno,
_("saving domain '%s' to '%s': open failed"),
def->name, path);
goto error;
}
if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) {
virReportSystemError(errno,
_("saving domain '%s' to '%s': write failed"),
def->name, path);
goto error;
}
len = strlen(xml);
if (safewrite(fd, (char*)&len, sizeof(len)) < 0) {
virReportSystemError(errno,
_("saving domain '%s' to '%s': write failed"),
def->name, path);
goto error;
}
if (safewrite(fd, xml, len) < 0) {
virReportSystemError(errno,
_("saving domain '%s' to '%s': write failed"),
def->name, path);
goto error;
}
if (VIR_CLOSE(fd) < 0) {
virReportSystemError(errno,
_("saving domain '%s' to '%s': write failed"),
def->name, path);
goto error;
}
return true;
error:
/* Don't report failure in close or unlink, because
* in either case we're already in a failure scenario
* and have reported an earlier error */
VIR_FORCE_CLOSE(fd);
unlink(path);
return false;
}
static int
testDomainSaveFlags(virDomainPtr domain, const char *path,
const char *dxml, unsigned int flags)
{
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (dxml) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("xml modification unsupported"));
return -1;
}
if (!(privdom = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjCheckActive(privdom) < 0)
goto cleanup;
if (!testDomainSaveImageWrite(privconn, path, privdom->def))
goto cleanup;
testDomainShutdownState(domain, privdom, VIR_DOMAIN_SHUTOFF_SAVED);
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SAVED);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!privdom->persistent)
virDomainObjListRemove(privconn->domains, privdom);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int
testDomainSave(virDomainPtr domain,
const char *path)
{
return testDomainSaveFlags(domain, path, NULL, 0);
}
static int
testDomainRestoreFlags(virConnectPtr conn,
const char *path,
const char *dxml,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
char magic[15];
int fd = -1;
int len;
virDomainDefPtr def = NULL;
virDomainObjPtr dom = NULL;
virObjectEventPtr event = NULL;
int ret = -1;
VIR_AUTOFREE(char *) xml = NULL;
virCheckFlags(0, -1);
if (dxml) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("xml modification unsupported"));
return -1;
}
if ((fd = open(path, O_RDONLY)) < 0) {
virReportSystemError(errno,
_("cannot read domain image '%s'"),
path);
goto cleanup;
}
if (saferead(fd, magic, sizeof(magic)) != sizeof(magic)) {
virReportSystemError(errno,
_("incomplete save header in '%s'"),
path);
goto cleanup;
}
if (memcmp(magic, TEST_SAVE_MAGIC, sizeof(magic))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("mismatched header magic"));
goto cleanup;
}
if (saferead(fd, (char*)&len, sizeof(len)) != sizeof(len)) {
virReportSystemError(errno,
_("failed to read metadata length in '%s'"),
path);
goto cleanup;
}
if (len < 1 || len > 8192) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("length of metadata out of range"));
goto cleanup;
}
if (VIR_ALLOC_N(xml, len+1) < 0)
goto cleanup;
if (saferead(fd, xml, len) != len) {
virReportSystemError(errno,
_("incomplete metadata in '%s'"), path);
goto cleanup;
}
xml[len] = '\0';
def = virDomainDefParseString(xml, privconn->caps, privconn->xmlopt,
NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (!def)
goto cleanup;
if (testDomainGenerateIfnames(def) < 0)
goto cleanup;
if (!(dom = virDomainObjListAdd(privconn->domains,
def,
privconn->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
NULL)))
goto cleanup;
def = NULL;
if (testDomainStartState(privconn, dom, VIR_DOMAIN_RUNNING_RESTORED) < 0) {
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!dom->persistent)
virDomainObjListRemove(privconn->domains, dom);
goto cleanup;
}
event = virDomainEventLifecycleNewFromObj(dom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_RESTORED);
ret = 0;
cleanup:
virDomainDefFree(def);
VIR_FORCE_CLOSE(fd);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&dom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int
testDomainRestore(virConnectPtr conn,
const char *path)
{
return testDomainRestoreFlags(conn, path, NULL, 0);
}
static int testDomainCoreDumpWithFormat(virDomainPtr domain,
const char *to,
unsigned int dumpformat,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
int fd = -1;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(VIR_DUMP_CRASH, -1);
if (!(privdom = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjCheckActive(privdom) < 0)
goto cleanup;
if ((fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) {
virReportSystemError(errno,
_("domain '%s' coredump: failed to open %s"),
domain->name, to);
goto cleanup;
}
if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) {
virReportSystemError(errno,
_("domain '%s' coredump: failed to write header to %s"),
domain->name, to);
goto cleanup;
}
if (VIR_CLOSE(fd) < 0) {
virReportSystemError(errno,
_("domain '%s' coredump: write failed: %s"),
domain->name, to);
goto cleanup;
}
/* we don't support non-raw formats in test driver */
if (dumpformat != VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("kdump-compressed format is not supported here"));
goto cleanup;
}
if (flags & VIR_DUMP_CRASH) {
testDomainShutdownState(domain, privdom, VIR_DOMAIN_SHUTOFF_CRASHED);
event = virDomainEventLifecycleNewFromObj(privdom,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_CRASHED);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!privdom->persistent)
virDomainObjListRemove(privconn->domains, privdom);
}
ret = 0;
cleanup:
VIR_FORCE_CLOSE(fd);
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int
testDomainCoreDump(virDomainPtr domain,
const char *to,
unsigned int flags)
{
return testDomainCoreDumpWithFormat(domain, to,
VIR_DOMAIN_CORE_DUMP_FORMAT_RAW, flags);
}
static char *
testDomainGetOSType(virDomainPtr dom ATTRIBUTE_UNUSED)
{
char *ret;
ignore_value(VIR_STRDUP(ret, "linux"));
return ret;
}
static unsigned long long
testDomainGetMaxMemory(virDomainPtr domain)
{
virDomainObjPtr privdom;
xml: use long long internally, to centralize overflow checks On 64-bit platforms, unsigned long and unsigned long long are identical, so we don't have to worry about overflow checks. On 32-bit platforms, anywhere we narrow unsigned long long back to unsigned long, we have to worry about overflow; it's easier to do this in one place by having most of the code use the same or wider types, and only doing the narrowing at the last minute. Therefore, the memory set commands remain unsigned long, and the memory get command now centralizes the overflow check into libvirt.c, so that drivers don't have to repeat the work. This also fixes a bug where xen returned the wrong value on failure (most APIs return -1 on failure, but getMaxMemory must return 0 on failure). * src/driver.h (virDrvDomainGetMaxMemory): Use long long. * src/libvirt.c (virDomainGetMaxMemory): Raise overflow. * src/test/test_driver.c (testGetMaxMemory): Fix driver. * src/rpc/gendispatch.pl (name_to_ProcName): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorGetMaxMemory): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainGetMaxMemory): Likewise. * src/xen/xend_internal.c (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xend_internal.h (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xm_internal.c (xenXMDomainGetMaxMemory): Likewise. * src/xen/xm_internal.h (xenXMDomainGetMaxMemory): Likewise. * src/xen/xs_internal.c (xenStoreDomainGetMaxMemory): Likewise. * src/xen/xs_internal.h (xenStoreDomainGetMaxMemory): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainGetMaxMemory): Likewise. * src/esx/esx_driver.c (esxDomainGetMaxMemory): Likewise. * src/libxl/libxl_driver.c (libxlDomainGetMaxMemory): Likewise. * src/qemu/qemu_driver.c (qemudDomainGetMaxMemory): Likewise. * src/lxc/lxc_driver.c (lxcDomainGetMaxMemory): Likewise. * src/uml/uml_driver.c (umlDomainGetMaxMemory): Likewise.
2012-03-03 00:47:16 +00:00
unsigned long long ret = 0;
if (!(privdom = testDomObjFromDomain(domain)))
return 0;
ret = virDomainDefGetMemoryTotal(privdom->def);
virDomainObjEndAPI(&privdom);
return ret;
}
static int testDomainSetMaxMemory(virDomainPtr domain,
unsigned long memory)
{
virDomainObjPtr privdom;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
/* XXX validate not over host memory wrt to other domains */
virDomainDefSetMemoryTotal(privdom->def, memory);
virDomainObjEndAPI(&privdom);
return 0;
}
static int testDomainSetMemory(virDomainPtr domain,
unsigned long memory)
{
virDomainObjPtr privdom;
int ret = -1;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
if (memory > virDomainDefGetMemoryTotal(privdom->def)) {
virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__);
goto cleanup;
}
privdom->def->mem.cur_balloon = memory;
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
return ret;
}
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
static int
testDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
{
virDomainObjPtr vm;
virDomainDefPtr def;
int ret = -1;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG |
VIR_DOMAIN_VCPU_MAXIMUM, -1);
if (!(vm = testDomObjFromDomain(domain)))
return -1;
if (!(def = virDomainObjGetOneDef(vm, flags)))
goto cleanup;
if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
ret = virDomainDefGetVcpusMax(def);
else
ret = virDomainDefGetVcpus(def);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
static int
testDomainGetMaxVcpus(virDomainPtr domain)
{
return testDomainGetVcpusFlags(domain, (VIR_DOMAIN_AFFECT_LIVE |
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
VIR_DOMAIN_VCPU_MAXIMUM));
}
static int
testDomainSetVcpusFlags(virDomainPtr domain, unsigned int nrCpus,
unsigned int flags)
{
testDriverPtr driver = domain->conn->privateData;
virDomainObjPtr privdom = NULL;
virDomainDefPtr def;
virDomainDefPtr persistentDef;
int ret = -1, maxvcpus;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG |
VIR_DOMAIN_VCPU_MAXIMUM, -1);
if ((maxvcpus = testConnectGetMaxVcpus(domain->conn, NULL)) < 0)
return -1;
if (nrCpus > maxvcpus) {
virReportError(VIR_ERR_INVALID_ARG,
_("requested cpu amount exceeds maximum supported amount "
"(%d > %d)"), nrCpus, maxvcpus);
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
return -1;
}
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
if (virDomainObjGetDefs(privdom, flags, &def, &persistentDef) < 0)
goto cleanup;
if (def && virDomainDefGetVcpusMax(def) < nrCpus) {
virReportError(VIR_ERR_INVALID_ARG,
_("requested cpu amount exceeds maximum (%d > %d)"),
nrCpus, virDomainDefGetVcpusMax(def));
goto cleanup;
}
if (persistentDef &&
!(flags & VIR_DOMAIN_VCPU_MAXIMUM) &&
virDomainDefGetVcpusMax(persistentDef) < nrCpus) {
virReportError(VIR_ERR_INVALID_ARG,
_("requested cpu amount exceeds maximum (%d > %d)"),
nrCpus, virDomainDefGetVcpusMax(persistentDef));
goto cleanup;
}
if (def &&
virDomainDefSetVcpus(def, nrCpus) < 0)
goto cleanup;
if (persistentDef) {
if (flags & VIR_DOMAIN_VCPU_MAXIMUM) {
if (virDomainDefSetVcpusMax(persistentDef, nrCpus,
driver->xmlopt) < 0)
goto cleanup;
} else {
if (virDomainDefSetVcpus(persistentDef, nrCpus) < 0)
goto cleanup;
}
}
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
return ret;
}
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
static int
testDomainSetVcpus(virDomainPtr domain, unsigned int nrCpus)
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
{
return testDomainSetVcpusFlags(domain, nrCpus, VIR_DOMAIN_AFFECT_LIVE);
vcpu: make old API trivially wrap to new API Note - this wrapping is completely mechanical; the old API will function identically, since the new API validates that the exact same flags are provided by the old API. On a per-driver basis, it may make sense to have the old API pass a different set of flags, but that should be done in the per-driver patch that implements the full range of flag support in the new API. * src/esx/esx_driver.c (esxDomainSetVcpus, escDomainGetMaxVpcus): Move guts... (esxDomainSetVcpusFlags, esxDomainGetVcpusFlags): ...to new functions. (esxDriver): Trivially support the new API. * src/openvz/openvz_driver.c (openvzDomainSetVcpus) (openvzDomainSetVcpusFlags, openvzDomainGetMaxVcpus) (openvzDomainGetVcpusFlags, openvzDriver): Likewise. * src/phyp/phyp_driver.c (phypDomainSetCPU) (phypDomainSetVcpusFlags, phypGetLparCPUMAX) (phypDomainGetVcpusFlags, phypDriver): Likewise. * src/qemu/qemu_driver.c (qemudDomainSetVcpus) (qemudDomainSetVcpusFlags, qemudDomainGetMaxVcpus) (qemudDomainGetVcpusFlags, qemuDriver): Likewise. * src/test/test_driver.c (testSetVcpus, testDomainSetVcpusFlags) (testDomainGetMaxVcpus, testDomainGetVcpusFlags, testDriver): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainSetVcpus) (vboxDomainSetVcpusFlags, virDomainGetMaxVcpus) (virDomainGetVcpusFlags, virDriver): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainSetVcpus) (xenUnifiedDomainSetVcpusFlags, xenUnifiedDomainGetMaxVcpus) (xenUnifiedDomainGetVcpusFlags, xenUnifiedDriver): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainSetVcpus) (xenapiDomainSetVcpusFlags, xenapiDomainGetMaxVcpus) (xenapiDomainGetVcpusFlags, xenapiDriver): Likewise. (xenapiError): New helper macro.
2010-09-27 22:37:53 +00:00
}
2009-10-21 19:04:14 +00:00
static int testDomainGetVcpus(virDomainPtr domain,
virVcpuInfoPtr info,
int maxinfo,
unsigned char *cpumaps,
int maplen)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
2009-10-21 19:04:14 +00:00
virDomainObjPtr privdom;
virDomainDefPtr def;
size_t i;
int hostcpus;
2009-10-21 19:04:14 +00:00
int ret = -1;
struct timeval tv;
unsigned long long statbase;
virBitmapPtr allcpumap = NULL;
2009-10-21 19:04:14 +00:00
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
2009-10-21 19:04:14 +00:00
if (!virDomainObjIsActive(privdom)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot list vcpus for an inactive domain"));
2009-10-21 19:04:14 +00:00
goto cleanup;
}
def = privdom->def;
2009-10-21 19:04:14 +00:00
if (gettimeofday(&tv, NULL) < 0) {
virReportSystemError(errno,
2009-10-21 19:04:14 +00:00
"%s", _("getting time of day"));
goto cleanup;
}
statbase = (tv.tv_sec * 1000UL * 1000UL) + tv.tv_usec;
hostcpus = VIR_NODEINFO_MAXCPUS(privconn->nodeInfo);
if (!(allcpumap = virBitmapNew(hostcpus)))
goto cleanup;
virBitmapSetAll(allcpumap);
2009-10-21 19:04:14 +00:00
/* Clamp to actual number of vcpus */
if (maxinfo > virDomainDefGetVcpus(privdom->def))
maxinfo = virDomainDefGetVcpus(privdom->def);
2009-10-21 19:04:14 +00:00
memset(info, 0, sizeof(*info) * maxinfo);
memset(cpumaps, 0, maxinfo * maplen);
2009-10-21 19:04:14 +00:00
for (i = 0; i < maxinfo; i++) {
virDomainVcpuDefPtr vcpu = virDomainDefGetVcpu(def, i);
virBitmapPtr bitmap = NULL;
2009-10-21 19:04:14 +00:00
if (!vcpu->online)
continue;
2009-10-21 19:04:14 +00:00
if (vcpu->cpumask)
bitmap = vcpu->cpumask;
else if (def->cpumask)
bitmap = def->cpumask;
else
bitmap = allcpumap;
2009-10-21 19:04:14 +00:00
if (cpumaps)
virBitmapToDataBuf(bitmap, VIR_GET_CPUMAP(cpumaps, maplen, i), maplen);
2009-10-21 19:04:14 +00:00
info[i].number = i;
info[i].state = VIR_VCPU_RUNNING;
info[i].cpu = virBitmapLastSetBit(bitmap);
2009-10-21 19:04:14 +00:00
/* Fake an increasing cpu time value */
info[i].cpuTime = statbase / 10;
2009-10-21 19:04:14 +00:00
}
ret = maxinfo;
cleanup:
virBitmapFree(allcpumap);
virDomainObjEndAPI(&privdom);
2009-10-21 19:04:14 +00:00
return ret;
}
2009-10-27 22:25:38 +00:00
static int testDomainPinVcpu(virDomainPtr domain,
unsigned int vcpu,
unsigned char *cpumap,
int maplen)
{
virDomainVcpuDefPtr vcpuinfo;
2009-10-27 22:25:38 +00:00
virDomainObjPtr privdom;
virDomainDefPtr def;
2009-10-27 22:25:38 +00:00
int ret = -1;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
2009-10-27 22:25:38 +00:00
def = privdom->def;
2009-10-27 22:25:38 +00:00
if (!virDomainObjIsActive(privdom)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot pin vcpus on an inactive domain"));
2009-10-27 22:25:38 +00:00
goto cleanup;
}
if (!(vcpuinfo = virDomainDefGetVcpu(def, vcpu)) ||
!vcpuinfo->online) {
virReportError(VIR_ERR_INVALID_ARG,
_("requested vcpu '%d' is not present in the domain"),
vcpu);
2009-10-27 22:25:38 +00:00
goto cleanup;
}
virBitmapFree(vcpuinfo->cpumask);
if (!(vcpuinfo->cpumask = virBitmapNewData(cpumap, maplen)))
goto cleanup;
2009-10-27 22:25:38 +00:00
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
2009-10-27 22:25:38 +00:00
return ret;
}
static int
testDomainGetVcpuPinInfo(virDomainPtr dom,
int ncpumaps,
unsigned char *cpumaps,
int maplen,
unsigned int flags)
{
testDriverPtr driver = dom->conn->privateData;
virDomainObjPtr privdom;
virDomainDefPtr def;
int ret = -1;
if (!(privdom = testDomObjFromDomain(dom)))
return -1;
if (!(def = virDomainObjGetOneDef(privdom, flags)))
goto cleanup;
ret = virDomainDefGetVcpuPinInfoHelper(def, maplen, ncpumaps, cpumaps,
VIR_NODEINFO_MAXCPUS(driver->nodeInfo),
NULL);
cleanup:
virDomainObjEndAPI(&privdom);
return ret;
}
static int
testDomainRenameCallback(virDomainObjPtr privdom,
const char *new_name,
unsigned int flags,
void *opaque)
{
testDriverPtr driver = opaque;
virObjectEventPtr event_new = NULL;
virObjectEventPtr event_old = NULL;
int ret = -1;
VIR_AUTOFREE(char *) new_dom_name = NULL;
virCheckFlags(0, -1);
if (strchr(new_name, '/')) {
virReportError(VIR_ERR_XML_ERROR,
_("name %s cannot contain '/'"), new_name);
return -1;
}
if (VIR_STRDUP(new_dom_name, new_name) < 0)
goto cleanup;
event_old = virDomainEventLifecycleNewFromObj(privdom,
VIR_DOMAIN_EVENT_UNDEFINED,
VIR_DOMAIN_EVENT_UNDEFINED_RENAMED);
/* Switch name in domain definition. */
VIR_FREE(privdom->def->name);
VIR_STEAL_PTR(privdom->def->name, new_dom_name);
event_new = virDomainEventLifecycleNewFromObj(privdom,
VIR_DOMAIN_EVENT_DEFINED,
VIR_DOMAIN_EVENT_DEFINED_RENAMED);
ret = 0;
cleanup:
virObjectEventStateQueue(driver->eventState, event_old);
virObjectEventStateQueue(driver->eventState, event_new);
return ret;
}
static int
testDomainRename(virDomainPtr dom,
const char *new_name,
unsigned int flags)
{
testDriverPtr driver = dom->conn->privateData;
virDomainObjPtr privdom = NULL;
int ret = -1;
virCheckFlags(0, ret);
if (!(privdom = testDomObjFromDomain(dom)))
goto cleanup;
if (virDomainObjIsActive(privdom)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot rename active domain"));
goto cleanup;
}
if (!privdom->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot rename a transient domain"));
goto cleanup;
}
if (virDomainObjGetState(privdom, NULL) != VIR_DOMAIN_SHUTOFF) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain has to be shutoff before renaming"));
goto cleanup;
}
if (virDomainObjListRename(driver->domains, privdom, new_name, flags,
testDomainRenameCallback, driver) < 0)
goto cleanup;
/* Success, domain has been renamed. */
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
return ret;
}
drivers: prefer unsigned int for flags Now that the public APIs always use unsigned flags, the internal driver callbacks might as well do likewise. * src/driver.h (vrDrvOpen, virDrvDomainCoreDump) (virDrvDomainGetXMLDesc, virDrvNetworkGetXMLDesc) (virDrvNWFilterGetXMLDesc): Update type. * src/remote/remote_protocol.x (remote_open_args) (remote_domain_core_dump_args, remote_domain_get_xml_desc_args) (remote_network_get_xml_desc_args) (remote_nwfilter_get_xml_desc_args): Likewise. * src/test/test_driver.c: Update clients. * src/remote/remote_driver.c: Likewise. * src/xen/xen_hypervisor.c: Likewise. * src/xen/xen_hypervisor.h: Likewise. * src/xen/xen_driver.c: Likewise. * src/xen/xend_internal.c: Likewise. * src/xen/xend_internal.h: Likewise. * src/xen/xm_internal.c: Likewise. * src/xen/xm_internal.h: Likewise. * src/xen/xs_internal.c: Likewise. * src/xen/xs_internal.h: Likewise. * src/xen/xen_inotify.c: Likewise. * src/xen/xen_inotify.h: Likewise. * src/phyp/phyp_driver.c: Likewise. * src/openvz/openvz_driver.c: Likewise. * src/vmware/vmware_driver.c: Likewise. * src/vbox/vbox_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xenapi/xenapi_driver.c: Likewise. * src/esx/esx_driver.c: Likewise. * src/esx/esx_interface_driver.c: Likewise. * src/esx/esx_network_driver.c: Likewise. * src/esx/esx_storage_driver.c: Likewise. * src/esx/esx_device_monitor.c: Likewise. * src/esx/esx_secret_driver.c: Likewise. * src/esx/esx_nwfilter_driver.c: Likewise. * src/interface/netcf_driver.c: Likewise. * src/nwfilter/nwfilter_driver.c: Likewise. * src/libxl/libxl_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/secret/secret_driver.c: Likewise. * src/storage/storage_driver.c: Likewise. * src/node_device/node_device_hal.c: Likewise. * src/node_device/node_device_udev.c: Likewise. * src/remote_protocol-structs: Likewise.
2011-07-06 20:40:19 +00:00
static char *testDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
testDriverPtr privconn = domain->conn->privateData;
virDomainDefPtr def;
virDomainObjPtr privdom;
char *ret = NULL;
domain: Fix unknown flags diagnosis in virDomainGetXMLDesc Many drivers had a comment that they did not validate the incoming 'flags' to virDomainGetXMLDesc() because they were relying on virDomainDefFormat() to do it instead. This used to be the case (at least since 461e0f1a and friends in 0.9.4 added unknown flag checking in general), but regressed in commit 0ecd6851 (1.2.12), when all of the drivers were changed to pass 'flags' through the new helper virDomainDefFormatConvertXMLFlags(). Since this helper silently ignores unknown flags, we need to implement flag checking in each driver instead. Annoyingly, this means that any new flag values added will silently be ignored when targeting an older libvirt, rather than our usual practice of loudly diagnosing an unsupported flag. Add comments in domain_conf.[ch] to remind us to be extra vigilant about the impact when adding flags (a new flag to add data is safe if the older server omitting the requested data doesn't break things in the newer client; a new flag to suppress data rather than enhancing the existing VIR_DOMAIN_XML_SECURE may form a data leak or even a security hole). In the qemu driver, there are multiple callers all funnelling to qemuDomainDefFormatBufInternal(); many of them already validated flags (and often only a subset of the full set of possible flags), but for ease of maintenance, we can also check flags at the common helper function. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-14 20:25:01 +00:00
virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
if (!(privdom = testDomObjFromDomain(domain)))
return NULL;
def = (flags & VIR_DOMAIN_XML_INACTIVE) &&
privdom->newDef ? privdom->newDef : privdom->def;
ret = virDomainDefFormat(def, privconn->caps,
virDomainDefFormatConvertXMLFlags(flags));
virDomainObjEndAPI(&privdom);
return ret;
}
static int testConnectNumOfDefinedDomains(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
return virDomainObjListNumOfDomains(privconn->domains, false, NULL, NULL);
}
static int testConnectListDefinedDomains(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
memset(names, 0, sizeof(*names)*maxnames);
return virDomainObjListGetInactiveNames(privconn->domains, names, maxnames,
NULL, NULL);
}
static virDomainPtr testDomainDefineXMLFlags(virConnectPtr conn,
const char *xml,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virDomainPtr ret = NULL;
virDomainDefPtr def;
virDomainObjPtr dom = NULL;
virObjectEventPtr event = NULL;
virDomainDefPtr oldDef = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
if (flags & VIR_DOMAIN_DEFINE_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
if ((def = virDomainDefParseString(xml, privconn->caps, privconn->xmlopt,
NULL, parse_flags)) == NULL)
goto cleanup;
if (virXMLCheckIllegalChars("name", def->name, "\n") < 0)
goto cleanup;
if (testDomainGenerateIfnames(def) < 0)
goto cleanup;
if (!(dom = virDomainObjListAdd(privconn->domains,
def,
privconn->xmlopt,
0,
&oldDef)))
goto cleanup;
def = NULL;
dom->persistent = 1;
event = virDomainEventLifecycleNewFromObj(dom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_DEFINED,
!oldDef ?
VIR_DOMAIN_EVENT_DEFINED_ADDED :
VIR_DOMAIN_EVENT_DEFINED_UPDATED);
ret = virGetDomain(conn, dom->def->name, dom->def->uuid, dom->def->id);
cleanup:
virDomainDefFree(def);
virDomainDefFree(oldDef);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&dom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static virDomainPtr
testDomainDefineXML(virConnectPtr conn, const char *xml)
{
return testDomainDefineXMLFlags(conn, xml, 0);
}
static char *testDomainGetMetadata(virDomainPtr dom,
int type,
const char *uri,
unsigned int flags)
{
virDomainObjPtr privdom;
char *ret;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, NULL);
if (!(privdom = testDomObjFromDomain(dom)))
return NULL;
ret = virDomainObjGetMetadata(privdom, type, uri, flags);
virDomainObjEndAPI(&privdom);
return ret;
}
static int testDomainSetMetadata(virDomainPtr dom,
int type,
const char *metadata,
const char *key,
const char *uri,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = dom->conn->privateData;
virDomainObjPtr privdom;
int ret;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, -1);
if (!(privdom = testDomObjFromDomain(dom)))
return -1;
ret = virDomainObjSetMetadata(privdom, type, metadata, key, uri,
privconn->caps, privconn->xmlopt,
NULL, NULL, flags);
if (ret == 0) {
virObjectEventPtr ev = NULL;
ev = virDomainEventMetadataChangeNewFromObj(privdom, type, uri);
virObjectEventStateQueue(privconn->eventState, ev);
}
virDomainObjEndAPI(&privdom);
return ret;
}
static int testNodeGetCellsFreeMemory(virConnectPtr conn,
unsigned long long *freemems,
int startCell, int maxCells)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int cell;
size_t i;
int ret = -1;
virObjectLock(privconn);
if (startCell >= privconn->numCells) {
virReportError(VIR_ERR_INVALID_ARG,
"%s", _("Range exceeds available cells"));
goto cleanup;
}
for (cell = startCell, i = 0;
(cell < privconn->numCells && i < maxCells);
++cell, ++i) {
freemems[i] = privconn->cells[cell].mem;
}
ret = i;
cleanup:
virObjectUnlock(privconn);
return ret;
}
#define TEST_NB_CPU_STATS 4
static int
testNodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED,
int cpuNum ATTRIBUTE_UNUSED,
virNodeCPUStatsPtr params,
int *nparams,
unsigned int flags)
{
size_t i = 0;
virCheckFlags(0, -1);
if (params == NULL) {
*nparams = TEST_NB_CPU_STATS;
return 0;
}
for (i = 0; i < *nparams && i < 4; i++) {
switch (i) {
case 0:
if (virHostCPUStatsAssign(&params[i],
VIR_NODE_CPU_STATS_USER, 9797400000) < 0)
return -1;
break;
case 1:
if (virHostCPUStatsAssign(&params[i],
VIR_NODE_CPU_STATS_KERNEL, 34678723400000) < 0)
return -1;
break;
case 2:
if (virHostCPUStatsAssign(&params[i],
VIR_NODE_CPU_STATS_IDLE, 87264900000) < 0)
return -1;
break;
case 3:
if (virHostCPUStatsAssign(&params[i],
VIR_NODE_CPU_STATS_IOWAIT, 763600000) < 0)
return -1;
break;
}
}
*nparams = i;
return 0;
}
static unsigned long long
testNodeGetFreeMemory(virConnectPtr conn)
{
testDriverPtr privconn = conn->privateData;
unsigned int freeMem = 0;
size_t i;
virObjectLock(privconn);
for (i = 0; i < privconn->numCells; i++)
freeMem += privconn->cells[i].freeMem;
virObjectUnlock(privconn);
return freeMem;
}
static int
testNodeGetFreePages(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned int npages,
unsigned int *pages ATTRIBUTE_UNUSED,
int startCell ATTRIBUTE_UNUSED,
unsigned int cellCount,
unsigned long long *counts,
unsigned int flags)
{
size_t i = 0, j = 0;
int x = 6;
virCheckFlags(0, -1);
for (i = 0; i < cellCount; i++) {
for (j = 0; j < npages; j++) {
x = x * 2 + 7;
counts[(i * npages) + j] = x;
}
}
return 0;
}
static int testDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(0, -1);
virObjectLock(privconn);
if (!(privdom = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjGetState(privdom, NULL) != VIR_DOMAIN_SHUTOFF) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Domain '%s' is already running"), domain->name);
goto cleanup;
}
if (testDomainStartState(privconn, privdom,
VIR_DOMAIN_RUNNING_BOOTED) < 0)
goto cleanup;
domain->id = privdom->def->id;
event = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_BOOTED);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
virObjectUnlock(privconn);
return ret;
}
static int testDomainCreate(virDomainPtr domain)
{
return testDomainCreateWithFlags(domain, 0);
}
static int testDomainUndefineFlags(virDomainPtr domain,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr privdom;
virObjectEventPtr event = NULL;
int nsnapshots;
int ret = -1;
virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE |
VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, -1);
if (!(privdom = testDomObjFromDomain(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;
}
/* 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 = virDomainEventLifecycleNewFromObj(privdom,
2009-01-20 20:23:53 +00:00
VIR_DOMAIN_EVENT_UNDEFINED,
VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
privdom->hasManagedSave = false;
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (virDomainObjIsActive(privdom))
privdom->persistent = 0;
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
else
virDomainObjListRemove(privconn->domains, privdom);
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int testDomainUndefine(virDomainPtr domain)
{
return testDomainUndefineFlags(domain, 0);
}
static int testDomainGetAutostart(virDomainPtr domain,
int *autostart)
{
virDomainObjPtr privdom;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
*autostart = privdom->autostart;
virDomainObjEndAPI(&privdom);
return 0;
}
static int testDomainSetAutostart(virDomainPtr domain,
int autostart)
{
virDomainObjPtr privdom;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
privdom->autostart = autostart ? 1 : 0;
virDomainObjEndAPI(&privdom);
return 0;
}
static int testDomainGetDiskErrors(virDomainPtr dom,
virDomainDiskErrorPtr errors,
unsigned int maxerrors,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
int ret = -1;
size_t i;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromDomain(dom)))
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
if (errors) {
for (i = 0; i < MIN(vm->def->ndisks, maxerrors); i++) {
if (VIR_STRDUP(errors[i].disk, vm->def->disks[i]->dst) < 0)
goto cleanup;
errors[i].error = i % VIR_DOMAIN_DISK_ERROR_LAST;
}
ret = i;
} else {
ret = vm->def->ndisks;
}
cleanup:
virDomainObjEndAPI(&vm);
if (ret < 0) {
for (i = 0; i < MIN(vm->def->ndisks, maxerrors); i++)
VIR_FREE(errors[i].disk);
}
return ret;
}
static char *testDomainGetSchedulerType(virDomainPtr domain ATTRIBUTE_UNUSED,
int *nparams)
{
char *type = NULL;
if (nparams)
*nparams = 1;
ignore_value(VIR_STRDUP(type, "fair"));
return type;
}
static int
testDomainGetSchedulerParametersFlags(virDomainPtr domain,
virTypedParameterPtr params,
int *nparams,
unsigned int flags)
{
virDomainObjPtr privdom;
int ret = -1;
virCheckFlags(0, -1);
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
if (virTypedParameterAssign(params, VIR_DOMAIN_SCHEDULER_WEIGHT,
VIR_TYPED_PARAM_UINT, 50) < 0)
goto cleanup;
/* XXX */
/*params[0].value.ui = privdom->weight;*/
*nparams = 1;
ret = 0;
cleanup:
virDomainObjEndAPI(&privdom);
return ret;
}
static int
testDomainGetSchedulerParameters(virDomainPtr domain,
virTypedParameterPtr params,
int *nparams)
{
return testDomainGetSchedulerParametersFlags(domain, params, nparams, 0);
}
static int
testDomainSetSchedulerParametersFlags(virDomainPtr domain,
virTypedParameterPtr params,
int nparams,
unsigned int flags)
{
virDomainObjPtr privdom;
int ret = -1;
size_t i;
virCheckFlags(0, -1);
if (virTypedParamsValidate(params, nparams,
VIR_DOMAIN_SCHEDULER_WEIGHT,
VIR_TYPED_PARAM_UINT,
NULL) < 0)
return -1;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
for (i = 0; i < nparams; i++) {
if (STREQ(params[i].field, VIR_DOMAIN_SCHEDULER_WEIGHT)) {
/* XXX */
/*privdom->weight = params[i].value.ui;*/
}
}
ret = 0;
virDomainObjEndAPI(&privdom);
return ret;
}
static int
testDomainSetSchedulerParameters(virDomainPtr domain,
virTypedParameterPtr params,
int nparams)
{
return testDomainSetSchedulerParametersFlags(domain, params, nparams, 0);
}
static int testDomainBlockStats(virDomainPtr domain,
const char *path,
virDomainBlockStatsPtr stats)
{
virDomainObjPtr privdom;
struct timeval tv;
unsigned long long statbase;
int ret = -1;
if (!*path) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("summary statistics are not supported yet"));
return ret;
}
if (!(privdom = testDomObjFromDomain(domain)))
return ret;
if (virDomainObjCheckActive(privdom) < 0)
goto error;
if (virDomainDiskIndexByName(privdom->def, path, false) < 0) {
virReportError(VIR_ERR_INVALID_ARG,
_("invalid path: %s"), path);
goto error;
}
if (gettimeofday(&tv, NULL) < 0) {
virReportSystemError(errno,
"%s", _("getting time of day"));
goto error;
}
/* No significance to these numbers, just enough to mix it up*/
statbase = (tv.tv_sec * 1000UL * 1000UL) + tv.tv_usec;
stats->rd_req = statbase / 10;
stats->rd_bytes = statbase / 20;
stats->wr_req = statbase / 30;
stats->wr_bytes = statbase / 40;
stats->errs = tv.tv_sec / 2;
ret = 0;
error:
virDomainObjEndAPI(&privdom);
return ret;
}
static int
testDomainInterfaceAddresses(virDomainPtr dom,
virDomainInterfacePtr **ifaces,
unsigned int source ATTRIBUTE_UNUSED,
unsigned int flags)
{
size_t i;
size_t ifaces_count = 0;
int ret = -1;
char macaddr[VIR_MAC_STRING_BUFLEN];
virDomainObjPtr vm = NULL;
virDomainInterfacePtr iface = NULL;
virDomainInterfacePtr *ifaces_ret = NULL;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromDomain(dom)))
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
if (VIR_ALLOC_N(ifaces_ret, vm->def->nnets) < 0)
goto cleanup;
for (i = 0; i < vm->def->nnets; i++) {
if (VIR_ALLOC(iface) < 0)
goto cleanup;
if (VIR_STRDUP(iface->name, vm->def->nets[i]->ifname) < 0)
goto cleanup;
virMacAddrFormat(&(vm->def->nets[i]->mac), macaddr);
if (VIR_STRDUP(iface->hwaddr, macaddr) < 0)
goto cleanup;
if (VIR_ALLOC(iface->addrs) < 0)
goto cleanup;
iface->addrs[0].type = VIR_IP_ADDR_TYPE_IPV4;
iface->addrs[0].prefix = 24;
if (virAsprintf(&iface->addrs[0].addr, "192.168.0.%zu", 1 + i) < 0)
goto cleanup;
iface->naddrs = 1;
VIR_APPEND_ELEMENT_INPLACE(ifaces_ret, ifaces_count, iface);
}
VIR_STEAL_PTR(*ifaces, ifaces_ret);
ret = ifaces_count;
cleanup:
virDomainObjEndAPI(&vm);
if (ifaces_ret) {
for (i = 0; i < ifaces_count; i++)
virDomainInterfaceFree(ifaces_ret[i]);
}
virDomainInterfaceFree(iface);
VIR_FREE(ifaces_ret);
return ret;
}
static int
testDomainInterfaceStats(virDomainPtr domain,
const char *device,
virDomainInterfaceStatsPtr stats)
{
virDomainObjPtr privdom;
struct timeval tv;
unsigned long long statbase;
virDomainNetDefPtr net = NULL;
int ret = -1;
if (!(privdom = testDomObjFromDomain(domain)))
return -1;
if (virDomainObjCheckActive(privdom) < 0)
goto error;
if (!(net = virDomainNetFind(privdom->def, device)))
goto error;
if (gettimeofday(&tv, NULL) < 0) {
virReportSystemError(errno,
"%s", _("getting time of day"));
goto error;
}
/* No significance to these numbers, just enough to mix it up*/
statbase = (tv.tv_sec * 1000UL * 1000UL) + tv.tv_usec;
stats->rx_bytes = statbase / 10;
stats->rx_packets = statbase / 100;
stats->rx_errs = tv.tv_sec / 1;
stats->rx_drop = tv.tv_sec / 2;
stats->tx_bytes = statbase / 20;
stats->tx_packets = statbase / 110;
stats->tx_errs = tv.tv_sec / 3;
stats->tx_drop = tv.tv_sec / 4;
ret = 0;
error:
virDomainObjEndAPI(&privdom);
return ret;
}
static virNetworkObjPtr
testNetworkObjFindByUUID(testDriverPtr privconn,
const unsigned char *uuid)
{
virNetworkObjPtr obj;
char uuidstr[VIR_UUID_STRING_BUFLEN];
if (!(obj = virNetworkObjFindByUUID(privconn->networks, uuid))) {
virUUIDFormat(uuid, uuidstr);
virReportError(VIR_ERR_NO_NETWORK,
_("no network with matching uuid '%s'"),
uuidstr);
}
return obj;
}
static virNetworkPtr
testNetworkLookupByUUID(virConnectPtr conn,
const unsigned char *uuid)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virNetworkObjPtr obj;
virNetworkDefPtr def;
virNetworkPtr net = NULL;
if (!(obj = testNetworkObjFindByUUID(privconn, uuid)))
goto cleanup;
def = virNetworkObjGetDef(obj);
net = virGetNetwork(conn, def->name, def->uuid);
cleanup:
virNetworkObjEndAPI(&obj);
return net;
}
static virNetworkObjPtr
testNetworkObjFindByName(testDriverPtr privconn,
const char *name)
{
virNetworkObjPtr obj;
if (!(obj = virNetworkObjFindByName(privconn->networks, name)))
virReportError(VIR_ERR_NO_NETWORK,
_("no network with matching name '%s'"),
name);
return obj;
}
static virNetworkPtr
testNetworkLookupByName(virConnectPtr conn,
const char *name)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virNetworkObjPtr obj;
virNetworkDefPtr def;
virNetworkPtr net = NULL;
if (!(obj = testNetworkObjFindByName(privconn, name)))
goto cleanup;
def = virNetworkObjGetDef(obj);
net = virGetNetwork(conn, def->name, def->uuid);
cleanup:
virNetworkObjEndAPI(&obj);
return net;
}
static int
testConnectNumOfNetworks(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int numActive;
numActive = virNetworkObjListNumOfNetworks(privconn->networks,
true, NULL, conn);
return numActive;
}
static int
testConnectListNetworks(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int n;
n = virNetworkObjListGetNames(privconn->networks,
true, names, maxnames, NULL, conn);
return n;
}
static int
testConnectNumOfDefinedNetworks(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int numInactive;
numInactive = virNetworkObjListNumOfNetworks(privconn->networks,
false, NULL, conn);
return numInactive;
}
static int
testConnectListDefinedNetworks(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int n;
n = virNetworkObjListGetNames(privconn->networks,
false, names, maxnames, NULL, conn);
return n;
}
static int
testConnectListAllNetworks(virConnectPtr conn,
virNetworkPtr **nets,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1);
return virNetworkObjListExport(conn, privconn->networks, nets, NULL, flags);
}
static int
testNetworkIsActive(virNetworkPtr net)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
int ret = -1;
if (!(obj = testNetworkObjFindByUUID(privconn, net->uuid)))
goto cleanup;
ret = virNetworkObjIsActive(obj);
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
static int
testNetworkIsPersistent(virNetworkPtr net)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
int ret = -1;
if (!(obj = testNetworkObjFindByUUID(privconn, net->uuid)))
goto cleanup;
ret = virNetworkObjIsPersistent(obj);
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
static virNetworkPtr
testNetworkCreateXML(virConnectPtr conn, const char *xml)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virNetworkDefPtr newDef;
virNetworkObjPtr obj = NULL;
virNetworkDefPtr def;
virNetworkPtr net = NULL;
virObjectEventPtr event = NULL;
if ((newDef = virNetworkDefParseString(xml)) == NULL)
goto cleanup;
if (!(obj = virNetworkObjAssignDef(privconn->networks, newDef,
VIR_NETWORK_OBJ_LIST_ADD_LIVE |
VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE)))
goto cleanup;
newDef = NULL;
def = virNetworkObjGetDef(obj);
virNetworkObjSetActive(obj, true);
event = virNetworkEventLifecycleNew(def->name, def->uuid,
VIR_NETWORK_EVENT_STARTED,
0);
net = virGetNetwork(conn, def->name, def->uuid);
cleanup:
virNetworkDefFree(newDef);
virObjectEventStateQueue(privconn->eventState, event);
virNetworkObjEndAPI(&obj);
return net;
}
static virNetworkPtr
testNetworkDefineXML(virConnectPtr conn,
const char *xml)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virNetworkDefPtr newDef;
virNetworkObjPtr obj = NULL;
virNetworkDefPtr def;
virNetworkPtr net = NULL;
virObjectEventPtr event = NULL;
if ((newDef = virNetworkDefParseString(xml)) == NULL)
goto cleanup;
if (!(obj = virNetworkObjAssignDef(privconn->networks, newDef, 0)))
goto cleanup;
newDef = NULL;
def = virNetworkObjGetDef(obj);
event = virNetworkEventLifecycleNew(def->name, def->uuid,
VIR_NETWORK_EVENT_DEFINED,
0);
net = virGetNetwork(conn, def->name, def->uuid);
cleanup:
virNetworkDefFree(newDef);
virObjectEventStateQueue(privconn->eventState, event);
virNetworkObjEndAPI(&obj);
return net;
}
static int
testNetworkUndefine(virNetworkPtr net)
{
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
int ret = -1;
virObjectEventPtr event = NULL;
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
if (virNetworkObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("Network '%s' is still running"), net->name);
goto cleanup;
}
event = virNetworkEventLifecycleNew(net->name, net->uuid,
VIR_NETWORK_EVENT_UNDEFINED,
0);
virNetworkObjRemoveInactive(privconn->networks, obj);
ret = 0;
cleanup:
virObjectEventStateQueue(privconn->eventState, event);
virNetworkObjEndAPI(&obj);
return ret;
}
static int
testNetworkUpdate(virNetworkPtr net,
unsigned int command,
unsigned int section,
int parentIndex,
const char *xml,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj = NULL;
int isActive, ret = -1;
virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE |
VIR_NETWORK_UPDATE_AFFECT_CONFIG,
-1);
if (!(obj = testNetworkObjFindByUUID(privconn, net->uuid)))
goto cleanup;
/* VIR_NETWORK_UPDATE_AFFECT_CURRENT means "change LIVE if network
* is active, else change CONFIG
*/
isActive = virNetworkObjIsActive(obj);
if ((flags & (VIR_NETWORK_UPDATE_AFFECT_LIVE
| VIR_NETWORK_UPDATE_AFFECT_CONFIG)) ==
VIR_NETWORK_UPDATE_AFFECT_CURRENT) {
if (isActive)
flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
else
flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
}
/* update the network config in memory/on disk */
if (virNetworkObjUpdate(obj, command, section, parentIndex, xml, flags) < 0)
goto cleanup;
ret = 0;
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
static int
testNetworkCreate(virNetworkPtr net)
{
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
virNetworkDefPtr def;
int ret = -1;
virObjectEventPtr event = NULL;
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
def = virNetworkObjGetDef(obj);
if (virNetworkObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("Network '%s' is already running"), net->name);
goto cleanup;
}
virNetworkObjSetActive(obj, true);
event = virNetworkEventLifecycleNew(def->name, def->uuid,
VIR_NETWORK_EVENT_STARTED,
0);
ret = 0;
cleanup:
virObjectEventStateQueue(privconn->eventState, event);
virNetworkObjEndAPI(&obj);
return ret;
}
static int
testNetworkDestroy(virNetworkPtr net)
{
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
virNetworkDefPtr def;
int ret = -1;
virObjectEventPtr event = NULL;
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
def = virNetworkObjGetDef(obj);
virNetworkObjSetActive(obj, false);
event = virNetworkEventLifecycleNew(def->name, def->uuid,
VIR_NETWORK_EVENT_STOPPED,
0);
if (!virNetworkObjIsPersistent(obj))
virNetworkObjRemoveInactive(privconn->networks, obj);
ret = 0;
cleanup:
virObjectEventStateQueue(privconn->eventState, event);
virNetworkObjEndAPI(&obj);
return ret;
}
static char *
testNetworkGetXMLDesc(virNetworkPtr net,
unsigned int flags)
drivers: prefer unsigned int for flags Now that the public APIs always use unsigned flags, the internal driver callbacks might as well do likewise. * src/driver.h (vrDrvOpen, virDrvDomainCoreDump) (virDrvDomainGetXMLDesc, virDrvNetworkGetXMLDesc) (virDrvNWFilterGetXMLDesc): Update type. * src/remote/remote_protocol.x (remote_open_args) (remote_domain_core_dump_args, remote_domain_get_xml_desc_args) (remote_network_get_xml_desc_args) (remote_nwfilter_get_xml_desc_args): Likewise. * src/test/test_driver.c: Update clients. * src/remote/remote_driver.c: Likewise. * src/xen/xen_hypervisor.c: Likewise. * src/xen/xen_hypervisor.h: Likewise. * src/xen/xen_driver.c: Likewise. * src/xen/xend_internal.c: Likewise. * src/xen/xend_internal.h: Likewise. * src/xen/xm_internal.c: Likewise. * src/xen/xm_internal.h: Likewise. * src/xen/xs_internal.c: Likewise. * src/xen/xs_internal.h: Likewise. * src/xen/xen_inotify.c: Likewise. * src/xen/xen_inotify.h: Likewise. * src/phyp/phyp_driver.c: Likewise. * src/openvz/openvz_driver.c: Likewise. * src/vmware/vmware_driver.c: Likewise. * src/vbox/vbox_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xenapi/xenapi_driver.c: Likewise. * src/esx/esx_driver.c: Likewise. * src/esx/esx_interface_driver.c: Likewise. * src/esx/esx_network_driver.c: Likewise. * src/esx/esx_storage_driver.c: Likewise. * src/esx/esx_device_monitor.c: Likewise. * src/esx/esx_secret_driver.c: Likewise. * src/esx/esx_nwfilter_driver.c: Likewise. * src/interface/netcf_driver.c: Likewise. * src/nwfilter/nwfilter_driver.c: Likewise. * src/libxl/libxl_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/secret/secret_driver.c: Likewise. * src/storage/storage_driver.c: Likewise. * src/node_device/node_device_hal.c: Likewise. * src/node_device/node_device_udev.c: Likewise. * src/remote_protocol-structs: Likewise.
2011-07-06 20:40:19 +00:00
{
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
char *ret = NULL;
virCheckFlags(0, NULL);
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
ret = virNetworkDefFormat(virNetworkObjGetDef(obj), flags);
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
static char *
testNetworkGetBridgeName(virNetworkPtr net)
{
testDriverPtr privconn = net->conn->privateData;
char *bridge = NULL;
virNetworkObjPtr obj;
virNetworkDefPtr def;
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
def = virNetworkObjGetDef(obj);
if (!(def->bridge)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("network '%s' does not have a bridge name."),
def->name);
goto cleanup;
}
ignore_value(VIR_STRDUP(bridge, def->bridge));
cleanup:
virNetworkObjEndAPI(&obj);
return bridge;
}
static int
testNetworkGetAutostart(virNetworkPtr net,
int *autostart)
{
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
int ret = -1;
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
*autostart = virNetworkObjIsAutostart(obj) ? 1 : 0;
ret = 0;
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
static int
testNetworkSetAutostart(virNetworkPtr net,
int autostart)
{
testDriverPtr privconn = net->conn->privateData;
virNetworkObjPtr obj;
bool new_autostart = (autostart != 0);
int ret = -1;
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
goto cleanup;
virNetworkObjSetAutostart(obj, new_autostart);
ret = 0;
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
2008-10-30 17:40:57 +00:00
/*
* Physical host interface routines
*/
static virInterfaceObjPtr
testInterfaceObjFindByName(testDriverPtr privconn,
const char *name)
{
virInterfaceObjPtr obj;
virObjectLock(privconn);
obj = virInterfaceObjListFindByName(privconn->ifaces, name);
virObjectUnlock(privconn);
if (!obj)
virReportError(VIR_ERR_NO_INTERFACE,
_("no interface with matching name '%s'"),
name);
return obj;
}
static int
testConnectNumOfInterfaces(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ninterfaces;
virObjectLock(privconn);
ninterfaces = virInterfaceObjListNumOfInterfaces(privconn->ifaces, true);
virObjectUnlock(privconn);
return ninterfaces;
}
static int
testConnectListInterfaces(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int nnames;
virObjectLock(privconn);
nnames = virInterfaceObjListGetNames(privconn->ifaces, true,
names, maxnames);
virObjectUnlock(privconn);
return nnames;
}
static int
testConnectNumOfDefinedInterfaces(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ninterfaces;
virObjectLock(privconn);
ninterfaces = virInterfaceObjListNumOfInterfaces(privconn->ifaces, false);
virObjectUnlock(privconn);
return ninterfaces;
}
static int
testConnectListDefinedInterfaces(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int nnames;
virObjectLock(privconn);
nnames = virInterfaceObjListGetNames(privconn->ifaces, false,
names, maxnames);
virObjectUnlock(privconn);
return nnames;
}
static int
testConnectListAllInterfaces(virConnectPtr conn,
virInterfacePtr **ifaces,
unsigned int flags)
{
testDriverPtr privconn = conn->privateData;
virCheckFlags(VIR_CONNECT_LIST_INTERFACES_FILTERS_ACTIVE, -1);
return virInterfaceObjListExport(conn, privconn->ifaces, ifaces,
NULL, flags);
}
static virInterfacePtr
testInterfaceLookupByName(virConnectPtr conn,
const char *name)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virInterfaceObjPtr obj;
virInterfaceDefPtr def;
virInterfacePtr ret = NULL;
if (!(obj = testInterfaceObjFindByName(privconn, name)))
return NULL;
def = virInterfaceObjGetDef(obj);
ret = virGetInterface(conn, def->name, def->mac);
virInterfaceObjEndAPI(&obj);
return ret;
}
static virInterfacePtr
testInterfaceLookupByMACString(virConnectPtr conn,
const char *mac)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ifacect;
char *ifacenames[] = { NULL, NULL };
virInterfacePtr ret = NULL;
virObjectLock(privconn);
ifacect = virInterfaceObjListFindByMACString(privconn->ifaces, mac,
ifacenames, 2);
virObjectUnlock(privconn);
if (ifacect == 0) {
virReportError(VIR_ERR_NO_INTERFACE,
_("no interface with matching mac '%s'"), mac);
goto cleanup;
}
if (ifacect > 1) {
virReportError(VIR_ERR_MULTIPLE_INTERFACES, NULL);
goto cleanup;
}
ret = virGetInterface(conn, ifacenames[0], mac);
cleanup:
VIR_FREE(ifacenames[0]);
VIR_FREE(ifacenames[1]);
return ret;
}
static int
testInterfaceIsActive(virInterfacePtr iface)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = iface->conn->privateData;
virInterfaceObjPtr obj;
int ret = -1;
if (!(obj = testInterfaceObjFindByName(privconn, iface->name)))
return -1;
ret = virInterfaceObjIsActive(obj);
virInterfaceObjEndAPI(&obj);
return ret;
}
static int
testInterfaceChangeBegin(virConnectPtr conn,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ret = -1;
virCheckFlags(0, -1);
virObjectLock(privconn);
if (privconn->transaction_running) {
maint: don't permit format strings without % Any time we have a string with no % passed through gettext, a translator can inject a % to cause a stack overread. When there is nothing to format, it's easier to ask for a string that cannot be used as a formatter, by using a trivial "%s" format instead. In the past, we have used --disable-nls to catch some of the offenders, but that doesn't get run very often, and many more uses have crept in. Syntax check to the rescue! The syntax check can catch uses such as virReportError(code, _("split " "string")); by using a sed script to fold context lines into one pattern space before checking for a string without %. This patch is just mechanical insertion of %s; there are probably several messages touched by this patch where we would be better off giving the user more information than a fixed string. * cfg.mk (sc_prohibit_diagnostic_without_format): New rule. * src/datatypes.c (virUnrefConnect, virGetDomain) (virUnrefDomain, virGetNetwork, virUnrefNetwork, virGetInterface) (virUnrefInterface, virGetStoragePool, virUnrefStoragePool) (virGetStorageVol, virUnrefStorageVol, virGetNodeDevice) (virGetSecret, virUnrefSecret, virGetNWFilter, virUnrefNWFilter) (virGetDomainSnapshot, virUnrefDomainSnapshot): Add %s wrapper. * src/lxc/lxc_driver.c (lxcDomainSetBlkioParameters) (lxcDomainGetBlkioParameters): Likewise. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML) (virDomainDiskDefParseXML, virDomainGraphicsDefParseXML): Likewise. * src/conf/network_conf.c (virNetworkDNSHostsDefParseXML) (virNetworkDefParseXML): Likewise. * src/conf/nwfilter_conf.c (virNWFilterIsValidChainName): Likewise. * src/conf/nwfilter_params.c (virNWFilterVarValueCreateSimple) (virNWFilterVarAccessParse): Likewise. * src/libvirt.c (virDomainSave, virDomainSaveFlags) (virDomainRestore, virDomainRestoreFlags) (virDomainSaveImageGetXMLDesc, virDomainSaveImageDefineXML) (virDomainCoreDump, virDomainGetXMLDesc) (virDomainMigrateVersion1, virDomainMigrateVersion2) (virDomainMigrateVersion3, virDomainMigrate, virDomainMigrate2) (virStreamSendAll, virStreamRecvAll) (virDomainSnapshotGetXMLDesc): Likewise. * src/nwfilter/nwfilter_dhcpsnoop.c (virNWFilterSnoopReqLeaseDel) (virNWFilterDHCPSnoopReq): Likewise. * src/openvz/openvz_driver.c (openvzUpdateDevice): Likewise. * src/openvz/openvz_util.c (openvzKBPerPages): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupCgroup): Likewise. * src/qemu/qemu_command.c (qemuBuildHubDevStr, qemuBuildChrChardevStr) (qemuBuildCommandLine): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetPercpuStats): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/rpc/virnetsaslcontext.c (virNetSASLSessionGetIdentity): Likewise. * src/rpc/virnetsocket.c (virNetSocketNewConnectUNIX) (virNetSocketSendFD, virNetSocketRecvFD): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskBuildPool): Likewise. * src/storage/storage_backend_fs.c (virStorageBackendFileSystemProbe) (virStorageBackendFileSystemBuild): Likewise. * src/storage/storage_backend_rbd.c (virStorageBackendRBDOpenRADOSConn): Likewise. * src/storage/storage_driver.c (storageVolumeResize): Likewise. * src/test/test_driver.c (testInterfaceChangeBegin) (testInterfaceChangeCommit, testInterfaceChangeRollback): Likewise. * src/vbox/vbox_tmpl.c (vboxListAllDomains): Likewise. * src/xenxs/xen_sxpr.c (xenFormatSxprDisk, xenFormatSxpr): Likewise. * src/xenxs/xen_xm.c (xenXMConfigGetUUID, xenFormatXMDisk) (xenFormatXM): Likewise.
2012-07-23 20:33:08 +00:00
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("there is another transaction running."));
goto cleanup;
}
privconn->transaction_running = true;
if (!(privconn->backupIfaces = virInterfaceObjListClone(privconn->ifaces)))
goto cleanup;
ret = 0;
cleanup:
virObjectUnlock(privconn);
return ret;
}
static int
testInterfaceChangeCommit(virConnectPtr conn,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ret = -1;
virCheckFlags(0, -1);
virObjectLock(privconn);
if (!privconn->transaction_running) {
maint: don't permit format strings without % Any time we have a string with no % passed through gettext, a translator can inject a % to cause a stack overread. When there is nothing to format, it's easier to ask for a string that cannot be used as a formatter, by using a trivial "%s" format instead. In the past, we have used --disable-nls to catch some of the offenders, but that doesn't get run very often, and many more uses have crept in. Syntax check to the rescue! The syntax check can catch uses such as virReportError(code, _("split " "string")); by using a sed script to fold context lines into one pattern space before checking for a string without %. This patch is just mechanical insertion of %s; there are probably several messages touched by this patch where we would be better off giving the user more information than a fixed string. * cfg.mk (sc_prohibit_diagnostic_without_format): New rule. * src/datatypes.c (virUnrefConnect, virGetDomain) (virUnrefDomain, virGetNetwork, virUnrefNetwork, virGetInterface) (virUnrefInterface, virGetStoragePool, virUnrefStoragePool) (virGetStorageVol, virUnrefStorageVol, virGetNodeDevice) (virGetSecret, virUnrefSecret, virGetNWFilter, virUnrefNWFilter) (virGetDomainSnapshot, virUnrefDomainSnapshot): Add %s wrapper. * src/lxc/lxc_driver.c (lxcDomainSetBlkioParameters) (lxcDomainGetBlkioParameters): Likewise. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML) (virDomainDiskDefParseXML, virDomainGraphicsDefParseXML): Likewise. * src/conf/network_conf.c (virNetworkDNSHostsDefParseXML) (virNetworkDefParseXML): Likewise. * src/conf/nwfilter_conf.c (virNWFilterIsValidChainName): Likewise. * src/conf/nwfilter_params.c (virNWFilterVarValueCreateSimple) (virNWFilterVarAccessParse): Likewise. * src/libvirt.c (virDomainSave, virDomainSaveFlags) (virDomainRestore, virDomainRestoreFlags) (virDomainSaveImageGetXMLDesc, virDomainSaveImageDefineXML) (virDomainCoreDump, virDomainGetXMLDesc) (virDomainMigrateVersion1, virDomainMigrateVersion2) (virDomainMigrateVersion3, virDomainMigrate, virDomainMigrate2) (virStreamSendAll, virStreamRecvAll) (virDomainSnapshotGetXMLDesc): Likewise. * src/nwfilter/nwfilter_dhcpsnoop.c (virNWFilterSnoopReqLeaseDel) (virNWFilterDHCPSnoopReq): Likewise. * src/openvz/openvz_driver.c (openvzUpdateDevice): Likewise. * src/openvz/openvz_util.c (openvzKBPerPages): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupCgroup): Likewise. * src/qemu/qemu_command.c (qemuBuildHubDevStr, qemuBuildChrChardevStr) (qemuBuildCommandLine): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetPercpuStats): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/rpc/virnetsaslcontext.c (virNetSASLSessionGetIdentity): Likewise. * src/rpc/virnetsocket.c (virNetSocketNewConnectUNIX) (virNetSocketSendFD, virNetSocketRecvFD): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskBuildPool): Likewise. * src/storage/storage_backend_fs.c (virStorageBackendFileSystemProbe) (virStorageBackendFileSystemBuild): Likewise. * src/storage/storage_backend_rbd.c (virStorageBackendRBDOpenRADOSConn): Likewise. * src/storage/storage_driver.c (storageVolumeResize): Likewise. * src/test/test_driver.c (testInterfaceChangeBegin) (testInterfaceChangeCommit, testInterfaceChangeRollback): Likewise. * src/vbox/vbox_tmpl.c (vboxListAllDomains): Likewise. * src/xenxs/xen_sxpr.c (xenFormatSxprDisk, xenFormatSxpr): Likewise. * src/xenxs/xen_xm.c (xenXMConfigGetUUID, xenFormatXMDisk) (xenFormatXM): Likewise.
2012-07-23 20:33:08 +00:00
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("no transaction running, "
"nothing to be committed."));
goto cleanup;
}
virObjectUnref(privconn->backupIfaces);
privconn->transaction_running = false;
ret = 0;
cleanup:
virObjectUnlock(privconn);
return ret;
}
static int
testInterfaceChangeRollback(virConnectPtr conn,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ret = -1;
virCheckFlags(0, -1);
virObjectLock(privconn);
if (!privconn->transaction_running) {
maint: don't permit format strings without % Any time we have a string with no % passed through gettext, a translator can inject a % to cause a stack overread. When there is nothing to format, it's easier to ask for a string that cannot be used as a formatter, by using a trivial "%s" format instead. In the past, we have used --disable-nls to catch some of the offenders, but that doesn't get run very often, and many more uses have crept in. Syntax check to the rescue! The syntax check can catch uses such as virReportError(code, _("split " "string")); by using a sed script to fold context lines into one pattern space before checking for a string without %. This patch is just mechanical insertion of %s; there are probably several messages touched by this patch where we would be better off giving the user more information than a fixed string. * cfg.mk (sc_prohibit_diagnostic_without_format): New rule. * src/datatypes.c (virUnrefConnect, virGetDomain) (virUnrefDomain, virGetNetwork, virUnrefNetwork, virGetInterface) (virUnrefInterface, virGetStoragePool, virUnrefStoragePool) (virGetStorageVol, virUnrefStorageVol, virGetNodeDevice) (virGetSecret, virUnrefSecret, virGetNWFilter, virUnrefNWFilter) (virGetDomainSnapshot, virUnrefDomainSnapshot): Add %s wrapper. * src/lxc/lxc_driver.c (lxcDomainSetBlkioParameters) (lxcDomainGetBlkioParameters): Likewise. * src/conf/domain_conf.c (virSecurityDeviceLabelDefParseXML) (virDomainDiskDefParseXML, virDomainGraphicsDefParseXML): Likewise. * src/conf/network_conf.c (virNetworkDNSHostsDefParseXML) (virNetworkDefParseXML): Likewise. * src/conf/nwfilter_conf.c (virNWFilterIsValidChainName): Likewise. * src/conf/nwfilter_params.c (virNWFilterVarValueCreateSimple) (virNWFilterVarAccessParse): Likewise. * src/libvirt.c (virDomainSave, virDomainSaveFlags) (virDomainRestore, virDomainRestoreFlags) (virDomainSaveImageGetXMLDesc, virDomainSaveImageDefineXML) (virDomainCoreDump, virDomainGetXMLDesc) (virDomainMigrateVersion1, virDomainMigrateVersion2) (virDomainMigrateVersion3, virDomainMigrate, virDomainMigrate2) (virStreamSendAll, virStreamRecvAll) (virDomainSnapshotGetXMLDesc): Likewise. * src/nwfilter/nwfilter_dhcpsnoop.c (virNWFilterSnoopReqLeaseDel) (virNWFilterDHCPSnoopReq): Likewise. * src/openvz/openvz_driver.c (openvzUpdateDevice): Likewise. * src/openvz/openvz_util.c (openvzKBPerPages): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupCgroup): Likewise. * src/qemu/qemu_command.c (qemuBuildHubDevStr, qemuBuildChrChardevStr) (qemuBuildCommandLine): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetPercpuStats): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/rpc/virnetsaslcontext.c (virNetSASLSessionGetIdentity): Likewise. * src/rpc/virnetsocket.c (virNetSocketNewConnectUNIX) (virNetSocketSendFD, virNetSocketRecvFD): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskBuildPool): Likewise. * src/storage/storage_backend_fs.c (virStorageBackendFileSystemProbe) (virStorageBackendFileSystemBuild): Likewise. * src/storage/storage_backend_rbd.c (virStorageBackendRBDOpenRADOSConn): Likewise. * src/storage/storage_driver.c (storageVolumeResize): Likewise. * src/test/test_driver.c (testInterfaceChangeBegin) (testInterfaceChangeCommit, testInterfaceChangeRollback): Likewise. * src/vbox/vbox_tmpl.c (vboxListAllDomains): Likewise. * src/xenxs/xen_sxpr.c (xenFormatSxprDisk, xenFormatSxpr): Likewise. * src/xenxs/xen_xm.c (xenXMConfigGetUUID, xenFormatXMDisk) (xenFormatXM): Likewise.
2012-07-23 20:33:08 +00:00
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("no transaction running, "
"nothing to rollback."));
goto cleanup;
}
virObjectUnref(privconn->ifaces);
privconn->ifaces = privconn->backupIfaces;
privconn->backupIfaces = NULL;
privconn->transaction_running = false;
ret = 0;
cleanup:
virObjectUnlock(privconn);
return ret;
}
static char *
testInterfaceGetXMLDesc(virInterfacePtr iface,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = iface->conn->privateData;
virInterfaceObjPtr obj;
virInterfaceDefPtr def;
char *ret = NULL;
virCheckFlags(0, NULL);
if (!(obj = testInterfaceObjFindByName(privconn, iface->name)))
return NULL;
def = virInterfaceObjGetDef(obj);
ret = virInterfaceDefFormat(def);
virInterfaceObjEndAPI(&obj);
return ret;
}
static virInterfacePtr
testInterfaceDefineXML(virConnectPtr conn,
const char *xmlStr,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virInterfaceDefPtr def;
virInterfaceObjPtr obj = NULL;
virInterfaceDefPtr objdef;
virInterfacePtr ret = NULL;
virCheckFlags(0, NULL);
virObjectLock(privconn);
if ((def = virInterfaceDefParseString(xmlStr)) == NULL)
goto cleanup;
if ((obj = virInterfaceObjListAssignDef(privconn->ifaces, def)) == NULL)
goto cleanup;
def = NULL;
objdef = virInterfaceObjGetDef(obj);
ret = virGetInterface(conn, objdef->name, objdef->mac);
cleanup:
virInterfaceDefFree(def);
virInterfaceObjEndAPI(&obj);
virObjectUnlock(privconn);
return ret;
}
static int
testInterfaceUndefine(virInterfacePtr iface)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = iface->conn->privateData;
virInterfaceObjPtr obj;
if (!(obj = testInterfaceObjFindByName(privconn, iface->name)))
return -1;
virInterfaceObjListRemove(privconn->ifaces, obj);
virObjectUnref(obj);
return 0;
}
static int
testInterfaceCreate(virInterfacePtr iface,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = iface->conn->privateData;
virInterfaceObjPtr obj;
int ret = -1;
virCheckFlags(0, -1);
if (!(obj = testInterfaceObjFindByName(privconn, iface->name)))
return -1;
if (virInterfaceObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID, NULL);
goto cleanup;
}
virInterfaceObjSetActive(obj, true);
ret = 0;
cleanup:
virInterfaceObjEndAPI(&obj);
return ret;
}
static int
testInterfaceDestroy(virInterfacePtr iface,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = iface->conn->privateData;
virInterfaceObjPtr obj;
int ret = -1;
virCheckFlags(0, -1);
if (!(obj = testInterfaceObjFindByName(privconn, iface->name)))
return -1;
if (!virInterfaceObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID, NULL);
goto cleanup;
}
virInterfaceObjSetActive(obj, false);
ret = 0;
cleanup:
virInterfaceObjEndAPI(&obj);
return ret;
}
2008-10-30 17:40:57 +00:00
/*
* Storage Driver routines
*/
static int
testStoragePoolObjSetDefaults(virStoragePoolObjPtr obj)
{
char *configFile;
virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
def->capacity = defaultPoolCap;
def->allocation = defaultPoolAlloc;
def->available = defaultPoolCap - defaultPoolAlloc;
2008-10-30 17:40:57 +00:00
if (VIR_STRDUP(configFile, "") < 0)
return -1;
virStoragePoolObjSetConfigFile(obj, configFile);
return 0;
2008-10-30 17:40:57 +00:00
}
static virStoragePoolObjPtr
testStoragePoolObjFindByName(testDriverPtr privconn,
const char *name)
{
virStoragePoolObjPtr obj;
virObjectLock(privconn);
obj = virStoragePoolObjFindByName(privconn->pools, name);
virObjectUnlock(privconn);
if (!obj)
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching name '%s'"),
name);
return obj;
}
static virStoragePoolObjPtr
testStoragePoolObjFindActiveByName(testDriverPtr privconn,
const char *name)
{
virStoragePoolObjPtr obj;
if (!(obj = testStoragePoolObjFindByName(privconn, name)))
return NULL;
if (!virStoragePoolObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is not active"), name);
virStoragePoolObjEndAPI(&obj);
return NULL;
}
return obj;
}
static virStoragePoolObjPtr
testStoragePoolObjFindInactiveByName(testDriverPtr privconn,
const char *name)
{
virStoragePoolObjPtr obj;
if (!(obj = testStoragePoolObjFindByName(privconn, name)))
return NULL;
if (virStoragePoolObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("storage pool '%s' is active"), name);
virStoragePoolObjEndAPI(&obj);
return NULL;
}
return obj;
}
static virStoragePoolObjPtr
testStoragePoolObjFindByUUID(testDriverPtr privconn,
const unsigned char *uuid)
{
virStoragePoolObjPtr obj;
char uuidstr[VIR_UUID_STRING_BUFLEN];
virObjectLock(privconn);
obj = virStoragePoolObjFindByUUID(privconn->pools, uuid);
virObjectUnlock(privconn);
if (!obj) {
virUUIDFormat(uuid, uuidstr);
virReportError(VIR_ERR_NO_STORAGE_POOL,
_("no storage pool with matching uuid '%s'"),
uuidstr);
}
return obj;
}
2008-10-30 17:40:57 +00:00
static virStoragePoolPtr
testStoragePoolLookupByUUID(virConnectPtr conn,
const unsigned char *uuid)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStoragePoolPtr pool = NULL;
2008-10-30 17:40:57 +00:00
if (!(obj = testStoragePoolObjFindByUUID(privconn, uuid)))
return NULL;
def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
virStoragePoolObjEndAPI(&obj);
return pool;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static virStoragePoolPtr
testStoragePoolLookupByName(virConnectPtr conn,
const char *name)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStoragePoolPtr pool = NULL;
2008-10-30 17:40:57 +00:00
if (!(obj = testStoragePoolObjFindByName(privconn, name)))
return NULL;
def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
virStoragePoolObjEndAPI(&obj);
return pool;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static virStoragePoolPtr
testStoragePoolLookupByVolume(virStorageVolPtr vol)
{
2008-10-30 17:40:57 +00:00
return testStoragePoolLookupByName(vol->conn, vol->pool);
}
2008-10-30 17:40:57 +00:00
static int
testConnectNumOfStoragePools(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int numActive = 0;
2008-10-30 17:40:57 +00:00
virObjectLock(privconn);
numActive = virStoragePoolObjNumOfStoragePools(privconn->pools, conn,
true, NULL);
virObjectUnlock(privconn);
2008-10-30 17:40:57 +00:00
return numActive;
}
2008-10-30 17:40:57 +00:00
static int
testConnectListStoragePools(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int n = 0;
2008-10-30 17:40:57 +00:00
virObjectLock(privconn);
n = virStoragePoolObjGetNames(privconn->pools, conn, true, NULL,
names, maxnames);
virObjectUnlock(privconn);
2008-10-30 17:40:57 +00:00
return n;
}
2008-10-30 17:40:57 +00:00
static int
testConnectNumOfDefinedStoragePools(virConnectPtr conn)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int numInactive = 0;
2008-10-30 17:40:57 +00:00
virObjectLock(privconn);
numInactive = virStoragePoolObjNumOfStoragePools(privconn->pools, conn,
false, NULL);
virObjectUnlock(privconn);
2008-10-30 17:40:57 +00:00
return numInactive;
}
2008-10-30 17:40:57 +00:00
static int
testConnectListDefinedStoragePools(virConnectPtr conn,
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int n = 0;
2008-10-30 17:40:57 +00:00
virObjectLock(privconn);
n = virStoragePoolObjGetNames(privconn->pools, conn, false, NULL,
names, maxnames);
virObjectUnlock(privconn);
2008-10-30 17:40:57 +00:00
return n;
}
static int
testConnectListAllStoragePools(virConnectPtr conn,
virStoragePoolPtr **pools,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
int ret = -1;
virCheckFlags(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ALL, -1);
virObjectLock(privconn);
ret = virStoragePoolObjListExport(conn, privconn->pools, pools,
NULL, flags);
virObjectUnlock(privconn);
return ret;
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolIsActive(virStoragePoolPtr pool)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
int ret = -1;
if (!(obj = testStoragePoolObjFindByUUID(privconn, pool->uuid)))
goto cleanup;
ret = virStoragePoolObjIsActive(obj);
cleanup:
if (obj)
virStoragePoolObjEndAPI(&obj);
return ret;
}
static int
testStoragePoolIsPersistent(virStoragePoolPtr pool)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
int ret = -1;
if (!(obj = testStoragePoolObjFindByUUID(privconn, pool->uuid)))
return -1;
ret = virStoragePoolObjGetConfigFile(obj) ? 1 : 0;
virStoragePoolObjEndAPI(&obj);
return ret;
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolCreate(virStoragePoolPtr pool,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virObjectEventPtr event = NULL;
virCheckFlags(0, -1);
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
virStoragePoolObjSetActive(obj, true);
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
VIR_STORAGE_POOL_EVENT_STARTED,
0);
2008-10-30 17:40:57 +00:00
virObjectEventStateQueue(privconn->eventState, event);
virStoragePoolObjEndAPI(&obj);
return 0;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static char *
testConnectFindStoragePoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
const char *type,
const char *srcSpec,
unsigned int flags)
2008-10-30 17:40:57 +00:00
{
int pool_type;
char *ret = NULL;
VIR_AUTOPTR(virStoragePoolSource) source = NULL;
virCheckFlags(0, NULL);
pool_type = virStoragePoolTypeFromString(type);
if (!pool_type) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown storage pool type %s"), type);
return NULL;
}
if (srcSpec) {
source = virStoragePoolDefParseSourceString(srcSpec, pool_type);
if (!source)
return NULL;
}
switch (pool_type) {
case VIR_STORAGE_POOL_LOGICAL:
ignore_value(VIR_STRDUP(ret, defaultPoolSourcesLogicalXML));
return ret;
case VIR_STORAGE_POOL_NETFS:
if (!source || !source->hosts[0].name) {
virReportError(VIR_ERR_INVALID_ARG,
"%s", _("hostname must be specified for netfs sources"));
return NULL;
}
ignore_value(virAsprintf(&ret, defaultPoolSourcesNetFSXML,
source->hosts[0].name));
return ret;
default:
virReportError(VIR_ERR_NO_SUPPORT,
_("pool type '%s' does not support source discovery"), type);
}
return NULL;
2008-10-30 17:40:57 +00:00
}
static virNodeDeviceObjPtr
testNodeDeviceMockCreateVport(testDriverPtr driver,
const char *wwnn,
const char *wwpn);
static int
testCreateVport(testDriverPtr driver,
const char *wwnn,
const char *wwpn)
{
virNodeDeviceObjPtr obj = NULL;
/* The storage_backend_scsi createVport() will use the input adapter
* fields parent name, parent_wwnn/parent_wwpn, or parent_fabric_wwn
* in order to determine whether the provided parent can be used to
* create a vHBA or will find "an available vport capable" to create
* a vHBA. In order to do this, it uses the virVHBA* API's which traverse
* the sysfs looking at various fields (rather than going via nodedev).
*
* Since the test environ doesn't have the sysfs for the storage pool
* test, at least for now use the node device test infrastructure to
* create the vHBA. In the long run the result is the same. */
if (!(obj = testNodeDeviceMockCreateVport(driver, wwnn, wwpn)))
return -1;
virNodeDeviceObjEndAPI(&obj);
return 0;
}
2008-10-30 17:40:57 +00:00
static virStoragePoolPtr
testStoragePoolCreateXML(virConnectPtr conn,
const char *xml,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virStoragePoolObjPtr obj = NULL;
virStoragePoolDefPtr def;
virStoragePoolPtr pool = NULL;
virObjectEventPtr event = NULL;
VIR_AUTOPTR(virStoragePoolDef) newDef = NULL;
2008-10-30 17:40:57 +00:00
virCheckFlags(0, NULL);
virObjectLock(privconn);
if (!(newDef = virStoragePoolDefParseString(xml)))
goto cleanup;
2008-10-30 17:40:57 +00:00
if (!(obj = virStoragePoolObjAssignDef(privconn->pools, newDef, true)))
goto cleanup;
newDef = NULL;
def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
if (def->source.adapter.type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
/* In the real code, we'd call virVHBAManageVport followed by
* find_new_device, but we cannot do that here since we're not
* mocking udev. The mock routine will copy an existing vHBA and
* rename a few fields to mock that. */
if (testCreateVport(privconn,
def->source.adapter.data.fchost.wwnn,
def->source.adapter.data.fchost.wwpn) < 0) {
virStoragePoolObjRemove(privconn->pools, obj);
virObjectUnref(obj);
obj = NULL;
goto cleanup;
}
}
if (testStoragePoolObjSetDefaults(obj) == -1) {
virStoragePoolObjRemove(privconn->pools, obj);
virObjectUnref(obj);
obj = NULL;
goto cleanup;
2008-10-30 17:40:57 +00:00
}
/* *SetDefaults fills this in for the persistent pools, but this
* would be a transient pool so remove it; otherwise, the Destroy
* code will not Remove the pool */
virStoragePoolObjSetConfigFile(obj, NULL);
virStoragePoolObjSetActive(obj, true);
2008-10-30 17:40:57 +00:00
event = virStoragePoolEventLifecycleNew(def->name, def->uuid,
VIR_STORAGE_POOL_EVENT_STARTED,
0);
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
cleanup:
virObjectEventStateQueue(privconn->eventState, event);
virStoragePoolObjEndAPI(&obj);
virObjectUnlock(privconn);
return pool;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static virStoragePoolPtr
testStoragePoolDefineXML(virConnectPtr conn,
const char *xml,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virStoragePoolObjPtr obj = NULL;
virStoragePoolDefPtr def;
virStoragePoolPtr pool = NULL;
virObjectEventPtr event = NULL;
VIR_AUTOPTR(virStoragePoolDef) newDef = NULL;
2008-10-30 17:40:57 +00:00
virCheckFlags(0, NULL);
virObjectLock(privconn);
if (!(newDef = virStoragePoolDefParseString(xml)))
goto cleanup;
2008-10-30 17:40:57 +00:00
newDef->capacity = defaultPoolCap;
newDef->allocation = defaultPoolAlloc;
newDef->available = defaultPoolCap - defaultPoolAlloc;
2008-10-30 17:40:57 +00:00
if (!(obj = virStoragePoolObjAssignDef(privconn->pools, newDef, false)))
goto cleanup;
newDef = NULL;
def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
event = virStoragePoolEventLifecycleNew(def->name, def->uuid,
VIR_STORAGE_POOL_EVENT_DEFINED,
0);
if (testStoragePoolObjSetDefaults(obj) == -1) {
virStoragePoolObjRemove(privconn->pools, obj);
virObjectUnref(obj);
obj = NULL;
goto cleanup;
2008-10-30 17:40:57 +00:00
}
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
cleanup:
virObjectEventStateQueue(privconn->eventState, event);
virStoragePoolObjEndAPI(&obj);
virObjectUnlock(privconn);
return pool;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolUndefine(virStoragePoolPtr pool)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virObjectEventPtr event = NULL;
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
VIR_STORAGE_POOL_EVENT_UNDEFINED,
0);
virStoragePoolObjRemove(privconn->pools, obj);
virObjectUnref(obj);
2008-10-30 17:40:57 +00:00
virObjectEventStateQueue(privconn->eventState, event);
return 0;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolBuild(virStoragePoolPtr pool,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virObjectEventPtr event = NULL;
virCheckFlags(0, -1);
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
VIR_STORAGE_POOL_EVENT_CREATED,
0);
virStoragePoolObjEndAPI(&obj);
virObjectEventStateQueue(privconn->eventState, event);
return 0;
2008-10-30 17:40:57 +00:00
}
static int
testDestroyVport(testDriverPtr privconn,
const char *wwnn ATTRIBUTE_UNUSED,
const char *wwpn ATTRIBUTE_UNUSED)
{
virNodeDeviceObjPtr obj = NULL;
virObjectEventPtr event = NULL;
/* NB: Cannot use virVHBAGetHostByWWN (yet) like the storage_backend_scsi
* deleteVport() helper since that traverses the file system looking for
* the wwnn/wwpn. So our choice short term is to cheat and use the name
* (scsi_host12) we know was created.
*
* Reaching across the boundaries of space and time into the
* Node Device in order to remove */
if (!(obj = virNodeDeviceObjListFindByName(privconn->devs,
"scsi_host12"))) {
virReportError(VIR_ERR_NO_NODE_DEVICE, "%s",
_("no node device with matching name 'scsi_host12'"));
return -1;
}
event = virNodeDeviceEventLifecycleNew("scsi_host12",
VIR_NODE_DEVICE_EVENT_DELETED,
0);
virNodeDeviceObjListRemove(privconn->devs, obj);
virObjectUnref(obj);
virObjectEventStateQueue(privconn->eventState, event);
return 0;
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolDestroy(virStoragePoolPtr pool)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
int ret = -1;
virObjectEventPtr event = NULL;
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return -1;
def = virStoragePoolObjGetDef(obj);
virStoragePoolObjSetActive(obj, false);
if (def->source.adapter.type == VIR_STORAGE_ADAPTER_TYPE_FC_HOST) {
if (testDestroyVport(privconn,
def->source.adapter.data.fchost.wwnn,
def->source.adapter.data.fchost.wwpn) < 0)
goto cleanup;
}
event = virStoragePoolEventLifecycleNew(def->name,
def->uuid,
VIR_STORAGE_POOL_EVENT_STOPPED,
0);
2008-10-30 17:40:57 +00:00
if (!(virStoragePoolObjGetConfigFile(obj))) {
virStoragePoolObjRemove(privconn->pools, obj);
virObjectUnref(obj);
obj = NULL;
}
ret = 0;
2008-10-30 17:40:57 +00:00
cleanup:
virObjectEventStateQueue(privconn->eventState, event);
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
static int
testStoragePoolDelete(virStoragePoolPtr pool,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virObjectEventPtr event = NULL;
virCheckFlags(0, -1);
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
VIR_STORAGE_POOL_EVENT_DELETED,
0);
virObjectEventStateQueue(privconn->eventState, event);
virStoragePoolObjEndAPI(&obj);
return 0;
2008-10-30 17:40:57 +00:00
}
static int
testStoragePoolRefresh(virStoragePoolPtr pool,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virObjectEventPtr event = NULL;
virCheckFlags(0, -1);
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return -1;
event = virStoragePoolEventRefreshNew(pool->name, pool->uuid);
2008-10-30 17:40:57 +00:00
virObjectEventStateQueue(privconn->eventState, event);
virStoragePoolObjEndAPI(&obj);
return 0;
2008-10-30 17:40:57 +00:00
}
static int
testStoragePoolGetInfo(virStoragePoolPtr pool,
virStoragePoolInfoPtr info)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
return -1;
def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
memset(info, 0, sizeof(virStoragePoolInfo));
if (virStoragePoolObjIsActive(obj))
2008-10-30 17:40:57 +00:00
info->state = VIR_STORAGE_POOL_RUNNING;
else
info->state = VIR_STORAGE_POOL_INACTIVE;
info->capacity = def->capacity;
info->allocation = def->allocation;
info->available = def->available;
2008-10-30 17:40:57 +00:00
virStoragePoolObjEndAPI(&obj);
return 0;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static char *
testStoragePoolGetXMLDesc(virStoragePoolPtr pool,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
char *ret = NULL;
virCheckFlags(0, NULL);
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
return NULL;
ret = virStoragePoolDefFormat(virStoragePoolObjGetDef(obj));
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolGetAutostart(virStoragePoolPtr pool,
int *autostart)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
if (!virStoragePoolObjGetConfigFile(obj))
2008-10-30 17:40:57 +00:00
*autostart = 0;
else
*autostart = virStoragePoolObjIsAutostart(obj) ? 1 : 0;
2008-10-30 17:40:57 +00:00
virStoragePoolObjEndAPI(&obj);
return 0;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolSetAutostart(virStoragePoolPtr pool,
int autostart)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
bool new_autostart = (autostart != 0);
int ret = -1;
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
if (!virStoragePoolObjGetConfigFile(obj)) {
virReportError(VIR_ERR_INVALID_ARG,
"%s", _("pool has no config file"));
goto cleanup;
2008-10-30 17:40:57 +00:00
}
virStoragePoolObjSetAutostart(obj, new_autostart);
ret = 0;
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
static int
testStoragePoolNumOfVolumes(virStoragePoolPtr pool)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
int ret = -1;
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return -1;
2008-10-30 17:40:57 +00:00
ret = virStoragePoolObjNumOfVolumes(obj, pool->conn, NULL);
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static int
testStoragePoolListVolumes(virStoragePoolPtr pool,
2008-10-30 17:40:57 +00:00
char **const names,
int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
int n = -1;
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return -1;
n = virStoragePoolObjVolumeGetNames(obj, pool->conn, NULL, names, maxnames);
2008-10-30 17:40:57 +00:00
virStoragePoolObjEndAPI(&obj);
2008-10-30 17:40:57 +00:00
return n;
}
static int
testStoragePoolListAllVolumes(virStoragePoolPtr pool,
virStorageVolPtr **vols,
unsigned int flags)
{
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
int ret = -1;
virCheckFlags(0, -1);
if (!(obj = testStoragePoolObjFindByUUID(privconn, pool->uuid)))
return -1;
if (!virStoragePoolObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("storage pool is not active"));
goto cleanup;
}
ret = virStoragePoolObjVolumeListExport(pool->conn, obj, vols, NULL);
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
}
2008-10-30 17:40:57 +00:00
static virStorageVolDefPtr
testStorageVolDefFindByName(virStoragePoolObjPtr obj,
const char *name)
{
virStorageVolDefPtr privvol;
if (!(privvol = virStorageVolDefFindByName(obj, name))) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"), name);
}
return privvol;
}
2008-10-30 17:40:57 +00:00
static virStorageVolPtr
testStorageVolLookupByName(virStoragePoolPtr pool,
const char *name)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStorageVolDefPtr privvol;
virStorageVolPtr ret = NULL;
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return NULL;
def = virStoragePoolObjGetDef(obj);
if (!(privvol = testStorageVolDefFindByName(obj, name)))
goto cleanup;
2008-10-30 17:40:57 +00:00
ret = virGetStorageVol(pool->conn, def->name,
privvol->name, privvol->key,
NULL, NULL);
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
struct storageVolLookupData {
const char *key;
const char *path;
virStorageVolDefPtr voldef;
};
static bool
testStorageVolLookupByKeyCallback(virStoragePoolObjPtr obj,
const void *opaque)
{
struct storageVolLookupData *data = (struct storageVolLookupData *)opaque;
if (virStoragePoolObjIsActive(obj))
data->voldef = virStorageVolDefFindByKey(obj, data->key);
return !!data->voldef;
}
2008-10-30 17:40:57 +00:00
static virStorageVolPtr
testStorageVolLookupByKey(virConnectPtr conn,
const char *key)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
struct storageVolLookupData data = {
.key = key, .voldef = NULL };
virStorageVolPtr vol = NULL;
2008-10-30 17:40:57 +00:00
virObjectLock(privconn);
if ((obj = virStoragePoolObjListSearch(privconn->pools,
testStorageVolLookupByKeyCallback,
&data)) && data.voldef) {
def = virStoragePoolObjGetDef(obj);
vol = virGetStorageVol(conn, def->name,
data.voldef->name, data.voldef->key,
NULL, NULL);
virStoragePoolObjEndAPI(&obj);
2008-10-30 17:40:57 +00:00
}
virObjectUnlock(privconn);
2008-10-30 17:40:57 +00:00
if (!vol)
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching key '%s'"), key);
return vol;
}
static bool
testStorageVolLookupByPathCallback(virStoragePoolObjPtr obj,
const void *opaque)
{
struct storageVolLookupData *data = (struct storageVolLookupData *)opaque;
if (virStoragePoolObjIsActive(obj))
data->voldef = virStorageVolDefFindByPath(obj, data->path);
return !!data->voldef;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static virStorageVolPtr
testStorageVolLookupByPath(virConnectPtr conn,
const char *path)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
struct storageVolLookupData data = {
.path = path, .voldef = NULL };
virStorageVolPtr vol = NULL;
2008-10-30 17:40:57 +00:00
virObjectLock(privconn);
if ((obj = virStoragePoolObjListSearch(privconn->pools,
testStorageVolLookupByPathCallback,
&data)) && data.voldef) {
def = virStoragePoolObjGetDef(obj);
vol = virGetStorageVol(conn, def->name,
data.voldef->name, data.voldef->key,
NULL, NULL);
virStoragePoolObjEndAPI(&obj);
2008-10-30 17:40:57 +00:00
}
virObjectUnlock(privconn);
2008-10-30 17:40:57 +00:00
if (!vol)
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching path '%s'"), path);
return vol;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static virStorageVolPtr
testStorageVolCreateXML(virStoragePoolPtr pool,
const char *xmldesc,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStorageVolPtr ret = NULL;
VIR_AUTOPTR(virStorageVolDef) privvol = NULL;
virCheckFlags(0, NULL);
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return NULL;
def = virStoragePoolObjGetDef(obj);
2008-10-30 17:40:57 +00:00
privvol = virStorageVolDefParseString(def, xmldesc, 0);
if (privvol == NULL)
goto cleanup;
if (virStorageVolDefFindByName(obj, privvol->name)) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("storage vol already exists"));
goto cleanup;
2008-10-30 17:40:57 +00:00
}
/* Make sure enough space */
if ((def->allocation + privvol->target.allocation) >
def->capacity) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Not enough free space in pool for volume '%s'"),
privvol->name);
goto cleanup;
2008-10-30 17:40:57 +00:00
}
if (virAsprintf(&privvol->target.path, "%s/%s",
def->target.path, privvol->name) < 0)
goto cleanup;
2008-10-30 17:40:57 +00:00
if (VIR_STRDUP(privvol->key, privvol->target.path) < 0 ||
virStoragePoolObjAddVol(obj, privvol) < 0)
goto cleanup;
2008-10-30 17:40:57 +00:00
def->allocation += privvol->target.allocation;
def->available = (def->capacity - def->allocation);
2008-10-30 17:40:57 +00:00
ret = virGetStorageVol(pool->conn, def->name,
privvol->name, privvol->key,
NULL, NULL);
2008-12-07 20:45:28 +00:00
privvol = NULL;
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
static virStorageVolPtr
testStorageVolCreateXMLFrom(virStoragePoolPtr pool,
const char *xmldesc,
virStorageVolPtr clonevol,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = pool->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStorageVolDefPtr origvol = NULL;
virStorageVolPtr ret = NULL;
VIR_AUTOPTR(virStorageVolDef) privvol = NULL;
virCheckFlags(0, NULL);
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
return NULL;
def = virStoragePoolObjGetDef(obj);
privvol = virStorageVolDefParseString(def, xmldesc, 0);
if (privvol == NULL)
goto cleanup;
if (virStorageVolDefFindByName(obj, privvol->name)) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("storage vol already exists"));
goto cleanup;
}
origvol = virStorageVolDefFindByName(obj, clonevol->name);
if (!origvol) {
virReportError(VIR_ERR_NO_STORAGE_VOL,
_("no storage vol with matching name '%s'"),
clonevol->name);
goto cleanup;
}
/* Make sure enough space */
if ((def->allocation + privvol->target.allocation) > def->capacity) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Not enough free space in pool for volume '%s'"),
privvol->name);
goto cleanup;
}
def->available = (def->capacity - def->allocation);
if (virAsprintf(&privvol->target.path, "%s/%s",
def->target.path, privvol->name) < 0)
goto cleanup;
if (VIR_STRDUP(privvol->key, privvol->target.path) < 0 ||
virStoragePoolObjAddVol(obj, privvol) < 0)
goto cleanup;
def->allocation += privvol->target.allocation;
def->available = (def->capacity - def->allocation);
ret = virGetStorageVol(pool->conn, def->name,
privvol->name, privvol->key,
NULL, NULL);
privvol = NULL;
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
}
2008-10-30 17:40:57 +00:00
static int
testStorageVolDelete(virStorageVolPtr vol,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStorageVolDefPtr privvol;
int ret = -1;
2008-10-30 17:40:57 +00:00
virCheckFlags(0, -1);
if (!(obj = testStoragePoolObjFindActiveByName(privconn, vol->pool)))
return -1;
def = virStoragePoolObjGetDef(obj);
if (!(privvol = testStorageVolDefFindByName(obj, vol->name)))
goto cleanup;
def->allocation -= privvol->target.allocation;
def->available = (def->capacity - def->allocation);
2008-10-30 17:40:57 +00:00
virStoragePoolObjRemoveVol(obj, privvol);
2008-10-30 17:40:57 +00:00
ret = 0;
2008-10-30 17:40:57 +00:00
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
static int
testStorageVolumeTypeForPool(int pooltype)
{
switch ((virStoragePoolType) pooltype) {
case VIR_STORAGE_POOL_DIR:
case VIR_STORAGE_POOL_FS:
case VIR_STORAGE_POOL_NETFS:
case VIR_STORAGE_POOL_VSTORAGE:
return VIR_STORAGE_VOL_FILE;
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_ISCSI_DIRECT:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_RBD:
return VIR_STORAGE_VOL_NETWORK;
case VIR_STORAGE_POOL_LOGICAL:
case VIR_STORAGE_POOL_DISK:
case VIR_STORAGE_POOL_MPATH:
case VIR_STORAGE_POOL_ISCSI:
case VIR_STORAGE_POOL_SCSI:
case VIR_STORAGE_POOL_ZFS:
return VIR_STORAGE_VOL_BLOCK;
case VIR_STORAGE_POOL_LAST:
default:
virReportEnumRangeError(virStoragePoolType, pooltype);
return -1;
2008-10-30 17:40:57 +00:00
}
}
2008-10-30 17:40:57 +00:00
static int
testStorageVolGetInfo(virStorageVolPtr vol,
virStorageVolInfoPtr info)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr obj;
virStoragePoolDefPtr def;
virStorageVolDefPtr privvol;
int ret = -1;
if (!(obj = testStoragePoolObjFindActiveByName(privconn, vol->pool)))
return -1;
def = virStoragePoolObjGetDef(obj);
if (!(privvol = testStorageVolDefFindByName(obj, vol->name)))
goto cleanup;
2008-10-30 17:40:57 +00:00
memset(info, 0, sizeof(*info));
if ((info->type = testStorageVolumeTypeForPool(def->type)) < 0)
goto cleanup;
conf: track sizes directly in source struct One of the features of qcow2 is that a wrapper file can have more capacity than its backing file from the guest's perspective; what's more, sparse files make tracking allocation of both the active and backing file worthwhile. As such, it makes more sense to show allocation numbers for each file in a chain, and not just the top-level file. This sets up the fields for the tracking, although it does not modify XML to display any new information. * src/util/virstoragefile.h (_virStorageSource): Add fields. * src/conf/storage_conf.h (_virStorageVolDef): Drop redundant fields. * src/storage/storage_backend.c (virStorageBackendCreateBlockFrom) (createRawFile, virStorageBackendCreateQemuImgCmd) (virStorageBackendCreateQcowCreate): Update clients. * src/storage/storage_driver.c (storageVolDelete) (storageVolCreateXML, storageVolCreateXMLFrom, storageVolResize) (storageVolWipeInternal, storageVolGetInfo): Likewise. * src/storage/storage_backend_fs.c (virStorageBackendProbeTarget) (virStorageBackendFileSystemRefresh) (virStorageBackendFileSystemVolResize) (virStorageBackendFileSystemVolRefresh): Likewise. * src/storage/storage_backend_logical.c (virStorageBackendLogicalMakeVol) (virStorageBackendLogicalCreateVol): Likewise. * src/storage/storage_backend_scsi.c (virStorageBackendSCSINewLun): Likewise. * src/storage/storage_backend_mpath.c (virStorageBackendMpathNewVol): Likewise. * src/storage/storage_backend_rbd.c (volStorageBackendRBDRefreshVolInfo) (virStorageBackendRBDCreateImage): Likewise. * src/storage/storage_backend_disk.c (virStorageBackendDiskMakeDataVol) (virStorageBackendDiskCreateVol): Likewise. * src/storage/storage_backend_sheepdog.c (virStorageBackendSheepdogBuildVol) (virStorageBackendSheepdogParseVdiList): Likewise. * src/storage/storage_backend_gluster.c (virStorageBackendGlusterRefreshVol): Likewise. * src/conf/storage_conf.c (virStorageVolDefFormat) (virStorageVolDefParseXML): Likewise. * src/test/test_driver.c (testOpenVolumesForPool) (testStorageVolCreateXML, testStorageVolCreateXMLFrom) (testStorageVolDelete, testStorageVolGetInfo): Likewise. * src/esx/esx_storage_backend_iscsi.c (esxStorageVolGetXMLDesc): Likewise. * src/esx/esx_storage_backend_vmfs.c (esxStorageVolGetXMLDesc) (esxStorageVolCreateXML): Likewise. * src/parallels/parallels_driver.c (parallelsAddHddByVolume): Likewise. * src/parallels/parallels_storage.c (parallelsDiskDescParseNode) (parallelsStorageVolDefineXML, parallelsStorageVolCreateXMLFrom) (parallelsStorageVolDefRemove, parallelsStorageVolGetInfo): Likewise. * src/vbox/vbox_tmpl.c (vboxStorageVolCreateXML) (vboxStorageVolGetXMLDesc): Likewise. * tests/storagebackendsheepdogtest.c (test_vdi_list_parser): Likewise. * src/phyp/phyp_driver.c (phypStorageVolCreateXML): Likewise.
2014-04-01 23:43:36 +00:00
info->capacity = privvol->target.capacity;
info->allocation = privvol->target.allocation;
ret = 0;
2008-10-30 17:40:57 +00:00
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static char *
testStorageVolGetXMLDesc(virStorageVolPtr vol,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr obj;
virStorageVolDefPtr privvol;
char *ret = NULL;
virCheckFlags(0, NULL);
if (!(obj = testStoragePoolObjFindActiveByName(privconn, vol->pool)))
return NULL;
if (!(privvol = testStorageVolDefFindByName(obj, vol->name)))
goto cleanup;
2008-10-30 17:40:57 +00:00
ret = virStorageVolDefFormat(virStoragePoolObjGetDef(obj), privvol);
cleanup:
virStoragePoolObjEndAPI(&obj);
return ret;
2008-10-30 17:40:57 +00:00
}
2008-10-30 17:40:57 +00:00
static char *
testStorageVolGetPath(virStorageVolPtr vol)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = vol->conn->privateData;
virStoragePoolObjPtr obj;
virStorageVolDefPtr privvol;
char *ret = NULL;
if (!(obj = testStoragePoolObjFindActiveByName(privconn, vol->pool)))
return NULL;
if (!(privvol = testStorageVolDefFindByName(obj, vol->name)))
goto cleanup;
ignore_value(VIR_STRDUP(ret, privvol->target.path));
cleanup:
virStoragePoolObjEndAPI(&obj);
2008-10-30 17:40:57 +00:00
return ret;
}
/* Node device implementations */
static virNodeDeviceObjPtr
testNodeDeviceObjFindByName(testDriverPtr driver,
const char *name)
{
virNodeDeviceObjPtr obj;
if (!(obj = virNodeDeviceObjListFindByName(driver->devs, name)))
virReportError(VIR_ERR_NO_NODE_DEVICE,
_("no node device with matching name '%s'"),
name);
return obj;
}
static int
testNodeNumOfDevices(virConnectPtr conn,
const char *cap,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
virCheckFlags(0, -1);
return virNodeDeviceObjListNumOfDevices(driver->devs, conn, cap, NULL);
}
static int
testNodeListDevices(virConnectPtr conn,
const char *cap,
char **const names,
int maxnames,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
virCheckFlags(0, -1);
return virNodeDeviceObjListGetNames(driver->devs, conn, NULL,
cap, names, maxnames);
}
static int
testConnectListAllNodeDevices(virConnectPtr conn,
virNodeDevicePtr **devices,
unsigned int flags)
{
testDriverPtr driver = conn->privateData;
virCheckFlags(VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP, -1);
return virNodeDeviceObjListExport(conn, driver->devs, devices,
NULL, flags);
}
static virNodeDevicePtr
testNodeDeviceLookupByName(virConnectPtr conn, const char *name)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
virNodeDeviceObjPtr obj;
virNodeDeviceDefPtr def;
virNodeDevicePtr ret = NULL;
if (!(obj = testNodeDeviceObjFindByName(driver, name)))
return NULL;
def = virNodeDeviceObjGetDef(obj);
if ((ret = virGetNodeDevice(conn, name))) {
if (VIR_STRDUP(ret->parentName, def->parent) < 0) {
virObjectUnref(ret);
ret = NULL;
}
}
virNodeDeviceObjEndAPI(&obj);
return ret;
}
static char *
testNodeDeviceGetXMLDesc(virNodeDevicePtr dev,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = dev->conn->privateData;
virNodeDeviceObjPtr obj;
char *ret = NULL;
virCheckFlags(0, NULL);
if (!(obj = testNodeDeviceObjFindByName(driver, dev->name)))
return NULL;
ret = virNodeDeviceDefFormat(virNodeDeviceObjGetDef(obj));
virNodeDeviceObjEndAPI(&obj);
return ret;
}
static char *
testNodeDeviceGetParent(virNodeDevicePtr dev)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = dev->conn->privateData;
virNodeDeviceObjPtr obj;
virNodeDeviceDefPtr def;
char *ret = NULL;
if (!(obj = testNodeDeviceObjFindByName(driver, dev->name)))
return NULL;
def = virNodeDeviceObjGetDef(obj);
if (def->parent) {
ignore_value(VIR_STRDUP(ret, def->parent));
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("no parent for this device"));
}
virNodeDeviceObjEndAPI(&obj);
return ret;
}
static int
testNodeDeviceNumOfCaps(virNodeDevicePtr dev)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = dev->conn->privateData;
virNodeDeviceObjPtr obj;
virNodeDeviceDefPtr def;
virNodeDevCapsDefPtr caps;
int ncaps = 0;
if (!(obj = testNodeDeviceObjFindByName(driver, dev->name)))
return -1;
def = virNodeDeviceObjGetDef(obj);
for (caps = def->caps; caps; caps = caps->next)
++ncaps;
virNodeDeviceObjEndAPI(&obj);
return ncaps;
}
static int
testNodeDeviceListCaps(virNodeDevicePtr dev, char **const names, int maxnames)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = dev->conn->privateData;
virNodeDeviceObjPtr obj;
virNodeDeviceDefPtr def;
virNodeDevCapsDefPtr caps;
int ncaps = 0;
if (!(obj = testNodeDeviceObjFindByName(driver, dev->name)))
return -1;
def = virNodeDeviceObjGetDef(obj);
for (caps = def->caps; caps && ncaps < maxnames; caps = caps->next) {
if (VIR_STRDUP(names[ncaps],
virNodeDevCapTypeToString(caps->data.type)) < 0)
goto error;
ncaps++;
}
virNodeDeviceObjEndAPI(&obj);
return ncaps;
error:
while (--ncaps >= 0)
VIR_FREE(names[ncaps]);
virNodeDeviceObjEndAPI(&obj);
return -1;
}
static virNodeDeviceObjPtr
testNodeDeviceMockCreateVport(testDriverPtr driver,
const char *wwnn,
const char *wwpn)
{
virNodeDeviceDefPtr def = NULL;
virNodeDevCapsDefPtr caps;
virNodeDeviceObjPtr obj = NULL, objcopy = NULL;
virNodeDeviceDefPtr objdef;
virObjectEventPtr event = NULL;
VIR_AUTOFREE(char *) xml = NULL;
/* In the real code, we'd call virVHBAManageVport which would take the
* wwnn/wwpn from the input XML in order to call the "vport_create"
* function for the parent. That in turn would set off a sequence of
* events resulting in the creation of a vHBA scsi_hostN in the
* node device objects list using the "next" host number with the
* wwnn/wwpn from the input XML. The following will mock this by
* using the scsi_host11 definition, changing the name and the
* scsi_host capability fields before calling virNodeDeviceAssignDef
* to add the def to the node device objects list. */
if (!(objcopy = virNodeDeviceObjListFindByName(driver->devs,
"scsi_host11")))
goto cleanup;
xml = virNodeDeviceDefFormat(virNodeDeviceObjGetDef(objcopy));
virNodeDeviceObjEndAPI(&objcopy);
if (!xml)
goto cleanup;
if (!(def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL)))
goto cleanup;
VIR_FREE(def->name);
if (VIR_STRDUP(def->name, "scsi_host12") < 0)
goto cleanup;
/* Find the 'scsi_host' cap and alter the host # and unique_id and
* then for the 'fc_host' capability modify the wwnn/wwpn to be that
* of the input XML. */
caps = def->caps;
while (caps) {
if (caps->data.type != VIR_NODE_DEV_CAP_SCSI_HOST)
continue;
/* For the "fc_host" cap - change the wwnn/wwpn to match the input */
if (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
VIR_FREE(caps->data.scsi_host.wwnn);
VIR_FREE(caps->data.scsi_host.wwpn);
if (VIR_STRDUP(caps->data.scsi_host.wwnn, wwnn) < 0 ||
VIR_STRDUP(caps->data.scsi_host.wwpn, wwpn) < 0)
goto cleanup;
} else {
/* For the "scsi_host" cap, increment our host and unique_id to
* give the appearance that something new was created - then add
* that to the node device driver */
caps->data.scsi_host.host++;
caps->data.scsi_host.unique_id++;
}
caps = caps->next;
}
if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, def)))
goto cleanup;
virNodeDeviceObjSetSkipUpdateCaps(obj, true);
def = NULL;
objdef = virNodeDeviceObjGetDef(obj);
event = virNodeDeviceEventLifecycleNew(objdef->name,
VIR_NODE_DEVICE_EVENT_CREATED,
0);
virObjectEventStateQueue(driver->eventState, event);
cleanup:
virNodeDeviceDefFree(def);
return obj;
}
static virNodeDevicePtr
testNodeDeviceCreateXML(virConnectPtr conn,
const char *xmlDesc,
unsigned int flags)
{
testDriverPtr driver = conn->privateData;
virNodeDeviceDefPtr def = NULL;
virNodeDevicePtr dev = NULL, ret = NULL;
virNodeDeviceObjPtr obj = NULL;
virNodeDeviceDefPtr objdef;
VIR_AUTOFREE(char *) wwnn = NULL;
VIR_AUTOFREE(char *) wwpn = NULL;
virCheckFlags(0, NULL);
if (!(def = virNodeDeviceDefParseString(xmlDesc, CREATE_DEVICE, NULL)))
goto cleanup;
/* We run this simply for validation - it essentially validates that
* the input XML either has a wwnn/wwpn or virNodeDevCapSCSIHostParseXML
* generated a wwnn/wwpn */
if (virNodeDeviceGetWWNs(def, &wwnn, &wwpn) < 0)
goto cleanup;
/* Unlike the "real" code we don't need the parent_host in order to
* call virVHBAManageVport, but still let's make sure the code finds
* something valid and no one messed up the mock environment. */
if (virNodeDeviceObjListGetParentHost(driver->devs, def) < 0)
goto cleanup;
/* In the real code, we'd call virVHBAManageVport followed by
* find_new_device, but we cannot do that here since we're not
* mocking udev. The mock routine will copy an existing vHBA and
* rename a few fields to mock that. So in order to allow that to
* work properly, we need to drop our lock */
if (!(obj = testNodeDeviceMockCreateVport(driver, wwnn, wwpn)))
goto cleanup;
objdef = virNodeDeviceObjGetDef(obj);
if (!(dev = virGetNodeDevice(conn, objdef->name)))
goto cleanup;
VIR_FREE(dev->parentName);
if (VIR_STRDUP(dev->parentName, def->parent) < 0)
goto cleanup;
VIR_STEAL_PTR(ret, dev);
cleanup:
virNodeDeviceObjEndAPI(&obj);
virNodeDeviceDefFree(def);
virObjectUnref(dev);
return ret;
}
static int
testNodeDeviceDestroy(virNodeDevicePtr dev)
{
int ret = 0;
2015-06-22 14:25:16 +00:00
testDriverPtr driver = dev->conn->privateData;
virNodeDeviceObjPtr obj = NULL;
virNodeDeviceObjPtr parentobj = NULL;
virNodeDeviceDefPtr def;
virObjectEventPtr event = NULL;
VIR_AUTOFREE(char *) wwnn = NULL;
VIR_AUTOFREE(char *) wwpn = NULL;
if (!(obj = testNodeDeviceObjFindByName(driver, dev->name)))
return -1;
def = virNodeDeviceObjGetDef(obj);
if (virNodeDeviceGetWWNs(def, &wwnn, &wwpn) == -1)
goto cleanup;
/* Unlike the real code we cannot run into the udevAddOneDevice race
* which would replace obj->def, so no need to save off the parent,
* but do need to drop the @obj lock so that the FindByName code doesn't
* deadlock on ourselves */
virObjectUnlock(obj);
/* We do this just for basic validation and throw away the parentobj
* since there's no vport_delete to be run */
if (!(parentobj = virNodeDeviceObjListFindByName(driver->devs,
def->parent))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find parent '%s' definition"), def->parent);
virObjectLock(obj);
goto cleanup;
}
virNodeDeviceObjEndAPI(&parentobj);
event = virNodeDeviceEventLifecycleNew(dev->name,
VIR_NODE_DEVICE_EVENT_DELETED,
0);
virObjectLock(obj);
virNodeDeviceObjListRemove(driver->devs, obj);
virObjectUnref(obj);
obj = NULL;
cleanup:
virNodeDeviceObjEndAPI(&obj);
virObjectEventStateQueue(driver->eventState, event);
return ret;
}
/* Domain event implementations */
2009-01-20 20:23:53 +00:00
static int
testConnectDomainEventRegister(virConnectPtr conn,
virConnectDomainEventCallback callback,
void *opaque,
virFreeCallback freecb)
2009-01-20 20:23:53 +00:00
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
int ret = 0;
2009-01-20 20:23:53 +00:00
event: move event filtering to daemon (regression fix) https://bugzilla.redhat.com/show_bug.cgi?id=1058839 Commit f9f56340 for CVE-2014-0028 almost had the right idea - we need to check the ACL rules to filter which events to send. But it overlooked one thing: the event dispatch queue is running in the main loop thread, and therefore does not normally have a current virIdentityPtr. But filter checks can be based on current identity, so when libvirtd.conf contains access_drivers=["polkit"], we ended up rejecting access for EVERY event due to failure to look up the current identity, even if it should have been allowed. Furthermore, even for events that are triggered by API calls, it is important to remember that the point of events is that they can be copied across multiple connections, which may have separate identities and permissions. So even if events were dispatched from a context where we have an identity, we must change to the correct identity of the connection that will be receiving the event, rather than basing a decision on the context that triggered the event, when deciding whether to filter an event to a particular connection. If there were an easy way to get from virConnectPtr to the appropriate virIdentityPtr, then object_event.c could adjust the identity prior to checking whether to dispatch an event. But setting up that back-reference is a bit invasive. Instead, it is easier to delay the filtering check until lower down the stack, at the point where we have direct access to the RPC client object that owns an identity. As such, this patch ends up reverting a large portion of the framework of commit f9f56340. We also have to teach 'make check' to special-case the fact that the event registration filtering is done at the point of dispatch, rather than the point of registration. Note that even though we don't actually use virConnectDomainEventRegisterCheckACL (because the RegisterAny variant is sufficient), we still generate the function for the purposes of documenting that the filtering takes place. Also note that I did not entirely delete the notion of a filter from object_event.c; I still plan on using that for my upcoming patch series for qemu monitor events in libvirt-qemu.so. In other words, while this patch changes ACL filtering to live in remote.c and therefore we have no current client of the filtering in object_event.c, the notion of filtering in object_event.c is still useful down the road. * src/check-aclrules.pl: Exempt event registration from having to pass checkACL filter down call stack. * daemon/remote.c (remoteRelayDomainEventCheckACL) (remoteRelayNetworkEventCheckACL): New functions. (remoteRelay*Event*): Use new functions. * src/conf/domain_event.h (virDomainEventStateRegister) (virDomainEventStateRegisterID): Drop unused parameter. * src/conf/network_event.h (virNetworkEventStateRegisterID): Likewise. * src/conf/domain_event.c (virDomainEventFilter): Delete unused function. * src/conf/network_event.c (virNetworkEventFilter): Likewise. * src/libxl/libxl_driver.c: Adjust caller. * src/lxc/lxc_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/remote/remote_driver.c: Likewise. * src/test/test_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xen/xen_driver.c: Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-28 21:50:02 +00:00
if (virDomainEventStateRegister(conn, driver->eventState,
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
callback, opaque, freecb) < 0)
ret = -1;
2009-01-20 20:23:53 +00:00
return ret;
}
2009-01-20 20:23:53 +00:00
static int
testConnectDomainEventDeregister(virConnectPtr conn,
virConnectDomainEventCallback callback)
2009-01-20 20:23:53 +00:00
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
int ret = 0;
2009-01-20 20:23:53 +00:00
if (virDomainEventStateDeregister(conn, driver->eventState,
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
callback) < 0)
ret = -1;
2009-01-20 20:23:53 +00:00
return ret;
}
static int
testConnectDomainEventRegisterAny(virConnectPtr conn,
virDomainPtr dom,
int eventID,
virConnectDomainEventGenericCallback callback,
void *opaque,
virFreeCallback freecb)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
int ret;
event: move event filtering to daemon (regression fix) https://bugzilla.redhat.com/show_bug.cgi?id=1058839 Commit f9f56340 for CVE-2014-0028 almost had the right idea - we need to check the ACL rules to filter which events to send. But it overlooked one thing: the event dispatch queue is running in the main loop thread, and therefore does not normally have a current virIdentityPtr. But filter checks can be based on current identity, so when libvirtd.conf contains access_drivers=["polkit"], we ended up rejecting access for EVERY event due to failure to look up the current identity, even if it should have been allowed. Furthermore, even for events that are triggered by API calls, it is important to remember that the point of events is that they can be copied across multiple connections, which may have separate identities and permissions. So even if events were dispatched from a context where we have an identity, we must change to the correct identity of the connection that will be receiving the event, rather than basing a decision on the context that triggered the event, when deciding whether to filter an event to a particular connection. If there were an easy way to get from virConnectPtr to the appropriate virIdentityPtr, then object_event.c could adjust the identity prior to checking whether to dispatch an event. But setting up that back-reference is a bit invasive. Instead, it is easier to delay the filtering check until lower down the stack, at the point where we have direct access to the RPC client object that owns an identity. As such, this patch ends up reverting a large portion of the framework of commit f9f56340. We also have to teach 'make check' to special-case the fact that the event registration filtering is done at the point of dispatch, rather than the point of registration. Note that even though we don't actually use virConnectDomainEventRegisterCheckACL (because the RegisterAny variant is sufficient), we still generate the function for the purposes of documenting that the filtering takes place. Also note that I did not entirely delete the notion of a filter from object_event.c; I still plan on using that for my upcoming patch series for qemu monitor events in libvirt-qemu.so. In other words, while this patch changes ACL filtering to live in remote.c and therefore we have no current client of the filtering in object_event.c, the notion of filtering in object_event.c is still useful down the road. * src/check-aclrules.pl: Exempt event registration from having to pass checkACL filter down call stack. * daemon/remote.c (remoteRelayDomainEventCheckACL) (remoteRelayNetworkEventCheckACL): New functions. (remoteRelay*Event*): Use new functions. * src/conf/domain_event.h (virDomainEventStateRegister) (virDomainEventStateRegisterID): Drop unused parameter. * src/conf/network_event.h (virNetworkEventStateRegisterID): Likewise. * src/conf/domain_event.c (virDomainEventFilter): Delete unused function. * src/conf/network_event.c (virNetworkEventFilter): Likewise. * src/libxl/libxl_driver.c: Adjust caller. * src/lxc/lxc_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/remote/remote_driver.c: Likewise. * src/test/test_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xen/xen_driver.c: Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-28 21:50:02 +00:00
if (virDomainEventStateRegisterID(conn, driver->eventState,
dom, eventID,
callback, opaque, freecb, &ret) < 0)
ret = -1;
return ret;
}
static int
testConnectDomainEventDeregisterAny(virConnectPtr conn,
int callbackID)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
int ret = 0;
if (virObjectEventStateDeregisterID(conn, driver->eventState,
events: Avoid double free possibility on remote call failure If a remote call fails during event registration (more than likely from a network failure or remote libvirtd restart timed just right), then when calling the virObjectEventStateDeregisterID we don't want to call the registered @freecb function because that breaks our contract that we would only call it after succesfully returning. If the @freecb routine were called, it could result in a double free from properly coded applications that free their opaque data on failure to register, as seen in the following details: Program terminated with signal 6, Aborted. #0 0x00007fc45cba15d7 in raise #1 0x00007fc45cba2cc8 in abort #2 0x00007fc45cbe12f7 in __libc_message #3 0x00007fc45cbe86d3 in _int_free #4 0x00007fc45d8d292c in PyDict_Fini #5 0x00007fc45d94f46a in Py_Finalize #6 0x00007fc45d960735 in Py_Main #7 0x00007fc45cb8daf5 in __libc_start_main #8 0x0000000000400721 in _start The double dereference of 'pyobj_cbData' is triggered in the following way: (1) libvirt_virConnectDomainEventRegisterAny is invoked. (2) the event is successfully added to the event callback list (virDomainEventStateRegisterClient in remoteConnectDomainEventRegisterAny returns 1 which means ok). (3) when function remoteConnectDomainEventRegisterAny is hit, network connection disconnected coincidently (or libvirtd is restarted) in the context of function 'call' then the connection is lost and the function 'call' failed, the branch virObjectEventStateDeregisterID is therefore taken. (4) 'pyobj_conn' is dereferenced the 1st time in libvirt_virConnectDomainEventFreeFunc. (5) 'pyobj_cbData' (refered to pyobj_conn) is dereferenced the 2nd time in libvirt_virConnectDomainEventRegisterAny. (6) the double free error is triggered. Resolve this by adding a @doFreeCb boolean in order to avoid calling the freeCb in virObjectEventStateDeregisterID for any remote call failure in a remoteConnect*EventRegister* API. For remoteConnect*EventDeregister* calls, the passed value would be true indicating they should run the freecb if it exists; whereas, it's false for the remote call failure path. Patch based on the investigation and initial patch posted by fangying <fangying1@huawei.com>.
2017-06-14 11:32:15 +00:00
callbackID, true) < 0)
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
ret = -1;
return ret;
}
static int
testConnectNetworkEventRegisterAny(virConnectPtr conn,
virNetworkPtr net,
int eventID,
virConnectNetworkEventGenericCallback callback,
void *opaque,
virFreeCallback freecb)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
int ret;
event: move event filtering to daemon (regression fix) https://bugzilla.redhat.com/show_bug.cgi?id=1058839 Commit f9f56340 for CVE-2014-0028 almost had the right idea - we need to check the ACL rules to filter which events to send. But it overlooked one thing: the event dispatch queue is running in the main loop thread, and therefore does not normally have a current virIdentityPtr. But filter checks can be based on current identity, so when libvirtd.conf contains access_drivers=["polkit"], we ended up rejecting access for EVERY event due to failure to look up the current identity, even if it should have been allowed. Furthermore, even for events that are triggered by API calls, it is important to remember that the point of events is that they can be copied across multiple connections, which may have separate identities and permissions. So even if events were dispatched from a context where we have an identity, we must change to the correct identity of the connection that will be receiving the event, rather than basing a decision on the context that triggered the event, when deciding whether to filter an event to a particular connection. If there were an easy way to get from virConnectPtr to the appropriate virIdentityPtr, then object_event.c could adjust the identity prior to checking whether to dispatch an event. But setting up that back-reference is a bit invasive. Instead, it is easier to delay the filtering check until lower down the stack, at the point where we have direct access to the RPC client object that owns an identity. As such, this patch ends up reverting a large portion of the framework of commit f9f56340. We also have to teach 'make check' to special-case the fact that the event registration filtering is done at the point of dispatch, rather than the point of registration. Note that even though we don't actually use virConnectDomainEventRegisterCheckACL (because the RegisterAny variant is sufficient), we still generate the function for the purposes of documenting that the filtering takes place. Also note that I did not entirely delete the notion of a filter from object_event.c; I still plan on using that for my upcoming patch series for qemu monitor events in libvirt-qemu.so. In other words, while this patch changes ACL filtering to live in remote.c and therefore we have no current client of the filtering in object_event.c, the notion of filtering in object_event.c is still useful down the road. * src/check-aclrules.pl: Exempt event registration from having to pass checkACL filter down call stack. * daemon/remote.c (remoteRelayDomainEventCheckACL) (remoteRelayNetworkEventCheckACL): New functions. (remoteRelay*Event*): Use new functions. * src/conf/domain_event.h (virDomainEventStateRegister) (virDomainEventStateRegisterID): Drop unused parameter. * src/conf/network_event.h (virNetworkEventStateRegisterID): Likewise. * src/conf/domain_event.c (virDomainEventFilter): Delete unused function. * src/conf/network_event.c (virNetworkEventFilter): Likewise. * src/libxl/libxl_driver.c: Adjust caller. * src/lxc/lxc_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/remote/remote_driver.c: Likewise. * src/test/test_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xen/xen_driver.c: Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-28 21:50:02 +00:00
if (virNetworkEventStateRegisterID(conn, driver->eventState,
net, eventID, callback,
opaque, freecb, &ret) < 0)
ret = -1;
return ret;
}
static int
testConnectNetworkEventDeregisterAny(virConnectPtr conn,
int callbackID)
{
2015-06-22 14:25:16 +00:00
testDriverPtr driver = conn->privateData;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
int ret = 0;
if (virObjectEventStateDeregisterID(conn, driver->eventState,
events: Avoid double free possibility on remote call failure If a remote call fails during event registration (more than likely from a network failure or remote libvirtd restart timed just right), then when calling the virObjectEventStateDeregisterID we don't want to call the registered @freecb function because that breaks our contract that we would only call it after succesfully returning. If the @freecb routine were called, it could result in a double free from properly coded applications that free their opaque data on failure to register, as seen in the following details: Program terminated with signal 6, Aborted. #0 0x00007fc45cba15d7 in raise #1 0x00007fc45cba2cc8 in abort #2 0x00007fc45cbe12f7 in __libc_message #3 0x00007fc45cbe86d3 in _int_free #4 0x00007fc45d8d292c in PyDict_Fini #5 0x00007fc45d94f46a in Py_Finalize #6 0x00007fc45d960735 in Py_Main #7 0x00007fc45cb8daf5 in __libc_start_main #8 0x0000000000400721 in _start The double dereference of 'pyobj_cbData' is triggered in the following way: (1) libvirt_virConnectDomainEventRegisterAny is invoked. (2) the event is successfully added to the event callback list (virDomainEventStateRegisterClient in remoteConnectDomainEventRegisterAny returns 1 which means ok). (3) when function remoteConnectDomainEventRegisterAny is hit, network connection disconnected coincidently (or libvirtd is restarted) in the context of function 'call' then the connection is lost and the function 'call' failed, the branch virObjectEventStateDeregisterID is therefore taken. (4) 'pyobj_conn' is dereferenced the 1st time in libvirt_virConnectDomainEventFreeFunc. (5) 'pyobj_cbData' (refered to pyobj_conn) is dereferenced the 2nd time in libvirt_virConnectDomainEventRegisterAny. (6) the double free error is triggered. Resolve this by adding a @doFreeCb boolean in order to avoid calling the freeCb in virObjectEventStateDeregisterID for any remote call failure in a remoteConnect*EventRegister* API. For remoteConnect*EventDeregister* calls, the passed value would be true indicating they should run the freecb if it exists; whereas, it's false for the remote call failure path. Patch based on the investigation and initial patch posted by fangying <fangying1@huawei.com>.
2017-06-14 11:32:15 +00:00
callbackID, true) < 0)
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
ret = -1;
return ret;
}
static int
testConnectStoragePoolEventRegisterAny(virConnectPtr conn,
virStoragePoolPtr pool,
int eventID,
virConnectStoragePoolEventGenericCallback callback,
void *opaque,
virFreeCallback freecb)
{
testDriverPtr driver = conn->privateData;
int ret;
if (virStoragePoolEventStateRegisterID(conn, driver->eventState,
pool, eventID, callback,
opaque, freecb, &ret) < 0)
ret = -1;
return ret;
}
static int
testConnectStoragePoolEventDeregisterAny(virConnectPtr conn,
int callbackID)
{
testDriverPtr driver = conn->privateData;
int ret = 0;
if (virObjectEventStateDeregisterID(conn, driver->eventState,
events: Avoid double free possibility on remote call failure If a remote call fails during event registration (more than likely from a network failure or remote libvirtd restart timed just right), then when calling the virObjectEventStateDeregisterID we don't want to call the registered @freecb function because that breaks our contract that we would only call it after succesfully returning. If the @freecb routine were called, it could result in a double free from properly coded applications that free their opaque data on failure to register, as seen in the following details: Program terminated with signal 6, Aborted. #0 0x00007fc45cba15d7 in raise #1 0x00007fc45cba2cc8 in abort #2 0x00007fc45cbe12f7 in __libc_message #3 0x00007fc45cbe86d3 in _int_free #4 0x00007fc45d8d292c in PyDict_Fini #5 0x00007fc45d94f46a in Py_Finalize #6 0x00007fc45d960735 in Py_Main #7 0x00007fc45cb8daf5 in __libc_start_main #8 0x0000000000400721 in _start The double dereference of 'pyobj_cbData' is triggered in the following way: (1) libvirt_virConnectDomainEventRegisterAny is invoked. (2) the event is successfully added to the event callback list (virDomainEventStateRegisterClient in remoteConnectDomainEventRegisterAny returns 1 which means ok). (3) when function remoteConnectDomainEventRegisterAny is hit, network connection disconnected coincidently (or libvirtd is restarted) in the context of function 'call' then the connection is lost and the function 'call' failed, the branch virObjectEventStateDeregisterID is therefore taken. (4) 'pyobj_conn' is dereferenced the 1st time in libvirt_virConnectDomainEventFreeFunc. (5) 'pyobj_cbData' (refered to pyobj_conn) is dereferenced the 2nd time in libvirt_virConnectDomainEventRegisterAny. (6) the double free error is triggered. Resolve this by adding a @doFreeCb boolean in order to avoid calling the freeCb in virObjectEventStateDeregisterID for any remote call failure in a remoteConnect*EventRegister* API. For remoteConnect*EventDeregister* calls, the passed value would be true indicating they should run the freecb if it exists; whereas, it's false for the remote call failure path. Patch based on the investigation and initial patch posted by fangying <fangying1@huawei.com>.
2017-06-14 11:32:15 +00:00
callbackID, true) < 0)
ret = -1;
return ret;
}
static int
testConnectNodeDeviceEventRegisterAny(virConnectPtr conn,
virNodeDevicePtr dev,
int eventID,
virConnectNodeDeviceEventGenericCallback callback,
void *opaque,
virFreeCallback freecb)
{
testDriverPtr driver = conn->privateData;
int ret;
if (virNodeDeviceEventStateRegisterID(conn, driver->eventState,
dev, eventID, callback,
opaque, freecb, &ret) < 0)
ret = -1;
return ret;
}
static int
testConnectNodeDeviceEventDeregisterAny(virConnectPtr conn,
int callbackID)
{
testDriverPtr driver = conn->privateData;
int ret = 0;
if (virObjectEventStateDeregisterID(conn, driver->eventState,
events: Avoid double free possibility on remote call failure If a remote call fails during event registration (more than likely from a network failure or remote libvirtd restart timed just right), then when calling the virObjectEventStateDeregisterID we don't want to call the registered @freecb function because that breaks our contract that we would only call it after succesfully returning. If the @freecb routine were called, it could result in a double free from properly coded applications that free their opaque data on failure to register, as seen in the following details: Program terminated with signal 6, Aborted. #0 0x00007fc45cba15d7 in raise #1 0x00007fc45cba2cc8 in abort #2 0x00007fc45cbe12f7 in __libc_message #3 0x00007fc45cbe86d3 in _int_free #4 0x00007fc45d8d292c in PyDict_Fini #5 0x00007fc45d94f46a in Py_Finalize #6 0x00007fc45d960735 in Py_Main #7 0x00007fc45cb8daf5 in __libc_start_main #8 0x0000000000400721 in _start The double dereference of 'pyobj_cbData' is triggered in the following way: (1) libvirt_virConnectDomainEventRegisterAny is invoked. (2) the event is successfully added to the event callback list (virDomainEventStateRegisterClient in remoteConnectDomainEventRegisterAny returns 1 which means ok). (3) when function remoteConnectDomainEventRegisterAny is hit, network connection disconnected coincidently (or libvirtd is restarted) in the context of function 'call' then the connection is lost and the function 'call' failed, the branch virObjectEventStateDeregisterID is therefore taken. (4) 'pyobj_conn' is dereferenced the 1st time in libvirt_virConnectDomainEventFreeFunc. (5) 'pyobj_cbData' (refered to pyobj_conn) is dereferenced the 2nd time in libvirt_virConnectDomainEventRegisterAny. (6) the double free error is triggered. Resolve this by adding a @doFreeCb boolean in order to avoid calling the freeCb in virObjectEventStateDeregisterID for any remote call failure in a remoteConnect*EventRegister* API. For remoteConnect*EventDeregister* calls, the passed value would be true indicating they should run the freecb if it exists; whereas, it's false for the remote call failure path. Patch based on the investigation and initial patch posted by fangying <fangying1@huawei.com>.
2017-06-14 11:32:15 +00:00
callbackID, true) < 0)
ret = -1;
return ret;
}
static int testConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = conn->privateData;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
return virDomainObjListExport(privconn->domains, conn, domains,
NULL, flags);
}
static int
testNodeGetCPUMap(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned char **cpumap,
unsigned int *online,
unsigned int flags)
{
virCheckFlags(0, -1);
if (cpumap) {
if (VIR_ALLOC_N(*cpumap, 1) < 0)
return -1;
*cpumap[0] = 0x15;
}
if (online)
*online = 3;
return 8;
}
static char *
testDomainScreenshot(virDomainPtr dom ATTRIBUTE_UNUSED,
virStreamPtr st,
unsigned int screen ATTRIBUTE_UNUSED,
unsigned int flags)
{
char *ret = NULL;
virCheckFlags(0, NULL);
if (VIR_STRDUP(ret, "image/png") < 0)
return NULL;
if (virFDStreamOpenFile(st, PKGDATADIR "/test-screenshot.png", 0, 0, O_RDONLY) < 0)
VIR_FREE(ret);
return ret;
}
static int
testDomainSendKey(virDomainPtr domain,
unsigned int codeset,
unsigned int holdtime ATTRIBUTE_UNUSED,
unsigned int *keycodes,
int nkeycodes,
unsigned int flags)
{
int ret = -1;
size_t i;
virDomainObjPtr vm = NULL;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromDomain(domain)))
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
for (i = 0; i < nkeycodes; i++) {
if (virKeycodeValueTranslate(codeset, codeset, keycodes[i]) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid keycode %u of %s codeset"),
keycodes[i],
virKeycodeSetTypeToString(codeset));
goto cleanup;
}
}
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
testConnectGetCPUModelNames(virConnectPtr conn ATTRIBUTE_UNUSED,
const char *archName,
char ***models,
unsigned int flags)
{
virArch arch;
virCheckFlags(0, -1);
if (!(arch = virArchFromString(archName))) {
virReportError(VIR_ERR_INVALID_ARG,
_("cannot find architecture %s"),
archName);
return -1;
}
return virCPUGetModels(arch, models);
}
static int
testDomainManagedSave(virDomainPtr dom, unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = dom->conn->privateData;
virDomainObjPtr vm = NULL;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE |
VIR_DOMAIN_SAVE_RUNNING |
VIR_DOMAIN_SAVE_PAUSED, -1);
if (!(vm = testDomObjFromDomain(dom)))
return -1;
if (virDomainObjCheckActive(vm) < 0)
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 = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SAVED);
vm->hasManagedSave = true;
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
virObjectEventStateQueue(privconn->eventState, event);
return ret;
}
static int
testDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
int ret;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromDomain(dom)))
return -1;
ret = vm->hasManagedSave;
virDomainObjEndAPI(&vm);
return ret;
}
static int
testDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromDomain(dom)))
return -1;
vm->hasManagedSave = false;
virDomainObjEndAPI(&vm);
return 0;
}
static int
testDomainMemoryPeek(virDomainPtr dom,
unsigned long long start,
size_t size,
void *buffer,
unsigned int flags)
{
int ret = -1;
size_t i;
unsigned char b = start;
virDomainObjPtr vm = NULL;
virCheckFlags(VIR_MEMORY_VIRTUAL | VIR_MEMORY_PHYSICAL, -1);
if (flags != VIR_MEMORY_VIRTUAL && flags != VIR_MEMORY_PHYSICAL) {
virReportError(VIR_ERR_INVALID_ARG,
"%s", _("flags parameter must be VIR_MEMORY_VIRTUAL or VIR_MEMORY_PHYSICAL"));
goto cleanup;
}
if (!(vm = testDomObjFromDomain(dom)))
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
for (i = 0; i < size; i++)
((unsigned char *) buffer)[i] = b++;
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
/*
* Snapshot APIs
*/
static virDomainMomentObjPtr
testSnapObjFromName(virDomainObjPtr vm,
const char *name)
{
virDomainMomentObjPtr 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 virDomainMomentObjPtr
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;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL |
VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1);
if (!(vm = testDomObjFromDomain(domain)))
return -1;
n = virDomainSnapshotObjListNum(vm->snapshots, NULL, flags);
virDomainObjEndAPI(&vm);
return n;
}
static int
testDomainSnapshotListNames(virDomainPtr domain,
char **names,
int nameslen,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
int n;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL |
VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1);
if (!(vm = testDomObjFromDomain(domain)))
return -1;
n = virDomainSnapshotObjListGetNames(vm->snapshots, NULL, names, nameslen,
flags);
virDomainObjEndAPI(&vm);
return n;
}
static int
testDomainListAllSnapshots(virDomainPtr domain,
virDomainSnapshotPtr **snaps,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
int n;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL |
VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1);
if (!(vm = testDomObjFromDomain(domain)))
return -1;
n = virDomainListSnapshots(vm->snapshots, NULL, domain, snaps, flags);
virDomainObjEndAPI(&vm);
return n;
}
static int
testDomainSnapshotListChildrenNames(virDomainSnapshotPtr snapshot,
char **names,
int nameslen,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
virDomainMomentObjPtr snap = NULL;
int n = -1;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL |
VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return -1;
if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
goto cleanup;
n = virDomainSnapshotObjListGetNames(vm->snapshots, snap, names, nameslen,
flags);
cleanup:
virDomainObjEndAPI(&vm);
return n;
}
static int
testDomainSnapshotNumChildren(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
virDomainMomentObjPtr snap = NULL;
int n = -1;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL |
VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return -1;
if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
goto cleanup;
n = virDomainSnapshotObjListNum(vm->snapshots, snap, flags);
cleanup:
virDomainObjEndAPI(&vm);
return n;
}
static int
testDomainSnapshotListAllChildren(virDomainSnapshotPtr snapshot,
virDomainSnapshotPtr **snaps,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
virDomainMomentObjPtr snap = NULL;
int n = -1;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL |
VIR_DOMAIN_SNAPSHOT_FILTERS_ALL, -1);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return -1;
if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
goto cleanup;
n = virDomainListSnapshots(vm->snapshots, snap, snapshot->domain, snaps,
flags);
cleanup:
virDomainObjEndAPI(&vm);
return n;
}
static virDomainSnapshotPtr
testDomainSnapshotLookupByName(virDomainPtr domain,
const char *name,
unsigned int flags)
{
virDomainObjPtr vm;
virDomainMomentObjPtr snap = NULL;
virDomainSnapshotPtr snapshot = NULL;
virCheckFlags(0, NULL);
if (!(vm = testDomObjFromDomain(domain)))
return NULL;
if (!(snap = testSnapObjFromName(vm, name)))
goto cleanup;
snapshot = virGetDomainSnapshot(domain, snap->def->name);
cleanup:
virDomainObjEndAPI(&vm);
return snapshot;
}
static int
testDomainHasCurrentSnapshot(virDomainPtr domain,
unsigned int flags)
{
virDomainObjPtr vm;
int ret;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromDomain(domain)))
return -1;
ret = (virDomainSnapshotGetCurrent(vm->snapshots) != NULL);
virDomainObjEndAPI(&vm);
return ret;
}
static virDomainSnapshotPtr
testDomainSnapshotGetParent(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainObjPtr vm;
virDomainMomentObjPtr snap = NULL;
virDomainSnapshotPtr parent = NULL;
virCheckFlags(0, NULL);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return NULL;
if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
goto cleanup;
if (!snap->def->parent_name) {
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_name);
cleanup:
virDomainObjEndAPI(&vm);
return parent;
}
static virDomainSnapshotPtr
testDomainSnapshotCurrent(virDomainPtr domain,
unsigned int flags)
{
virDomainObjPtr vm;
virDomainSnapshotPtr snapshot = NULL;
virDomainMomentObjPtr current;
virCheckFlags(0, NULL);
if (!(vm = testDomObjFromDomain(domain)))
return NULL;
current = virDomainSnapshotGetCurrent(vm->snapshots);
if (!current) {
virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, "%s",
_("the domain does not have a current snapshot"));
goto cleanup;
}
snapshot = virGetDomainSnapshot(domain, current->def->name);
cleanup:
virDomainObjEndAPI(&vm);
return snapshot;
}
static char *
testDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
char *xml = NULL;
virDomainMomentObjPtr snap = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
testDriverPtr privconn = snapshot->domain->conn->privateData;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_XML_SECURE, NULL);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return NULL;
if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
goto cleanup;
virUUIDFormat(snapshot->domain->uuid, uuidstr);
xml = virDomainSnapshotDefFormat(uuidstr, virDomainSnapshotObjGetDef(snap),
privconn->caps, privconn->xmlopt,
virDomainSnapshotFormatConvertXMLFlags(flags));
cleanup:
virDomainObjEndAPI(&vm);
return xml;
}
static int
testDomainSnapshotIsCurrent(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
int ret = -1;
virDomainMomentObjPtr snap = NULL;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return -1;
if (!(snap = testSnapObjFromSnapshot(vm, snapshot)))
goto cleanup;
ret = snap == virDomainSnapshotGetCurrent(vm->snapshots);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
testDomainSnapshotHasMetadata(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = testDomObjFromSnapshot(snapshot)))
return -1;
2013-10-04 12:48:39 +00:00
if (!testSnapObjFromSnapshot(vm, snapshot))
goto cleanup;
ret = 1;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
testDomainSnapshotAlignDisks(virDomainObjPtr vm,
virDomainSnapshotDefPtr def,
unsigned int flags)
{
int align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
bool 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_SNAPSHOT_DISK_SNAPSHOT;
else
def->state = VIR_DOMAIN_SNAPSHOT_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_SNAPSHOT_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)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = domain->conn->privateData;
virDomainObjPtr vm = NULL;
virDomainMomentObjPtr snap = NULL;
virDomainSnapshotPtr snapshot = NULL;
virObjectEventPtr event = NULL;
2013-08-21 20:54:17 +00:00
bool update_current = true;
bool redefine = flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
unsigned int parse_flags = VIR_DOMAIN_SNAPSHOT_PARSE_DISKS;
VIR_AUTOUNREF(virDomainSnapshotDefPtr) def = NULL;
/*
* DISK_ONLY: Not implemented yet
* REUSE_EXT: Not implemented yet
*
* NO_METADATA: Explicitly not implemented
*
2013-08-21 20:54:17 +00:00
* REDEFINE + CURRENT: Implemented
* HALT: Implemented
* QUIESCE: Nothing to do
* ATOMIC: Nothing to do
* LIVE: Nothing to do
*/
virCheckFlags(
2013-08-21 20:54:17 +00:00
VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT |
VIR_DOMAIN_SNAPSHOT_CREATE_HALT |
VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE |
VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC |
VIR_DOMAIN_SNAPSHOT_CREATE_LIVE, NULL);
2013-08-21 20:54:17 +00:00
if ((redefine && !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT)))
update_current = false;
if (redefine)
parse_flags |= VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE;
if (!(vm = testDomObjFromDomain(domain)))
goto cleanup;
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,
snapshot: Drop virDomainSnapshotDef.current The only use for the 'current' member of virDomainSnapshotDef was with the PARSE/FORMAT_INTERNAL flag for controlling an internal-use <active> element marking whether a particular snapshot definition was current, and even then, only by the qemu driver on output, and by qemu and test driver on input. But this duplicates vm->snapshot_current, and gets in the way of potential simplifications to have qemu store a single file for all snapshots rather than one file per snapshot. Get rid of the member by adding a bool* parameter during parse (ignored if the PARSE_INTERNAL flag is not set), and by adding a new flag during format (if FORMAT_INTERNAL is set, the value printed in <active> depends on the new FORMAT_CURRENT). Then update the qemu driver accordingly, which involves hoisting assignments to vm->current_snapshot to occur prior to any point where a snapshot XML file is written (although qemu kept vm->current_snapshot and snapshot->def_current in sync by the end of the function, they were not always identical in the middle of functions, so the shuffling gets a bit interesting). Later patches will clean up some of that confusing churn to vm->current_snapshot. Note: even if later patches refactor qemu to no longer use FORMAT_INTERNAL for output (by storing bulk snapshot XML instead), we will always need PARSE_INTERNAL for input (because on upgrade, a new libvirt still has to parse XML left from a previous libvirt). Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-03-19 03:56:19 +00:00
NULL,
parse_flags)))
goto cleanup;
2013-08-21 20:54:17 +00:00
if (redefine) {
2013-10-04 12:48:39 +00:00
if (virDomainSnapshotRedefinePrep(domain, vm, &def, &snap,
privconn->xmlopt,
2013-10-04 12:48:39 +00:00
&update_current, flags) < 0)
2013-08-21 20:54:17 +00:00
goto cleanup;
} else {
if (!(def->parent.dom = virDomainDefCopy(vm->def,
privconn->caps,
privconn->xmlopt,
NULL,
true)))
2013-08-21 20:54:17 +00:00
goto cleanup;
2013-08-21 20:54:17 +00:00
if (testDomainSnapshotAlignDisks(vm, def, flags) < 0)
goto cleanup;
}
2013-08-21 20:54:17 +00:00
if (!snap) {
if (!(snap = virDomainSnapshotAssignDef(vm->snapshots, def)))
goto cleanup;
def = NULL;
}
2013-08-21 20:54:17 +00:00
if (!redefine) {
if (VIR_STRDUP(snap->def->parent_name,
virDomainSnapshotGetCurrentName(vm->snapshots)) < 0)
2013-08-21 20:54:17 +00:00
goto cleanup;
if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT) &&
virDomainObjIsActive(vm)) {
testDomainShutdownState(domain, vm,
VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
2013-08-21 20:54:17 +00:00
VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
}
}
snapshot = virGetDomainSnapshot(domain, snap->def->name);
cleanup:
if (vm) {
if (snapshot) {
virDomainMomentObjPtr other;
2013-08-21 20:54:17 +00:00
if (update_current)
virDomainSnapshotSetCurrent(vm->snapshots, snap);
other = virDomainSnapshotFindByName(vm->snapshots,
snap->def->parent_name);
virDomainMomentSetParent(snap, other);
}
virDomainObjEndAPI(&vm);
}
virObjectEventStateQueue(privconn->eventState, event);
return snapshot;
}
typedef struct _testSnapRemoveData testSnapRemoveData;
typedef testSnapRemoveData *testSnapRemoveDataPtr;
struct _testSnapRemoveData {
virDomainObjPtr vm;
bool current;
};
static int
testDomainSnapshotDiscardAll(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *data)
{
virDomainMomentObjPtr snap = payload;
testSnapRemoveDataPtr curr = data;
curr->current |= virDomainSnapshotObjListRemove(curr->vm->snapshots, snap);
return 0;
}
typedef struct _testSnapReparentData testSnapReparentData;
typedef testSnapReparentData *testSnapReparentDataPtr;
struct _testSnapReparentData {
virDomainMomentObjPtr parent;
virDomainObjPtr vm;
int err;
};
static int
testDomainSnapshotReparentChildren(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *data)
{
virDomainMomentObjPtr snap = payload;
testSnapReparentDataPtr rep = data;
if (rep->err < 0)
return 0;
VIR_FREE(snap->def->parent_name);
if (rep->parent->def &&
VIR_STRDUP(snap->def->parent_name, rep->parent->def->name) < 0) {
rep->err = -1;
return 0;
}
return 0;
}
static int
testDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
virDomainMomentObjPtr snap = NULL;
virDomainMomentObjPtr 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;
virDomainMomentForEachDescendant(snap,
testDomainSnapshotDiscardAll,
&rem);
test: Avoid use-after-free on virDomainSnapshotDelete The following virsh command was triggering a use-after-free: $ virsh -c test:///default ' snapshot-create-as test s1 snapshot-create-as test s2 snapshot-delete --children-only test s1 snapshot-current --name test' Domain snapshot s1 created Domain snapshot s2 created Domain snapshot s1 children deleted error: name in virGetDomainSnapshot must not be NULL I got lucky on that run - although the error message is quite unexpected. On other runs, I was able to get a core dump, and valgrind confirms there is a definitive problem. The culprit? We were inconsistent about whether we set vm->current_snapshot, snap->def->current, or both when updating how the current snapshot was being tracked. As a result, deletion did not see that snapshot s2 was previously current, and failed to update vm->current_snapshot, so that the next API using the current snapshot failed because it referenced stale memory for the now-gone s2 (instead of the intended s1). The test driver code was copied from the qemu code (which DOES track both pieces of state everywhere), but was purposefully simplified because the test driver does not have to write persistent snapshot state to the file system. But when you realize that the only reason snap->def->current needs to exist is when writing out one file per snapshot for qemu, it's just as easy to state that the test driver never has to mess with the field (rather than chasing down which places forgot to set the field), and have vm->current_snapshot be the sole source of truth in the test driver. Ideally, I'd get rid of the 'current' member in virDomainSnapshotDef, as well as the 'current_snapshot' member in virDomainDef, and instead track the current member in virDomainSnapshotObjList, coupled with writing ALL snapshot state for qemu in a single file (where I can use <snapshots current='...'> as a wrapper, rather than VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL to output <current>1</current> XML on a per-snapshot file basis). But that's a bigger change, so for now I'm just patching things to avoid the test driver segfault. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
2019-03-17 03:57:45 +00:00
if (rem.current)
virDomainSnapshotSetCurrent(vm->snapshots, snap);
} else if (snap->nchildren) {
testSnapReparentData rep;
rep.parent = snap->parent;
rep.vm = vm;
rep.err = 0;
virDomainMomentForEachChild(snap,
testDomainSnapshotReparentChildren,
&rep);
if (rep.err < 0)
goto cleanup;
virDomainMomentMoveChildren(snap, snap->parent);
}
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) {
virDomainMomentDropChildren(snap);
} else {
virDomainMomentDropParent(snap);
if (snap == virDomainSnapshotGetCurrent(vm->snapshots)) {
if (snap->def->parent_name) {
parentsnap = virDomainSnapshotFindByName(vm->snapshots,
snap->def->parent_name);
test: Avoid use-after-free on virDomainSnapshotDelete The following virsh command was triggering a use-after-free: $ virsh -c test:///default ' snapshot-create-as test s1 snapshot-create-as test s2 snapshot-delete --children-only test s1 snapshot-current --name test' Domain snapshot s1 created Domain snapshot s2 created Domain snapshot s1 children deleted error: name in virGetDomainSnapshot must not be NULL I got lucky on that run - although the error message is quite unexpected. On other runs, I was able to get a core dump, and valgrind confirms there is a definitive problem. The culprit? We were inconsistent about whether we set vm->current_snapshot, snap->def->current, or both when updating how the current snapshot was being tracked. As a result, deletion did not see that snapshot s2 was previously current, and failed to update vm->current_snapshot, so that the next API using the current snapshot failed because it referenced stale memory for the now-gone s2 (instead of the intended s1). The test driver code was copied from the qemu code (which DOES track both pieces of state everywhere), but was purposefully simplified because the test driver does not have to write persistent snapshot state to the file system. But when you realize that the only reason snap->def->current needs to exist is when writing out one file per snapshot for qemu, it's just as easy to state that the test driver never has to mess with the field (rather than chasing down which places forgot to set the field), and have vm->current_snapshot be the sole source of truth in the test driver. Ideally, I'd get rid of the 'current' member in virDomainSnapshotDef, as well as the 'current_snapshot' member in virDomainDef, and instead track the current member in virDomainSnapshotObjList, coupled with writing ALL snapshot state for qemu in a single file (where I can use <snapshots current='...'> as a wrapper, rather than VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL to output <current>1</current> XML on a per-snapshot file basis). But that's a bigger change, so for now I'm just patching things to avoid the test driver segfault. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
2019-03-17 03:57:45 +00:00
if (!parentsnap)
VIR_WARN("missing parent snapshot matching name '%s'",
snap->def->parent_name);
}
virDomainSnapshotSetCurrent(vm->snapshots, parentsnap);
}
virDomainSnapshotObjListRemove(vm->snapshots, snap);
}
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
testDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
2015-06-22 14:25:16 +00:00
testDriverPtr privconn = snapshot->domain->conn->privateData;
virDomainObjPtr vm = NULL;
virDomainMomentObjPtr snap = NULL;
virObjectEventPtr event = NULL;
virObjectEventPtr event2 = NULL;
virDomainDefPtr config = NULL;
virDomainSnapshotDefPtr snapdef;
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;
snapdef = virDomainSnapshotObjGetDef(snap);
if (!vm->persistent &&
snapdef->state != VIR_DOMAIN_SNAPSHOT_RUNNING &&
snapdef->state != VIR_DOMAIN_SNAPSHOT_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) &&
!(snapdef->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
snapdef->state == VIR_DOMAIN_SNAPSHOT_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;
}
}
virDomainSnapshotSetCurrent(vm->snapshots, NULL);
config = virDomainDefCopy(snap->def->dom, privconn->caps,
privconn->xmlopt, NULL, true);
if (!config)
goto cleanup;
if (snapdef->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
snapdef->state == VIR_DOMAIN_SNAPSHOT_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,
privconn->xmlopt)) {
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 = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
virObjectEventStateQueue(privconn->eventState, 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 = virDomainEventLifecycleNewFromObj(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 = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT);
}
/* Touch up domain state. */
if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING) &&
(snapdef->state == VIR_DOMAIN_SNAPSHOT_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 = virDomainEventLifecycleNewFromObj(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 */
virObjectUnref(event);
event = NULL;
if (was_stopped) {
/* Transition 2 */
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT);
} else if (was_running) {
/* Transition 8 */
event = virDomainEventLifecycleNewFromObj(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 = virDomainEventLifecycleNewFromObj(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;
virObjectEventStateQueue(privconn->eventState, event);
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT);
if (paused) {
event2 = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_SUSPENDED,
VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT);
}
}
}
virDomainSnapshotSetCurrent(vm->snapshots, snap);
ret = 0;
cleanup:
if (event) {
virObjectEventStateQueue(privconn->eventState, event);
virObjectEventStateQueue(privconn->eventState, event2);
2013-10-04 12:48:39 +00:00
} else {
virObjectUnref(event2);
}
virDomainObjEndAPI(&vm);
return ret;
}
static virHypervisorDriver testHypervisorDriver = {
.name = "Test",
.connectOpen = testConnectOpen, /* 0.1.1 */
.connectClose = testConnectClose, /* 0.1.1 */
.connectGetVersion = testConnectGetVersion, /* 0.1.1 */
.connectGetHostname = testConnectGetHostname, /* 0.6.3 */
.connectGetMaxVcpus = testConnectGetMaxVcpus, /* 0.3.2 */
.nodeGetInfo = testNodeGetInfo, /* 0.1.1 */
.nodeGetCPUStats = testNodeGetCPUStats, /* 2.3.0 */
.nodeGetFreeMemory = testNodeGetFreeMemory, /* 2.3.0 */
.nodeGetFreePages = testNodeGetFreePages, /* 2.3.0 */
.connectGetCapabilities = testConnectGetCapabilities, /* 0.2.1 */
.connectGetSysinfo = testConnectGetSysinfo, /* 2.3.0 */
.connectGetType = testConnectGetType, /* 2.3.0 */
.connectListDomains = testConnectListDomains, /* 0.1.1 */
.connectNumOfDomains = testConnectNumOfDomains, /* 0.1.1 */
.connectListAllDomains = testConnectListAllDomains, /* 0.9.13 */
.domainCreateXML = testDomainCreateXML, /* 0.1.4 */
.domainLookupByID = testDomainLookupByID, /* 0.1.1 */
.domainLookupByUUID = testDomainLookupByUUID, /* 0.1.1 */
.domainLookupByName = testDomainLookupByName, /* 0.1.1 */
.domainSuspend = testDomainSuspend, /* 0.1.1 */
.domainResume = testDomainResume, /* 0.1.1 */
.domainShutdown = testDomainShutdown, /* 0.1.1 */
.domainShutdownFlags = testDomainShutdownFlags, /* 0.9.10 */
.domainReboot = testDomainReboot, /* 0.1.1 */
.domainDestroy = testDomainDestroy, /* 0.1.1 */
.domainDestroyFlags = testDomainDestroyFlags, /* 4.2.0 */
.domainGetOSType = testDomainGetOSType, /* 0.1.9 */
.domainGetMaxMemory = testDomainGetMaxMemory, /* 0.1.4 */
.domainSetMaxMemory = testDomainSetMaxMemory, /* 0.1.1 */
.domainSetMemory = testDomainSetMemory, /* 0.1.4 */
.domainGetHostname = testDomainGetHostname, /* 5.5.0 */
.domainGetInfo = testDomainGetInfo, /* 0.1.1 */
.domainGetState = testDomainGetState, /* 0.9.2 */
.domainGetTime = testDomainGetTime, /* 5.4.0 */
.domainSave = testDomainSave, /* 0.3.2 */
.domainSaveFlags = testDomainSaveFlags, /* 0.9.4 */
.domainRestore = testDomainRestore, /* 0.3.2 */
.domainRestoreFlags = testDomainRestoreFlags, /* 0.9.4 */
.domainCoreDump = testDomainCoreDump, /* 0.3.2 */
.domainCoreDumpWithFormat = testDomainCoreDumpWithFormat, /* 1.2.3 */
.domainSetVcpus = testDomainSetVcpus, /* 0.1.4 */
.domainSetVcpusFlags = testDomainSetVcpusFlags, /* 0.8.5 */
.domainGetVcpusFlags = testDomainGetVcpusFlags, /* 0.8.5 */
.domainPinVcpu = testDomainPinVcpu, /* 0.7.3 */
.domainGetVcpus = testDomainGetVcpus, /* 0.7.3 */
.domainGetVcpuPinInfo = testDomainGetVcpuPinInfo, /* 1.2.18 */
.domainGetMaxVcpus = testDomainGetMaxVcpus, /* 0.7.3 */
.domainGetXMLDesc = testDomainGetXMLDesc, /* 0.1.4 */
.connectListDefinedDomains = testConnectListDefinedDomains, /* 0.1.11 */
.connectNumOfDefinedDomains = testConnectNumOfDefinedDomains, /* 0.1.11 */
.domainCreate = testDomainCreate, /* 0.1.11 */
.domainCreateWithFlags = testDomainCreateWithFlags, /* 0.8.2 */
.domainDefineXML = testDomainDefineXML, /* 0.1.11 */
.domainDefineXMLFlags = testDomainDefineXMLFlags, /* 1.2.12 */
.domainUndefine = testDomainUndefine, /* 0.1.11 */
.domainUndefineFlags = testDomainUndefineFlags, /* 0.9.4 */
.domainGetAutostart = testDomainGetAutostart, /* 0.3.2 */
.domainSetAutostart = testDomainSetAutostart, /* 0.3.2 */
.domainGetDiskErrors = testDomainGetDiskErrors, /* 5.4.0 */
.domainGetSchedulerType = testDomainGetSchedulerType, /* 0.3.2 */
.domainGetSchedulerParameters = testDomainGetSchedulerParameters, /* 0.3.2 */
.domainGetSchedulerParametersFlags = testDomainGetSchedulerParametersFlags, /* 0.9.2 */
.domainSetSchedulerParameters = testDomainSetSchedulerParameters, /* 0.3.2 */
.domainSetSchedulerParametersFlags = testDomainSetSchedulerParametersFlags, /* 0.9.2 */
.domainBlockStats = testDomainBlockStats, /* 0.7.0 */
.domainInterfaceAddresses = testDomainInterfaceAddresses, /* 5.4.0 */
.domainInterfaceStats = testDomainInterfaceStats, /* 0.7.0 */
.nodeGetCellsFreeMemory = testNodeGetCellsFreeMemory, /* 0.4.2 */
.connectDomainEventRegister = testConnectDomainEventRegister, /* 0.6.0 */
.connectDomainEventDeregister = testConnectDomainEventDeregister, /* 0.6.0 */
.connectIsEncrypted = testConnectIsEncrypted, /* 0.7.3 */
.connectIsSecure = testConnectIsSecure, /* 0.7.3 */
.domainIsActive = testDomainIsActive, /* 0.7.3 */
.domainIsPersistent = testDomainIsPersistent, /* 0.7.3 */
.domainIsUpdated = testDomainIsUpdated, /* 0.8.6 */
.connectDomainEventRegisterAny = testConnectDomainEventRegisterAny, /* 0.8.0 */
.connectDomainEventDeregisterAny = testConnectDomainEventDeregisterAny, /* 0.8.0 */
.connectIsAlive = testConnectIsAlive, /* 0.9.8 */
.nodeGetCPUMap = testNodeGetCPUMap, /* 1.0.0 */
.domainRename = testDomainRename, /* 4.1.0 */
.domainScreenshot = testDomainScreenshot, /* 1.0.5 */
.domainSendKey = testDomainSendKey, /* 5.5.0 */
.domainGetMetadata = testDomainGetMetadata, /* 1.1.3 */
.domainSetMetadata = testDomainSetMetadata, /* 1.1.3 */
.connectGetCPUModelNames = testConnectGetCPUModelNames, /* 1.1.3 */
.domainManagedSave = testDomainManagedSave, /* 1.1.4 */
.domainHasManagedSaveImage = testDomainHasManagedSaveImage, /* 1.1.4 */
.domainManagedSaveRemove = testDomainManagedSaveRemove, /* 1.1.4 */
.domainMemoryPeek = testDomainMemoryPeek, /* 5.4.0 */
.domainSnapshotNum = testDomainSnapshotNum, /* 1.1.4 */
.domainSnapshotListNames = testDomainSnapshotListNames, /* 1.1.4 */
.domainListAllSnapshots = testDomainListAllSnapshots, /* 1.1.4 */
.domainSnapshotGetXMLDesc = testDomainSnapshotGetXMLDesc, /* 1.1.4 */
.domainSnapshotNumChildren = testDomainSnapshotNumChildren, /* 1.1.4 */
.domainSnapshotListChildrenNames = testDomainSnapshotListChildrenNames, /* 1.1.4 */
.domainSnapshotListAllChildren = testDomainSnapshotListAllChildren, /* 1.1.4 */
.domainSnapshotLookupByName = testDomainSnapshotLookupByName, /* 1.1.4 */
.domainHasCurrentSnapshot = testDomainHasCurrentSnapshot, /* 1.1.4 */
.domainSnapshotGetParent = testDomainSnapshotGetParent, /* 1.1.4 */
.domainSnapshotCurrent = testDomainSnapshotCurrent, /* 1.1.4 */
.domainSnapshotIsCurrent = testDomainSnapshotIsCurrent, /* 1.1.4 */
.domainSnapshotHasMetadata = testDomainSnapshotHasMetadata, /* 1.1.4 */
.domainSnapshotCreateXML = testDomainSnapshotCreateXML, /* 1.1.4 */
.domainRevertToSnapshot = testDomainRevertToSnapshot, /* 1.1.4 */
.domainSnapshotDelete = testDomainSnapshotDelete, /* 1.1.4 */
.connectBaselineCPU = testConnectBaselineCPU, /* 1.2.0 */
};
static virNetworkDriver testNetworkDriver = {
.connectNumOfNetworks = testConnectNumOfNetworks, /* 0.3.2 */
.connectListNetworks = testConnectListNetworks, /* 0.3.2 */
.connectNumOfDefinedNetworks = testConnectNumOfDefinedNetworks, /* 0.3.2 */
.connectListDefinedNetworks = testConnectListDefinedNetworks, /* 0.3.2 */
.connectListAllNetworks = testConnectListAllNetworks, /* 0.10.2 */
.connectNetworkEventRegisterAny = testConnectNetworkEventRegisterAny, /* 1.2.1 */
.connectNetworkEventDeregisterAny = testConnectNetworkEventDeregisterAny, /* 1.2.1 */
.networkLookupByUUID = testNetworkLookupByUUID, /* 0.3.2 */
.networkLookupByName = testNetworkLookupByName, /* 0.3.2 */
.networkCreateXML = testNetworkCreateXML, /* 0.3.2 */
.networkDefineXML = testNetworkDefineXML, /* 0.3.2 */
.networkUndefine = testNetworkUndefine, /* 0.3.2 */
.networkUpdate = testNetworkUpdate, /* 0.10.2 */
.networkCreate = testNetworkCreate, /* 0.3.2 */
.networkDestroy = testNetworkDestroy, /* 0.3.2 */
.networkGetXMLDesc = testNetworkGetXMLDesc, /* 0.3.2 */
.networkGetBridgeName = testNetworkGetBridgeName, /* 0.3.2 */
.networkGetAutostart = testNetworkGetAutostart, /* 0.3.2 */
.networkSetAutostart = testNetworkSetAutostart, /* 0.3.2 */
.networkIsActive = testNetworkIsActive, /* 0.7.3 */
.networkIsPersistent = testNetworkIsPersistent, /* 0.7.3 */
};
static virInterfaceDriver testInterfaceDriver = {
.connectNumOfInterfaces = testConnectNumOfInterfaces, /* 0.7.0 */
.connectListInterfaces = testConnectListInterfaces, /* 0.7.0 */
.connectNumOfDefinedInterfaces = testConnectNumOfDefinedInterfaces, /* 0.7.0 */
.connectListDefinedInterfaces = testConnectListDefinedInterfaces, /* 0.7.0 */
.connectListAllInterfaces = testConnectListAllInterfaces, /* 4.6.0 */
.interfaceLookupByName = testInterfaceLookupByName, /* 0.7.0 */
.interfaceLookupByMACString = testInterfaceLookupByMACString, /* 0.7.0 */
.interfaceGetXMLDesc = testInterfaceGetXMLDesc, /* 0.7.0 */
.interfaceDefineXML = testInterfaceDefineXML, /* 0.7.0 */
.interfaceUndefine = testInterfaceUndefine, /* 0.7.0 */
.interfaceCreate = testInterfaceCreate, /* 0.7.0 */
.interfaceDestroy = testInterfaceDestroy, /* 0.7.0 */
.interfaceIsActive = testInterfaceIsActive, /* 0.7.3 */
.interfaceChangeBegin = testInterfaceChangeBegin, /* 0.9.2 */
.interfaceChangeCommit = testInterfaceChangeCommit, /* 0.9.2 */
.interfaceChangeRollback = testInterfaceChangeRollback, /* 0.9.2 */
};
static virStorageDriver testStorageDriver = {
.connectNumOfStoragePools = testConnectNumOfStoragePools, /* 0.5.0 */
.connectListStoragePools = testConnectListStoragePools, /* 0.5.0 */
.connectNumOfDefinedStoragePools = testConnectNumOfDefinedStoragePools, /* 0.5.0 */
.connectListDefinedStoragePools = testConnectListDefinedStoragePools, /* 0.5.0 */
.connectListAllStoragePools = testConnectListAllStoragePools, /* 0.10.2 */
.connectFindStoragePoolSources = testConnectFindStoragePoolSources, /* 0.5.0 */
.connectStoragePoolEventRegisterAny = testConnectStoragePoolEventRegisterAny, /* 2.0.0 */
.connectStoragePoolEventDeregisterAny = testConnectStoragePoolEventDeregisterAny, /* 2.0.0 */
.storagePoolLookupByName = testStoragePoolLookupByName, /* 0.5.0 */
.storagePoolLookupByUUID = testStoragePoolLookupByUUID, /* 0.5.0 */
.storagePoolLookupByVolume = testStoragePoolLookupByVolume, /* 0.5.0 */
.storagePoolCreateXML = testStoragePoolCreateXML, /* 0.5.0 */
.storagePoolDefineXML = testStoragePoolDefineXML, /* 0.5.0 */
.storagePoolBuild = testStoragePoolBuild, /* 0.5.0 */
.storagePoolUndefine = testStoragePoolUndefine, /* 0.5.0 */
.storagePoolCreate = testStoragePoolCreate, /* 0.5.0 */
.storagePoolDestroy = testStoragePoolDestroy, /* 0.5.0 */
.storagePoolDelete = testStoragePoolDelete, /* 0.5.0 */
.storagePoolRefresh = testStoragePoolRefresh, /* 0.5.0 */
.storagePoolGetInfo = testStoragePoolGetInfo, /* 0.5.0 */
.storagePoolGetXMLDesc = testStoragePoolGetXMLDesc, /* 0.5.0 */
.storagePoolGetAutostart = testStoragePoolGetAutostart, /* 0.5.0 */
.storagePoolSetAutostart = testStoragePoolSetAutostart, /* 0.5.0 */
.storagePoolNumOfVolumes = testStoragePoolNumOfVolumes, /* 0.5.0 */
.storagePoolListVolumes = testStoragePoolListVolumes, /* 0.5.0 */
.storagePoolListAllVolumes = testStoragePoolListAllVolumes, /* 0.10.2 */
.storageVolLookupByName = testStorageVolLookupByName, /* 0.5.0 */
.storageVolLookupByKey = testStorageVolLookupByKey, /* 0.5.0 */
.storageVolLookupByPath = testStorageVolLookupByPath, /* 0.5.0 */
.storageVolCreateXML = testStorageVolCreateXML, /* 0.5.0 */
.storageVolCreateXMLFrom = testStorageVolCreateXMLFrom, /* 0.6.4 */
.storageVolDelete = testStorageVolDelete, /* 0.5.0 */
.storageVolGetInfo = testStorageVolGetInfo, /* 0.5.0 */
.storageVolGetXMLDesc = testStorageVolGetXMLDesc, /* 0.5.0 */
.storageVolGetPath = testStorageVolGetPath, /* 0.5.0 */
.storagePoolIsActive = testStoragePoolIsActive, /* 0.7.3 */
.storagePoolIsPersistent = testStoragePoolIsPersistent, /* 0.7.3 */
};
static virNodeDeviceDriver testNodeDeviceDriver = {
.connectListAllNodeDevices = testConnectListAllNodeDevices, /* 4.1.0 */
.connectNodeDeviceEventRegisterAny = testConnectNodeDeviceEventRegisterAny, /* 2.2.0 */
.connectNodeDeviceEventDeregisterAny = testConnectNodeDeviceEventDeregisterAny, /* 2.2.0 */
.nodeNumOfDevices = testNodeNumOfDevices, /* 0.7.2 */
.nodeListDevices = testNodeListDevices, /* 0.7.2 */
.nodeDeviceLookupByName = testNodeDeviceLookupByName, /* 0.7.2 */
.nodeDeviceGetXMLDesc = testNodeDeviceGetXMLDesc, /* 0.7.2 */
.nodeDeviceGetParent = testNodeDeviceGetParent, /* 0.7.2 */
.nodeDeviceNumOfCaps = testNodeDeviceNumOfCaps, /* 0.7.2 */
.nodeDeviceListCaps = testNodeDeviceListCaps, /* 0.7.2 */
.nodeDeviceCreateXML = testNodeDeviceCreateXML, /* 0.7.3 */
.nodeDeviceDestroy = testNodeDeviceDestroy, /* 0.7.3 */
};
static virConnectDriver testConnectDriver = {
.localOnly = true,
.uriSchemes = (const char *[]){ "test", NULL },
.hypervisorDriver = &testHypervisorDriver,
.interfaceDriver = &testInterfaceDriver,
.networkDriver = &testNetworkDriver,
.nodeDeviceDriver = &testNodeDeviceDriver,
.nwfilterDriver = NULL,
.secretDriver = NULL,
.storageDriver = &testStorageDriver,
};
/**
* testRegister:
*
* Registers the test driver
*/
int
testRegister(void)
{
return virRegisterConnectDriver(&testConnectDriver,
false);
}