mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-07 05:25:25 +00:00
072390cbf0
Since the behaviour of launch security is heavily dependent on 3rd party vendors (e.g. AMD SEV) where the data returned can be essentially anything, the most reasonable approach here in the test driver is not to try return any data. Signed-off-by: Ilias Stamatis <stamatis.iliass@gmail.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
7351 lines
207 KiB
C
7351 lines
207 KiB
C
/*
|
|
* test_driver.c: A "mock" hypervisor for use by application unit tests
|
|
*
|
|
* 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>
|
|
#include <libxml/xmlsave.h>
|
|
#include <libxml/xpathInternals.h>
|
|
|
|
|
|
#include "virerror.h"
|
|
#include "datatypes.h"
|
|
#include "test_driver.h"
|
|
#include "virbuffer.h"
|
|
#include "viruuid.h"
|
|
#include "capabilities.h"
|
|
#include "configmake.h"
|
|
#include "viralloc.h"
|
|
#include "virnetworkobj.h"
|
|
#include "interface_conf.h"
|
|
#include "domain_conf.h"
|
|
#include "domain_event.h"
|
|
#include "network_event.h"
|
|
#include "snapshot_conf.h"
|
|
#include "virfdstream.h"
|
|
#include "storage_conf.h"
|
|
#include "virstorageobj.h"
|
|
#include "storage_event.h"
|
|
#include "node_device_conf.h"
|
|
#include "virnodedeviceobj.h"
|
|
#include "node_device_event.h"
|
|
#include "virxml.h"
|
|
#include "virthread.h"
|
|
#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;
|
|
|
|
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;
|
|
|
|
/* 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;
|
|
};
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virCapsPtr caps;
|
|
virCapsGuestPtr guest;
|
|
int guest_types[] = { VIR_DOMAIN_OSTYPE_HVM,
|
|
VIR_DOMAIN_OSTYPE_XEN };
|
|
size_t i, j;
|
|
|
|
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;
|
|
|
|
if (virCapabilitiesAddGuestFeature(guest, "pae", true, true) == NULL)
|
|
goto error;
|
|
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>"
|
|
""
|
|
"<pool type='dir'>"
|
|
" <name>default-pool</name>"
|
|
" <uuid>dfe224cb-28fb-8dd0-c4b2-64eb3f0f4566</uuid>"
|
|
" <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>";
|
|
|
|
|
|
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";
|
|
|
|
static const unsigned long long defaultPoolCap = (100 * 1024 * 1024 * 1024ull);
|
|
static const unsigned long long defaultPoolAlloc;
|
|
|
|
static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr obj);
|
|
static int testNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info);
|
|
|
|
static virDomainObjPtr
|
|
testDomObjFromDomain(virDomainPtr domain)
|
|
{
|
|
virDomainObjPtr vm;
|
|
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
|
|
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
|
|
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;
|
|
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,
|
|
&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;
|
|
}
|
|
|
|
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
|
|
testParseDomains(testDriverPtr privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num, ret = -1;
|
|
size_t i;
|
|
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;
|
|
}
|
|
|
|
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,
|
|
VIR_DOMAIN_RUNNING_BOOTED) < 0)
|
|
goto error;
|
|
} else {
|
|
testDomainShutdownState(NULL, obj, 0);
|
|
}
|
|
virDomainObjSetState(obj, nsdata->runstate, 0);
|
|
|
|
virDomainObjEndAPI(&obj);
|
|
}
|
|
|
|
ret = 0;
|
|
error:
|
|
virDomainObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
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
|
|
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
|
|
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++) {
|
|
virStoragePoolDefPtr def;
|
|
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
|
|
"pool");
|
|
if (!node)
|
|
return -1;
|
|
|
|
def = virStoragePoolDefParseNode(ctxt->doc, node);
|
|
if (!def)
|
|
return -1;
|
|
|
|
if (!(obj = virStoragePoolObjAssignDef(privconn->pools, def, false))) {
|
|
virStoragePoolDefFree(def);
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
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
|
|
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;
|
|
}
|
|
|
|
static int
|
|
testOpenParse(testDriverPtr privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
if (!virXMLNodeNameEqual(ctxt->node, "node")) {
|
|
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;
|
|
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));
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virObjectLock(privconn);
|
|
memcpy(info, &privconn->nodeInfo, sizeof(virNodeInfo));
|
|
virObjectUnlock(privconn);
|
|
return 0;
|
|
}
|
|
|
|
static char *testConnectGetCapabilities(virConnectPtr conn)
|
|
{
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int count;
|
|
|
|
virObjectLock(privconn);
|
|
count = virDomainObjListNumOfDomains(privconn->domains, true, NULL, NULL);
|
|
virObjectUnlock(privconn);
|
|
|
|
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)
|
|
{
|
|
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) {
|
|
if (!dom->persistent)
|
|
virDomainObjListRemove(privconn->domains, dom);
|
|
goto cleanup;
|
|
}
|
|
|
|
event = virDomainEventLifecycleNewFromObj(dom,
|
|
VIR_DOMAIN_EVENT_STARTED,
|
|
VIR_DOMAIN_EVENT_STARTED_BOOTED);
|
|
|
|
ret = virGetDomain(conn, dom->def->name, dom->def->uuid, dom->def->id);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&dom);
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
virDomainDefFree(def);
|
|
virObjectUnlock(privconn);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static virDomainPtr testDomainLookupByID(virConnectPtr conn,
|
|
int id)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
|
|
return virDomainObjListGetActiveIDs(privconn->domains, ids, maxids,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static int testDomainDestroyFlags(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
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,
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
|
|
|
|
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)
|
|
{
|
|
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,
|
|
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)
|
|
{
|
|
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,
|
|
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)
|
|
{
|
|
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,
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
|
|
|
|
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)
|
|
{
|
|
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,
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
* testDomainSaveImageOpen:
|
|
* @driver: test driver data
|
|
* @path: path of the saved image
|
|
* @ret_def: returns domain definition created from the XML stored in the image
|
|
*
|
|
* Returns the opened fd of the save image file and fills ret_def on success.
|
|
* Returns -1, on error.
|
|
*/
|
|
static int ATTRIBUTE_NONNULL(3)
|
|
testDomainSaveImageOpen(testDriverPtr driver,
|
|
const char *path,
|
|
virDomainDefPtr *ret_def)
|
|
{
|
|
char magic[15];
|
|
int fd = -1;
|
|
int len;
|
|
virDomainDefPtr def = NULL;
|
|
VIR_AUTOFREE(char *) xml = NULL;
|
|
|
|
if ((fd = open(path, O_RDONLY)) < 0) {
|
|
virReportSystemError(errno, _("cannot read domain image '%s'"), path);
|
|
goto error;
|
|
}
|
|
|
|
if (saferead(fd, magic, sizeof(magic)) != sizeof(magic)) {
|
|
virReportSystemError(errno, _("incomplete save header in '%s'"), path);
|
|
goto error;
|
|
}
|
|
|
|
if (memcmp(magic, TEST_SAVE_MAGIC, sizeof(magic))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("mismatched header magic"));
|
|
goto error;
|
|
}
|
|
|
|
if (saferead(fd, (char*)&len, sizeof(len)) != sizeof(len)) {
|
|
virReportSystemError(errno,
|
|
_("failed to read metadata length in '%s'"),
|
|
path);
|
|
goto error;
|
|
}
|
|
|
|
if (len < 1 || len > 8192) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("length of metadata out of range"));
|
|
goto error;
|
|
}
|
|
|
|
if (VIR_ALLOC_N(xml, len+1) < 0)
|
|
goto error;
|
|
|
|
if (saferead(fd, xml, len) != len) {
|
|
virReportSystemError(errno, _("incomplete metadata in '%s'"), path);
|
|
goto error;
|
|
}
|
|
xml[len] = '\0';
|
|
|
|
if (!(def = virDomainDefParseString(xml, driver->caps, driver->xmlopt, NULL,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE |
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
|
|
goto error;
|
|
|
|
VIR_STEAL_PTR(*ret_def, def);
|
|
return fd;
|
|
|
|
error:
|
|
virDomainDefFree(def);
|
|
VIR_FORCE_CLOSE(fd);
|
|
return -1;
|
|
}
|
|
|
|
|
|
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,
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_SAVED);
|
|
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int fd = -1;
|
|
virDomainDefPtr def = NULL;
|
|
virDomainObjPtr dom = NULL;
|
|
virObjectEventPtr event = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
if (dxml) {
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
_("xml modification unsupported"));
|
|
return -1;
|
|
}
|
|
|
|
if ((fd = testDomainSaveImageOpen(privconn, path, &def)) < 0)
|
|
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) {
|
|
if (!dom->persistent)
|
|
virDomainObjListRemove(privconn->domains, dom);
|
|
goto cleanup;
|
|
}
|
|
|
|
event = virDomainEventLifecycleNewFromObj(dom,
|
|
VIR_DOMAIN_EVENT_STARTED,
|
|
VIR_DOMAIN_EVENT_STARTED_RESTORED);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainDefFree(def);
|
|
VIR_FORCE_CLOSE(fd);
|
|
virDomainObjEndAPI(&dom);
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testDomainRestore(virConnectPtr conn,
|
|
const char *path)
|
|
{
|
|
return testDomainRestoreFlags(conn, path, NULL, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSaveImageDefineXML(virConnectPtr conn,
|
|
const char *path,
|
|
const char *dxml,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
int fd = -1;
|
|
virDomainDefPtr def = NULL;
|
|
virDomainDefPtr newdef = NULL;
|
|
testDriverPtr privconn = conn->privateData;
|
|
|
|
virCheckFlags(VIR_DOMAIN_SAVE_RUNNING |
|
|
VIR_DOMAIN_SAVE_PAUSED, -1);
|
|
|
|
if ((fd = testDomainSaveImageOpen(privconn, path, &def)) < 0)
|
|
goto cleanup;
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
if ((newdef = virDomainDefParseString(dxml, privconn->caps, privconn->xmlopt, NULL,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE)) == NULL)
|
|
goto cleanup;
|
|
|
|
if (!testDomainSaveImageWrite(privconn, path, newdef))
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainDefFree(def);
|
|
virDomainDefFree(newdef);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
testDomainSaveImageGetXMLDesc(virConnectPtr conn,
|
|
const char *path,
|
|
unsigned int flags)
|
|
{
|
|
int fd = -1;
|
|
char *ret = NULL;
|
|
virDomainDefPtr def = NULL;
|
|
testDriverPtr privconn = conn->privateData;
|
|
|
|
virCheckFlags(VIR_DOMAIN_SAVE_IMAGE_XML_SECURE, NULL);
|
|
|
|
if ((fd = testDomainSaveImageOpen(privconn, path, &def)) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virDomainDefFormat(def, privconn->caps, VIR_DOMAIN_DEF_FORMAT_SECURE);
|
|
|
|
cleanup:
|
|
virDomainDefFree(def);
|
|
VIR_FORCE_CLOSE(fd);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int testDomainCoreDumpWithFormat(virDomainPtr domain,
|
|
const char *to,
|
|
unsigned int dumpformat,
|
|
unsigned int flags)
|
|
{
|
|
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);
|
|
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 int
|
|
testDomainGetLaunchSecurityInfo(virDomainPtr domain ATTRIBUTE_UNUSED,
|
|
virTypedParameterPtr *params ATTRIBUTE_UNUSED,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(0, -1);
|
|
|
|
*nparams = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static unsigned long long
|
|
testDomainGetMaxMemory(virDomainPtr domain)
|
|
{
|
|
virDomainObjPtr privdom;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
testDomainGetMaxVcpus(virDomainPtr domain)
|
|
{
|
|
return testDomainGetVcpusFlags(domain, (VIR_DOMAIN_AFFECT_LIVE |
|
|
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);
|
|
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;
|
|
}
|
|
|
|
static int
|
|
testDomainSetVcpus(virDomainPtr domain, unsigned int nrCpus)
|
|
{
|
|
return testDomainSetVcpusFlags(domain, nrCpus, VIR_DOMAIN_AFFECT_LIVE);
|
|
}
|
|
|
|
static int testDomainGetVcpus(virDomainPtr domain,
|
|
virVcpuInfoPtr info,
|
|
int maxinfo,
|
|
unsigned char *cpumaps,
|
|
int maplen)
|
|
{
|
|
testDriverPtr privconn = domain->conn->privateData;
|
|
virDomainObjPtr privdom;
|
|
virDomainDefPtr def;
|
|
size_t i;
|
|
int hostcpus;
|
|
int ret = -1;
|
|
struct timeval tv;
|
|
unsigned long long statbase;
|
|
virBitmapPtr allcpumap = NULL;
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
if (!virDomainObjIsActive(privdom)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cannot list vcpus for an inactive domain"));
|
|
goto cleanup;
|
|
}
|
|
|
|
def = privdom->def;
|
|
|
|
if (gettimeofday(&tv, NULL) < 0) {
|
|
virReportSystemError(errno,
|
|
"%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);
|
|
|
|
/* Clamp to actual number of vcpus */
|
|
if (maxinfo > virDomainDefGetVcpus(privdom->def))
|
|
maxinfo = virDomainDefGetVcpus(privdom->def);
|
|
|
|
memset(info, 0, sizeof(*info) * maxinfo);
|
|
memset(cpumaps, 0, maxinfo * maplen);
|
|
|
|
for (i = 0; i < maxinfo; i++) {
|
|
virDomainVcpuDefPtr vcpu = virDomainDefGetVcpu(def, i);
|
|
virBitmapPtr bitmap = NULL;
|
|
|
|
if (!vcpu->online)
|
|
continue;
|
|
|
|
if (vcpu->cpumask)
|
|
bitmap = vcpu->cpumask;
|
|
else if (def->cpumask)
|
|
bitmap = def->cpumask;
|
|
else
|
|
bitmap = allcpumap;
|
|
|
|
if (cpumaps)
|
|
virBitmapToDataBuf(bitmap, VIR_GET_CPUMAP(cpumaps, maplen, i), maplen);
|
|
|
|
info[i].number = i;
|
|
info[i].state = VIR_VCPU_RUNNING;
|
|
info[i].cpu = virBitmapLastSetBit(bitmap);
|
|
|
|
/* Fake an increasing cpu time value */
|
|
info[i].cpuTime = statbase / 10;
|
|
}
|
|
|
|
ret = maxinfo;
|
|
cleanup:
|
|
virBitmapFree(allcpumap);
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
static int testDomainPinVcpu(virDomainPtr domain,
|
|
unsigned int vcpu,
|
|
unsigned char *cpumap,
|
|
int maplen)
|
|
{
|
|
virDomainVcpuDefPtr vcpuinfo;
|
|
virDomainObjPtr privdom;
|
|
virDomainDefPtr def;
|
|
int ret = -1;
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
def = privdom->def;
|
|
|
|
if (!virDomainObjIsActive(privdom)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cannot pin vcpus on an inactive domain"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(vcpuinfo = virDomainDefGetVcpu(def, vcpu)) ||
|
|
!vcpuinfo->online) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("requested vcpu '%d' is not present in the domain"),
|
|
vcpu);
|
|
goto cleanup;
|
|
}
|
|
|
|
virBitmapFree(vcpuinfo->cpumask);
|
|
|
|
if (!(vcpuinfo->cpumask = virBitmapNewData(cpumap, maplen)))
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&privdom);
|
|
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;
|
|
}
|
|
|
|
static char *testDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = domain->conn->privateData;
|
|
virDomainDefPtr def;
|
|
virDomainObjPtr privdom;
|
|
char *ret = NULL;
|
|
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
|
|
return virDomainObjListNumOfDomains(privconn->domains, false, NULL, NULL);
|
|
}
|
|
|
|
static int testConnectListDefinedDomains(virConnectPtr conn,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
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)
|
|
{
|
|
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,
|
|
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);
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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(¶ms[i],
|
|
VIR_NODE_CPU_STATS_USER, 9797400000) < 0)
|
|
return -1;
|
|
break;
|
|
case 1:
|
|
if (virHostCPUStatsAssign(¶ms[i],
|
|
VIR_NODE_CPU_STATS_KERNEL, 34678723400000) < 0)
|
|
return -1;
|
|
break;
|
|
case 2:
|
|
if (virHostCPUStatsAssign(¶ms[i],
|
|
VIR_NODE_CPU_STATS_IDLE, 87264900000) < 0)
|
|
return -1;
|
|
break;
|
|
case 3:
|
|
if (virHostCPUStatsAssign(¶ms[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)
|
|
{
|
|
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,
|
|
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)
|
|
{
|
|
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,
|
|
VIR_DOMAIN_EVENT_UNDEFINED,
|
|
VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
|
|
privdom->hasManagedSave = false;
|
|
|
|
if (virDomainObjIsActive(privdom))
|
|
privdom->persistent = 0;
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int n;
|
|
|
|
n = virNetworkObjListGetNames(privconn->networks,
|
|
true, names, maxnames, NULL, conn);
|
|
return n;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNumOfDefinedNetworks(virConnectPtr conn)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int nnames;
|
|
|
|
virObjectLock(privconn);
|
|
nnames = virInterfaceObjListGetNames(privconn->ifaces, true,
|
|
names, maxnames);
|
|
virObjectUnlock(privconn);
|
|
|
|
return nnames;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNumOfDefinedInterfaces(virConnectPtr conn)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virObjectLock(privconn);
|
|
if (privconn->transaction_running) {
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virObjectLock(privconn);
|
|
|
|
if (!privconn->transaction_running) {
|
|
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)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virObjectLock(privconn);
|
|
|
|
if (!privconn->transaction_running) {
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Storage Driver routines
|
|
*/
|
|
|
|
static int
|
|
testStoragePoolObjSetDefaults(virStoragePoolObjPtr obj)
|
|
{
|
|
char *configFile;
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
|
|
|
|
def->capacity = defaultPoolCap;
|
|
def->allocation = defaultPoolAlloc;
|
|
def->available = defaultPoolCap - defaultPoolAlloc;
|
|
|
|
if (VIR_STRDUP(configFile, "") < 0)
|
|
return -1;
|
|
|
|
virStoragePoolObjSetConfigFile(obj, configFile);
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStoragePoolPtr
|
|
testStoragePoolLookupByUUID(virConnectPtr conn,
|
|
const unsigned char *uuid)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virStoragePoolDefPtr def;
|
|
virStoragePoolPtr pool = NULL;
|
|
|
|
if (!(obj = testStoragePoolObjFindByUUID(privconn, uuid)))
|
|
return NULL;
|
|
def = virStoragePoolObjGetDef(obj);
|
|
|
|
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return pool;
|
|
}
|
|
|
|
|
|
static virStoragePoolPtr
|
|
testStoragePoolLookupByName(virConnectPtr conn,
|
|
const char *name)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virStoragePoolDefPtr def;
|
|
virStoragePoolPtr pool = NULL;
|
|
|
|
if (!(obj = testStoragePoolObjFindByName(privconn, name)))
|
|
return NULL;
|
|
def = virStoragePoolObjGetDef(obj);
|
|
|
|
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return pool;
|
|
}
|
|
|
|
|
|
static virStoragePoolPtr
|
|
testStoragePoolLookupByVolume(virStorageVolPtr vol)
|
|
{
|
|
return testStoragePoolLookupByName(vol->conn, vol->pool);
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNumOfStoragePools(virConnectPtr conn)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int numActive = 0;
|
|
|
|
virObjectLock(privconn);
|
|
numActive = virStoragePoolObjNumOfStoragePools(privconn->pools, conn,
|
|
true, NULL);
|
|
virObjectUnlock(privconn);
|
|
|
|
return numActive;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectListStoragePools(virConnectPtr conn,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int n = 0;
|
|
|
|
virObjectLock(privconn);
|
|
n = virStoragePoolObjGetNames(privconn->pools, conn, true, NULL,
|
|
names, maxnames);
|
|
virObjectUnlock(privconn);
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNumOfDefinedStoragePools(virConnectPtr conn)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int numInactive = 0;
|
|
|
|
virObjectLock(privconn);
|
|
numInactive = virStoragePoolObjNumOfStoragePools(privconn->pools, conn,
|
|
false, NULL);
|
|
virObjectUnlock(privconn);
|
|
|
|
return numInactive;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectListDefinedStoragePools(virConnectPtr conn,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
int n = 0;
|
|
|
|
virObjectLock(privconn);
|
|
n = virStoragePoolObjGetNames(privconn->pools, conn, false, NULL,
|
|
names, maxnames);
|
|
virObjectUnlock(privconn);
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectListAllStoragePools(virConnectPtr conn,
|
|
virStoragePoolPtr **pools,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolIsActive(virStoragePoolPtr pool)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolCreate(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
virStoragePoolObjSetActive(obj, true);
|
|
|
|
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
|
|
VIR_STORAGE_POOL_EVENT_STARTED,
|
|
0);
|
|
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *
|
|
testConnectFindStoragePoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
const char *type,
|
|
const char *srcSpec,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStoragePoolPtr
|
|
testStoragePoolCreateXML(virConnectPtr conn,
|
|
const char *xml,
|
|
unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virStoragePoolObjPtr obj = NULL;
|
|
virStoragePoolDefPtr def;
|
|
virStoragePoolPtr pool = NULL;
|
|
virObjectEventPtr event = NULL;
|
|
VIR_AUTOPTR(virStoragePoolDef) newDef = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
virObjectLock(privconn);
|
|
if (!(newDef = virStoragePoolDefParseString(xml)))
|
|
goto cleanup;
|
|
|
|
if (!(obj = virStoragePoolObjAssignDef(privconn->pools, newDef, true)))
|
|
goto cleanup;
|
|
newDef = NULL;
|
|
def = virStoragePoolObjGetDef(obj);
|
|
|
|
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;
|
|
}
|
|
|
|
/* *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);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStoragePoolPtr
|
|
testStoragePoolDefineXML(virConnectPtr conn,
|
|
const char *xml,
|
|
unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virStoragePoolObjPtr obj = NULL;
|
|
virStoragePoolDefPtr def;
|
|
virStoragePoolPtr pool = NULL;
|
|
virObjectEventPtr event = NULL;
|
|
VIR_AUTOPTR(virStoragePoolDef) newDef = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
virObjectLock(privconn);
|
|
if (!(newDef = virStoragePoolDefParseString(xml)))
|
|
goto cleanup;
|
|
|
|
newDef->capacity = defaultPoolCap;
|
|
newDef->allocation = defaultPoolAlloc;
|
|
newDef->available = defaultPoolCap - defaultPoolAlloc;
|
|
|
|
if (!(obj = virStoragePoolObjAssignDef(privconn->pools, newDef, false)))
|
|
goto cleanup;
|
|
newDef = NULL;
|
|
def = virStoragePoolObjGetDef(obj);
|
|
|
|
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;
|
|
}
|
|
|
|
pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);
|
|
|
|
cleanup:
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
virObjectUnlock(privconn);
|
|
return pool;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolUndefine(virStoragePoolPtr pool)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
|
|
VIR_STORAGE_POOL_EVENT_UNDEFINED,
|
|
0);
|
|
|
|
virStoragePoolObjRemove(privconn->pools, obj);
|
|
virObjectUnref(obj);
|
|
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolBuild(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
|
|
VIR_STORAGE_POOL_EVENT_CREATED,
|
|
0);
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolDestroy(virStoragePoolPtr pool)
|
|
{
|
|
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);
|
|
|
|
if (!(virStoragePoolObjGetConfigFile(obj))) {
|
|
virStoragePoolObjRemove(privconn->pools, obj);
|
|
virObjectUnref(obj);
|
|
obj = NULL;
|
|
}
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolDelete(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(obj = testStoragePoolObjFindInactiveByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid,
|
|
VIR_STORAGE_POOL_EVENT_DELETED,
|
|
0);
|
|
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolRefresh(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
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);
|
|
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolGetInfo(virStoragePoolPtr pool,
|
|
virStoragePoolInfoPtr info)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virStoragePoolDefPtr def;
|
|
|
|
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
|
|
return -1;
|
|
def = virStoragePoolObjGetDef(obj);
|
|
|
|
memset(info, 0, sizeof(virStoragePoolInfo));
|
|
if (virStoragePoolObjIsActive(obj))
|
|
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;
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *
|
|
testStoragePoolGetXMLDesc(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolGetAutostart(virStoragePoolPtr pool,
|
|
int *autostart)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
|
|
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
if (!virStoragePoolObjGetConfigFile(obj))
|
|
*autostart = 0;
|
|
else
|
|
*autostart = virStoragePoolObjIsAutostart(obj) ? 1 : 0;
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolSetAutostart(virStoragePoolPtr pool,
|
|
int autostart)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
bool new_autostart = (autostart != 0);
|
|
int ret = -1;
|
|
|
|
if (!(obj = testStoragePoolObjFindByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
if (!virStoragePoolObjGetConfigFile(obj)) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
"%s", _("pool has no config file"));
|
|
goto cleanup;
|
|
}
|
|
|
|
virStoragePoolObjSetAutostart(obj, new_autostart);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolNumOfVolumes(virStoragePoolPtr pool)
|
|
{
|
|
testDriverPtr privconn = pool->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
int ret = -1;
|
|
|
|
if (!(obj = testStoragePoolObjFindActiveByName(privconn, pool->name)))
|
|
return -1;
|
|
|
|
ret = virStoragePoolObjNumOfVolumes(obj, pool->conn, NULL);
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolListVolumes(virStoragePoolPtr pool,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
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);
|
|
|
|
virStoragePoolObjEndAPI(&obj);
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStorageVolPtr
|
|
testStorageVolLookupByName(virStoragePoolPtr pool,
|
|
const char *name)
|
|
{
|
|
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;
|
|
|
|
ret = virGetStorageVol(pool->conn, def->name,
|
|
privvol->name, privvol->key,
|
|
NULL, NULL);
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStorageVolPtr
|
|
testStorageVolLookupByKey(virConnectPtr conn,
|
|
const char *key)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virStoragePoolDefPtr def;
|
|
struct storageVolLookupData data = {
|
|
.key = key, .voldef = NULL };
|
|
virStorageVolPtr vol = NULL;
|
|
|
|
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);
|
|
}
|
|
virObjectUnlock(privconn);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStorageVolPtr
|
|
testStorageVolLookupByPath(virConnectPtr conn,
|
|
const char *path)
|
|
{
|
|
testDriverPtr privconn = conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virStoragePoolDefPtr def;
|
|
struct storageVolLookupData data = {
|
|
.path = path, .voldef = NULL };
|
|
virStorageVolPtr vol = NULL;
|
|
|
|
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);
|
|
}
|
|
virObjectUnlock(privconn);
|
|
|
|
if (!vol)
|
|
virReportError(VIR_ERR_NO_STORAGE_VOL,
|
|
_("no storage vol with matching path '%s'"), path);
|
|
|
|
return vol;
|
|
}
|
|
|
|
|
|
static virStorageVolPtr
|
|
testStorageVolCreateXML(virStoragePoolPtr pool,
|
|
const char *xmldesc,
|
|
unsigned int flags)
|
|
{
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static virStorageVolPtr
|
|
testStorageVolCreateXMLFrom(virStoragePoolPtr pool,
|
|
const char *xmldesc,
|
|
virStorageVolPtr clonevol,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
testStorageVolDelete(virStorageVolPtr vol,
|
|
unsigned int flags)
|
|
{
|
|
testDriverPtr privconn = vol->conn->privateData;
|
|
virStoragePoolObjPtr obj;
|
|
virStoragePoolDefPtr def;
|
|
virStorageVolDefPtr privvol;
|
|
int ret = -1;
|
|
|
|
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);
|
|
|
|
virStoragePoolObjRemoveVol(obj, privvol);
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
testStorageVolGetInfo(virStorageVolPtr vol,
|
|
virStorageVolInfoPtr info)
|
|
{
|
|
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;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
if ((info->type = testStorageVolumeTypeForPool(def->type)) < 0)
|
|
goto cleanup;
|
|
info->capacity = privvol->target.capacity;
|
|
info->allocation = privvol->target.allocation;
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
testStorageVolGetXMLDesc(virStorageVolPtr vol,
|
|
unsigned int flags)
|
|
{
|
|
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;
|
|
|
|
ret = virStorageVolDefFormat(virStoragePoolObjGetDef(obj), privvol);
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
testStorageVolGetPath(virStorageVolPtr vol)
|
|
{
|
|
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);
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
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 */
|
|
static int
|
|
testConnectDomainEventRegister(virConnectPtr conn,
|
|
virConnectDomainEventCallback callback,
|
|
void *opaque,
|
|
virFreeCallback freecb)
|
|
{
|
|
testDriverPtr driver = conn->privateData;
|
|
int ret = 0;
|
|
|
|
if (virDomainEventStateRegister(conn, driver->eventState,
|
|
callback, opaque, freecb) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectDomainEventDeregister(virConnectPtr conn,
|
|
virConnectDomainEventCallback callback)
|
|
{
|
|
testDriverPtr driver = conn->privateData;
|
|
int ret = 0;
|
|
|
|
if (virDomainEventStateDeregister(conn, driver->eventState,
|
|
callback) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectDomainEventRegisterAny(virConnectPtr conn,
|
|
virDomainPtr dom,
|
|
int eventID,
|
|
virConnectDomainEventGenericCallback callback,
|
|
void *opaque,
|
|
virFreeCallback freecb)
|
|
{
|
|
testDriverPtr driver = conn->privateData;
|
|
int ret;
|
|
|
|
if (virDomainEventStateRegisterID(conn, driver->eventState,
|
|
dom, eventID,
|
|
callback, opaque, freecb, &ret) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testConnectDomainEventDeregisterAny(virConnectPtr conn,
|
|
int callbackID)
|
|
{
|
|
testDriverPtr driver = conn->privateData;
|
|
int ret = 0;
|
|
|
|
if (virObjectEventStateDeregisterID(conn, driver->eventState,
|
|
callbackID, true) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNetworkEventRegisterAny(virConnectPtr conn,
|
|
virNetworkPtr net,
|
|
int eventID,
|
|
virConnectNetworkEventGenericCallback callback,
|
|
void *opaque,
|
|
virFreeCallback freecb)
|
|
{
|
|
testDriverPtr driver = conn->privateData;
|
|
int ret;
|
|
|
|
if (virNetworkEventStateRegisterID(conn, driver->eventState,
|
|
net, eventID, callback,
|
|
opaque, freecb, &ret) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testConnectNetworkEventDeregisterAny(virConnectPtr conn,
|
|
int callbackID)
|
|
{
|
|
testDriverPtr driver = conn->privateData;
|
|
int ret = 0;
|
|
|
|
if (virObjectEventStateDeregisterID(conn, driver->eventState,
|
|
callbackID, true) < 0)
|
|
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,
|
|
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,
|
|
callbackID, true) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int testConnectListAllDomains(virConnectPtr conn,
|
|
virDomainPtr **domains,
|
|
unsigned int flags)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
testDriverPtr privconn = domain->conn->privateData;
|
|
virDomainObjPtr vm = NULL;
|
|
virDomainMomentObjPtr snap = NULL;
|
|
virDomainSnapshotPtr snapshot = NULL;
|
|
virObjectEventPtr event = NULL;
|
|
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
|
|
*
|
|
* REDEFINE + CURRENT: Implemented
|
|
* HALT: Implemented
|
|
* QUIESCE: Nothing to do
|
|
* ATOMIC: Nothing to do
|
|
* LIVE: Nothing to do
|
|
*/
|
|
virCheckFlags(
|
|
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);
|
|
|
|
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,
|
|
NULL,
|
|
parse_flags)))
|
|
goto cleanup;
|
|
|
|
if (redefine) {
|
|
if (virDomainSnapshotRedefinePrep(domain, vm, &def, &snap,
|
|
privconn->xmlopt,
|
|
&update_current, flags) < 0)
|
|
goto cleanup;
|
|
} else {
|
|
if (!(def->parent.dom = virDomainDefCopy(vm->def,
|
|
privconn->caps,
|
|
privconn->xmlopt,
|
|
NULL,
|
|
true)))
|
|
goto cleanup;
|
|
|
|
if (testDomainSnapshotAlignDisks(vm, def, flags) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!snap) {
|
|
if (!(snap = virDomainSnapshotAssignDef(vm->snapshots, def)))
|
|
goto cleanup;
|
|
def = NULL;
|
|
}
|
|
|
|
if (!redefine) {
|
|
if (VIR_STRDUP(snap->def->parent_name,
|
|
virDomainSnapshotGetCurrentName(vm->snapshots)) < 0)
|
|
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,
|
|
VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
|
|
}
|
|
}
|
|
|
|
snapshot = virGetDomainSnapshot(domain, snap->def->name);
|
|
cleanup:
|
|
if (vm) {
|
|
if (snapshot) {
|
|
virDomainMomentObjPtr other;
|
|
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);
|
|
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);
|
|
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)
|
|
{
|
|
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);
|
|
} 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 */
|
|
.domainGetLaunchSecurityInfo = testDomainGetLaunchSecurityInfo, /* 5.5.0 */
|
|
.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 */
|
|
.domainSaveImageDefineXML = testDomainSaveImageDefineXML, /* 5.5.0 */
|
|
.domainSaveImageGetXMLDesc = testDomainSaveImageGetXMLDesc, /* 5.5.0 */
|
|
.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);
|
|
}
|