mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-07 21:45:22 +00:00
5dae71ee8c
Signed-off-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
9736 lines
281 KiB
C
9736 lines
281 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 "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 "checkpoint_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 "virdomainobjlist.h"
|
|
#include "virinterfaceobj.h"
|
|
#include "virhostcpu.h"
|
|
#include "virdomaincheckpointobjlist.h"
|
|
#include "virdomainsnapshotobjlist.h"
|
|
#include "virkeycode.h"
|
|
#include "virutil.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;
|
|
|
|
struct _testAuth {
|
|
char *username;
|
|
char *password;
|
|
};
|
|
typedef struct _testAuth testAuth;
|
|
|
|
struct _testDriver {
|
|
virObjectLockable parent;
|
|
|
|
virNodeInfo nodeInfo;
|
|
virInterfaceObjList *ifaces;
|
|
bool transaction_running;
|
|
virInterfaceObjList *backupIfaces;
|
|
virStoragePoolObjList *pools;
|
|
virNodeDeviceObjList *devs;
|
|
int numCells;
|
|
testCell *cells;
|
|
size_t numAuths;
|
|
struct _testAuth *auths;
|
|
|
|
/* g_atomic access only */
|
|
volatile int nextDomID;
|
|
|
|
/* immutable pointer, immutable object after being initialized with
|
|
* testBuildCapabilities */
|
|
virCaps *caps;
|
|
|
|
/* immutable pointer, immutable object */
|
|
virDomainXMLOption *xmlopt;
|
|
|
|
/* immutable pointer, self-locking APIs */
|
|
virDomainObjList *domains;
|
|
virNetworkObjList *networks;
|
|
virObjectEventState *eventState;
|
|
};
|
|
typedef struct _testDriver testDriver;
|
|
|
|
static testDriver *defaultPrivconn;
|
|
static virMutex defaultLock = VIR_MUTEX_INITIALIZER;
|
|
|
|
static virClass *testDriverClass;
|
|
static __thread bool testDriverDisposed;
|
|
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)
|
|
{
|
|
testDriver *driver = obj;
|
|
size_t i;
|
|
|
|
virObjectUnref(driver->caps);
|
|
virObjectUnref(driver->xmlopt);
|
|
virObjectUnref(driver->domains);
|
|
virNodeDeviceObjListFree(driver->devs);
|
|
virObjectUnref(driver->networks);
|
|
virObjectUnref(driver->ifaces);
|
|
virObjectUnref(driver->pools);
|
|
virObjectUnref(driver->eventState);
|
|
for (i = 0; i < driver->numAuths; i++) {
|
|
g_free(driver->auths[i].username);
|
|
g_free(driver->auths[i].password);
|
|
}
|
|
g_free(driver->cells);
|
|
g_free(driver->auths);
|
|
|
|
testDriverDisposed = true;
|
|
}
|
|
|
|
typedef struct _testDomainNamespaceDef testDomainNamespaceDef;
|
|
struct _testDomainNamespaceDef {
|
|
int runstate;
|
|
bool transient;
|
|
bool hasManagedSave;
|
|
|
|
unsigned int num_snap_nodes;
|
|
xmlNodePtr *snap_nodes;
|
|
};
|
|
|
|
static void
|
|
testDomainDefNamespaceFree(void *data)
|
|
{
|
|
testDomainNamespaceDef *nsdata = data;
|
|
size_t i;
|
|
|
|
if (!nsdata)
|
|
return;
|
|
|
|
for (i = 0; i < nsdata->num_snap_nodes; i++)
|
|
xmlFreeNode(nsdata->snap_nodes[i]);
|
|
|
|
g_free(nsdata->snap_nodes);
|
|
g_free(nsdata);
|
|
}
|
|
|
|
static int
|
|
testDomainDefNamespaceParse(xmlXPathContextPtr ctxt,
|
|
void **data)
|
|
{
|
|
testDomainNamespaceDef *nsdata = NULL;
|
|
int tmp, n;
|
|
size_t i;
|
|
unsigned int tmpuint;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
nsdata = g_new0(testDomainNamespaceDef, 1);
|
|
|
|
n = virXPathNodeSet("./test:domainsnapshot", ctxt, &nodes);
|
|
if (n < 0)
|
|
goto error;
|
|
|
|
if (n)
|
|
nsdata->snap_nodes = g_new0(xmlNodePtr, n);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
xmlNodePtr newnode = xmlCopyNode(nodes[i], 1);
|
|
if (!newnode) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Failed to copy XML node"));
|
|
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 virCaps *
|
|
testBuildCapabilities(virConnectPtr conn)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virCaps *caps;
|
|
virCapsGuest *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);
|
|
|
|
caps->host.pagesSize = g_new0(unsigned int, 4);
|
|
|
|
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;
|
|
|
|
caps->host.numa = virCapabilitiesHostNUMANew();
|
|
for (i = 0; i < privconn->numCells; i++) {
|
|
virCapsHostNUMACellCPU *cpu_cells;
|
|
virCapsHostNUMACellPageInfo *pages;
|
|
size_t nPages = caps->host.nPagesSize - 1;
|
|
|
|
cpu_cells = g_new0(virCapsHostNUMACellCPU, privconn->cells[i].numCpus);
|
|
pages = g_new0(virCapsHostNUMACellPageInfo, nPages);
|
|
|
|
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;
|
|
|
|
virCapabilitiesHostNUMAAddCell(caps->host.numa,
|
|
i, privconn->cells[i].mem,
|
|
privconn->cells[i].numCpus, &cpu_cells,
|
|
0, NULL,
|
|
nPages, &pages,
|
|
NULL);
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(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;
|
|
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_PAE);
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_NONPAE);
|
|
}
|
|
|
|
caps->host.nsecModels = 1;
|
|
caps->host.secModels = g_new0(virCapsHostSecModel, caps->host.nsecModels);
|
|
caps->host.secModels[0].model = g_strdup("testSecurity");
|
|
|
|
caps->host.secModels[0].doi = g_strdup("");
|
|
|
|
return caps;
|
|
|
|
error:
|
|
virObjectUnref(caps);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
typedef struct _testDomainObjPrivate testDomainObjPrivate;
|
|
struct _testDomainObjPrivate {
|
|
testDriver *driver;
|
|
|
|
bool frozen[2]; /* used by file system related calls */
|
|
|
|
/* used by get/set time APIs */
|
|
long long seconds;
|
|
unsigned int nseconds;
|
|
};
|
|
|
|
|
|
static void *
|
|
testDomainObjPrivateAlloc(void *opaque)
|
|
{
|
|
testDomainObjPrivate *priv;
|
|
|
|
priv = g_new0(testDomainObjPrivate, 1);
|
|
|
|
priv->driver = opaque;
|
|
priv->frozen[0] = priv->frozen[1] = false;
|
|
|
|
priv->seconds = 627319920;
|
|
priv->nseconds = 0;
|
|
|
|
return priv;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainDevicesDefPostParse(virDomainDeviceDef *dev G_GNUC_UNUSED,
|
|
const virDomainDef *def G_GNUC_UNUSED,
|
|
unsigned int parseFlags G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED,
|
|
void *parseOpaque G_GNUC_UNUSED)
|
|
{
|
|
if (dev->type == VIR_DOMAIN_DEVICE_VIDEO &&
|
|
dev->data.video->type == VIR_DOMAIN_VIDEO_TYPE_DEFAULT) {
|
|
if (def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
|
|
def->os.type == VIR_DOMAIN_OSTYPE_LINUX)
|
|
dev->data.video->type = VIR_DOMAIN_VIDEO_TYPE_XEN;
|
|
else if (ARCH_IS_PPC64(def->os.arch))
|
|
dev->data.video->type = VIR_DOMAIN_VIDEO_TYPE_VGA;
|
|
else
|
|
dev->data.video->type = VIR_DOMAIN_VIDEO_TYPE_CIRRUS;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
testDomainObjPrivateFree(void *data)
|
|
{
|
|
testDomainObjPrivate *priv = data;
|
|
g_free(priv);
|
|
}
|
|
|
|
|
|
static testDriver *
|
|
testDriverNew(void)
|
|
{
|
|
virXMLNamespace ns = {
|
|
.parse = testDomainDefNamespaceParse,
|
|
.free = testDomainDefNamespaceFree,
|
|
.prefix = "test",
|
|
.uri = "http://libvirt.org/schemas/domain/test/1.0",
|
|
};
|
|
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,
|
|
.devicesPostParseCallback = testDomainDevicesDefPostParse,
|
|
.defArch = VIR_ARCH_I686,
|
|
};
|
|
virDomainXMLPrivateDataCallbacks privatecb = {
|
|
.alloc = testDomainObjPrivateAlloc,
|
|
.free = testDomainObjPrivateFree,
|
|
};
|
|
testDriver *ret;
|
|
|
|
if (testDriverInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(ret = virObjectLockableNew(testDriverClass)))
|
|
return NULL;
|
|
|
|
if (!(ret->xmlopt = virDomainXMLOptionNew(&config, &privatecb, &ns, NULL, NULL)) ||
|
|
!(ret->eventState = virObjectEventStateNew()) ||
|
|
!(ret->ifaces = virInterfaceObjListNew()) ||
|
|
!(ret->domains = virDomainObjListNew()) ||
|
|
!(ret->networks = virNetworkObjListNew()) ||
|
|
!(ret->devs = virNodeDeviceObjListNew()) ||
|
|
!(ret->pools = virStoragePoolObjListNew()))
|
|
goto error;
|
|
|
|
g_atomic_int_set(&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>"
|
|
" <disk type='file' device='disk'>"
|
|
" <source file='/guest/diskimage1'/>"
|
|
" <target dev='vda' bus='virtio'/>"
|
|
" <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>"
|
|
" </disk>"
|
|
" <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>"
|
|
" <memballoon model='virtio'>"
|
|
" <address type='pci' domain='0x0000' bus='0x00' slot='0x2' function='0x0'/>"
|
|
" </memballoon>"
|
|
" </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(virStoragePoolObj *obj);
|
|
static int testNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info);
|
|
static virNetworkObj *testNetworkObjFindByName(testDriver *privconn, const char *name);
|
|
|
|
static virDomainObj *
|
|
testDomObjFromDomain(virDomainPtr domain)
|
|
{
|
|
virDomainObj *vm;
|
|
testDriver *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(virDomainDef *domdef)
|
|
{
|
|
int maxif = 1024;
|
|
int ifctr;
|
|
|
|
for (ifctr = 0; ifctr < maxif; ++ifctr) {
|
|
virDomainNetDef *net = NULL;
|
|
char *ifname;
|
|
|
|
ifname = g_strdup_printf("testnet%d", ifctr);
|
|
|
|
/* 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(virDomainDef *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,
|
|
virDomainObj *privdom,
|
|
virDomainShutoffReason reason)
|
|
{
|
|
virDomainObjRemoveTransientDef(privdom);
|
|
virDomainObjSetState(privdom, VIR_DOMAIN_SHUTOFF, reason);
|
|
|
|
if (domain)
|
|
domain->id = -1;
|
|
}
|
|
|
|
/* Set up domain runtime state */
|
|
static int
|
|
testDomainStartState(testDriver *privconn,
|
|
virDomainObj *dom,
|
|
virDomainRunningReason reason)
|
|
{
|
|
int ret = -1;
|
|
|
|
virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, reason);
|
|
dom->def->id = g_atomic_int_add(&privconn->nextDomID, 1);
|
|
|
|
if (virDomainObjSetDefTransient(privconn->xmlopt,
|
|
dom, NULL) < 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)
|
|
{
|
|
g_autofree char *basename = NULL;
|
|
|
|
if (g_path_is_absolute(filename))
|
|
return g_strdup(filename);
|
|
|
|
basename = g_path_get_dirname(relativeTo);
|
|
|
|
return g_strdup_printf("%s/%s", basename, filename);
|
|
}
|
|
|
|
static void
|
|
testDomainObjCheckCPUTaint(virDomainObj *obj)
|
|
{
|
|
switch (obj->def->cpu->mode) {
|
|
case VIR_CPU_MODE_CUSTOM:
|
|
if (obj->def->cpu->model)
|
|
if (STREQ(obj->def->cpu->model, "Deprecated-Test")) {
|
|
virDomainObjTaint(obj, VIR_DOMAIN_TAINT_DEPRECATED_CONFIG);
|
|
virDomainObjDeprecation(obj, "CPU model Deprecated-Test");
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
testDomainObjCheckDiskTaint(virDomainObj *obj,
|
|
virDomainDiskDef *disk)
|
|
{
|
|
if (disk->rawio == VIR_TRISTATE_BOOL_YES)
|
|
virDomainObjTaint(obj, VIR_DOMAIN_TAINT_HIGH_PRIVILEGES);
|
|
|
|
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
|
|
virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_BLOCK &&
|
|
disk->src->path)
|
|
virDomainObjTaint(obj, VIR_DOMAIN_TAINT_CDROM_PASSTHROUGH);
|
|
}
|
|
|
|
static void
|
|
testDomainObjCheckHostdevTaint(virDomainObj *obj,
|
|
virDomainHostdevDef *hostdev)
|
|
{
|
|
if (!virHostdevIsSCSIDevice(hostdev))
|
|
return;
|
|
|
|
if (hostdev->source.subsys.u.scsi.rawio == VIR_TRISTATE_BOOL_YES)
|
|
virDomainObjTaint(obj, VIR_DOMAIN_TAINT_HIGH_PRIVILEGES);
|
|
}
|
|
|
|
static void
|
|
testDomainObjCheckNetTaint(virDomainObj *obj,
|
|
virDomainNetDef *net)
|
|
{
|
|
/* script is only useful for NET_TYPE_ETHERNET (qemu) and
|
|
* NET_TYPE_BRIDGE (xen), but could be (incorrectly) specified for
|
|
* any interface type. In any case, it's adding user sauce into
|
|
* the soup, so it should taint the domain.
|
|
*/
|
|
if (net->script != NULL)
|
|
virDomainObjTaint(obj, VIR_DOMAIN_TAINT_SHELL_SCRIPTS);
|
|
}
|
|
|
|
static void
|
|
testDomainObjCheckTaint(virDomainObj *obj)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < obj->def->ndisks; i++)
|
|
testDomainObjCheckDiskTaint(obj, obj->def->disks[i]);
|
|
|
|
for (i = 0; i < obj->def->nhostdevs; i++)
|
|
testDomainObjCheckHostdevTaint(obj, obj->def->hostdevs[i]);
|
|
|
|
for (i = 0; i < obj->def->nnets; i++)
|
|
testDomainObjCheckNetTaint(obj, obj->def->nets[i]);
|
|
|
|
if (obj->def->cpu)
|
|
testDomainObjCheckCPUTaint(obj);
|
|
|
|
if (obj->def->os.dtb)
|
|
virDomainObjTaint(obj, VIR_DOMAIN_TAINT_CUSTOM_DTB);
|
|
}
|
|
|
|
static xmlNodePtr
|
|
testParseXMLDocFromFile(xmlNodePtr node, const char *file, const char *type)
|
|
{
|
|
xmlNodePtr ret = NULL;
|
|
xmlDocPtr doc = NULL;
|
|
g_autofree char *absFile = NULL;
|
|
g_autofree char *relFile = NULL;
|
|
|
|
if ((relFile = virXMLPropString(node, "file"))) {
|
|
absFile = testBuildFilename(file, relFile);
|
|
|
|
if (!(doc = virXMLParse(absFile, NULL, type, NULL, false)))
|
|
goto error;
|
|
|
|
ret = xmlCopyNode(xmlDocGetRootElement(doc), 1);
|
|
if (!ret) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Failed to copy XML node"));
|
|
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;
|
|
g_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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testParseDomainSnapshots(testDriver *privconn,
|
|
virDomainObj *domobj,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
size_t i;
|
|
testDomainNamespaceDef *nsdata = domobj->def->namespaceData;
|
|
xmlNodePtr *nodes = nsdata->snap_nodes;
|
|
bool cur;
|
|
|
|
for (i = 0; i < nsdata->num_snap_nodes; i++) {
|
|
virDomainMomentObj *snap;
|
|
virDomainSnapshotDef *def;
|
|
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
|
|
"domainsnapshot");
|
|
if (!node)
|
|
return -1;
|
|
|
|
def = virDomainSnapshotDefParseNode(ctxt->doc, node,
|
|
privconn->xmlopt,
|
|
NULL,
|
|
&cur,
|
|
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
|
|
VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL |
|
|
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE);
|
|
if (!def)
|
|
return -1;
|
|
|
|
if (!(snap = virDomainSnapshotAssignDef(domobj->snapshots, def))) {
|
|
virObjectUnref(def);
|
|
return -1;
|
|
}
|
|
|
|
if (cur) {
|
|
if (virDomainSnapshotGetCurrent(domobj->snapshots)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("more than one snapshot claims to be active"));
|
|
return -1;
|
|
}
|
|
|
|
virDomainSnapshotSetCurrent(domobj->snapshots, snap);
|
|
}
|
|
}
|
|
|
|
if (virDomainSnapshotUpdateRelations(domobj->snapshots) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Snapshots have inconsistent relations for "
|
|
"domain %s"), domobj->def->name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testParseDomains(testDriver *privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num, ret = -1;
|
|
size_t i;
|
|
virDomainObj *obj = NULL;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
num = virXPathNodeSet("/node/domain", ctxt, &nodes);
|
|
if (num < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
virDomainDef *def;
|
|
testDomainNamespaceDef *nsdata;
|
|
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "domain");
|
|
if (!node)
|
|
goto error;
|
|
|
|
def = virDomainDefParseNode(ctxt->doc, node,
|
|
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);
|
|
|
|
testDomainObjCheckTaint(obj);
|
|
|
|
virDomainObjEndAPI(&obj);
|
|
}
|
|
|
|
ret = 0;
|
|
error:
|
|
virDomainObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testParseNetworks(testDriver *privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num;
|
|
size_t i;
|
|
virNetworkObj *obj;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
num = virXPathNodeSet("/node/network", ctxt, &nodes);
|
|
if (num < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
virNetworkDef *def;
|
|
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file, "network");
|
|
if (!node)
|
|
return -1;
|
|
|
|
def = virNetworkDefParseNode(ctxt->doc, node, NULL);
|
|
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(testDriver *privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num;
|
|
size_t i;
|
|
virInterfaceObj *obj;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
num = virXPathNodeSet("/node/interface", ctxt, &nodes);
|
|
if (num < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
virInterfaceDef *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,
|
|
virStoragePoolObj *obj,
|
|
int objidx)
|
|
{
|
|
virStoragePoolDef *def = virStoragePoolObjGetDef(obj);
|
|
size_t i;
|
|
int num;
|
|
g_autofree char *vol_xpath = NULL;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
g_autoptr(virStorageVolDef) volDef = NULL;
|
|
|
|
/* Find storage volumes */
|
|
vol_xpath = g_strdup_printf("/node/pool[%d]/volume", objidx);
|
|
|
|
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) {
|
|
volDef->target.path = g_strdup_printf("%s/%s", def->target.path,
|
|
volDef->name);
|
|
}
|
|
|
|
if (!volDef->key)
|
|
volDef->key = g_strdup(volDef->target.path);
|
|
|
|
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(testDriver *privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num;
|
|
size_t i;
|
|
virStoragePoolObj *obj;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
num = virXPathNodeSet("/node/pool", ctxt, &nodes);
|
|
if (num < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
virStoragePoolDef *def;
|
|
xmlNodePtr node = testParseXMLDocFromFile(nodes[i], file,
|
|
"pool");
|
|
if (!node)
|
|
return -1;
|
|
|
|
def = virStoragePoolDefParseNode(ctxt->doc, node);
|
|
if (!def)
|
|
return -1;
|
|
|
|
if (!(obj = virStoragePoolObjListAdd(privconn->pools, def, 0))) {
|
|
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(testDriver *privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num;
|
|
size_t i;
|
|
virNodeDeviceObj *obj;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
num = virXPathNodeSet("/node/device", ctxt, &nodes);
|
|
if (num < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
virNodeDeviceDef *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(testDriver *privconn,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int num;
|
|
size_t i;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
|
|
num = virXPathNodeSet("/node/auth/user", ctxt, &nodes);
|
|
if (num < 0)
|
|
return -1;
|
|
|
|
privconn->numAuths = num;
|
|
if (num)
|
|
privconn->auths = g_new0(testAuth, num);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
g_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");
|
|
privconn->auths[i].username = g_steal_pointer(&username);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testOpenParse(testDriver *privconn,
|
|
const char *file,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
if (!virXMLNodeNameEqual(ctxt->node, "node")) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("Root element is not 'node'"));
|
|
return -1;
|
|
}
|
|
|
|
if (testParseNodeInfo(&privconn->nodeInfo, ctxt) < 0)
|
|
return -1;
|
|
if (testParseDomains(privconn, file, ctxt) < 0)
|
|
return -1;
|
|
if (testParseNetworks(privconn, file, ctxt) < 0)
|
|
return -1;
|
|
if (testParseInterfaces(privconn, file, ctxt) < 0)
|
|
return -1;
|
|
if (testParseStorage(privconn, file, ctxt) < 0)
|
|
return -1;
|
|
if (testParseNodedevs(privconn, file, ctxt) < 0)
|
|
return -1;
|
|
if (testParseAuthUsers(privconn, ctxt) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* No shared state between simultaneous test connections initialized
|
|
* from a file. */
|
|
static int
|
|
testOpenFromFile(virConnectPtr conn, const char *file)
|
|
{
|
|
xmlDocPtr doc = NULL;
|
|
g_autoptr(xmlXPathContext) ctxt = NULL;
|
|
testDriver *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;
|
|
|
|
xmlFreeDoc(doc);
|
|
virObjectUnlock(privconn);
|
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
|
|
error:
|
|
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;
|
|
testDriver *privconn = NULL;
|
|
xmlDocPtr doc = NULL;
|
|
g_autoptr(xmlXPathContext) 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;
|
|
privconn->cells = g_new0(testCell, privconn->numCells);
|
|
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++) {
|
|
virBitmap *siblings = virBitmapNew(16);
|
|
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);
|
|
xmlFreeDoc(doc);
|
|
return ret;
|
|
|
|
error:
|
|
virObjectUnref(privconn);
|
|
conn->privateData = NULL;
|
|
goto cleanup;
|
|
}
|
|
|
|
static int
|
|
testConnectAuthenticate(virConnectPtr conn,
|
|
virConnectAuthPtr auth)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
int ret = -1;
|
|
ssize_t i;
|
|
g_autofree char *username = NULL;
|
|
g_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(testDriver *driver)
|
|
{
|
|
virMutexLock(&defaultLock);
|
|
testDriverDisposed = false;
|
|
virObjectUnref(driver);
|
|
if (testDriverDisposed && driver == defaultPrivconn)
|
|
defaultPrivconn = NULL;
|
|
virMutexUnlock(&defaultLock);
|
|
}
|
|
|
|
|
|
static virDrvOpenStatus
|
|
testConnectOpen(virConnectPtr conn,
|
|
virConnectAuthPtr auth,
|
|
virConf *conf G_GNUC_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 G_GNUC_UNUSED,
|
|
unsigned long *hvVer)
|
|
{
|
|
*hvVer = 2;
|
|
return 0;
|
|
}
|
|
|
|
static char *testConnectGetHostname(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return virGetHostname();
|
|
}
|
|
|
|
|
|
static int testConnectIsSecure(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int testConnectIsEncrypted(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int testConnectIsAlive(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int testConnectGetMaxVcpus(virConnectPtr conn G_GNUC_UNUSED,
|
|
const char *type G_GNUC_UNUSED)
|
|
{
|
|
return 32;
|
|
}
|
|
|
|
static char *
|
|
testConnectBaselineCPU(virConnectPtr conn G_GNUC_UNUSED,
|
|
const char **xmlCPUs,
|
|
unsigned int ncpus,
|
|
unsigned int flags)
|
|
{
|
|
virCPUDef **cpus = NULL;
|
|
virCPUDef *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virObjectLock(privconn);
|
|
memcpy(info, &privconn->nodeInfo, sizeof(virNodeInfo));
|
|
virObjectUnlock(privconn);
|
|
return 0;
|
|
}
|
|
|
|
static char *testConnectGetCapabilities(virConnectPtr conn)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
char *xml;
|
|
virObjectLock(privconn);
|
|
xml = virCapabilitiesFormatXML(privconn->caps);
|
|
virObjectUnlock(privconn);
|
|
return xml;
|
|
}
|
|
|
|
static char *
|
|
testConnectGetSysinfo(virConnectPtr conn G_GNUC_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);
|
|
|
|
ret = g_strdup(sysinfo);
|
|
return ret;
|
|
}
|
|
|
|
static const char *
|
|
testConnectGetType(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return "TEST";
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectSupportsFeature(virConnectPtr conn G_GNUC_UNUSED,
|
|
int feature)
|
|
{
|
|
switch ((virDrvFeature) feature) {
|
|
case VIR_DRV_FEATURE_TYPED_PARAM_STRING:
|
|
case VIR_DRV_FEATURE_NETWORK_UPDATE_HAS_CORRECT_ORDER:
|
|
return 1;
|
|
case VIR_DRV_FEATURE_MIGRATION_V2:
|
|
case VIR_DRV_FEATURE_MIGRATION_V3:
|
|
case VIR_DRV_FEATURE_MIGRATION_P2P:
|
|
case VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION:
|
|
case VIR_DRV_FEATURE_FD_PASSING:
|
|
case VIR_DRV_FEATURE_XML_MIGRATABLE:
|
|
case VIR_DRV_FEATURE_MIGRATION_OFFLINE:
|
|
case VIR_DRV_FEATURE_MIGRATION_PARAMS:
|
|
case VIR_DRV_FEATURE_MIGRATION_DIRECT:
|
|
case VIR_DRV_FEATURE_MIGRATION_V1:
|
|
case VIR_DRV_FEATURE_PROGRAM_KEEPALIVE:
|
|
case VIR_DRV_FEATURE_REMOTE:
|
|
case VIR_DRV_FEATURE_REMOTE_CLOSE_CALLBACK:
|
|
case VIR_DRV_FEATURE_REMOTE_EVENT_CALLBACK:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int testConnectNumOfDomains(virConnectPtr conn)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
int count;
|
|
|
|
virObjectLock(privconn);
|
|
count = virDomainObjListNumOfDomains(privconn->domains, true, NULL, NULL);
|
|
virObjectUnlock(privconn);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int testDomainIsActive(virDomainPtr dom)
|
|
{
|
|
virDomainObj *obj;
|
|
int ret;
|
|
|
|
if (!(obj = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
ret = virDomainObjIsActive(obj);
|
|
virDomainObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
static int testDomainIsPersistent(virDomainPtr dom)
|
|
{
|
|
virDomainObj *obj;
|
|
int ret;
|
|
|
|
if (!(obj = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
ret = obj->persistent;
|
|
|
|
virDomainObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
static int testDomainIsUpdated(virDomainPtr dom G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static virDomainPtr
|
|
testDomainCreateXML(virConnectPtr conn, const char *xml,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virDomainPtr ret = NULL;
|
|
virDomainDef *def;
|
|
virDomainObj *dom = NULL;
|
|
virObjectEvent *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->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
|
|
testDomainCreateXMLWithFiles(virConnectPtr conn,
|
|
const char *xml,
|
|
unsigned int nfiles G_GNUC_UNUSED,
|
|
int *files G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
return testDomainCreateXML(conn, xml, flags);
|
|
}
|
|
|
|
|
|
static virDomainPtr testDomainLookupByID(virConnectPtr conn,
|
|
int id)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virDomainPtr ret = NULL;
|
|
virDomainObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virDomainPtr ret = NULL;
|
|
virDomainObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virDomainPtr ret = NULL;
|
|
virDomainObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
|
|
return virDomainObjListGetActiveIDs(privconn->domains, ids, maxids,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static int testDomainDestroyFlags(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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 void
|
|
testDomainActionSetState(virDomainObj *dom,
|
|
int lifecycle_type)
|
|
{
|
|
switch (lifecycle_type) {
|
|
case VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY:
|
|
virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
|
|
VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
break;
|
|
|
|
case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART:
|
|
virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
|
|
VIR_DOMAIN_RUNNING_BOOTED);
|
|
break;
|
|
|
|
case VIR_DOMAIN_LIFECYCLE_ACTION_PRESERVE:
|
|
virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
|
|
VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
break;
|
|
|
|
case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART_RENAME:
|
|
virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
|
|
VIR_DOMAIN_RUNNING_BOOTED);
|
|
break;
|
|
|
|
default:
|
|
virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
|
|
VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static int testDomainShutdownFlags(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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;
|
|
}
|
|
|
|
testDomainActionSetState(privdom, privdom->def->onPoweroff);
|
|
|
|
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 int testDomainShutdown(virDomainPtr domain)
|
|
{
|
|
return testDomainShutdownFlags(domain, 0);
|
|
}
|
|
|
|
/* Similar behaviour as shutdown */
|
|
static int testDomainReboot(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *event = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_REBOOT_DEFAULT |
|
|
VIR_DOMAIN_REBOOT_ACPI_POWER_BTN |
|
|
VIR_DOMAIN_REBOOT_GUEST_AGENT |
|
|
VIR_DOMAIN_REBOOT_INITCTL |
|
|
VIR_DOMAIN_REBOOT_SIGNAL |
|
|
VIR_DOMAIN_REBOOT_PARAVIRT, -1);
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(privdom) < 0)
|
|
goto cleanup;
|
|
|
|
testDomainActionSetState(privdom, privdom->def->onReboot);
|
|
|
|
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 int
|
|
testDomainReset(virDomainPtr dom,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
testDomainGetHostname(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
char *ret = NULL;
|
|
virDomainObj *vm = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(vm = testDomObjFromDomain(domain)))
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
ret = g_strdup_printf("%shost", domain->name);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int testDomainGetInfo(virDomainPtr domain,
|
|
virDomainInfoPtr info)
|
|
{
|
|
virDomainObj *privdom;
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
info->state = virDomainObjGetState(privdom, NULL);
|
|
info->memory = privdom->def->mem.cur_balloon;
|
|
info->maxMem = virDomainDefGetMemoryTotal(privdom->def);
|
|
info->nrVirtCpu = virDomainDefGetVcpus(privdom->def);
|
|
info->cpuTime = g_get_real_time() * 1000;
|
|
|
|
virDomainObjEndAPI(&privdom);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testDomainGetState(virDomainPtr domain,
|
|
int *state,
|
|
int *reason,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *privdom;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
*state = virDomainObjGetState(privdom, reason);
|
|
|
|
virDomainObjEndAPI(&privdom);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testDomainGetControlInfo(virDomainPtr dom,
|
|
virDomainControlInfoPtr info,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
testDomainObjPrivate *priv;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
if (priv->seconds > 0 && priv->seconds < 10000) {
|
|
info->state = VIR_DOMAIN_CONTROL_JOB;
|
|
info->stateTime = priv->seconds;
|
|
} else if (priv->seconds < 30000 && priv->seconds >= 10000) {
|
|
info->state = VIR_DOMAIN_CONTROL_OCCUPIED;
|
|
info->stateTime = priv->seconds - 10000;
|
|
} else if (priv->seconds < 60000 && priv->seconds >= 30000) {
|
|
info->state = VIR_DOMAIN_CONTROL_ERROR;
|
|
switch (priv->seconds % 4) {
|
|
case 0:
|
|
info->details = VIR_DOMAIN_CONTROL_ERROR_REASON_NONE;
|
|
break;
|
|
|
|
case 1:
|
|
info->details = VIR_DOMAIN_CONTROL_ERROR_REASON_UNKNOWN;
|
|
break;
|
|
|
|
case 2:
|
|
info->details = VIR_DOMAIN_CONTROL_ERROR_REASON_MONITOR;
|
|
break;
|
|
|
|
case 3:
|
|
info->details = VIR_DOMAIN_CONTROL_ERROR_REASON_INTERNAL;
|
|
break;
|
|
|
|
default:
|
|
info->details = VIR_DOMAIN_CONTROL_ERROR_REASON_NONE;
|
|
break;
|
|
}
|
|
info->stateTime = priv->seconds - 30000;
|
|
} else {
|
|
info->state = VIR_DOMAIN_CONTROL_OK;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testDomainGetTime(virDomainPtr dom,
|
|
long long *seconds,
|
|
unsigned int *nseconds,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
testDomainObjPrivate *priv;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("domain is not running"));
|
|
goto cleanup;
|
|
}
|
|
|
|
priv = vm->privateData;
|
|
*seconds = priv->seconds;
|
|
*nseconds = priv->nseconds;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSetTime(virDomainPtr dom,
|
|
long long seconds,
|
|
unsigned int nseconds,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
testDomainObjPrivate *priv;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_TIME_SYNC, ret);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
priv->seconds = seconds;
|
|
priv->nseconds = nseconds;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
#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(testDriver *driver,
|
|
const char *path,
|
|
virDomainDef *def)
|
|
{
|
|
int len;
|
|
int fd = -1;
|
|
g_autofree char *xml = NULL;
|
|
|
|
xml = virDomainDefFormat(def, driver->xmlopt,
|
|
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(testDriver *driver,
|
|
const char *path,
|
|
virDomainDef **ret_def)
|
|
{
|
|
char magic[15];
|
|
int fd = -1;
|
|
int len;
|
|
virDomainDef *def = NULL;
|
|
g_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;
|
|
}
|
|
|
|
xml = g_new0(char, len + 1);
|
|
|
|
if (saferead(fd, xml, len) != len) {
|
|
virReportSystemError(errno, _("incomplete metadata in '%s'"), path);
|
|
goto error;
|
|
}
|
|
xml[len] = '\0';
|
|
|
|
if (!(def = virDomainDefParseString(xml, driver->xmlopt, NULL,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE |
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
|
|
goto error;
|
|
|
|
*ret_def = g_steal_pointer(&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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
int fd = -1;
|
|
virDomainDef *def = NULL;
|
|
virDomainObj *dom = NULL;
|
|
virObjectEvent *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;
|
|
virDomainDef *def = NULL;
|
|
virDomainDef *newdef = NULL;
|
|
testDriver *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->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;
|
|
virDomainDef *def = NULL;
|
|
testDriver *privconn = conn->privateData;
|
|
|
|
virCheckFlags(VIR_DOMAIN_SAVE_IMAGE_XML_SECURE, NULL);
|
|
|
|
if ((fd = testDomainSaveImageOpen(privconn, path, &def)) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virDomainDefFormat(def, privconn->xmlopt,
|
|
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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
int fd = -1;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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 G_GNUC_UNUSED)
|
|
{
|
|
char *ret;
|
|
|
|
ret = g_strdup("linux");
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetLaunchSecurityInfo(virDomainPtr domain G_GNUC_UNUSED,
|
|
virTypedParameterPtr *params G_GNUC_UNUSED,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(0, -1);
|
|
|
|
*nparams = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static unsigned long long
|
|
testDomainGetMaxMemory(virDomainPtr domain)
|
|
{
|
|
virDomainObj *privdom;
|
|
unsigned long long ret = 0;
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
return 0;
|
|
|
|
ret = virDomainDefGetMemoryTotal(privdom->def);
|
|
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
static int testDomainSetMemoryStatsPeriod(virDomainPtr dom,
|
|
int period,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainDef *def;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (!virDomainDefHasMemballoon(def)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("No memory balloon device configured, "
|
|
"can not set the collection period"));
|
|
goto cleanup;
|
|
}
|
|
|
|
def->memballoon->period = period;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int testDomainSetMemoryFlags(virDomainPtr domain,
|
|
unsigned long memory,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainDef *def;
|
|
int ret = -1;
|
|
bool live = false;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_DOMAIN_MEM_MAXIMUM, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDefState(vm, flags, &live)))
|
|
goto cleanup;
|
|
|
|
if (flags & VIR_DOMAIN_MEM_MAXIMUM) {
|
|
if (live) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("cannot resize the maximum memory on an "
|
|
"active domain"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainNumaGetNodeCount(def->numa) > 0) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("initial memory size of a domain with NUMA "
|
|
"nodes cannot be modified with this API"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (def->mem.max_memory && def->mem.max_memory < memory) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("cannot set initial memory size greater than "
|
|
"the maximum memory size"));
|
|
goto cleanup;
|
|
}
|
|
|
|
virDomainDefSetMemoryTotal(def, memory);
|
|
|
|
if (def->mem.cur_balloon > memory)
|
|
def->mem.cur_balloon = memory;
|
|
} else {
|
|
if (memory > virDomainDefGetMemoryTotal(def)) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("cannot set memory higher than max memory"));
|
|
goto cleanup;
|
|
}
|
|
|
|
def->mem.cur_balloon = memory;
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int testDomainSetMemory(virDomainPtr domain,
|
|
unsigned long memory)
|
|
{
|
|
return testDomainSetMemoryFlags(domain, memory, VIR_DOMAIN_AFFECT_LIVE);
|
|
}
|
|
|
|
|
|
static int testDomainSetMaxMemory(virDomainPtr domain,
|
|
unsigned long memory)
|
|
{
|
|
return testDomainSetMemoryFlags(domain, memory, VIR_DOMAIN_MEM_MAXIMUM);
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainPinEmulator(virDomainPtr dom,
|
|
unsigned char *cpumap,
|
|
int maplen,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virBitmap *pcpumap = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (!(pcpumap = virBitmapNewData(cpumap, maplen)))
|
|
goto cleanup;
|
|
|
|
if (virBitmapIsAllClear(pcpumap)) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Empty cpu list for pinning"));
|
|
goto cleanup;
|
|
}
|
|
|
|
virBitmapFree(def->cputune.emulatorpin);
|
|
def->cputune.emulatorpin = virBitmapNewCopy(pcpumap);
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virBitmapFree(pcpumap);
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetEmulatorPinInfo(virDomainPtr dom,
|
|
unsigned char *cpumaps,
|
|
int maplen,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *driver = dom->conn->privateData;
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virBitmap *cpumask = NULL;
|
|
virBitmap *bitmap = NULL;
|
|
int hostcpus;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
hostcpus = VIR_NODEINFO_MAXCPUS(driver->nodeInfo);
|
|
|
|
if (def->cputune.emulatorpin) {
|
|
cpumask = def->cputune.emulatorpin;
|
|
} else if (def->cpumask) {
|
|
cpumask = def->cpumask;
|
|
} else {
|
|
bitmap = virBitmapNew(hostcpus);
|
|
virBitmapSetAll(bitmap);
|
|
cpumask = bitmap;
|
|
}
|
|
|
|
virBitmapToDataBuf(cpumask, cpumaps, maplen);
|
|
|
|
ret = 1;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
virBitmapFree(bitmap);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainDef *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)
|
|
{
|
|
testDriver *driver = domain->conn->privateData;
|
|
virDomainObj *privdom = NULL;
|
|
virDomainDef *def;
|
|
virDomainDef *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
|
|
testDomainSetUserPassword(virDomainPtr dom,
|
|
const char *user G_GNUC_UNUSED,
|
|
const char *password G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
virDomainObj *vm;
|
|
|
|
virCheckFlags(VIR_DOMAIN_PASSWORD_ENCRYPTED, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virDomainDef *def;
|
|
size_t i;
|
|
int hostcpus;
|
|
int ret = -1;
|
|
unsigned long long statbase;
|
|
virBitmap *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;
|
|
|
|
statbase = g_get_real_time();
|
|
|
|
hostcpus = VIR_NODEINFO_MAXCPUS(privconn->nodeInfo);
|
|
allcpumap = virBitmapNew(hostcpus);
|
|
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++) {
|
|
virDomainVcpuDef *vcpu = virDomainDefGetVcpu(def, i);
|
|
virBitmap *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 testDomainPinVcpuFlags(virDomainPtr domain,
|
|
unsigned int vcpu,
|
|
unsigned char *cpumap,
|
|
int maplen,
|
|
unsigned int flags)
|
|
{
|
|
virDomainVcpuDef *vcpuinfo;
|
|
virDomainObj *privdom;
|
|
virDomainDef *def;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -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 testDomainPinVcpu(virDomainPtr domain,
|
|
unsigned int vcpu,
|
|
unsigned char *cpumap,
|
|
int maplen)
|
|
{
|
|
return testDomainPinVcpuFlags(domain, vcpu, cpumap, maplen, 0);
|
|
}
|
|
|
|
static int
|
|
testDomainGetVcpuPinInfo(virDomainPtr dom,
|
|
int ncpumaps,
|
|
unsigned char *cpumaps,
|
|
int maplen,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *driver = dom->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virDomainDef *def;
|
|
g_autoptr(virBitmap) hostcpus = NULL;
|
|
int ret = -1;
|
|
|
|
if (!(privdom = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(privdom, flags)))
|
|
goto cleanup;
|
|
|
|
hostcpus = virBitmapNew(VIR_NODEINFO_MAXCPUS(driver->nodeInfo));
|
|
virBitmapSetAll(hostcpus);
|
|
|
|
ret = virDomainDefGetVcpuPinInfoHelper(def, maplen, ncpumaps, cpumaps,
|
|
hostcpus, NULL);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testDomainRenameCallback(virDomainObj *privdom,
|
|
const char *new_name,
|
|
unsigned int flags,
|
|
void *opaque)
|
|
{
|
|
testDriver *driver = opaque;
|
|
virObjectEvent *event_new = NULL;
|
|
virObjectEvent *event_old = NULL;
|
|
int ret = -1;
|
|
g_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;
|
|
}
|
|
|
|
new_dom_name = g_strdup(new_name);
|
|
|
|
event_old = virDomainEventLifecycleNewFromObj(privdom,
|
|
VIR_DOMAIN_EVENT_UNDEFINED,
|
|
VIR_DOMAIN_EVENT_UNDEFINED_RENAMED);
|
|
|
|
/* Switch name in domain definition. */
|
|
VIR_FREE(privdom->def->name);
|
|
privdom->def->name = g_steal_pointer(&new_dom_name);
|
|
|
|
event_new = virDomainEventLifecycleNewFromObj(privdom,
|
|
VIR_DOMAIN_EVENT_DEFINED,
|
|
VIR_DOMAIN_EVENT_DEFINED_RENAMED);
|
|
ret = 0;
|
|
|
|
virObjectEventStateQueue(driver->eventState, event_old);
|
|
virObjectEventStateQueue(driver->eventState, event_new);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testDomainRename(virDomainPtr dom,
|
|
const char *new_name,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *driver = dom->conn->privateData;
|
|
virDomainObj *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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainDef *def;
|
|
virDomainObj *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->xmlopt,
|
|
virDomainDefFormatConvertXMLFlags(flags));
|
|
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#define TEST_SET_PARAM(index, name, type, value) \
|
|
if (index < *nparams && \
|
|
virTypedParameterAssign(¶ms[index], name, type, value) < 0) \
|
|
goto cleanup
|
|
|
|
|
|
static int
|
|
testDomainSetMemoryParameters(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
unsigned long long swap_hard_limit = 0;
|
|
unsigned long long hard_limit = 0;
|
|
unsigned long long soft_limit = 0;
|
|
bool set_swap_hard_limit = false;
|
|
bool set_hard_limit = false;
|
|
bool set_soft_limit = false;
|
|
int rc;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (virTypedParamsValidate(params, nparams,
|
|
VIR_DOMAIN_MEMORY_HARD_LIMIT,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_MEMORY_SOFT_LIMIT,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
#define VIR_GET_LIMIT_PARAMETER(PARAM, VALUE) \
|
|
if ((rc = virTypedParamsGetULLong(params, nparams, PARAM, &VALUE)) < 0) \
|
|
goto cleanup; \
|
|
\
|
|
if (rc == 1) \
|
|
set_ ## VALUE = true;
|
|
|
|
VIR_GET_LIMIT_PARAMETER(VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, swap_hard_limit)
|
|
VIR_GET_LIMIT_PARAMETER(VIR_DOMAIN_MEMORY_HARD_LIMIT, hard_limit)
|
|
VIR_GET_LIMIT_PARAMETER(VIR_DOMAIN_MEMORY_SOFT_LIMIT, soft_limit)
|
|
|
|
#undef VIR_GET_LIMIT_PARAMETER
|
|
|
|
if (set_swap_hard_limit || set_hard_limit) {
|
|
unsigned long long mem_limit = vm->def->mem.hard_limit;
|
|
unsigned long long swap_limit = vm->def->mem.swap_hard_limit;
|
|
|
|
if (set_swap_hard_limit)
|
|
swap_limit = swap_hard_limit;
|
|
|
|
if (set_hard_limit)
|
|
mem_limit = hard_limit;
|
|
|
|
if (mem_limit > swap_limit) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("memory hard_limit tunable value must be lower "
|
|
"than or equal to swap_hard_limit"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (set_soft_limit)
|
|
def->mem.soft_limit = soft_limit;
|
|
|
|
if (set_hard_limit)
|
|
def->mem.hard_limit = hard_limit;
|
|
|
|
if (set_swap_hard_limit)
|
|
def->mem.swap_hard_limit = swap_hard_limit;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetMemoryParameters(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
if ((*nparams) == 0) {
|
|
*nparams = 3;
|
|
return 0;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
TEST_SET_PARAM(0, VIR_DOMAIN_MEMORY_HARD_LIMIT, VIR_TYPED_PARAM_ULLONG, def->mem.hard_limit);
|
|
TEST_SET_PARAM(1, VIR_DOMAIN_MEMORY_SOFT_LIMIT, VIR_TYPED_PARAM_ULLONG, def->mem.soft_limit);
|
|
TEST_SET_PARAM(2, VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, VIR_TYPED_PARAM_ULLONG, def->mem.swap_hard_limit);
|
|
|
|
if (*nparams > 3)
|
|
*nparams = 3;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSetNumaParameters(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virBitmap *nodeset = NULL;
|
|
virDomainNumatuneMemMode config_mode;
|
|
bool live;
|
|
size_t i;
|
|
int mode = -1;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (virTypedParamsValidate(params, nparams,
|
|
VIR_DOMAIN_NUMA_MODE,
|
|
VIR_TYPED_PARAM_INT,
|
|
VIR_DOMAIN_NUMA_NODESET,
|
|
VIR_TYPED_PARAM_STRING,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDefState(vm, flags, &live)))
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
virTypedParameterPtr param = ¶ms[i];
|
|
|
|
if (STREQ(param->field, VIR_DOMAIN_NUMA_MODE)) {
|
|
mode = param->value.i;
|
|
|
|
if (mode < 0 || mode >= VIR_DOMAIN_NUMATUNE_MEM_LAST) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unsupported numatune mode: '%d'"), mode);
|
|
goto cleanup;
|
|
}
|
|
|
|
} else if (STREQ(param->field, VIR_DOMAIN_NUMA_NODESET)) {
|
|
if (virBitmapParse(param->value.s, &nodeset,
|
|
VIR_DOMAIN_CPUMASK_LEN) < 0)
|
|
goto cleanup;
|
|
|
|
if (virBitmapIsAllClear(nodeset)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("Invalid nodeset of 'numatune': %s"),
|
|
param->value.s);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (live &&
|
|
mode != -1 &&
|
|
virDomainNumatuneGetMode(def->numa, -1, &config_mode) == 0 &&
|
|
config_mode != mode) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("can't change numatune mode for running domain"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainNumatuneSet(def->numa,
|
|
def->placement_mode ==
|
|
VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC,
|
|
-1, mode, nodeset) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virBitmapFree(nodeset);
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetNumaParameters(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virDomainNumatuneMemMode mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
|
|
g_autofree char *nodeset = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
if ((*nparams) == 0) {
|
|
*nparams = 2;
|
|
return 0;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
ignore_value(virDomainNumatuneGetMode(def->numa, -1, &mode));
|
|
nodeset = virDomainNumatuneFormatNodeset(def->numa, NULL, -1);
|
|
|
|
TEST_SET_PARAM(0, VIR_DOMAIN_NUMA_MODE, VIR_TYPED_PARAM_INT, mode);
|
|
TEST_SET_PARAM(1, VIR_DOMAIN_NUMA_NODESET, VIR_TYPED_PARAM_STRING, nodeset);
|
|
|
|
nodeset = NULL;
|
|
|
|
if (*nparams > 2)
|
|
*nparams = 2;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSetInterfaceParameters(virDomainPtr dom,
|
|
const char *device,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def;
|
|
virDomainNetDef *net = NULL;
|
|
g_autoptr(virNetDevBandwidth) bandwidth = NULL;
|
|
bool inboundSpecified = false;
|
|
bool outboundSpecified = false;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (virTypedParamsValidate(params, nparams,
|
|
VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
|
|
VIR_TYPED_PARAM_UINT,
|
|
VIR_DOMAIN_BANDWIDTH_IN_PEAK,
|
|
VIR_TYPED_PARAM_UINT,
|
|
VIR_DOMAIN_BANDWIDTH_IN_BURST,
|
|
VIR_TYPED_PARAM_UINT,
|
|
VIR_DOMAIN_BANDWIDTH_IN_FLOOR,
|
|
VIR_TYPED_PARAM_UINT,
|
|
VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
|
|
VIR_TYPED_PARAM_UINT,
|
|
VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
|
|
VIR_TYPED_PARAM_UINT,
|
|
VIR_DOMAIN_BANDWIDTH_OUT_BURST,
|
|
VIR_TYPED_PARAM_UINT,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (!(net = virDomainNetFind(def, device)))
|
|
goto cleanup;
|
|
|
|
bandwidth = g_new0(virNetDevBandwidth, 1);
|
|
bandwidth->in = g_new0(virNetDevBandwidthRate, 1);
|
|
bandwidth->out = g_new0(virNetDevBandwidthRate, 1);
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
virTypedParameterPtr param = ¶ms[i];
|
|
|
|
if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE)) {
|
|
bandwidth->in->average = param->value.ui;
|
|
inboundSpecified = true;
|
|
} else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_PEAK)) {
|
|
bandwidth->in->peak = param->value.ui;
|
|
} else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_BURST)) {
|
|
bandwidth->in->burst = param->value.ui;
|
|
} else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_IN_FLOOR)) {
|
|
bandwidth->in->floor = param->value.ui;
|
|
inboundSpecified = true;
|
|
} else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE)) {
|
|
bandwidth->out->average = param->value.ui;
|
|
outboundSpecified = true;
|
|
} else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK)) {
|
|
bandwidth->out->peak = param->value.ui;
|
|
} else if (STREQ(param->field, VIR_DOMAIN_BANDWIDTH_OUT_BURST)) {
|
|
bandwidth->out->burst = param->value.ui;
|
|
}
|
|
}
|
|
|
|
/* average or floor are mandatory, peak and burst are optional */
|
|
if (!bandwidth->in->average && !bandwidth->in->floor)
|
|
VIR_FREE(bandwidth->in);
|
|
if (!bandwidth->out->average)
|
|
VIR_FREE(bandwidth->out);
|
|
|
|
if (!net->bandwidth) {
|
|
net->bandwidth = g_steal_pointer(&bandwidth);
|
|
} else {
|
|
if (bandwidth->in) {
|
|
VIR_FREE(net->bandwidth->in);
|
|
net->bandwidth->in = g_steal_pointer(&bandwidth->in);
|
|
} else if (inboundSpecified) {
|
|
/* if we got here it means user requested @inbound to be cleared */
|
|
VIR_FREE(net->bandwidth->in);
|
|
}
|
|
if (bandwidth->out) {
|
|
VIR_FREE(net->bandwidth->out);
|
|
net->bandwidth->out = g_steal_pointer(&bandwidth->out);
|
|
} else if (outboundSpecified) {
|
|
/* if we got here it means user requested @outbound to be cleared */
|
|
VIR_FREE(net->bandwidth->out);
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetInterfaceParameters(virDomainPtr dom,
|
|
const char *device,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virNetDevBandwidthRate in = {0};
|
|
virNetDevBandwidthRate out = {0};
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virDomainNetDef *net = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
if ((*nparams) == 0) {
|
|
*nparams = 7;
|
|
return 0;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (!(net = virDomainNetFind(def, device)))
|
|
goto cleanup;
|
|
|
|
if (net->bandwidth) {
|
|
if (net->bandwidth->in)
|
|
in = *net->bandwidth->in;
|
|
if (net->bandwidth->out)
|
|
out = *net->bandwidth->out;
|
|
}
|
|
|
|
TEST_SET_PARAM(0, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE, VIR_TYPED_PARAM_UINT, in.average);
|
|
TEST_SET_PARAM(1, VIR_DOMAIN_BANDWIDTH_IN_PEAK, VIR_TYPED_PARAM_UINT, in.peak);
|
|
TEST_SET_PARAM(2, VIR_DOMAIN_BANDWIDTH_IN_BURST, VIR_TYPED_PARAM_UINT, in.burst);
|
|
TEST_SET_PARAM(3, VIR_DOMAIN_BANDWIDTH_IN_FLOOR, VIR_TYPED_PARAM_UINT, in.floor);
|
|
TEST_SET_PARAM(4, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE, VIR_TYPED_PARAM_UINT, out.average);
|
|
TEST_SET_PARAM(5, VIR_DOMAIN_BANDWIDTH_OUT_PEAK, VIR_TYPED_PARAM_UINT, out.peak);
|
|
TEST_SET_PARAM(6, VIR_DOMAIN_BANDWIDTH_OUT_BURST, VIR_TYPED_PARAM_UINT, out.burst);
|
|
|
|
if (*nparams > 7)
|
|
*nparams = 7;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#define TEST_BLOCK_IOTUNE_MAX 1000000000000000LL
|
|
|
|
static int
|
|
testDomainSetBlockIoTune(virDomainPtr dom,
|
|
const char *path,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virDomainBlockIoTuneInfo info = {0};
|
|
virDomainDiskDef *conf_disk = NULL;
|
|
virTypedParameterPtr eventParams = NULL;
|
|
int eventNparams = 0;
|
|
int eventMaxparams = 0;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (virTypedParamsValidate(params, nparams,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_SIZE_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME,
|
|
VIR_TYPED_PARAM_STRING,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (!(conf_disk = virDomainDiskByName(def, path, true))) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("missing persistent configuration for disk '%s'"),
|
|
path);
|
|
goto cleanup;
|
|
}
|
|
|
|
info = conf_disk->blkdeviotune;
|
|
info.group_name = g_strdup(conf_disk->blkdeviotune.group_name);
|
|
|
|
if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_DISK, path) < 0)
|
|
goto cleanup;
|
|
|
|
#define SET_IOTUNE_FIELD(FIELD, STR, TUNABLE_STR) \
|
|
if (STREQ(param->field, STR)) { \
|
|
info.FIELD = param->value.ul; \
|
|
if (virTypedParamsAddULLong(&eventParams, &eventNparams, \
|
|
&eventMaxparams, \
|
|
TUNABLE_STR, \
|
|
param->value.ul) < 0) \
|
|
goto cleanup; \
|
|
continue; \
|
|
}
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
virTypedParameterPtr param = ¶ms[i];
|
|
|
|
if (param->value.ul > TEST_BLOCK_IOTUNE_MAX) {
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
_("block I/O throttle limit value must"
|
|
" be no more than %llu"), TEST_BLOCK_IOTUNE_MAX);
|
|
goto cleanup;
|
|
}
|
|
|
|
SET_IOTUNE_FIELD(total_bytes_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC);
|
|
SET_IOTUNE_FIELD(read_bytes_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC);
|
|
SET_IOTUNE_FIELD(write_bytes_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC);
|
|
SET_IOTUNE_FIELD(total_iops_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC);
|
|
SET_IOTUNE_FIELD(read_iops_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC);
|
|
SET_IOTUNE_FIELD(write_iops_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC);
|
|
|
|
SET_IOTUNE_FIELD(total_bytes_sec_max,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC_MAX);
|
|
SET_IOTUNE_FIELD(read_bytes_sec_max,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC_MAX);
|
|
SET_IOTUNE_FIELD(write_bytes_sec_max,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC_MAX);
|
|
SET_IOTUNE_FIELD(total_iops_sec_max,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC_MAX);
|
|
SET_IOTUNE_FIELD(read_iops_sec_max,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC_MAX);
|
|
SET_IOTUNE_FIELD(write_iops_sec_max,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC_MAX);
|
|
SET_IOTUNE_FIELD(size_iops_sec,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_SIZE_IOPS_SEC,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_SIZE_IOPS_SEC);
|
|
|
|
if (STREQ(param->field, VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME)) {
|
|
VIR_FREE(info.group_name);
|
|
info.group_name = g_strdup(param->value.s);
|
|
if (virTypedParamsAddString(&eventParams,
|
|
&eventNparams,
|
|
&eventMaxparams,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_GROUP_NAME,
|
|
param->value.s) < 0)
|
|
goto cleanup;
|
|
continue;
|
|
}
|
|
|
|
SET_IOTUNE_FIELD(total_bytes_sec_max_length,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX_LENGTH,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC_MAX_LENGTH);
|
|
SET_IOTUNE_FIELD(read_bytes_sec_max_length,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX_LENGTH,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC_MAX_LENGTH);
|
|
SET_IOTUNE_FIELD(write_bytes_sec_max_length,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX_LENGTH,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC_MAX_LENGTH);
|
|
SET_IOTUNE_FIELD(total_iops_sec_max_length,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX_LENGTH,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC_MAX_LENGTH);
|
|
SET_IOTUNE_FIELD(read_iops_sec_max_length,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX_LENGTH,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC_MAX_LENGTH);
|
|
SET_IOTUNE_FIELD(write_iops_sec_max_length,
|
|
VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX_LENGTH,
|
|
VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC_MAX_LENGTH);
|
|
}
|
|
#undef SET_IOTUNE_FIELD
|
|
|
|
if ((info.total_bytes_sec && info.read_bytes_sec) ||
|
|
(info.total_bytes_sec && info.write_bytes_sec)) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("total and read/write of bytes_sec "
|
|
"cannot be set at the same time"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((info.total_iops_sec && info.read_iops_sec) ||
|
|
(info.total_iops_sec && info.write_iops_sec)) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("total and read/write of iops_sec "
|
|
"cannot be set at the same time"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((info.total_bytes_sec_max && info.read_bytes_sec_max) ||
|
|
(info.total_bytes_sec_max && info.write_bytes_sec_max)) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("total and read/write of bytes_sec_max "
|
|
"cannot be set at the same time"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((info.total_iops_sec_max && info.read_iops_sec_max) ||
|
|
(info.total_iops_sec_max && info.write_iops_sec_max)) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("total and read/write of iops_sec_max "
|
|
"cannot be set at the same time"));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
#define TEST_BLOCK_IOTUNE_MAX_CHECK(FIELD, FIELD_MAX) \
|
|
do { \
|
|
if (info.FIELD > info.FIELD_MAX) { \
|
|
virReportError(VIR_ERR_INVALID_ARG, \
|
|
_("%s cannot be set higher than %s "), \
|
|
#FIELD, #FIELD_MAX); \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0);
|
|
|
|
TEST_BLOCK_IOTUNE_MAX_CHECK(total_bytes_sec, total_bytes_sec_max);
|
|
TEST_BLOCK_IOTUNE_MAX_CHECK(read_bytes_sec, read_bytes_sec_max);
|
|
TEST_BLOCK_IOTUNE_MAX_CHECK(write_bytes_sec, write_bytes_sec_max);
|
|
TEST_BLOCK_IOTUNE_MAX_CHECK(total_iops_sec, total_iops_sec_max);
|
|
TEST_BLOCK_IOTUNE_MAX_CHECK(read_iops_sec, read_iops_sec_max);
|
|
TEST_BLOCK_IOTUNE_MAX_CHECK(write_iops_sec, write_iops_sec_max);
|
|
|
|
#undef TEST_BLOCK_IOTUNE_MAX_CHECK
|
|
|
|
virDomainDiskSetBlockIOTune(conf_disk, &info);
|
|
info.group_name = NULL;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
VIR_FREE(info.group_name);
|
|
virDomainObjEndAPI(&vm);
|
|
if (eventNparams)
|
|
virTypedParamsFree(eventParams, eventNparams);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetBlockIoTune(virDomainPtr dom,
|
|
const char *path,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virDomainDiskDef *disk;
|
|
virDomainBlockIoTuneInfo reply = {0};
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
flags &= ~VIR_TYPED_PARAM_STRING_OKAY;
|
|
|
|
if (*nparams == 0) {
|
|
*nparams = 20;
|
|
return 0;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (!(disk = virDomainDiskByName(def, path, true))) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("disk '%s' was not found in the domain config"),
|
|
path);
|
|
goto cleanup;
|
|
}
|
|
|
|
reply = disk->blkdeviotune;
|
|
reply.group_name = g_strdup(disk->blkdeviotune.group_name);
|
|
|
|
TEST_SET_PARAM(0, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.total_bytes_sec);
|
|
TEST_SET_PARAM(1, VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.read_bytes_sec);
|
|
TEST_SET_PARAM(2, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.write_bytes_sec);
|
|
|
|
TEST_SET_PARAM(3, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.total_iops_sec);
|
|
TEST_SET_PARAM(4, VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.read_iops_sec);
|
|
TEST_SET_PARAM(5, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.write_iops_sec);
|
|
|
|
TEST_SET_PARAM(6, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG, reply.total_bytes_sec_max);
|
|
TEST_SET_PARAM(7, VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG, reply.read_bytes_sec_max);
|
|
TEST_SET_PARAM(8, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG, reply.write_bytes_sec_max);
|
|
|
|
TEST_SET_PARAM(9, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG, reply.total_iops_sec_max);
|
|
TEST_SET_PARAM(10, VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG, reply.read_iops_sec_max);
|
|
TEST_SET_PARAM(11, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX,
|
|
VIR_TYPED_PARAM_ULLONG, reply.write_iops_sec_max);
|
|
|
|
TEST_SET_PARAM(12, VIR_DOMAIN_BLOCK_IOTUNE_SIZE_IOPS_SEC,
|
|
VIR_TYPED_PARAM_ULLONG, reply.size_iops_sec);
|
|
|
|
TEST_SET_PARAM(13, VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME,
|
|
VIR_TYPED_PARAM_STRING, reply.group_name);
|
|
reply.group_name = NULL;
|
|
|
|
TEST_SET_PARAM(14, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG, reply.total_bytes_sec_max_length);
|
|
TEST_SET_PARAM(15, VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG, reply.read_bytes_sec_max_length);
|
|
TEST_SET_PARAM(16, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG, reply.write_bytes_sec_max_length);
|
|
|
|
TEST_SET_PARAM(17, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG, reply.total_iops_sec_max_length);
|
|
TEST_SET_PARAM(18, VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG, reply.read_iops_sec_max_length);
|
|
TEST_SET_PARAM(19, VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX_LENGTH,
|
|
VIR_TYPED_PARAM_ULLONG, reply.write_iops_sec_max_length);
|
|
|
|
if (*nparams > 20)
|
|
*nparams = 20;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
VIR_FREE(reply.group_name);
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
#undef TEST_SET_PARAM
|
|
|
|
|
|
static int testConnectNumOfDefinedDomains(virConnectPtr conn)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
|
|
return virDomainObjListNumOfDomains(privconn->domains, false, NULL, NULL);
|
|
}
|
|
|
|
static int testConnectListDefinedDomains(virConnectPtr conn,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virDomainPtr ret = NULL;
|
|
virDomainDef *def;
|
|
virDomainObj *dom = NULL;
|
|
virObjectEvent *event = NULL;
|
|
virDomainDef *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->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)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
testDriver *privconn = dom->conn->privateData;
|
|
virDomainObj *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->xmlopt,
|
|
NULL, NULL, flags);
|
|
|
|
if (ret == 0) {
|
|
virObjectEvent *ev = NULL;
|
|
ev = virDomainEventMetadataChangeNewFromObj(privdom, type, uri);
|
|
virObjectEventStateQueue(privconn->eventState, ev);
|
|
}
|
|
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
#define TEST_TOTAL_CPUTIME 48772617035LL
|
|
|
|
static int
|
|
testDomainGetDomainTotalCpuStats(virTypedParameterPtr params,
|
|
int nparams)
|
|
{
|
|
if (nparams == 0) /* return supported number of params */
|
|
return 3;
|
|
|
|
if (virTypedParameterAssign(¶ms[0], VIR_DOMAIN_CPU_STATS_CPUTIME,
|
|
VIR_TYPED_PARAM_ULLONG, TEST_TOTAL_CPUTIME) < 0)
|
|
return -1;
|
|
|
|
if (nparams > 1 &&
|
|
virTypedParameterAssign(¶ms[1],
|
|
VIR_DOMAIN_CPU_STATS_USERTIME,
|
|
VIR_TYPED_PARAM_ULLONG, 5540000000) < 0)
|
|
return -1;
|
|
|
|
if (nparams > 2 &&
|
|
virTypedParameterAssign(¶ms[2],
|
|
VIR_DOMAIN_CPU_STATS_SYSTEMTIME,
|
|
VIR_TYPED_PARAM_ULLONG, 6460000000) < 0)
|
|
return -1;
|
|
|
|
if (nparams > 3)
|
|
nparams = 3;
|
|
|
|
return nparams;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetPercpuStats(virTypedParameterPtr params,
|
|
unsigned int nparams,
|
|
int start_cpu,
|
|
unsigned int ncpus,
|
|
int total_cpus)
|
|
{
|
|
size_t i;
|
|
int need_cpus;
|
|
int param_idx;
|
|
unsigned long long percpu_time = (TEST_TOTAL_CPUTIME / total_cpus);
|
|
|
|
/* return the number of supported params */
|
|
if (nparams == 0 && ncpus != 0)
|
|
return 2;
|
|
|
|
/* return total number of cpus */
|
|
if (ncpus == 0)
|
|
return total_cpus;
|
|
|
|
if (start_cpu >= total_cpus) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("start_cpu %d larger than maximum of %d"),
|
|
start_cpu, total_cpus - 1);
|
|
return -1;
|
|
}
|
|
|
|
/* return percpu cputime in index 0 */
|
|
param_idx = 0;
|
|
|
|
/* number of cpus to compute */
|
|
need_cpus = MIN(total_cpus, start_cpu + ncpus);
|
|
|
|
for (i = start_cpu; i < need_cpus; i++) {
|
|
int idx = (i - start_cpu) * nparams + param_idx;
|
|
|
|
if (virTypedParameterAssign(¶ms[idx],
|
|
VIR_DOMAIN_CPU_STATS_CPUTIME,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
percpu_time + i) < 0)
|
|
return -1;
|
|
}
|
|
|
|
/* return percpu vcputime in index 1 */
|
|
param_idx = 1;
|
|
|
|
if (param_idx < nparams) {
|
|
for (i = start_cpu; i < need_cpus; i++) {
|
|
int idx = (i - start_cpu) * nparams + param_idx;
|
|
|
|
if (virTypedParameterAssign(¶ms[idx],
|
|
VIR_DOMAIN_CPU_STATS_VCPUTIME,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
percpu_time + i - 1234567890) < 0)
|
|
return -1;
|
|
}
|
|
param_idx++;
|
|
}
|
|
|
|
return param_idx;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetCPUStats(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
unsigned int nparams,
|
|
int start_cpu,
|
|
unsigned int ncpus,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
testDriver *privconn = dom->conn->privateData;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
if (start_cpu == -1)
|
|
ret = testDomainGetDomainTotalCpuStats(params, nparams);
|
|
else
|
|
ret = testDomainGetPercpuStats(params, nparams, start_cpu, ncpus,
|
|
privconn->nodeInfo.cores);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSendProcessSignal(virDomainPtr dom,
|
|
long long pid_value,
|
|
unsigned int signum,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
virDomainObj *vm = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (pid_value != 1) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("only sending a signal to pid 1 is supported"));
|
|
return -1;
|
|
}
|
|
|
|
if (signum >= VIR_DOMAIN_PROCESS_SIGNAL_LAST) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("signum value %d is out of range"),
|
|
signum);
|
|
return -1;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
/* do nothing */
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int testNodeGetCellsFreeMemory(virConnectPtr conn,
|
|
unsigned long long *freemems,
|
|
int startCell, int maxCells)
|
|
{
|
|
testDriver *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 G_GNUC_UNUSED,
|
|
int cpuNum G_GNUC_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)
|
|
{
|
|
testDriver *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 G_GNUC_UNUSED,
|
|
unsigned int npages,
|
|
unsigned int *pages G_GNUC_UNUSED,
|
|
int startCell G_GNUC_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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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 testDomainCreateWithFiles(virDomainPtr domain,
|
|
unsigned int nfiles G_GNUC_UNUSED,
|
|
int *files G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
return testDomainCreateWithFlags(domain, flags);
|
|
}
|
|
|
|
|
|
static int testDomainUndefineFlags(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *privdom;
|
|
virObjectEvent *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
|
|
testDomainFSFreeze(virDomainPtr dom,
|
|
const char **mountpoints,
|
|
unsigned int nmountpoints,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
testDomainObjPrivate *priv;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (nmountpoints == 0) {
|
|
ret = 2 - (priv->frozen[0] + priv->frozen[1]);
|
|
priv->frozen[0] = priv->frozen[1] = true;
|
|
} else {
|
|
int nfreeze = 0;
|
|
bool freeze[2];
|
|
|
|
memcpy(&freeze, priv->frozen, 2);
|
|
|
|
for (i = 0; i < nmountpoints; i++) {
|
|
if (STREQ(mountpoints[i], "/")) {
|
|
if (!freeze[0]) {
|
|
freeze[0] = true;
|
|
nfreeze++;
|
|
}
|
|
} else if (STREQ(mountpoints[i], "/boot")) {
|
|
if (!freeze[1]) {
|
|
freeze[1] = true;
|
|
nfreeze++;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("mount point not found: %s"),
|
|
mountpoints[i]);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* steal the helper copy */
|
|
memcpy(priv->frozen, &freeze, 2);
|
|
ret = nfreeze;
|
|
}
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainFSThaw(virDomainPtr dom,
|
|
const char **mountpoints,
|
|
unsigned int nmountpoints,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
testDomainObjPrivate *priv;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (nmountpoints == 0) {
|
|
ret = priv->frozen[0] + priv->frozen[1];
|
|
priv->frozen[0] = priv->frozen[1] = false;
|
|
} else {
|
|
int nthaw = 0;
|
|
bool freeze[2];
|
|
|
|
memcpy(&freeze, priv->frozen, 2);
|
|
|
|
for (i = 0; i < nmountpoints; i++) {
|
|
if (STREQ(mountpoints[i], "/")) {
|
|
if (freeze[0]) {
|
|
freeze[0] = false;
|
|
nthaw++;
|
|
}
|
|
} else if (STREQ(mountpoints[i], "/boot")) {
|
|
if (freeze[1]) {
|
|
freeze[1] = false;
|
|
nthaw++;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("mount point not found: %s"),
|
|
mountpoints[i]);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* steal the helper copy */
|
|
memcpy(priv->frozen, &freeze, 2);
|
|
ret = nthaw;
|
|
}
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainFSTrim(virDomainPtr dom,
|
|
const char *mountPoint,
|
|
unsigned long long minimum G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
if (mountPoint && STRNEQ(mountPoint, "/") && STRNEQ(mountPoint, "/boot")) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("mount point not found: %s"),
|
|
mountPoint);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int testDomainGetAutostart(virDomainPtr domain,
|
|
int *autostart)
|
|
{
|
|
virDomainObj *privdom;
|
|
|
|
if (!(privdom = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
*autostart = privdom->autostart;
|
|
|
|
virDomainObjEndAPI(&privdom);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int testDomainSetAutostart(virDomainPtr domain,
|
|
int autostart)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int ret = -1;
|
|
size_t i;
|
|
size_t nerrors = 0;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
nerrors = MIN(vm->def->ndisks, maxerrors);
|
|
|
|
if (errors) {
|
|
/* sanitize input */
|
|
memset(errors, 0, sizeof(virDomainDiskError) * nerrors);
|
|
|
|
for (i = 0; i < nerrors; i++) {
|
|
errors[i].disk = g_strdup(vm->def->disks[i]->dst);
|
|
errors[i].error = (i % (VIR_DOMAIN_DISK_ERROR_LAST - 1)) + 1;
|
|
}
|
|
ret = i;
|
|
} else {
|
|
ret = vm->def->ndisks;
|
|
}
|
|
|
|
cleanup:
|
|
if (ret < 0) {
|
|
for (i = 0; i < nerrors; i++)
|
|
VIR_FREE(errors[i].disk);
|
|
}
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
testDomainGetFSInfo(virDomainPtr dom,
|
|
virDomainFSInfoPtr **info,
|
|
unsigned int flags)
|
|
{
|
|
size_t i;
|
|
virDomainObj *vm;
|
|
virDomainFSInfoPtr *info_ret = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
*info = NULL;
|
|
|
|
for (i = 0; i < vm->def->ndisks; i++) {
|
|
if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
|
|
char *name = vm->def->disks[i]->dst;
|
|
|
|
info_ret = g_new0(virDomainFSInfo *, 2);
|
|
info_ret[0] = g_new0(virDomainFSInfo, 1);
|
|
info_ret[0]->devAlias = g_new0(char *, 1);
|
|
|
|
info_ret[0]->mountpoint = g_strdup("/");
|
|
info_ret[0]->fstype = g_strdup("ext4");
|
|
info_ret[0]->devAlias[0] = g_strdup(name);
|
|
info_ret[0]->name = g_strdup_printf("%s1", name);
|
|
|
|
info_ret[1] = g_new0(virDomainFSInfo, 1);
|
|
info_ret[1]->devAlias = g_new0(char *, 1);
|
|
|
|
info_ret[1]->mountpoint = g_strdup("/boot");
|
|
info_ret[1]->fstype = g_strdup("ext4");
|
|
info_ret[1]->devAlias[0] = g_strdup(name);
|
|
info_ret[1]->name = g_strdup_printf("%s2", name);
|
|
|
|
info_ret[0]->ndevAlias = info_ret[1]->ndevAlias = 1;
|
|
|
|
*info = g_steal_pointer(&info_ret);
|
|
|
|
ret = 2;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (info_ret) {
|
|
virDomainFSInfoFree(info_ret[0]);
|
|
virDomainFSInfoFree(info_ret[1]);
|
|
VIR_FREE(info_ret);
|
|
}
|
|
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSetPerfEvents(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (virTypedParamsValidate(params, nparams,
|
|
VIR_PERF_PARAM_CMT, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_MBMT, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_MBML, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_CPU_CYCLES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_INSTRUCTIONS, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_CACHE_REFERENCES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_CACHE_MISSES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_BRANCH_INSTRUCTIONS, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_BRANCH_MISSES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_BUS_CYCLES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_STALLED_CYCLES_FRONTEND, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_STALLED_CYCLES_BACKEND, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_REF_CPU_CYCLES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_CPU_CLOCK, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_TASK_CLOCK, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_PAGE_FAULTS, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_CONTEXT_SWITCHES, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_CPU_MIGRATIONS, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_PAGE_FAULTS_MIN, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_PAGE_FAULTS_MAJ, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_ALIGNMENT_FAULTS, VIR_TYPED_PARAM_BOOLEAN,
|
|
VIR_PERF_PARAM_EMULATION_FAULTS, VIR_TYPED_PARAM_BOOLEAN,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
virTypedParameterPtr param = ¶ms[i];
|
|
virPerfEventType type = virPerfEventTypeFromString(param->field);
|
|
|
|
if (param->value.b)
|
|
def->perf.events[type] = VIR_TRISTATE_BOOL_YES;
|
|
else
|
|
def->perf.events[type] = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetPerfEvents(virDomainPtr dom,
|
|
virTypedParameterPtr *params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virTypedParameterPtr par = NULL;
|
|
size_t i;
|
|
int maxpar = 0;
|
|
int npar = 0;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < VIR_PERF_EVENT_LAST; i++) {
|
|
if (virTypedParamsAddBoolean(&par, &npar, &maxpar,
|
|
virPerfEventTypeToString(i),
|
|
def->perf.events[i] == VIR_TRISTATE_BOOL_YES) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
*params = g_steal_pointer(&par);
|
|
*nparams = npar;
|
|
npar = 0;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
virTypedParamsFree(par, npar);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *testDomainGetSchedulerType(virDomainPtr domain G_GNUC_UNUSED,
|
|
int *nparams)
|
|
{
|
|
if (nparams)
|
|
*nparams = 1;
|
|
|
|
return g_strdup("fair");
|
|
}
|
|
|
|
static int
|
|
testDomainGetSchedulerParametersFlags(virDomainPtr domain,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *privdom;
|
|
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;
|
|
}
|
|
|
|
/* No significance to these numbers, just enough to mix it up */
|
|
statbase = g_get_real_time();
|
|
stats->rd_req = statbase / 10;
|
|
stats->rd_bytes = statbase / 20;
|
|
stats->wr_req = statbase / 30;
|
|
stats->wr_bytes = statbase / 40;
|
|
stats->errs = statbase / (1000LL * 1000LL * 2);
|
|
|
|
ret = 0;
|
|
error:
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainInterfaceAddressFromNet(testDriver *driver,
|
|
const virDomainNetDef *net,
|
|
size_t addr_offset,
|
|
virDomainInterfacePtr iface)
|
|
{
|
|
virSocketAddr addr;
|
|
virNetworkObj *net_obj = NULL;
|
|
virNetworkDef *net_def = NULL;
|
|
int ret = -1;
|
|
|
|
if (!(net_obj = testNetworkObjFindByName(driver, net->data.network.name)))
|
|
return -1;
|
|
|
|
net_def = virNetworkObjGetDef(net_obj);
|
|
|
|
iface->addrs[0].prefix = virSocketAddrGetIPPrefix(&net_def->ips->address,
|
|
&net_def->ips->netmask,
|
|
net_def->ips->prefix);
|
|
|
|
if (net_def->ips->nranges > 0)
|
|
addr = net_def->ips->ranges[0].addr.start;
|
|
else
|
|
addr = net_def->ips->address;
|
|
|
|
if (net_def->ips->family && STREQ(net_def->ips->family, "ipv6")) {
|
|
iface->addrs[0].type = VIR_IP_ADDR_TYPE_IPV6;
|
|
addr.data.inet6.sin6_addr.s6_addr[15] += addr_offset;
|
|
} else {
|
|
iface->addrs[0].type = VIR_IP_ADDR_TYPE_IPV4;
|
|
addr.data.inet4.sin_addr.s_addr = \
|
|
htonl(ntohl(addr.data.inet4.sin_addr.s_addr) + addr_offset);
|
|
}
|
|
|
|
if (!(iface->addrs[0].addr = virSocketAddrFormat(&addr)))
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virNetworkObjEndAPI(&net_obj);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testDomainGetSecurityLabel(virDomainPtr dom,
|
|
virSecurityLabelPtr seclabel)
|
|
{
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
memset(seclabel, 0, sizeof(*seclabel));
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjIsActive(vm)) {
|
|
if (virStrcpyStatic(seclabel->label, "libvirt-test") < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("security label exceeds maximum: %zu"),
|
|
sizeof(seclabel->label) - 1);
|
|
goto cleanup;
|
|
}
|
|
|
|
seclabel->enforcing = 1;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testNodeGetSecurityModel(virConnectPtr conn,
|
|
virSecurityModelPtr secmodel)
|
|
{
|
|
testDriver *driver = conn->privateData;
|
|
|
|
memset(secmodel, 0, sizeof(*secmodel));
|
|
|
|
if (driver->caps->host.nsecModels == 0 ||
|
|
driver->caps->host.secModels[0].model == NULL)
|
|
return 0;
|
|
|
|
if (virStrcpy(secmodel->model, driver->caps->host.secModels[0].model,
|
|
VIR_SECURITY_MODEL_BUFLEN) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("security model string exceeds max %d bytes"),
|
|
VIR_SECURITY_MODEL_BUFLEN - 1);
|
|
return -1;
|
|
}
|
|
|
|
if (virStrcpy(secmodel->doi, driver->caps->host.secModels[0].doi,
|
|
VIR_SECURITY_DOI_BUFLEN) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("security DOI string exceeds max %d bytes"),
|
|
VIR_SECURITY_DOI_BUFLEN - 1);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testDomainInterfaceAddresses(virDomainPtr dom,
|
|
virDomainInterfacePtr **ifaces,
|
|
unsigned int source,
|
|
unsigned int flags)
|
|
{
|
|
size_t i;
|
|
size_t ifaces_count = 0;
|
|
int ret = -1;
|
|
char macaddr[VIR_MAC_STRING_BUFLEN];
|
|
virDomainObj *vm = NULL;
|
|
virDomainInterfacePtr iface = NULL;
|
|
virDomainInterfacePtr *ifaces_ret = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (source >= VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LAST) {
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
_("Unknown IP address data source %d"),
|
|
source);
|
|
return -1;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
ifaces_ret = g_new0(virDomainInterfacePtr, vm->def->nnets);
|
|
|
|
for (i = 0; i < vm->def->nnets; i++) {
|
|
const virDomainNetDef *net = vm->def->nets[i];
|
|
|
|
iface = g_new0(virDomainInterface, 1);
|
|
|
|
iface->name = g_strdup(net->ifname);
|
|
|
|
virMacAddrFormat(&net->mac, macaddr);
|
|
iface->hwaddr = g_strdup(macaddr);
|
|
|
|
iface->addrs = g_new0(virDomainIPAddress, 1);
|
|
iface->naddrs = 1;
|
|
|
|
if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
|
|
/* try using different addresses per different inf and domain */
|
|
const size_t addr_offset = 20 * (vm->def->id - 1) + i + 1;
|
|
|
|
if (testDomainInterfaceAddressFromNet(dom->conn->privateData,
|
|
net, addr_offset, iface) < 0)
|
|
goto cleanup;
|
|
} else {
|
|
iface->addrs[0].type = VIR_IP_ADDR_TYPE_IPV4;
|
|
iface->addrs[0].prefix = 24;
|
|
iface->addrs[0].addr = g_strdup_printf("192.168.0.%zu", 1 + i);
|
|
|
|
}
|
|
|
|
VIR_APPEND_ELEMENT_INPLACE(ifaces_ret, ifaces_count, iface);
|
|
}
|
|
|
|
*ifaces = g_steal_pointer(&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)
|
|
{
|
|
virDomainObj *privdom;
|
|
unsigned long long statbase;
|
|
virDomainNetDef *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;
|
|
|
|
/* No significance to these numbers, just enough to mix it up */
|
|
statbase = g_get_real_time();
|
|
stats->rx_bytes = statbase / 10;
|
|
stats->rx_packets = statbase / 100;
|
|
stats->rx_errs = statbase / (1000LL * 1000LL * 1);
|
|
stats->rx_drop = statbase / (1000LL * 1000LL * 2);
|
|
stats->tx_bytes = statbase / 20;
|
|
stats->tx_packets = statbase / 110;
|
|
stats->tx_errs = statbase / (1000LL * 1000LL * 3);
|
|
stats->tx_drop = statbase / (1000LL * 1000LL * 4);
|
|
|
|
ret = 0;
|
|
error:
|
|
virDomainObjEndAPI(&privdom);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static virNetworkObj *
|
|
testNetworkObjFindByUUID(testDriver *privconn,
|
|
const unsigned char *uuid)
|
|
{
|
|
virNetworkObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virNetworkObj *obj;
|
|
virNetworkDef *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 virNetworkObj *
|
|
testNetworkObjFindByName(testDriver *privconn,
|
|
const char *name)
|
|
{
|
|
virNetworkObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virNetworkObj *obj;
|
|
virNetworkDef *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
return virNetworkObjListNumOfNetworks(privconn->networks, true, NULL,
|
|
conn);
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectListNetworks(virConnectPtr conn,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
return virNetworkObjListGetNames(privconn->networks, true, names,
|
|
maxnames, NULL, conn);
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNumOfDefinedNetworks(virConnectPtr conn)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
return virNetworkObjListNumOfNetworks(privconn->networks, false, NULL,
|
|
conn);
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectListDefinedNetworks(virConnectPtr conn,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
return virNetworkObjListGetNames(privconn->networks, false, names,
|
|
maxnames, NULL, conn);
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectListAllNetworks(virConnectPtr conn,
|
|
virNetworkPtr **nets,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
|
|
virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1);
|
|
|
|
return virNetworkObjListExport(conn, privconn->networks, nets, NULL, flags);
|
|
}
|
|
|
|
|
|
static int
|
|
testNetworkIsActive(virNetworkPtr net)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *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)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virNetworkDef *newDef;
|
|
virNetworkObj *obj = NULL;
|
|
virNetworkDef *def;
|
|
virNetworkPtr net = NULL;
|
|
virObjectEvent *event = NULL;
|
|
|
|
if ((newDef = virNetworkDefParseString(xml, NULL)) == 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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virNetworkDef *newDef;
|
|
virNetworkObj *obj = NULL;
|
|
virNetworkDef *def;
|
|
virNetworkPtr net = NULL;
|
|
virObjectEvent *event = NULL;
|
|
|
|
if ((newDef = virNetworkDefParseString(xml, NULL)) == 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)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *obj;
|
|
int ret = -1;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *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, NULL, flags) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virNetworkObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testNetworkCreate(virNetworkPtr net)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *obj;
|
|
virNetworkDef *def;
|
|
int ret = -1;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *obj;
|
|
virNetworkDef *def;
|
|
int ret = -1;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *obj;
|
|
char *ret = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(obj = testNetworkObjFindByName(privconn, net->name)))
|
|
goto cleanup;
|
|
|
|
ret = virNetworkDefFormat(virNetworkObjGetDef(obj), NULL, flags);
|
|
|
|
cleanup:
|
|
virNetworkObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
testNetworkGetBridgeName(virNetworkPtr net)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
char *bridge = NULL;
|
|
virNetworkObj *obj;
|
|
virNetworkDef *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;
|
|
}
|
|
|
|
bridge = g_strdup(def->bridge);
|
|
|
|
cleanup:
|
|
virNetworkObjEndAPI(&obj);
|
|
return bridge;
|
|
}
|
|
|
|
|
|
static int
|
|
testNetworkGetAutostart(virNetworkPtr net,
|
|
int *autostart)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *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)
|
|
{
|
|
testDriver *privconn = net->conn->privateData;
|
|
virNetworkObj *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 virInterfaceObj *
|
|
testInterfaceObjFindByName(testDriver *privconn,
|
|
const char *name)
|
|
{
|
|
virInterfaceObj *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
int nnames;
|
|
|
|
virObjectLock(privconn);
|
|
nnames = virInterfaceObjListGetNames(privconn->ifaces, true,
|
|
names, maxnames);
|
|
virObjectUnlock(privconn);
|
|
|
|
return nnames;
|
|
}
|
|
|
|
|
|
static int
|
|
testConnectNumOfDefinedInterfaces(virConnectPtr conn)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virInterfaceObj *obj;
|
|
virInterfaceDef *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *privconn = iface->conn->privateData;
|
|
virInterfaceObj *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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 = g_steal_pointer(&privconn->backupIfaces);
|
|
|
|
privconn->transaction_running = false;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virObjectUnlock(privconn);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
testInterfaceGetXMLDesc(virInterfacePtr iface,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = iface->conn->privateData;
|
|
virInterfaceObj *obj;
|
|
virInterfaceDef *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virInterfaceDef *def;
|
|
virInterfaceObj *obj = NULL;
|
|
virInterfaceDef *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)
|
|
{
|
|
testDriver *privconn = iface->conn->privateData;
|
|
virInterfaceObj *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)
|
|
{
|
|
testDriver *privconn = iface->conn->privateData;
|
|
virInterfaceObj *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)
|
|
{
|
|
testDriver *privconn = iface->conn->privateData;
|
|
virInterfaceObj *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(virStoragePoolObj *obj)
|
|
{
|
|
char *configFile;
|
|
virStoragePoolDef *def = virStoragePoolObjGetDef(obj);
|
|
|
|
def->capacity = defaultPoolCap;
|
|
def->allocation = defaultPoolAlloc;
|
|
def->available = defaultPoolCap - defaultPoolAlloc;
|
|
|
|
configFile = g_strdup("");
|
|
|
|
virStoragePoolObjSetConfigFile(obj, configFile);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virStoragePoolObj *
|
|
testStoragePoolObjFindByName(testDriver *privconn,
|
|
const char *name)
|
|
{
|
|
virStoragePoolObj *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 virStoragePoolObj *
|
|
testStoragePoolObjFindActiveByName(testDriver *privconn,
|
|
const char *name)
|
|
{
|
|
virStoragePoolObj *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 virStoragePoolObj *
|
|
testStoragePoolObjFindInactiveByName(testDriver *privconn,
|
|
const char *name)
|
|
{
|
|
virStoragePoolObj *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 virStoragePoolObj *
|
|
testStoragePoolObjFindByUUID(testDriver *privconn,
|
|
const unsigned char *uuid)
|
|
{
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virObjectEvent *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 G_GNUC_UNUSED,
|
|
const char *type,
|
|
const char *srcSpec,
|
|
unsigned int flags)
|
|
{
|
|
int pool_type;
|
|
char *ret = NULL;
|
|
g_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:
|
|
ret = g_strdup(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;
|
|
}
|
|
|
|
ret = g_strdup_printf(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 virNodeDeviceObj *
|
|
testNodeDeviceMockCreateVport(testDriver *driver,
|
|
const char *wwnn,
|
|
const char *wwpn);
|
|
static int
|
|
testCreateVport(testDriver *driver,
|
|
const char *wwnn,
|
|
const char *wwpn)
|
|
{
|
|
virNodeDeviceObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virStoragePoolObj *obj = NULL;
|
|
virStoragePoolDef *def;
|
|
virStoragePoolPtr pool = NULL;
|
|
virObjectEvent *event = NULL;
|
|
g_autoptr(virStoragePoolDef) newDef = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
virObjectLock(privconn);
|
|
if (!(newDef = virStoragePoolDefParseString(xml)))
|
|
goto cleanup;
|
|
|
|
if (!(obj = virStoragePoolObjListAdd(privconn->pools, newDef,
|
|
VIR_STORAGE_POOL_OBJ_LIST_ADD_CHECK_LIVE)))
|
|
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);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (testStoragePoolObjSetDefaults(obj) == -1) {
|
|
virStoragePoolObjRemove(privconn->pools, obj);
|
|
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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virStoragePoolObj *obj = NULL;
|
|
virStoragePoolDef *def;
|
|
virStoragePoolPtr pool = NULL;
|
|
virObjectEvent *event = NULL;
|
|
g_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 = virStoragePoolObjListAdd(privconn->pools, newDef, 0)))
|
|
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);
|
|
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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virObjectEvent *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);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolBuild(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virObjectEvent *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(testDriver *privconn,
|
|
const char *wwnn G_GNUC_UNUSED,
|
|
const char *wwpn G_GNUC_UNUSED)
|
|
{
|
|
virNodeDeviceObj *obj = NULL;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *def;
|
|
int ret = -1;
|
|
virObjectEvent *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);
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testStoragePoolDelete(virStoragePoolPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virObjectEvent *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *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 virStorageVolDef *
|
|
testStorageVolDefFindByName(virStoragePoolObj *obj,
|
|
const char *name)
|
|
{
|
|
virStorageVolDef *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *def;
|
|
virStorageVolDef *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;
|
|
virStorageVolDef *voldef;
|
|
};
|
|
|
|
static bool
|
|
testStorageVolLookupByKeyCallback(virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *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(virStoragePoolObj *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *def;
|
|
virStorageVolPtr ret = NULL;
|
|
g_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;
|
|
}
|
|
|
|
privvol->target.path = g_strdup_printf("%s/%s", def->target.path,
|
|
privvol->name);
|
|
|
|
privvol->key = g_strdup(privvol->target.path);
|
|
if (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)
|
|
{
|
|
testDriver *privconn = pool->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *def;
|
|
virStorageVolDef *origvol = NULL;
|
|
virStorageVolPtr ret = NULL;
|
|
g_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);
|
|
|
|
privvol->target.path = g_strdup_printf("%s/%s", def->target.path,
|
|
privvol->name);
|
|
|
|
privvol->key = g_strdup(privvol->target.path);
|
|
if (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)
|
|
{
|
|
testDriver *privconn = vol->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *def;
|
|
virStorageVolDef *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)
|
|
{
|
|
testDriver *privconn = vol->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStoragePoolDef *def;
|
|
virStorageVolDef *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)
|
|
{
|
|
testDriver *privconn = vol->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStorageVolDef *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)
|
|
{
|
|
testDriver *privconn = vol->conn->privateData;
|
|
virStoragePoolObj *obj;
|
|
virStorageVolDef *privvol;
|
|
char *ret = NULL;
|
|
|
|
if (!(obj = testStoragePoolObjFindActiveByName(privconn, vol->pool)))
|
|
return NULL;
|
|
|
|
if (!(privvol = testStorageVolDefFindByName(obj, vol->name)))
|
|
goto cleanup;
|
|
|
|
ret = g_strdup(privvol->target.path);
|
|
|
|
cleanup:
|
|
virStoragePoolObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Node device implementations */
|
|
|
|
static virNodeDeviceObj *
|
|
testNodeDeviceObjFindByName(testDriver *driver,
|
|
const char *name)
|
|
{
|
|
virNodeDeviceObj *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *driver = conn->privateData;
|
|
virNodeDeviceObj *obj;
|
|
virNodeDeviceDef *def;
|
|
virNodeDevicePtr ret = NULL;
|
|
|
|
if (!(obj = testNodeDeviceObjFindByName(driver, name)))
|
|
return NULL;
|
|
def = virNodeDeviceObjGetDef(obj);
|
|
|
|
if ((ret = virGetNodeDevice(conn, name)))
|
|
ret->parentName = g_strdup(def->parent);
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
testNodeDeviceGetXMLDesc(virNodeDevicePtr dev,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *driver = dev->conn->privateData;
|
|
virNodeDeviceObj *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)
|
|
{
|
|
testDriver *driver = dev->conn->privateData;
|
|
virNodeDeviceObj *obj;
|
|
virNodeDeviceDef *def;
|
|
char *ret = NULL;
|
|
|
|
if (!(obj = testNodeDeviceObjFindByName(driver, dev->name)))
|
|
return NULL;
|
|
def = virNodeDeviceObjGetDef(obj);
|
|
|
|
if (def->parent) {
|
|
ret = g_strdup(def->parent);
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("no parent for this device"));
|
|
}
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testNodeDeviceNumOfCaps(virNodeDevicePtr dev)
|
|
{
|
|
testDriver *driver = dev->conn->privateData;
|
|
virNodeDeviceObj *obj;
|
|
virNodeDeviceDef *def;
|
|
virNodeDevCapsDef *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)
|
|
{
|
|
testDriver *driver = dev->conn->privateData;
|
|
virNodeDeviceObj *obj;
|
|
virNodeDeviceDef *def;
|
|
virNodeDevCapsDef *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) {
|
|
names[ncaps] = g_strdup(virNodeDevCapTypeToString(caps->data.type));
|
|
ncaps++;
|
|
}
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
return ncaps;
|
|
}
|
|
|
|
|
|
static virNodeDeviceObj *
|
|
testNodeDeviceMockCreateVport(testDriver *driver,
|
|
const char *wwnn,
|
|
const char *wwpn)
|
|
{
|
|
virNodeDeviceDef *def = NULL;
|
|
virNodeDevCapsDef *caps;
|
|
virNodeDeviceObj *obj = NULL;
|
|
virNodeDeviceObj *objcopy = NULL;
|
|
virNodeDeviceDef *objdef;
|
|
virObjectEvent *event = NULL;
|
|
g_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, NULL,
|
|
NULL)))
|
|
goto cleanup;
|
|
|
|
VIR_FREE(def->name);
|
|
def->name = g_strdup("scsi_host12");
|
|
|
|
/* 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);
|
|
caps->data.scsi_host.wwnn = g_strdup(wwnn);
|
|
caps->data.scsi_host.wwpn = g_strdup(wwpn);
|
|
} 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)
|
|
{
|
|
testDriver *driver = conn->privateData;
|
|
virNodeDeviceDef *def = NULL;
|
|
virNodeDevicePtr dev = NULL, ret = NULL;
|
|
virNodeDeviceObj *obj = NULL;
|
|
virNodeDeviceDef *objdef;
|
|
g_autofree char *wwnn = NULL;
|
|
g_autofree char *wwpn = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(def = virNodeDeviceDefParseString(xmlDesc, CREATE_DEVICE, NULL,
|
|
NULL, 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);
|
|
dev->parentName = g_strdup(def->parent);
|
|
|
|
ret = g_steal_pointer(&dev);
|
|
|
|
cleanup:
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
virNodeDeviceDefFree(def);
|
|
virObjectUnref(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testNodeDeviceDestroy(virNodeDevicePtr dev)
|
|
{
|
|
int ret = 0;
|
|
testDriver *driver = dev->conn->privateData;
|
|
virNodeDeviceObj *obj = NULL;
|
|
virNodeDeviceObj *parentobj = NULL;
|
|
virNodeDeviceDef *def;
|
|
virObjectEvent *event = NULL;
|
|
g_autofree char *wwnn = NULL;
|
|
g_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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *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)
|
|
{
|
|
testDriver *privconn = conn->privateData;
|
|
|
|
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
|
|
|
|
return virDomainObjListExport(privconn->domains, conn, domains,
|
|
NULL, flags);
|
|
}
|
|
|
|
static int
|
|
testNodeGetCPUMap(virConnectPtr conn G_GNUC_UNUSED,
|
|
unsigned char **cpumap,
|
|
unsigned int *online,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(0, -1);
|
|
|
|
if (cpumap) {
|
|
*cpumap = g_new0(unsigned char, 1);
|
|
*cpumap[0] = 0x15;
|
|
}
|
|
|
|
if (online)
|
|
*online = 3;
|
|
|
|
return 8;
|
|
}
|
|
|
|
static char *
|
|
testDomainScreenshot(virDomainPtr dom G_GNUC_UNUSED,
|
|
virStreamPtr st,
|
|
unsigned int screen G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
char *ret = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
ret = g_strdup("image/png");
|
|
|
|
if (virFDStreamOpenFile(st, PKGDATADIR "/test-screenshot.png", 0, 0, O_RDONLY) < 0)
|
|
VIR_FREE(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainInjectNMI(virDomainPtr domain,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
/* do nothing */
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSendKey(virDomainPtr domain,
|
|
unsigned int codeset,
|
|
unsigned int holdtime G_GNUC_UNUSED,
|
|
unsigned int *keycodes,
|
|
int nkeycodes,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
size_t i;
|
|
virDomainObj *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 G_GNUC_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)
|
|
{
|
|
testDriver *privconn = dom->conn->privateData;
|
|
virDomainObj *vm = NULL;
|
|
virObjectEvent *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)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *vm;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
vm->hasManagedSave = false;
|
|
|
|
virDomainObjEndAPI(&vm);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainMemoryStats(virDomainPtr dom,
|
|
virDomainMemoryStatPtr stats,
|
|
unsigned int nr_stats,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int cur_memory;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
cur_memory = vm->def->mem.cur_balloon;
|
|
ret = 0;
|
|
|
|
#define STATS_SET_PARAM(name, value) \
|
|
if (ret < nr_stats) { \
|
|
stats[ret].tag = name; \
|
|
stats[ret].val = value; \
|
|
ret++; \
|
|
}
|
|
|
|
if (virDomainDefHasMemballoon(vm->def)) {
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON, cur_memory);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_SWAP_IN, 0);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, 0);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, 0);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, 0);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_UNUSED, cur_memory / 2);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_AVAILABLE, cur_memory);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_USABLE, cur_memory / 2);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE, 627319920);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_DISK_CACHES, cur_memory / 8);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGALLOC, 0);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGFAIL, 0);
|
|
STATS_SET_PARAM(VIR_DOMAIN_MEMORY_STAT_RSS, cur_memory / 2);
|
|
}
|
|
|
|
#undef STATS_SET_PARAM
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
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;
|
|
virDomainObj *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;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainGetBlockInfo(virDomainPtr dom,
|
|
const char *path,
|
|
virDomainBlockInfoPtr info,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDiskDef *disk;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (!(disk = virDomainDiskByName(vm->def, path, false))) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("invalid path %s not assigned to domain"), path);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virStorageSourceIsEmpty(disk->src)) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("disk '%s' does not currently have a source assigned"),
|
|
path);
|
|
goto cleanup;
|
|
}
|
|
|
|
info->capacity = 1099506450432;
|
|
info->allocation = 1099511627776;
|
|
info->physical = 1099511627776;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void
|
|
testDomainModifyLifecycleAction(virDomainDef *def,
|
|
virDomainLifecycle type,
|
|
virDomainLifecycleAction action)
|
|
{
|
|
switch (type) {
|
|
case VIR_DOMAIN_LIFECYCLE_POWEROFF:
|
|
def->onPoweroff = action;
|
|
break;
|
|
case VIR_DOMAIN_LIFECYCLE_REBOOT:
|
|
def->onReboot = action;
|
|
break;
|
|
case VIR_DOMAIN_LIFECYCLE_CRASH:
|
|
def->onCrash = action;
|
|
break;
|
|
case VIR_DOMAIN_LIFECYCLE_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainSetLifecycleAction(virDomainPtr dom,
|
|
unsigned int type,
|
|
unsigned int action,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def = NULL;
|
|
virDomainDef *persistentDef = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (!virDomainDefLifecycleActionAllowed(type, action))
|
|
return -1;
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0)
|
|
goto cleanup;
|
|
|
|
if (def)
|
|
testDomainModifyLifecycleAction(def, type, action);
|
|
|
|
if (persistentDef)
|
|
testDomainModifyLifecycleAction(persistentDef, type, action);
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Snapshot APIs
|
|
*/
|
|
|
|
static virDomainMomentObj *
|
|
testSnapObjFromName(virDomainObj *vm,
|
|
const char *name)
|
|
{
|
|
virDomainMomentObj *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 virDomainMomentObj *
|
|
testSnapObjFromSnapshot(virDomainObj *vm,
|
|
virDomainSnapshotPtr snapshot)
|
|
{
|
|
return testSnapObjFromName(vm, snapshot->name);
|
|
}
|
|
|
|
static virDomainObj *
|
|
testDomObjFromSnapshot(virDomainSnapshotPtr snapshot)
|
|
{
|
|
return testDomObjFromDomain(snapshot->domain);
|
|
}
|
|
|
|
static int
|
|
testDomainSnapshotNum(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *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)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainSnapshotPtr snapshot = NULL;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
char *xml = NULL;
|
|
virDomainMomentObj *snap = NULL;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
testDriver *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->xmlopt,
|
|
virDomainSnapshotFormatConvertXMLFlags(flags));
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return xml;
|
|
}
|
|
|
|
static int
|
|
testDomainSnapshotIsCurrent(virDomainSnapshotPtr snapshot,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int ret = -1;
|
|
virDomainMomentObj *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)
|
|
{
|
|
virDomainObj *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(virDomainObj *vm,
|
|
virDomainSnapshotDef *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)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *snap = NULL;
|
|
virDomainSnapshotPtr snapshot = NULL;
|
|
virObjectEvent *event = NULL;
|
|
bool update_current = true;
|
|
bool redefine = flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
|
|
unsigned int parse_flags = VIR_DOMAIN_SNAPSHOT_PARSE_DISKS;
|
|
g_autoptr(virDomainSnapshotDef) 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 |
|
|
VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE, 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 (virDomainListCheckpoints(vm->checkpoints, NULL, domain, NULL, 0) > 0) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
_("cannot create snapshot while checkpoint exists"));
|
|
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 (flags & VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE)
|
|
parse_flags |= VIR_DOMAIN_SNAPSHOT_PARSE_VALIDATE;
|
|
|
|
if (!(def = virDomainSnapshotDefParseString(xmlDesc,
|
|
privconn->xmlopt,
|
|
NULL, NULL,
|
|
parse_flags)))
|
|
goto cleanup;
|
|
|
|
if (redefine) {
|
|
if (virDomainSnapshotRedefinePrep(vm, &def, &snap,
|
|
privconn->xmlopt,
|
|
flags) < 0)
|
|
goto cleanup;
|
|
} else {
|
|
if (!(def->parent.dom = virDomainDefCopy(vm->def,
|
|
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) {
|
|
snap->def->parent_name = g_strdup(virDomainSnapshotGetCurrentName(vm->snapshots));
|
|
|
|
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) {
|
|
if (update_current)
|
|
virDomainSnapshotSetCurrent(vm->snapshots, snap);
|
|
virDomainSnapshotLinkParent(vm->snapshots, snap);
|
|
}
|
|
virDomainObjEndAPI(&vm);
|
|
}
|
|
virObjectEventStateQueue(privconn->eventState, event);
|
|
return snapshot;
|
|
}
|
|
|
|
|
|
typedef struct _testMomentRemoveData testMomentRemoveData;
|
|
struct _testMomentRemoveData {
|
|
virDomainObj *vm;
|
|
bool current;
|
|
};
|
|
|
|
static int
|
|
testDomainSnapshotDiscardAll(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *data)
|
|
{
|
|
virDomainMomentObj *snap = payload;
|
|
testMomentRemoveData *curr = data;
|
|
|
|
curr->current |= virDomainSnapshotObjListRemove(curr->vm->snapshots, snap);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct _testMomentReparentData testMomentReparentData;
|
|
struct _testMomentReparentData {
|
|
virDomainMomentObj *parent;
|
|
virDomainObj *vm;
|
|
int err;
|
|
};
|
|
|
|
static int
|
|
testDomainMomentReparentChildren(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *data)
|
|
{
|
|
virDomainMomentObj *moment = payload;
|
|
testMomentReparentData *rep = data;
|
|
|
|
if (rep->err < 0)
|
|
return 0;
|
|
|
|
VIR_FREE(moment->def->parent_name);
|
|
|
|
if (rep->parent->def)
|
|
moment->def->parent_name = g_strdup(rep->parent->def->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
testDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *snap = NULL;
|
|
virDomainMomentObj *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)) {
|
|
testMomentRemoveData rem;
|
|
rem.vm = vm;
|
|
rem.current = false;
|
|
virDomainMomentForEachDescendant(snap,
|
|
testDomainSnapshotDiscardAll,
|
|
&rem);
|
|
if (rem.current)
|
|
virDomainSnapshotSetCurrent(vm->snapshots, snap);
|
|
} else if (snap->nchildren) {
|
|
testMomentReparentData rep;
|
|
rep.parent = snap->parent;
|
|
rep.vm = vm;
|
|
rep.err = 0;
|
|
virDomainMomentForEachChild(snap,
|
|
testDomainMomentReparentChildren,
|
|
&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)
|
|
{
|
|
testDriver *privconn = snapshot->domain->conn->privateData;
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *snap = NULL;
|
|
virObjectEvent *event = NULL;
|
|
virObjectEvent *event2 = NULL;
|
|
virDomainDef *config = NULL;
|
|
virDomainSnapshotDef *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->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;
|
|
}
|
|
|
|
/*
|
|
* Checkpoint APIs
|
|
*/
|
|
|
|
static int
|
|
testDomainCheckpointDiscardAll(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *data)
|
|
{
|
|
virDomainMomentObj *chk = payload;
|
|
testMomentRemoveData *curr = data;
|
|
|
|
curr->current |= virDomainCheckpointObjListRemove(curr->vm->checkpoints,
|
|
chk);
|
|
return 0;
|
|
}
|
|
|
|
static virDomainObj *
|
|
testDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint)
|
|
{
|
|
return testDomObjFromDomain(checkpoint->domain);
|
|
}
|
|
|
|
static virDomainMomentObj *
|
|
testCheckpointObjFromName(virDomainObj *vm,
|
|
const char *name)
|
|
{
|
|
virDomainMomentObj *chk = NULL;
|
|
|
|
chk = virDomainCheckpointFindByName(vm->checkpoints, name);
|
|
if (!chk)
|
|
virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT,
|
|
_("no domain checkpoint with matching name '%s'"),
|
|
name);
|
|
|
|
return chk;
|
|
}
|
|
|
|
static virDomainMomentObj *
|
|
testCheckpointObjFromCheckpoint(virDomainObj *vm,
|
|
virDomainCheckpointPtr checkpoint)
|
|
{
|
|
return testCheckpointObjFromName(vm, checkpoint->name);
|
|
}
|
|
|
|
static virDomainCheckpointPtr
|
|
testDomainCheckpointCreateXML(virDomainPtr domain,
|
|
const char *xmlDesc,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = domain->conn->privateData;
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *chk = NULL;
|
|
virDomainCheckpointPtr checkpoint = NULL;
|
|
virDomainMomentObj *current = NULL;
|
|
bool update_current = true;
|
|
bool redefine = flags & VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE;
|
|
unsigned int parse_flags = 0;
|
|
g_autoptr(virDomainCheckpointDef) def = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE |
|
|
VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE, NULL);
|
|
|
|
if (redefine) {
|
|
parse_flags |= VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE;
|
|
update_current = false;
|
|
}
|
|
|
|
if (!(vm = testDomObjFromDomain(domain)))
|
|
goto cleanup;
|
|
|
|
if (virDomainSnapshotObjListNum(vm->snapshots, NULL, 0) > 0) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
_("cannot create checkpoint while snapshot exists"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
_("cannot create checkpoint for inactive domain"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(def = virDomainCheckpointDefParseString(xmlDesc,
|
|
privconn->xmlopt, NULL,
|
|
parse_flags)))
|
|
goto cleanup;
|
|
|
|
if (redefine) {
|
|
if (virDomainCheckpointRedefinePrep(vm, def, &update_current) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(chk = virDomainCheckpointRedefineCommit(vm, &def)))
|
|
goto cleanup;
|
|
} else {
|
|
if (!(def->parent.dom = virDomainDefCopy(vm->def,
|
|
privconn->xmlopt,
|
|
NULL,
|
|
true)))
|
|
goto cleanup;
|
|
|
|
if (virDomainCheckpointAlignDisks(def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(chk = virDomainCheckpointAssignDef(vm->checkpoints, def)))
|
|
goto cleanup;
|
|
|
|
def = NULL;
|
|
}
|
|
|
|
current = virDomainCheckpointGetCurrent(vm->checkpoints);
|
|
if (current) {
|
|
if (!redefine)
|
|
chk->def->parent_name = g_strdup(current->def->name);
|
|
if (update_current)
|
|
virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
|
|
}
|
|
|
|
/* actually do the checkpoint - except the test driver has nothing
|
|
* to actually do here */
|
|
|
|
/* If we fail after this point, there's not a whole lot we can do;
|
|
* we've successfully created the checkpoint, so we have to go
|
|
* forward the best we can.
|
|
*/
|
|
checkpoint = virGetDomainCheckpoint(domain, chk->def->name);
|
|
|
|
cleanup:
|
|
if (checkpoint) {
|
|
if (update_current)
|
|
virDomainCheckpointSetCurrent(vm->checkpoints, chk);
|
|
virDomainCheckpointLinkParent(vm->checkpoints, chk);
|
|
} else if (chk) {
|
|
virDomainCheckpointObjListRemove(vm->checkpoints, chk);
|
|
}
|
|
|
|
virDomainObjEndAPI(&vm);
|
|
return checkpoint;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainListAllCheckpoints(virDomainPtr domain,
|
|
virDomainCheckpointPtr **chks,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int n = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_CHECKPOINT_LIST_ROOTS |
|
|
VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL |
|
|
VIR_DOMAIN_CHECKPOINT_FILTERS_ALL, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
n = virDomainListCheckpoints(vm->checkpoints, NULL, domain, chks, flags);
|
|
|
|
virDomainObjEndAPI(&vm);
|
|
return n;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainCheckpointListAllChildren(virDomainCheckpointPtr checkpoint,
|
|
virDomainCheckpointPtr **chks,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainMomentObj *chk = NULL;
|
|
int n = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS |
|
|
VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL |
|
|
VIR_DOMAIN_CHECKPOINT_FILTERS_ALL, -1);
|
|
|
|
if (!(vm = testDomObjFromCheckpoint(checkpoint)))
|
|
return -1;
|
|
|
|
if (!(chk = testCheckpointObjFromCheckpoint(vm, checkpoint)))
|
|
goto cleanup;
|
|
|
|
n = virDomainListCheckpoints(vm->checkpoints, chk, checkpoint->domain,
|
|
chks, flags);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return n;
|
|
}
|
|
|
|
|
|
static virDomainCheckpointPtr
|
|
testDomainCheckpointLookupByName(virDomainPtr domain,
|
|
const char *name,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainMomentObj *chk = NULL;
|
|
virDomainCheckpointPtr checkpoint = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(vm = testDomObjFromDomain(domain)))
|
|
return NULL;
|
|
|
|
if (!(chk = testCheckpointObjFromName(vm, name)))
|
|
goto cleanup;
|
|
|
|
checkpoint = virGetDomainCheckpoint(domain, chk->def->name);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return checkpoint;
|
|
}
|
|
|
|
|
|
static virDomainCheckpointPtr
|
|
testDomainCheckpointGetParent(virDomainCheckpointPtr checkpoint,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainMomentObj *chk = NULL;
|
|
virDomainCheckpointPtr parent = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!(vm = testDomObjFromCheckpoint(checkpoint)))
|
|
return NULL;
|
|
|
|
if (!(chk = testCheckpointObjFromCheckpoint(vm, checkpoint)))
|
|
goto cleanup;
|
|
|
|
if (!chk->def->parent_name) {
|
|
virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT,
|
|
_("checkpoint '%s' does not have a parent"),
|
|
chk->def->name);
|
|
goto cleanup;
|
|
}
|
|
|
|
parent = virGetDomainCheckpoint(checkpoint->domain, chk->def->parent_name);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return parent;
|
|
}
|
|
|
|
|
|
static char *
|
|
testDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint,
|
|
unsigned int flags)
|
|
{
|
|
testDriver *privconn = checkpoint->domain->conn->privateData;
|
|
virDomainObj *vm = NULL;
|
|
char *xml = NULL;
|
|
virDomainMomentObj *chk = NULL;
|
|
size_t i;
|
|
virDomainCheckpointDef *chkdef;
|
|
unsigned int format_flags;
|
|
|
|
virCheckFlags(VIR_DOMAIN_CHECKPOINT_XML_SECURE |
|
|
VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN |
|
|
VIR_DOMAIN_CHECKPOINT_XML_SIZE, NULL);
|
|
|
|
if (!(vm = testDomObjFromCheckpoint(checkpoint)))
|
|
return NULL;
|
|
|
|
if (!(chk = testCheckpointObjFromCheckpoint(vm, checkpoint)))
|
|
goto cleanup;
|
|
chkdef = virDomainCheckpointObjGetDef(chk);
|
|
|
|
if (flags & VIR_DOMAIN_CHECKPOINT_XML_SIZE) {
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < chkdef->ndisks; i++) {
|
|
virDomainCheckpointDiskDef *disk = &chkdef->disks[i];
|
|
|
|
if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
|
|
continue;
|
|
disk->size = 1024; /* Any number will do... */
|
|
}
|
|
}
|
|
|
|
format_flags = virDomainCheckpointFormatConvertXMLFlags(flags);
|
|
xml = virDomainCheckpointDefFormat(chkdef,
|
|
privconn->xmlopt, format_flags);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return xml;
|
|
}
|
|
|
|
|
|
static int
|
|
testDomainCheckpointDelete(virDomainCheckpointPtr checkpoint,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int ret = -1;
|
|
virDomainMomentObj *chk = NULL;
|
|
virDomainMomentObj *parentchk = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN |
|
|
VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY |
|
|
VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY, -1);
|
|
|
|
if (!(vm = testDomObjFromCheckpoint(checkpoint)))
|
|
return -1;
|
|
|
|
if (!(chk = testCheckpointObjFromCheckpoint(vm, checkpoint)))
|
|
goto cleanup;
|
|
|
|
if (flags & (VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN |
|
|
VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY)) {
|
|
testMomentRemoveData rem;
|
|
|
|
rem.vm = vm;
|
|
rem.current = false;
|
|
virDomainMomentForEachDescendant(chk, testDomainCheckpointDiscardAll,
|
|
&rem);
|
|
if (rem.current)
|
|
virDomainCheckpointSetCurrent(vm->checkpoints, chk);
|
|
} else if (chk->nchildren) {
|
|
testMomentReparentData rep;
|
|
|
|
rep.parent = chk->parent;
|
|
rep.vm = vm;
|
|
rep.err = 0;
|
|
virDomainMomentForEachChild(chk, testDomainMomentReparentChildren,
|
|
&rep);
|
|
if (rep.err < 0)
|
|
goto cleanup;
|
|
virDomainMomentMoveChildren(chk, chk->parent);
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) {
|
|
virDomainMomentDropChildren(chk);
|
|
} else {
|
|
virDomainMomentDropParent(chk);
|
|
if (chk == virDomainCheckpointGetCurrent(vm->checkpoints)) {
|
|
if (chk->def->parent_name) {
|
|
parentchk = virDomainCheckpointFindByName(vm->checkpoints,
|
|
chk->def->parent_name);
|
|
if (!parentchk)
|
|
VIR_WARN("missing parent checkpoint matching name '%s'",
|
|
chk->def->parent_name);
|
|
}
|
|
virDomainCheckpointSetCurrent(vm->checkpoints, parentchk);
|
|
}
|
|
virDomainCheckpointObjListRemove(vm->checkpoints, chk);
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testDomainGetMessages(virDomainPtr dom,
|
|
char ***msgs,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int rv = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_MESSAGE_DEPRECATION |
|
|
VIR_DOMAIN_MESSAGE_TAINTING, -1);
|
|
|
|
if (!(vm = testDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
rv = virDomainObjGetMessages(vm, msgs, flags);
|
|
|
|
virDomainObjEndAPI(&vm);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Test driver
|
|
*/
|
|
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 */
|
|
.connectSupportsFeature = testConnectSupportsFeature, /* 5.6.0 */
|
|
.connectListDomains = testConnectListDomains, /* 0.1.1 */
|
|
.connectNumOfDomains = testConnectNumOfDomains, /* 0.1.1 */
|
|
.connectListAllDomains = testConnectListAllDomains, /* 0.9.13 */
|
|
.domainCreateXML = testDomainCreateXML, /* 0.1.4 */
|
|
.domainCreateXMLWithFiles = testDomainCreateXMLWithFiles, /* 5.7.0 */
|
|
.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 */
|
|
.domainReset = testDomainReset, /* 5.7.0 */
|
|
.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 */
|
|
.domainSetMemoryStatsPeriod = testDomainSetMemoryStatsPeriod, /* 5.6.0 */
|
|
.domainSetMemoryFlags = testDomainSetMemoryFlags, /* 5.6.0 */
|
|
.domainGetHostname = testDomainGetHostname, /* 5.5.0 */
|
|
.domainGetInfo = testDomainGetInfo, /* 0.1.1 */
|
|
.domainGetState = testDomainGetState, /* 0.9.2 */
|
|
.domainGetControlInfo = testDomainGetControlInfo, /* 7.6.0 */
|
|
.domainGetTime = testDomainGetTime, /* 5.4.0 */
|
|
.domainSetTime = testDomainSetTime, /* 5.7.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 */
|
|
.domainSetUserPassword = testDomainSetUserPassword, /* 5.6.0 */
|
|
.domainPinEmulator = testDomainPinEmulator, /* 5.6.0 */
|
|
.domainGetEmulatorPinInfo = testDomainGetEmulatorPinInfo, /* 5.6.0 */
|
|
.domainSetVcpus = testDomainSetVcpus, /* 0.1.4 */
|
|
.domainSetVcpusFlags = testDomainSetVcpusFlags, /* 0.8.5 */
|
|
.domainGetVcpusFlags = testDomainGetVcpusFlags, /* 0.8.5 */
|
|
.domainPinVcpu = testDomainPinVcpu, /* 0.7.3 */
|
|
.domainPinVcpuFlags = testDomainPinVcpuFlags, /* 5.6.0 */
|
|
.domainGetVcpus = testDomainGetVcpus, /* 0.7.3 */
|
|
.domainGetVcpuPinInfo = testDomainGetVcpuPinInfo, /* 1.2.18 */
|
|
.domainGetMaxVcpus = testDomainGetMaxVcpus, /* 0.7.3 */
|
|
.domainGetSecurityLabel = testDomainGetSecurityLabel, /* 7.5.0 */
|
|
.nodeGetSecurityModel = testNodeGetSecurityModel, /* 7.5.0 */
|
|
.domainGetXMLDesc = testDomainGetXMLDesc, /* 0.1.4 */
|
|
.domainSetMemoryParameters = testDomainSetMemoryParameters, /* 5.6.0 */
|
|
.domainGetMemoryParameters = testDomainGetMemoryParameters, /* 5.6.0 */
|
|
.domainSetNumaParameters = testDomainSetNumaParameters, /* 5.6.0 */
|
|
.domainGetNumaParameters = testDomainGetNumaParameters, /* 5.6.0 */
|
|
.domainSetInterfaceParameters = testDomainSetInterfaceParameters, /* 5.6.0 */
|
|
.domainGetInterfaceParameters = testDomainGetInterfaceParameters, /* 5.6.0 */
|
|
.domainSetBlockIoTune = testDomainSetBlockIoTune, /* 5.7.0 */
|
|
.domainGetBlockIoTune = testDomainGetBlockIoTune, /* 5.7.0 */
|
|
.connectListDefinedDomains = testConnectListDefinedDomains, /* 0.1.11 */
|
|
.connectNumOfDefinedDomains = testConnectNumOfDefinedDomains, /* 0.1.11 */
|
|
.domainCreate = testDomainCreate, /* 0.1.11 */
|
|
.domainCreateWithFlags = testDomainCreateWithFlags, /* 0.8.2 */
|
|
.domainCreateWithFiles = testDomainCreateWithFiles, /* 5.7.0 */
|
|
.domainDefineXML = testDomainDefineXML, /* 0.1.11 */
|
|
.domainDefineXMLFlags = testDomainDefineXMLFlags, /* 1.2.12 */
|
|
.domainUndefine = testDomainUndefine, /* 0.1.11 */
|
|
.domainUndefineFlags = testDomainUndefineFlags, /* 0.9.4 */
|
|
.domainFSFreeze = testDomainFSFreeze, /* 5.7.0 */
|
|
.domainFSThaw = testDomainFSThaw, /* 5.7.0 */
|
|
.domainFSTrim = testDomainFSTrim, /* 5.7.0 */
|
|
.domainGetAutostart = testDomainGetAutostart, /* 0.3.2 */
|
|
.domainSetAutostart = testDomainSetAutostart, /* 0.3.2 */
|
|
.domainGetDiskErrors = testDomainGetDiskErrors, /* 5.4.0 */
|
|
.domainGetFSInfo = testDomainGetFSInfo, /* 5.6.0 */
|
|
.domainSetPerfEvents = testDomainSetPerfEvents, /* 5.6.0 */
|
|
.domainGetPerfEvents = testDomainGetPerfEvents, /* 5.6.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 */
|
|
.domainInjectNMI = testDomainInjectNMI, /* 5.6.0 */
|
|
.domainSendKey = testDomainSendKey, /* 5.5.0 */
|
|
.domainGetMetadata = testDomainGetMetadata, /* 1.1.3 */
|
|
.domainSetMetadata = testDomainSetMetadata, /* 1.1.3 */
|
|
.domainGetCPUStats = testDomainGetCPUStats, /* 5.6.0 */
|
|
.domainSendProcessSignal = testDomainSendProcessSignal, /* 5.5.0 */
|
|
.connectGetCPUModelNames = testConnectGetCPUModelNames, /* 1.1.3 */
|
|
.domainManagedSave = testDomainManagedSave, /* 1.1.4 */
|
|
.domainHasManagedSaveImage = testDomainHasManagedSaveImage, /* 1.1.4 */
|
|
.domainManagedSaveRemove = testDomainManagedSaveRemove, /* 1.1.4 */
|
|
.domainMemoryStats = testDomainMemoryStats, /* 5.7.0 */
|
|
.domainMemoryPeek = testDomainMemoryPeek, /* 5.4.0 */
|
|
.domainGetBlockInfo = testDomainGetBlockInfo, /* 5.7.0 */
|
|
.domainSetLifecycleAction = testDomainSetLifecycleAction, /* 5.7.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 */
|
|
.domainCheckpointCreateXML = testDomainCheckpointCreateXML, /* 5.6.0 */
|
|
.domainCheckpointGetXMLDesc = testDomainCheckpointGetXMLDesc, /* 5.6.0 */
|
|
|
|
.domainListAllCheckpoints = testDomainListAllCheckpoints, /* 5.6.0 */
|
|
.domainCheckpointListAllChildren = testDomainCheckpointListAllChildren, /* 5.6.0 */
|
|
.domainCheckpointLookupByName = testDomainCheckpointLookupByName, /* 5.6.0 */
|
|
.domainCheckpointGetParent = testDomainCheckpointGetParent, /* 5.6.0 */
|
|
.domainCheckpointDelete = testDomainCheckpointDelete, /* 5.6.0 */
|
|
.domainGetMessages = testDomainGetMessages, /* 7.6.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);
|
|
}
|