libvirt/src/libxl/libxl_driver.c

4513 lines
130 KiB
C
Raw Normal View History

/*
* libxl_driver.c: core driver methods for managing libxenlight domains
*
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
* Copyright (C) 2006-2014 Red Hat, Inc.
* Copyright (C) 2011-2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
* Copyright (C) 2011 Univention GmbH.
*
* 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/>.
*
* Authors:
* Jim Fehlig <jfehlig@novell.com>
* Markus Groß <gross@univention.de>
* Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <math.h>
#include <libxl.h>
#include <libxl_utils.h>
#include <fcntl.h>
#include <regex.h>
#include "internal.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#include "virerror.h"
2012-12-12 16:35:35 +00:00
#include "virconf.h"
#include "datatypes.h"
#include "virfile.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
2012-12-13 18:01:25 +00:00
#include "viruuid.h"
#include "vircommand.h"
#include "libxl_domain.h"
#include "libxl_driver.h"
#include "libxl_conf.h"
#include "xen_xm.h"
#include "virtypedparam.h"
#include "viruri.h"
#include "virstring.h"
#include "virsysinfo.h"
#include "viraccessapicheck.h"
#include "viratomic.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
#define LIBXL_DOM_REQ_POWEROFF 0
#define LIBXL_DOM_REQ_REBOOT 1
#define LIBXL_DOM_REQ_SUSPEND 2
#define LIBXL_DOM_REQ_CRASH 3
#define LIBXL_DOM_REQ_HALT 4
#define LIBXL_CONFIG_FORMAT_XM "xen-xm"
/* Number of Xen scheduler parameters */
#define XEN_SCHED_CREDIT_NPARAM 2
static libxlDriverPrivatePtr libxl_driver = NULL;
/* Function declarations */
static int
libxlDomainManagedSaveLoad(virDomainObjPtr vm,
void *opaque);
static int
libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm,
bool start_paused, int restore_fd);
/* Function definitions */
static virDomainObjPtr
libxlDomObjFromDomain(virDomainPtr dom)
{
virDomainObjPtr vm;
libxlDriverPrivatePtr driver = dom->conn->privateData;
char uuidstr[VIR_UUID_STRING_BUFLEN];
vm = virDomainObjListFindByUUID(driver->domains, dom->uuid);
if (!vm) {
virUUIDFormat(dom->uuid, uuidstr);
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s' (%s)"),
uuidstr, dom->name);
return NULL;
}
return vm;
}
static void
libxlDomainEventQueue(libxlDriverPrivatePtr driver, virObjectEventPtr event)
{
virObjectEventStateQueue(driver->domainEventState, event);
}
static int
libxlAutostartDomain(virDomainObjPtr vm,
void *opaque)
{
libxlDriverPrivatePtr driver = opaque;
virErrorPtr err;
int ret = -1;
virObjectLock(vm);
virResetLastError();
if (vm->autostart && !virDomainObjIsActive(vm) &&
libxlVmStart(driver, vm, false, -1) < 0) {
err = virGetLastError();
VIR_ERROR(_("Failed to autostart VM '%s': %s"),
vm->def->name,
err ? err->message : _("unknown error"));
goto cleanup;
}
ret = 0;
cleanup:
virObjectUnlock(vm);
return ret;
}
static int
libxlDoNodeGetInfo(libxlDriverPrivatePtr driver, virNodeInfoPtr info)
{
libxl_physinfo phy_info;
virArch hostarch = virArchFromHost();
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
int ret = -1;
if (libxl_get_physinfo(cfg->ctx, &phy_info)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxl_get_physinfo_info failed"));
goto cleanup;
}
if (virStrcpyStatic(info->model, virArchToString(hostarch)) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("machine type %s too big for destination"),
virArchToString(hostarch));
goto cleanup;
}
info->memory = phy_info.total_pages * (cfg->verInfo->pagesize / 1024);
info->cpus = phy_info.nr_cpus;
info->nodes = phy_info.nr_nodes;
info->cores = phy_info.cores_per_socket;
info->threads = phy_info.threads_per_core;
info->sockets = 1;
info->mhz = phy_info.cpu_khz / 1000;
ret = 0;
cleanup:
virObjectUnref(cfg);
return ret;
}
static char *
libxlDomainManagedSavePath(libxlDriverPrivatePtr driver, virDomainObjPtr vm) {
char *ret;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
ignore_value(virAsprintf(&ret, "%s/%s.save", cfg->saveDir, vm->def->name));
virObjectUnref(cfg);
return ret;
}
/*
* This internal function expects the driver lock to already be held on
* entry.
*/
static int ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5)
libxlSaveImageOpen(libxlDriverPrivatePtr driver,
libxlDriverConfigPtr cfg,
const char *from,
virDomainDefPtr *ret_def,
libxlSavefileHeaderPtr ret_hdr)
{
int fd;
virDomainDefPtr def = NULL;
libxlSavefileHeader hdr;
char *xml = NULL;
util: refactor virFileOpenAs virFileOpenAs previously would only try opening a file as the current user, or as a different user, but wouldn't try both methods in a single call. This made it cumbersome to use as a replacement for open(2). Additionally, it had a lot of historical baggage that led to it being difficult to understand. This patch refactors virFileOpenAs in the following ways: * reorganize the code so that everything dealing with both the parent and child sides of the "fork+setuid+setgid+open" method are in a separate function. This makes the public function easier to understand. * Allow a single call to virFileOpenAs() to first attempt the open as the current user, and if that fails to automatically re-try after doing fork+setuid (if deemed appropriate, i.e. errno indicates it would now be successful, and the file is on a networkFS). This makes it possible (in many, but possibly not all, cases) to drop-in virFileOpenAs() as a replacement for open(2). (NB: currently qemuOpenFile() calls virFileOpenAs() twice, once without forking, then again with forking. That unfortunately can't be changed without at least some discussion of the ramifications, because the requested file permissions are different in each case, which is something that a single call to virFileOpenAs() can't deal with.) * Add a flag so that any fchown() of the file to a different uid:gid is explicitly requested when the function is called, rather than it being implied by the presence of the O_CREAT flag. This just makes for less subtle surprises to consumers. (Commit b1643dc15c5de886fefe56ad18608d65f1325a2c added the check for O_CREAT before forcing ownership. This patch just makes that restriction more explicit.) * If either the uid or gid is specified as "-1", virFileOpenAs will interpret this to mean "the current [gu]id". All current consumers of virFileOpenAs should retain their present behavior (after a few minor changes to their setup code and arguments).
2012-01-13 20:26:45 +00:00
if ((fd = virFileOpenAs(from, O_RDONLY, 0, -1, -1, 0)) < 0) {
util: fix virFileOpenAs return value and resulting error logs This resolves: https://bugzilla.redhat.com/show_bug.cgi?id=851411 https://bugzilla.redhat.com/show_bug.cgi?id=955500 The first problem was that virFileOpenAs was returning fd (-1) in one of the error cases rather than ret (-errno), so the caller thought that the error was EPERM rather than ENOENT. The second problem was that some log messages in the general purpose qemuOpenFile() function would always say "Failed to create" even if the caller hadn't included O_CREAT (i.e. they were trying to open an existing file). This fixes virFileOpenAs to jump down to the error return (which returns ret instead of fd) in the previously mentioned incorrect failure case of virFileOpenAs(), removes all error logging from virFileOpenAs() (since the callers report it), and modifies qemuOpenFile to appropriately use "open" or "create" in its log messages. NB: I seriously considered removing logging from all callers of virFileOpenAs(), but there is at least one case where the caller doesn't want virFileOpenAs() to log any errors, because it's just going to try again (qemuOpenFile()). We can't simply make a silent variation of virFileOpenAs() though, because qemuOpenFile() can't make the decision about whether or not it wants to retry until after virFileOpenAs() has already returned an error code. Likewise, I also considered changing virFileOpenAs() to return -1 with errno set on return, and may still do that, but only as a separate patch, as it obscures the intent of this patch too much.
2013-05-08 19:02:14 +00:00
virReportSystemError(-fd,
_("Failed to open domain image file '%s'"), from);
goto error;
}
if (saferead(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("failed to read libxl header"));
goto error;
}
if (memcmp(hdr.magic, LIBXL_SAVE_MAGIC, sizeof(hdr.magic))) {
virReportError(VIR_ERR_INVALID_ARG, "%s", _("image magic is incorrect"));
goto error;
}
if (hdr.version > LIBXL_SAVE_VERSION) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("image version is not supported (%d > %d)"),
hdr.version, LIBXL_SAVE_VERSION);
goto error;
}
if (hdr.xmlLen <= 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("invalid XML length: %d"), hdr.xmlLen);
goto error;
}
if (VIR_ALLOC_N(xml, hdr.xmlLen) < 0)
goto error;
if (saferead(fd, xml, hdr.xmlLen) != hdr.xmlLen) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to read XML"));
goto error;
}
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
VIR_DOMAIN_XML_INACTIVE)))
goto error;
VIR_FREE(xml);
*ret_def = def;
*ret_hdr = hdr;
return fd;
error:
VIR_FREE(xml);
virDomainDefFree(def);
VIR_FORCE_CLOSE(fd);
return -1;
}
/*
* Cleanup function for domain that has reached shutoff state.
*
* virDomainObjPtr should be locked on invocation
*/
static void
libxlVmCleanup(libxlDriverPrivatePtr driver,
virDomainObjPtr vm,
virDomainShutoffReason reason)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
int vnc_port;
char *file;
size_t i;
vm->def->id = -1;
if (priv->deathW) {
libxl_evdisable_domain_death(priv->ctx, priv->deathW);
priv->deathW = NULL;
}
if (vm->persistent)
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
if (virAtomicIntDecAndTest(&driver->nactive) && driver->inhibitCallback)
driver->inhibitCallback(false, driver->inhibitOpaque);
if ((vm->def->ngraphics == 1) &&
vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
vm->def->graphics[0]->data.vnc.autoport) {
vnc_port = vm->def->graphics[0]->data.vnc.port;
if (vnc_port >= LIBXL_VNC_PORT_MIN) {
if (virPortAllocatorRelease(driver->reservedVNCPorts,
vnc_port) < 0)
VIR_DEBUG("Could not mark port %d as unused", vnc_port);
}
}
/* Remove any cputune settings */
if (vm->def->cputune.nvcpupin) {
for (i = 0; i < vm->def->cputune.nvcpupin; ++i) {
virBitmapFree(vm->def->cputune.vcpupin[i]->cpumask);
VIR_FREE(vm->def->cputune.vcpupin[i]);
}
VIR_FREE(vm->def->cputune.vcpupin);
vm->def->cputune.nvcpupin = 0;
}
if (virAsprintf(&file, "%s/%s.xml", cfg->stateDir, vm->def->name) > 0) {
if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
VIR_DEBUG("Failed to remove domain XML for %s", vm->def->name);
VIR_FREE(file);
}
if (vm->newDef) {
virDomainDefFree(vm->def);
vm->def = vm->newDef;
vm->def->id = -1;
vm->newDef = NULL;
}
virObjectUnref(cfg);
}
/*
* Cleanup function for domain that has reached shutoff state.
* Executed in the context of a job.
*
* virDomainObjPtr should be locked on invocation
* Returns true if references remain on virDomainObjPtr, false otherwise.
*/
static bool
libxlVmCleanupJob(libxlDriverPrivatePtr driver,
virDomainObjPtr vm,
virDomainShutoffReason reason)
{
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_DESTROY) < 0)
return true;
libxlVmCleanup(driver, vm, reason);
return libxlDomainObjEndJob(driver, vm);
}
/*
* Handle previously registered event notification from libxenlight.
*
* Note: Xen 4.3 removed the const from the event handler signature.
* Detect which signature to use based on
* LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG.
*/
#ifdef LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG
# define VIR_LIBXL_EVENT_CONST /* empty */
#else
# define VIR_LIBXL_EVENT_CONST const
#endif
struct libxlShutdownThreadInfo
{
virDomainObjPtr vm;
libxl_event *event;
};
static void
libxlDomainShutdownThread(void *opaque)
{
libxlDriverPrivatePtr driver = libxl_driver;
struct libxlShutdownThreadInfo *shutdown_info = opaque;
virDomainObjPtr vm = shutdown_info->vm;
libxlDomainObjPrivatePtr priv = vm->privateData;
libxl_event *ev = shutdown_info->event;
libxl_ctx *ctx = priv->ctx;
virObjectEventPtr dom_event = NULL;
libxl_shutdown_reason xl_reason = ev->u.domain_shutdown.shutdown_reason;
virDomainShutoffReason reason;
virObjectLock(vm);
switch (xl_reason) {
case LIBXL_SHUTDOWN_REASON_POWEROFF:
case LIBXL_SHUTDOWN_REASON_CRASH:
if (xl_reason == LIBXL_SHUTDOWN_REASON_CRASH) {
dom_event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_CRASHED);
reason = VIR_DOMAIN_SHUTOFF_CRASHED;
} else {
reason = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
}
libxl_domain_destroy(ctx, vm->def->id, NULL);
if (libxlVmCleanupJob(driver, vm, reason)) {
if (!vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
}
break;
case LIBXL_SHUTDOWN_REASON_REBOOT:
libxl_domain_destroy(ctx, vm->def->id, NULL);
libxlVmCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
libxlVmStart(driver, vm, 0, -1);
break;
default:
VIR_INFO("Unhandled shutdown_reason %d", xl_reason);
break;
}
if (vm)
virObjectUnlock(vm);
if (dom_event)
libxlDomainEventQueue(driver, dom_event);
libxl_event_free(ctx, ev);
VIR_FREE(shutdown_info);
}
static void
libxlEventHandler(void *data, VIR_LIBXL_EVENT_CONST libxl_event *event)
{
virDomainObjPtr vm = data;
libxlDomainObjPrivatePtr priv = vm->privateData;
libxl_shutdown_reason xl_reason = event->u.domain_shutdown.shutdown_reason;
struct libxlShutdownThreadInfo *shutdown_info;
virThread thread;
if (event->type != LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN) {
VIR_INFO("Unhandled event type %d", event->type);
goto error;
}
/*
* Similar to the xl implementation, ignore SUSPEND. Any actions needed
* after calling libxl_domain_suspend() are handled by it's callers.
*/
if (xl_reason == LIBXL_SHUTDOWN_REASON_SUSPEND)
goto error;
/*
* Start a thread to handle shutdown. We don't want to be tying up
* libxl's event machinery by doing a potentially lengthy shutdown.
*/
if (VIR_ALLOC(shutdown_info) < 0)
goto error;
shutdown_info->vm = data;
shutdown_info->event = (libxl_event *)event;
if (virThreadCreate(&thread, false, libxlDomainShutdownThread,
shutdown_info) < 0) {
/*
* Not much we can do on error here except log it.
*/
VIR_ERROR(_("Failed to create thread to handle domain shutdown"));
goto error;
}
/*
* libxl_event freed in shutdown thread
*/
return;
error:
/* Cast away any const */
libxl_event_free(priv->ctx, (libxl_event *)event);
}
static const struct libxl_event_hooks ev_hooks = {
.event_occurs_mask = LIBXL_EVENTMASK_ALL,
.event_occurs = libxlEventHandler,
.disaster = NULL,
};
/*
* Register for domain events emitted by libxl.
*/
static int
libxlDomEventsRegister(virDomainObjPtr vm)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
libxl_event_register_callbacks(priv->ctx, &ev_hooks, vm);
/* Always enable domain death events */
if (libxl_evenable_domain_death(priv->ctx, vm->def->id, 0, &priv->deathW))
goto error;
return 0;
error:
if (priv->deathW) {
libxl_evdisable_domain_death(priv->ctx, priv->deathW);
priv->deathW = NULL;
}
return -1;
}
static int
libxlDomainSetVcpuAffinities(libxlDriverPrivatePtr driver, virDomainObjPtr vm)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
virDomainDefPtr def = vm->def;
libxl_bitmap map;
virBitmapPtr cpumask = NULL;
uint8_t *cpumap = NULL;
virNodeInfo nodeinfo;
size_t cpumaplen;
int vcpu;
size_t i;
int ret = -1;
if (libxlDoNodeGetInfo(driver, &nodeinfo) < 0)
goto cleanup;
cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo));
for (vcpu = 0; vcpu < def->cputune.nvcpupin; ++vcpu) {
if (vcpu != def->cputune.vcpupin[vcpu]->vcpuid)
continue;
if (VIR_ALLOC_N(cpumap, cpumaplen) < 0)
goto cleanup;
cpumask = def->cputune.vcpupin[vcpu]->cpumask;
for (i = 0; i < virBitmapSize(cpumask); ++i) {
bool bit;
ignore_value(virBitmapGetBit(cpumask, i, &bit));
if (bit)
2011-06-07 08:51:51 +00:00
VIR_USE_CPU(cpumap, i);
}
map.size = cpumaplen;
map.map = cpumap;
if (libxl_set_vcpuaffinity(priv->ctx, def->id, vcpu, &map) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to pin vcpu '%d' with libxenlight"), vcpu);
goto cleanup;
}
VIR_FREE(cpumap);
}
ret = 0;
cleanup:
VIR_FREE(cpumap);
return ret;
}
static int
libxlFreeMem(libxlDomainObjPrivatePtr priv, libxl_domain_config *d_config)
{
uint32_t needed_mem;
uint32_t free_mem;
size_t i;
int ret = -1;
int tries = 3;
int wait_secs = 10;
if ((ret = libxl_domain_need_memory(priv->ctx, &d_config->b_info,
&needed_mem)) >= 0) {
for (i = 0; i < tries; ++i) {
if ((ret = libxl_get_free_memory(priv->ctx, &free_mem)) < 0)
break;
if (free_mem >= needed_mem) {
ret = 0;
break;
}
if ((ret = libxl_set_memory_target(priv->ctx, 0,
free_mem - needed_mem,
/* relative */ 1, 0)) < 0)
break;
ret = libxl_wait_for_free_memory(priv->ctx, 0, needed_mem,
wait_secs);
if (ret == 0 || ret != ERROR_NOMEM)
break;
if ((ret = libxl_wait_for_memory_target(priv->ctx, 0, 1)) < 0)
break;
}
}
return ret;
}
/*
* Start a domain through libxenlight.
*
* virDomainObjPtr should be locked on invocation
*/
static int
libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm,
bool start_paused, int restore_fd)
{
libxl_domain_config d_config;
virDomainDefPtr def = NULL;
virObjectEventPtr event = NULL;
libxlSavefileHeader hdr;
int ret = -1;
uint32_t domid = 0;
char *dom_xml = NULL;
char *managed_save_path = NULL;
int managed_save_fd = -1;
libxlDomainObjPrivatePtr priv = vm->privateData;
libxlDriverConfigPtr cfg;
#ifdef LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS
libxl_domain_restore_params params;
#endif
if (libxlDomainObjPrivateInitCtx(vm) < 0)
return ret;
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
return ret;
cfg = libxlDriverConfigGet(driver);
/* If there is a managed saved state restore it instead of starting
* from scratch. The old state is removed once the restoring succeeded. */
if (restore_fd < 0) {
managed_save_path = libxlDomainManagedSavePath(driver, vm);
if (managed_save_path == NULL)
goto endjob;
if (virFileExists(managed_save_path)) {
managed_save_fd = libxlSaveImageOpen(driver, cfg,
managed_save_path,
&def, &hdr);
if (managed_save_fd < 0)
goto endjob;
restore_fd = managed_save_fd;
if (STRNEQ(vm->def->name, def->name) ||
memcmp(vm->def->uuid, def->uuid, VIR_UUID_BUFLEN)) {
char vm_uuidstr[VIR_UUID_STRING_BUFLEN];
char def_uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(vm->def->uuid, vm_uuidstr);
virUUIDFormat(def->uuid, def_uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("cannot restore domain '%s' uuid %s from a file"
" which belongs to domain '%s' uuid %s"),
vm->def->name, vm_uuidstr, def->name, def_uuidstr);
goto endjob;
}
virDomainObjAssignDef(vm, def, true, NULL);
def = NULL;
if (unlink(managed_save_path) < 0)
VIR_WARN("Failed to remove the managed state %s",
managed_save_path);
vm->hasManagedSave = false;
}
VIR_FREE(managed_save_path);
}
libxl_domain_config_init(&d_config);
if (libxlBuildDomainConfig(driver, vm, &d_config) < 0)
goto endjob;
if (cfg->autoballoon && libxlFreeMem(priv, &d_config) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to get free memory for domain '%s'"),
d_config.c_info.name);
goto endjob;
}
/* Unlock virDomainObj while creating the domain */
virObjectUnlock(vm);
if (restore_fd < 0) {
ret = libxl_domain_create_new(priv->ctx, &d_config,
&domid, NULL, NULL);
} else {
#ifdef LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS
params.checkpointed_stream = 0;
ret = libxl_domain_create_restore(priv->ctx, &d_config, &domid,
restore_fd, &params, NULL, NULL);
#else
ret = libxl_domain_create_restore(priv->ctx, &d_config, &domid,
restore_fd, NULL, NULL);
#endif
}
virObjectLock(vm);
if (ret) {
if (restore_fd < 0)
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to create new domain '%s'"),
d_config.c_info.name);
else
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to restore domain '%s'"),
d_config.c_info.name);
goto endjob;
}
/*
* The domain has been successfully created with libxl, so it should
* be cleaned up if there are any subsequent failures.
*/
vm->def->id = domid;
if (libxlDomEventsRegister(vm) < 0)
goto cleanup_dom;
if ((dom_xml = virDomainDefFormat(vm->def, 0)) == NULL)
goto cleanup_dom;
if (libxl_userdata_store(priv->ctx, domid, "libvirt-xml",
(uint8_t *)dom_xml, strlen(dom_xml) + 1)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxenlight failed to store userdata"));
goto cleanup_dom;
}
if (libxlDomainSetVcpuAffinities(driver, vm) < 0)
goto cleanup_dom;
if (!start_paused) {
libxl_domain_unpause(priv->ctx, domid);
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
} else {
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
}
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
goto cleanup_dom;
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
driver->inhibitCallback(true, driver->inhibitOpaque);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED,
restore_fd < 0 ?
VIR_DOMAIN_EVENT_STARTED_BOOTED :
VIR_DOMAIN_EVENT_STARTED_RESTORED);
if (event)
libxlDomainEventQueue(driver, event);
ret = 0;
goto endjob;
cleanup_dom:
libxl_domain_destroy(priv->ctx, domid, NULL);
vm->def->id = -1;
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_FAILED);
endjob:
if (!libxlDomainObjEndJob(driver, vm))
vm = NULL;
libxl_domain_config_dispose(&d_config);
VIR_FREE(dom_xml);
VIR_FREE(managed_save_path);
virDomainDefFree(def);
VIR_FORCE_CLOSE(managed_save_fd);
virObjectUnref(cfg);
return ret;
}
/*
* Reconnect to running domains that were previously started/created
* with libxenlight driver.
*/
static int
libxlReconnectDomain(virDomainObjPtr vm,
void *opaque)
{
libxlDriverPrivatePtr driver = opaque;
libxlDomainObjPrivatePtr priv = vm->privateData;
int rc;
libxl_dominfo d_info;
int len;
uint8_t *data = NULL;
virObjectLock(vm);
libxlDomainObjPrivateInitCtx(vm);
/* Does domain still exist? */
rc = libxl_domain_info(priv->ctx, &d_info, vm->def->id);
if (rc == ERROR_INVAL) {
goto out;
} else if (rc != 0) {
VIR_DEBUG("libxl_domain_info failed (code %d), ignoring domain %d",
rc, vm->def->id);
goto out;
}
/* Is this a domain that was under libvirt control? */
if (libxl_userdata_retrieve(priv->ctx, vm->def->id,
"libvirt-xml", &data, &len)) {
VIR_DEBUG("libxl_userdata_retrieve failed, ignoring domain %d", vm->def->id);
goto out;
}
/* Update domid in case it changed (e.g. reboot) while we were gone? */
vm->def->id = d_info.domid;
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNKNOWN);
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
driver->inhibitCallback(true, driver->inhibitOpaque);
/* Re-register domain death et. al. events */
libxlDomEventsRegister(vm);
virObjectUnlock(vm);
return 0;
out:
libxlVmCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_UNKNOWN);
if (!vm->persistent)
virDomainObjListRemoveLocked(driver->domains, vm);
else
virObjectUnlock(vm);
return -1;
}
static void
libxlReconnectDomains(libxlDriverPrivatePtr driver)
{
virDomainObjListForEach(driver->domains, libxlReconnectDomain, driver);
}
static int
libxlStateCleanup(void)
{
if (!libxl_driver)
return -1;
virObjectUnref(libxl_driver->config);
virObjectUnref(libxl_driver->xmlopt);
virObjectUnref(libxl_driver->domains);
virObjectUnref(libxl_driver->reservedVNCPorts);
virObjectEventStateFree(libxl_driver->domainEventState);
virSysinfoDefFree(libxl_driver->hostsysinfo);
virMutexDestroy(&libxl_driver->lock);
VIR_FREE(libxl_driver);
return 0;
}
static bool
libxlDriverShouldLoad(bool privileged)
{
bool ret = false;
virCommandPtr cmd;
int status;
/* Don't load if non-root */
if (!privileged) {
VIR_INFO("Not running privileged, disabling libxenlight driver");
return ret;
}
/* Don't load if not running on a Xen control domain (dom0) */
if (!virFileExists("/proc/xen/capabilities")) {
VIR_INFO("No Xen capabilities detected, probably not running "
"in a Xen Dom0. Disabling libxenlight driver");
return ret;
}
/* Don't load if legacy xen toolstack (xend) is in use */
cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL);
if (virCommandRun(cmd, &status) == 0 && status == 0) {
VIR_INFO("Legacy xen tool stack seems to be in use, disabling "
"libxenlight driver.");
} else {
ret = true;
}
virCommandFree(cmd);
return ret;
}
static int
libxlStateInitialize(bool privileged,
virStateInhibitCallback callback ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
libxlDriverConfigPtr cfg;
char ebuf[1024];
if (!libxlDriverShouldLoad(privileged))
return 0;
if (VIR_ALLOC(libxl_driver) < 0)
return -1;
if (virMutexInit(&libxl_driver->lock) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot initialize mutex"));
VIR_FREE(libxl_driver);
return -1;
}
/* Allocate bitmap for vnc port reservation */
if (!(libxl_driver->reservedVNCPorts =
virPortAllocatorNew(_("VNC"),
LIBXL_VNC_PORT_MIN,
LIBXL_VNC_PORT_MAX)))
goto error;
if (!(libxl_driver->domains = virDomainObjListNew()))
goto error;
if (!(cfg = libxlDriverConfigNew()))
goto error;
libxl_driver->config = cfg;
if (virFileMakePath(cfg->logDir) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to create log dir '%s': %s"),
cfg->logDir,
virStrerror(errno, ebuf, sizeof(ebuf)));
goto error;
}
if (virFileMakePath(cfg->stateDir) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to create state dir '%s': %s"),
cfg->stateDir,
virStrerror(errno, ebuf, sizeof(ebuf)));
goto error;
}
if (virFileMakePath(cfg->libDir) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to create lib dir '%s': %s"),
cfg->libDir,
virStrerror(errno, ebuf, sizeof(ebuf)));
goto error;
}
if (virFileMakePath(cfg->saveDir) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to create save dir '%s': %s"),
cfg->saveDir,
virStrerror(errno, ebuf, sizeof(ebuf)));
goto error;
}
/* read the host sysinfo */
libxl_driver->hostsysinfo = virSysinfoRead();
libxl_driver->domainEventState = virObjectEventStateNew();
if (!libxl_driver->domainEventState)
goto error;
if ((cfg->caps = libxlMakeCapabilities(cfg->ctx)) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot create capabilities for libxenlight"));
goto error;
}
if (!(libxl_driver->xmlopt = virDomainXMLOptionNew(&libxlDomainDefParserConfig,
&libxlDomainXMLPrivateDataCallbacks,
NULL)))
goto error;
/* Load running domains first. */
if (virDomainObjListLoadAllConfigs(libxl_driver->domains,
cfg->stateDir,
cfg->autostartDir,
1,
cfg->caps,
libxl_driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
NULL, NULL) < 0)
goto error;
libxlReconnectDomains(libxl_driver);
/* Then inactive persistent configs */
if (virDomainObjListLoadAllConfigs(libxl_driver->domains,
cfg->configDir,
cfg->autostartDir,
0,
cfg->caps,
libxl_driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
NULL, NULL) < 0)
goto error;
virDomainObjListForEach(libxl_driver->domains, libxlDomainManagedSaveLoad,
libxl_driver);
return 0;
error:
libxlStateCleanup();
return -1;
}
static void
libxlStateAutoStart(void)
{
if (!libxl_driver)
return;
virDomainObjListForEach(libxl_driver->domains, libxlAutostartDomain,
libxl_driver);
}
static int
libxlStateReload(void)
{
libxlDriverConfigPtr cfg;
if (!libxl_driver)
return 0;
cfg = libxlDriverConfigGet(libxl_driver);
virDomainObjListLoadAllConfigs(libxl_driver->domains,
cfg->configDir,
cfg->autostartDir,
1,
cfg->caps,
libxl_driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
NULL, libxl_driver);
virDomainObjListForEach(libxl_driver->domains, libxlAutostartDomain,
libxl_driver);
virObjectUnref(cfg);
return 0;
}
static virDrvOpenStatus
libxlConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
unsigned int flags)
{
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (conn->uri == NULL) {
if (libxl_driver == NULL)
return VIR_DRV_OPEN_DECLINED;
if (!(conn->uri = virURIParse("xen:///")))
return VIR_DRV_OPEN_ERROR;
} else {
/* Only xen scheme */
if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "xen"))
return VIR_DRV_OPEN_DECLINED;
/* If server name is given, its for remote driver */
if (conn->uri->server != NULL)
return VIR_DRV_OPEN_DECLINED;
/* Error if xen or libxl scheme specified but driver not started. */
if (libxl_driver == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxenlight state driver is not active"));
return VIR_DRV_OPEN_ERROR;
}
/* /session isn't supported in libxenlight */
if (conn->uri->path &&
STRNEQ(conn->uri->path, "") &&
STRNEQ(conn->uri->path, "/") &&
STRNEQ(conn->uri->path, "/system")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected Xen URI path '%s', try xen:///"),
NULLSTR(conn->uri->path));
return VIR_DRV_OPEN_ERROR;
}
}
if (virConnectOpenEnsureACL(conn) < 0)
return VIR_DRV_OPEN_ERROR;
conn->privateData = libxl_driver;
return VIR_DRV_OPEN_SUCCESS;
};
static int
libxlConnectClose(virConnectPtr conn ATTRIBUTE_UNUSED)
{
conn->privateData = NULL;
return 0;
}
static const char *
libxlConnectGetType(virConnectPtr conn)
{
if (virConnectGetTypeEnsureACL(conn) < 0)
return NULL;
return "Xen";
}
static int
libxlConnectGetVersion(virConnectPtr conn, unsigned long *version)
{
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg;
if (virConnectGetVersionEnsureACL(conn) < 0)
return 0;
cfg = libxlDriverConfigGet(driver);
*version = cfg->version;
virObjectUnref(cfg);
return 0;
}
static char *libxlConnectGetHostname(virConnectPtr conn)
{
if (virConnectGetHostnameEnsureACL(conn) < 0)
return NULL;
return virGetHostname();
}
static char *
libxlConnectGetSysinfo(virConnectPtr conn, unsigned int flags)
{
libxlDriverPrivatePtr driver = conn->privateData;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCheckFlags(0, NULL);
if (virConnectGetSysinfoEnsureACL(conn) < 0)
return NULL;
if (!driver->hostsysinfo) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Host SMBIOS information is not available"));
return NULL;
}
if (virSysinfoFormat(&buf, driver->hostsysinfo) < 0)
return NULL;
if (virBufferError(&buf)) {
virReportOOMError();
return NULL;
}
return virBufferContentAndReset(&buf);
}
static int
libxlConnectGetMaxVcpus(virConnectPtr conn, const char *type ATTRIBUTE_UNUSED)
{
int ret;
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg;
if (virConnectGetMaxVcpusEnsureACL(conn) < 0)
return -1;
cfg = libxlDriverConfigGet(driver);
ret = libxl_get_max_cpus(cfg->ctx);
/* On failure, libxl_get_max_cpus() will return ERROR_FAIL from Xen 4.4
* onward, but it ever returning 0 is obviously wrong too (and it is
* what happens, on failure, on Xen 4.3 and earlier). Therefore, a 'less
* or equal' is the catchall we want. */
if (ret <= 0)
ret = -1;
virObjectUnref(cfg);
return ret;
}
static int
libxlNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
{
if (virNodeGetInfoEnsureACL(conn) < 0)
return -1;
return libxlDoNodeGetInfo(conn->privateData, info);
}
static char *
libxlConnectGetCapabilities(virConnectPtr conn)
{
libxlDriverPrivatePtr driver = conn->privateData;
char *xml;
libxlDriverConfigPtr cfg;
if (virConnectGetCapabilitiesEnsureACL(conn) < 0)
return NULL;
cfg = libxlDriverConfigGet(driver);
if ((xml = virCapabilitiesFormatXML(cfg->caps)) == NULL)
virReportOOMError();
virObjectUnref(cfg);
return xml;
}
static int
libxlConnectListDomains(virConnectPtr conn, int *ids, int nids)
{
libxlDriverPrivatePtr driver = conn->privateData;
int n;
if (virConnectListDomainsEnsureACL(conn) < 0)
return -1;
n = virDomainObjListGetActiveIDs(driver->domains, ids, nids,
virConnectListDomainsCheckACL, conn);
return n;
}
static int
libxlConnectNumOfDomains(virConnectPtr conn)
{
libxlDriverPrivatePtr driver = conn->privateData;
int n;
if (virConnectNumOfDomainsEnsureACL(conn) < 0)
return -1;
n = virDomainObjListNumOfDomains(driver->domains, true,
virConnectNumOfDomainsCheckACL, conn);
return n;
}
static virDomainPtr
libxlDomainCreateXML(virConnectPtr conn, const char *xml,
unsigned int flags)
{
libxlDriverPrivatePtr driver = conn->privateData;
virDomainDefPtr def;
virDomainObjPtr vm = NULL;
virDomainPtr dom = NULL;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virCheckFlags(VIR_DOMAIN_START_PAUSED, NULL);
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
if (virDomainCreateXMLEnsureACL(conn, def) < 0)
goto cleanup;
if (!(vm = virDomainObjListAdd(driver->domains, def,
driver->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
NULL)))
goto cleanup;
def = NULL;
if (libxlVmStart(driver, vm, (flags & VIR_DOMAIN_START_PAUSED) != 0,
-1) < 0) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
goto cleanup;
}
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
cleanup:
virDomainDefFree(def);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return dom;
}
static virDomainPtr
libxlDomainLookupByID(virConnectPtr conn, int id)
{
libxlDriverPrivatePtr driver = conn->privateData;
virDomainObjPtr vm;
virDomainPtr dom = NULL;
vm = virDomainObjListFindByID(driver->domains, id);
if (!vm) {
virReportError(VIR_ERR_NO_DOMAIN, NULL);
goto cleanup;
}
if (virDomainLookupByIDEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
cleanup:
if (vm)
virObjectUnlock(vm);
return dom;
}
static virDomainPtr
libxlDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
{
libxlDriverPrivatePtr driver = conn->privateData;
virDomainObjPtr vm;
virDomainPtr dom = NULL;
vm = virDomainObjListFindByUUID(driver->domains, uuid);
if (!vm) {
virReportError(VIR_ERR_NO_DOMAIN, NULL);
goto cleanup;
}
if (virDomainLookupByUUIDEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
cleanup:
if (vm)
virObjectUnlock(vm);
return dom;
}
static virDomainPtr
libxlDomainLookupByName(virConnectPtr conn, const char *name)
{
libxlDriverPrivatePtr driver = conn->privateData;
virDomainObjPtr vm;
virDomainPtr dom = NULL;
vm = virDomainObjListFindByName(driver->domains, name);
if (!vm) {
virReportError(VIR_ERR_NO_DOMAIN, NULL);
goto cleanup;
}
if (virDomainLookupByNameEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
cleanup:
if (vm)
virObjectUnlock(vm);
return dom;
}
static int
libxlDomainSuspend(virDomainPtr dom)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm;
libxlDomainObjPrivatePtr priv;
virObjectEventPtr event = NULL;
int ret = -1;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainSuspendEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto endjob;
}
priv = vm->privateData;
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
if (libxl_domain_pause(priv->ctx, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to suspend domain '%d' with libxenlight"),
dom->id);
goto endjob;
}
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED,
VIR_DOMAIN_EVENT_SUSPENDED_PAUSED);
}
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
goto endjob;
ret = 0;
endjob:
if (!libxlDomainObjEndJob(driver, vm))
vm = NULL;
cleanup:
if (vm)
virObjectUnlock(vm);
if (event)
libxlDomainEventQueue(driver, event);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainResume(virDomainPtr dom)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm;
libxlDomainObjPrivatePtr priv;
virObjectEventPtr event = NULL;
int ret = -1;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainResumeEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto endjob;
}
priv = vm->privateData;
if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) {
if (libxl_domain_unpause(priv->ctx, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to resume domain '%d' with libxenlight"),
dom->id);
goto endjob;
}
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
VIR_DOMAIN_RUNNING_UNPAUSED);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_RESUMED,
VIR_DOMAIN_EVENT_RESUMED_UNPAUSED);
}
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
goto endjob;
ret = 0;
endjob:
if (!libxlDomainObjEndJob(driver, vm))
vm = NULL;
cleanup:
if (vm)
virObjectUnlock(vm);
if (event)
libxlDomainEventQueue(driver, event);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
int ret = -1;
libxlDomainObjPrivatePtr priv;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
api: require write permission for guest agent interaction I noticed that we allow virDomainGetVcpusFlags even for read-only connections, but that with a flag, it can require guest agent interaction. It is feasible that a malicious guest could intentionally abuse the replies it sends over the guest agent connection to possibly trigger a bug in libvirt's JSON parser, or withhold an answer so as to prevent the use of the agent in a later command such as a shutdown request. Although we don't know of any such exploits now (and therefore don't mind posting this patch publicly without trying to get a CVE assigned), it is better to err on the side of caution and explicitly require full access to any domain where the API requires guest interaction to operate correctly. I audited all commands that are marked as conditionally using a guest agent. Note that at least virDomainFSTrim is documented as needing a guest agent, but that such use is unconditional depending on the hypervisor (so the existing domain:fs_trim ACL should be sufficient there, rather than also requirng domain:write). But when designing future APIs, such as the plans for obtaining a domain's IP addresses, we should copy the approach of this patch in making interaction with the guest be specified via a flag, and use that flag to also require stricter access checks. * src/libvirt.c (virDomainGetVcpusFlags): Forbid guest interaction on read-only connection. (virDomainShutdownFlags, virDomainReboot): Improve docs on agent interaction. * src/remote/remote_protocol.x (REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML) (REMOTE_PROC_DOMAIN_SET_VCPUS_FLAGS) (REMOTE_PROC_DOMAIN_GET_VCPUS_FLAGS, REMOTE_PROC_DOMAIN_REBOOT) (REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS): Require domain:write for any conditional use of a guest agent. * src/xen/xen_driver.c: Fix clients. * src/libxl/libxl_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-21 17:37:29 +00:00
if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
if (libxl_domain_shutdown(priv->ctx, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to shutdown domain '%d' with libxenlight"),
dom->id);
goto cleanup;
}
/* vm is marked shutoff (or removed from domains list if not persistent)
* in shutdown event handler.
*/
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainShutdown(virDomainPtr dom)
{
return libxlDomainShutdownFlags(dom, 0);
}
static int
libxlDomainReboot(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
int ret = -1;
libxlDomainObjPrivatePtr priv;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
api: require write permission for guest agent interaction I noticed that we allow virDomainGetVcpusFlags even for read-only connections, but that with a flag, it can require guest agent interaction. It is feasible that a malicious guest could intentionally abuse the replies it sends over the guest agent connection to possibly trigger a bug in libvirt's JSON parser, or withhold an answer so as to prevent the use of the agent in a later command such as a shutdown request. Although we don't know of any such exploits now (and therefore don't mind posting this patch publicly without trying to get a CVE assigned), it is better to err on the side of caution and explicitly require full access to any domain where the API requires guest interaction to operate correctly. I audited all commands that are marked as conditionally using a guest agent. Note that at least virDomainFSTrim is documented as needing a guest agent, but that such use is unconditional depending on the hypervisor (so the existing domain:fs_trim ACL should be sufficient there, rather than also requirng domain:write). But when designing future APIs, such as the plans for obtaining a domain's IP addresses, we should copy the approach of this patch in making interaction with the guest be specified via a flag, and use that flag to also require stricter access checks. * src/libvirt.c (virDomainGetVcpusFlags): Forbid guest interaction on read-only connection. (virDomainShutdownFlags, virDomainReboot): Improve docs on agent interaction. * src/remote/remote_protocol.x (REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML) (REMOTE_PROC_DOMAIN_SET_VCPUS_FLAGS) (REMOTE_PROC_DOMAIN_GET_VCPUS_FLAGS, REMOTE_PROC_DOMAIN_REBOOT) (REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS): Require domain:write for any conditional use of a guest agent. * src/xen/xen_driver.c: Fix clients. * src/libxl/libxl_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-21 17:37:29 +00:00
if (virDomainRebootEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
if (libxl_domain_reboot(priv->ctx, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to reboot domain '%d' with libxenlight"),
dom->id);
goto cleanup;
}
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainDestroyFlags(virDomainPtr dom,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
virObjectEventPtr event = NULL;
libxlDomainObjPrivatePtr priv;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainDestroyFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
priv = vm->privateData;
if (libxl_domain_destroy(priv->ctx, vm->def->id, NULL) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to destroy domain '%d'"), dom->id);
goto cleanup;
}
if (libxlVmCleanupJob(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED)) {
if (!vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
}
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
if (event)
libxlDomainEventQueue(driver, event);
return ret;
}
static int
libxlDomainDestroy(virDomainPtr dom)
{
return libxlDomainDestroyFlags(dom, 0);
}
static char *
libxlDomainGetOSType(virDomainPtr dom)
{
virDomainObjPtr vm;
char *type = NULL;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetOSTypeEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (VIR_STRDUP(type, vm->def->os.type) < 0)
goto cleanup;
cleanup:
if (vm)
virObjectUnlock(vm);
return type;
}
xml: use long long internally, to centralize overflow checks On 64-bit platforms, unsigned long and unsigned long long are identical, so we don't have to worry about overflow checks. On 32-bit platforms, anywhere we narrow unsigned long long back to unsigned long, we have to worry about overflow; it's easier to do this in one place by having most of the code use the same or wider types, and only doing the narrowing at the last minute. Therefore, the memory set commands remain unsigned long, and the memory get command now centralizes the overflow check into libvirt.c, so that drivers don't have to repeat the work. This also fixes a bug where xen returned the wrong value on failure (most APIs return -1 on failure, but getMaxMemory must return 0 on failure). * src/driver.h (virDrvDomainGetMaxMemory): Use long long. * src/libvirt.c (virDomainGetMaxMemory): Raise overflow. * src/test/test_driver.c (testGetMaxMemory): Fix driver. * src/rpc/gendispatch.pl (name_to_ProcName): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorGetMaxMemory): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainGetMaxMemory): Likewise. * src/xen/xend_internal.c (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xend_internal.h (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xm_internal.c (xenXMDomainGetMaxMemory): Likewise. * src/xen/xm_internal.h (xenXMDomainGetMaxMemory): Likewise. * src/xen/xs_internal.c (xenStoreDomainGetMaxMemory): Likewise. * src/xen/xs_internal.h (xenStoreDomainGetMaxMemory): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainGetMaxMemory): Likewise. * src/esx/esx_driver.c (esxDomainGetMaxMemory): Likewise. * src/libxl/libxl_driver.c (libxlDomainGetMaxMemory): Likewise. * src/qemu/qemu_driver.c (qemudDomainGetMaxMemory): Likewise. * src/lxc/lxc_driver.c (lxcDomainGetMaxMemory): Likewise. * src/uml/uml_driver.c (umlDomainGetMaxMemory): Likewise.
2012-03-03 00:47:16 +00:00
static unsigned long long
2011-03-21 09:35:53 +00:00
libxlDomainGetMaxMemory(virDomainPtr dom)
{
virDomainObjPtr vm;
xml: use long long internally, to centralize overflow checks On 64-bit platforms, unsigned long and unsigned long long are identical, so we don't have to worry about overflow checks. On 32-bit platforms, anywhere we narrow unsigned long long back to unsigned long, we have to worry about overflow; it's easier to do this in one place by having most of the code use the same or wider types, and only doing the narrowing at the last minute. Therefore, the memory set commands remain unsigned long, and the memory get command now centralizes the overflow check into libvirt.c, so that drivers don't have to repeat the work. This also fixes a bug where xen returned the wrong value on failure (most APIs return -1 on failure, but getMaxMemory must return 0 on failure). * src/driver.h (virDrvDomainGetMaxMemory): Use long long. * src/libvirt.c (virDomainGetMaxMemory): Raise overflow. * src/test/test_driver.c (testGetMaxMemory): Fix driver. * src/rpc/gendispatch.pl (name_to_ProcName): Likewise. * src/xen/xen_hypervisor.c (xenHypervisorGetMaxMemory): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainGetMaxMemory): Likewise. * src/xen/xend_internal.c (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xend_internal.h (xenDaemonDomainGetMaxMemory): Likewise. * src/xen/xm_internal.c (xenXMDomainGetMaxMemory): Likewise. * src/xen/xm_internal.h (xenXMDomainGetMaxMemory): Likewise. * src/xen/xs_internal.c (xenStoreDomainGetMaxMemory): Likewise. * src/xen/xs_internal.h (xenStoreDomainGetMaxMemory): Likewise. * src/xenapi/xenapi_driver.c (xenapiDomainGetMaxMemory): Likewise. * src/esx/esx_driver.c (esxDomainGetMaxMemory): Likewise. * src/libxl/libxl_driver.c (libxlDomainGetMaxMemory): Likewise. * src/qemu/qemu_driver.c (qemudDomainGetMaxMemory): Likewise. * src/lxc/lxc_driver.c (lxcDomainGetMaxMemory): Likewise. * src/uml/uml_driver.c (umlDomainGetMaxMemory): Likewise.
2012-03-03 00:47:16 +00:00
unsigned long long ret = 0;
2011-03-21 09:35:53 +00:00
if (!(vm = libxlDomObjFromDomain(dom)))
2011-03-21 09:35:53 +00:00
goto cleanup;
if (virDomainGetMaxMemoryEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
2011-03-21 09:35:53 +00:00
ret = vm->def->mem.max_balloon;
cleanup:
if (vm)
virObjectUnlock(vm);
2011-03-21 09:35:53 +00:00
return ret;
}
static int
libxlDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem,
2011-03-21 09:35:53 +00:00
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
2011-03-21 09:35:53 +00:00
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
virDomainDefPtr persistentDef = NULL;
bool isActive;
2011-03-21 09:35:53 +00:00
int ret = -1;
virCheckFlags(VIR_DOMAIN_MEM_LIVE |
VIR_DOMAIN_MEM_CONFIG |
VIR_DOMAIN_MEM_MAXIMUM, -1);
2011-03-21 09:35:53 +00:00
if (!(vm = libxlDomObjFromDomain(dom)))
2011-03-21 09:35:53 +00:00
goto cleanup;
if (virDomainSetMemoryFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
goto cleanup;
isActive = virDomainObjIsActive(vm);
if (flags == VIR_DOMAIN_MEM_CURRENT) {
if (isActive)
flags = VIR_DOMAIN_MEM_LIVE;
else
flags = VIR_DOMAIN_MEM_CONFIG;
}
if (flags == VIR_DOMAIN_MEM_MAXIMUM) {
if (isActive)
flags = VIR_DOMAIN_MEM_LIVE | VIR_DOMAIN_MEM_MAXIMUM;
else
flags = VIR_DOMAIN_MEM_CONFIG | VIR_DOMAIN_MEM_MAXIMUM;
2011-03-21 09:35:53 +00:00
}
if (!isActive && (flags & VIR_DOMAIN_MEM_LIVE)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot set memory on an inactive domain"));
goto endjob;
2011-03-21 09:35:53 +00:00
}
if (flags & VIR_DOMAIN_MEM_CONFIG) {
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot change persistent config of a transient domain"));
goto endjob;
2011-03-21 09:35:53 +00:00
}
if (!(persistentDef = virDomainObjGetPersistentDef(cfg->caps,
driver->xmlopt,
vm)))
goto endjob;
2011-03-21 09:35:53 +00:00
}
if (flags & VIR_DOMAIN_MEM_MAXIMUM) {
/* resize the maximum memory */
2011-03-21 09:35:53 +00:00
if (flags & VIR_DOMAIN_MEM_LIVE) {
priv = vm->privateData;
if (libxl_domain_setmaxmem(priv->ctx, dom->id, newmem) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to set maximum memory for domain '%d'"
" with libxenlight"), dom->id);
goto endjob;
}
}
if (flags & VIR_DOMAIN_MEM_CONFIG) {
/* Help clang 2.8 decipher the logic flow. */
sa_assert(persistentDef);
persistentDef->mem.max_balloon = newmem;
if (persistentDef->mem.cur_balloon > newmem)
persistentDef->mem.cur_balloon = newmem;
ret = virDomainSaveConfig(cfg->configDir, persistentDef);
goto endjob;
2011-03-21 09:35:53 +00:00
}
} else {
/* resize the current memory */
2011-03-21 09:35:53 +00:00
if (newmem > vm->def->mem.max_balloon) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("cannot set memory higher than max memory"));
goto endjob;
}
if (flags & VIR_DOMAIN_MEM_LIVE) {
int res;
priv = vm->privateData;
/* Unlock virDomainObj while ballooning memory */
virObjectUnlock(vm);
res = libxl_set_memory_target(priv->ctx, dom->id, newmem, 0,
/* force */ 1);
virObjectLock(vm);
if (res < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to set memory for domain '%d'"
" with libxenlight"), dom->id);
goto endjob;
}
}
if (flags & VIR_DOMAIN_MEM_CONFIG) {
sa_assert(persistentDef);
persistentDef->mem.cur_balloon = newmem;
ret = virDomainSaveConfig(cfg->configDir, persistentDef);
goto endjob;
}
2011-03-21 09:35:53 +00:00
}
ret = 0;
endjob:
if (!libxlDomainObjEndJob(driver, vm))
vm = NULL;
2011-03-21 09:35:53 +00:00
cleanup:
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
2011-03-21 09:35:53 +00:00
return ret;
}
static int
libxlDomainSetMemory(virDomainPtr dom, unsigned long memory)
{
return libxlDomainSetMemoryFlags(dom, memory, VIR_DOMAIN_MEM_LIVE);
}
static int
libxlDomainSetMaxMemory(virDomainPtr dom, unsigned long memory)
{
return libxlDomainSetMemoryFlags(dom, memory, VIR_DOMAIN_MEM_MAXIMUM);
}
static int
libxlDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info)
{
virDomainObjPtr vm;
libxl_dominfo d_info;
libxlDomainObjPrivatePtr priv;
int ret = -1;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetInfoEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
priv = vm->privateData;
if (!virDomainObjIsActive(vm)) {
info->cpuTime = 0;
info->memory = vm->def->mem.cur_balloon;
info->maxMem = vm->def->mem.max_balloon;
} else {
if (libxl_domain_info(priv->ctx, &d_info, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxl_domain_info failed for domain '%d'"), dom->id);
goto cleanup;
}
info->cpuTime = d_info.cpu_time;
info->memory = d_info.current_memkb;
info->maxMem = d_info.max_memkb;
}
info->state = virDomainObjGetState(vm, NULL);
info->nrVirtCpu = vm->def->vcpus;
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainGetState(virDomainPtr dom,
int *state,
int *reason,
unsigned int flags)
{
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetStateEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
*state = virDomainObjGetState(vm, reason);
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
/*
* virDomainObjPtr must be locked on invocation
*/
static int
libxlDoDomainSave(libxlDriverPrivatePtr driver, virDomainObjPtr vm,
const char *to)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
libxlSavefileHeader hdr;
virObjectEventPtr event = NULL;
char *xml = NULL;
uint32_t xml_len;
int fd = -1;
int ret = -1;
if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("Domain '%d' has to be running because libxenlight will"
" suspend it"), vm->def->id);
goto cleanup;
}
if ((fd = virFileOpenAs(to, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR,
util: refactor virFileOpenAs virFileOpenAs previously would only try opening a file as the current user, or as a different user, but wouldn't try both methods in a single call. This made it cumbersome to use as a replacement for open(2). Additionally, it had a lot of historical baggage that led to it being difficult to understand. This patch refactors virFileOpenAs in the following ways: * reorganize the code so that everything dealing with both the parent and child sides of the "fork+setuid+setgid+open" method are in a separate function. This makes the public function easier to understand. * Allow a single call to virFileOpenAs() to first attempt the open as the current user, and if that fails to automatically re-try after doing fork+setuid (if deemed appropriate, i.e. errno indicates it would now be successful, and the file is on a networkFS). This makes it possible (in many, but possibly not all, cases) to drop-in virFileOpenAs() as a replacement for open(2). (NB: currently qemuOpenFile() calls virFileOpenAs() twice, once without forking, then again with forking. That unfortunately can't be changed without at least some discussion of the ramifications, because the requested file permissions are different in each case, which is something that a single call to virFileOpenAs() can't deal with.) * Add a flag so that any fchown() of the file to a different uid:gid is explicitly requested when the function is called, rather than it being implied by the presence of the O_CREAT flag. This just makes for less subtle surprises to consumers. (Commit b1643dc15c5de886fefe56ad18608d65f1325a2c added the check for O_CREAT before forcing ownership. This patch just makes that restriction more explicit.) * If either the uid or gid is specified as "-1", virFileOpenAs will interpret this to mean "the current [gu]id". All current consumers of virFileOpenAs should retain their present behavior (after a few minor changes to their setup code and arguments).
2012-01-13 20:26:45 +00:00
-1, -1, 0)) < 0) {
virReportSystemError(-fd,
_("Failed to create domain save file '%s'"), to);
goto cleanup;
}
if ((xml = virDomainDefFormat(vm->def, 0)) == NULL)
goto cleanup;
xml_len = strlen(xml) + 1;
memset(&hdr, 0, sizeof(hdr));
memcpy(hdr.magic, LIBXL_SAVE_MAGIC, sizeof(hdr.magic));
hdr.version = LIBXL_SAVE_VERSION;
hdr.xmlLen = xml_len;
if (safewrite(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Failed to write save file header"));
goto cleanup;
}
if (safewrite(fd, xml, xml_len) != xml_len) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Failed to write xml description"));
goto cleanup;
}
if (libxl_domain_suspend(priv->ctx, vm->def->id, fd, 0, NULL) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to save domain '%d' with libxenlight"),
vm->def->id);
goto cleanup;
}
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SAVED);
if (libxl_domain_destroy(priv->ctx, vm->def->id, NULL) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to destroy domain '%d'"), vm->def->id);
goto cleanup;
}
libxlVmCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_SAVED);
vm->hasManagedSave = true;
ret = 0;
cleanup:
VIR_FREE(xml);
if (VIR_CLOSE(fd) < 0)
virReportSystemError(errno, "%s", _("cannot close file"));
if (event)
libxlDomainEventQueue(driver, event);
return ret;
}
static int
libxlDomainSaveFlags(virDomainPtr dom, const char *to, const char *dxml,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(0, -1);
if (dxml) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("xml modification unsupported"));
return -1;
}
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainSaveFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto cleanup;
}
if (libxlDoDomainSave(driver, vm, to) < 0)
goto cleanup;
if (!vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainSave(virDomainPtr dom, const char *to)
{
return libxlDomainSaveFlags(dom, to, NULL, 0);
}
static int
libxlDomainRestoreFlags(virConnectPtr conn, const char *from,
const char *dxml, unsigned int flags)
{
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm = NULL;
virDomainDefPtr def = NULL;
libxlSavefileHeader hdr;
int fd = -1;
int ret = -1;
virCheckFlags(VIR_DOMAIN_SAVE_PAUSED, -1);
if (dxml) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("xml modification unsupported"));
return -1;
}
fd = libxlSaveImageOpen(driver, cfg, from, &def, &hdr);
if (fd < 0)
goto cleanup_unlock;
if (virDomainRestoreFlagsEnsureACL(conn, def) < 0)
goto cleanup_unlock;
if (!(vm = virDomainObjListAdd(driver->domains, def,
driver->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
NULL)))
goto cleanup_unlock;
def = NULL;
ret = libxlVmStart(driver, vm, (flags & VIR_DOMAIN_SAVE_PAUSED) != 0, fd);
if (ret < 0 && !vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
cleanup:
if (VIR_CLOSE(fd) < 0)
virReportSystemError(errno, "%s", _("cannot close file"));
virDomainDefFree(def);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
cleanup_unlock:
libxlDriverUnlock(driver);
goto cleanup;
}
static int
libxlDomainRestore(virConnectPtr conn, const char *from)
{
return libxlDomainRestoreFlags(conn, from, NULL, 0);
}
static int
drivers: prefer unsigned int for flags Now that the public APIs always use unsigned flags, the internal driver callbacks might as well do likewise. * src/driver.h (vrDrvOpen, virDrvDomainCoreDump) (virDrvDomainGetXMLDesc, virDrvNetworkGetXMLDesc) (virDrvNWFilterGetXMLDesc): Update type. * src/remote/remote_protocol.x (remote_open_args) (remote_domain_core_dump_args, remote_domain_get_xml_desc_args) (remote_network_get_xml_desc_args) (remote_nwfilter_get_xml_desc_args): Likewise. * src/test/test_driver.c: Update clients. * src/remote/remote_driver.c: Likewise. * src/xen/xen_hypervisor.c: Likewise. * src/xen/xen_hypervisor.h: Likewise. * src/xen/xen_driver.c: Likewise. * src/xen/xend_internal.c: Likewise. * src/xen/xend_internal.h: Likewise. * src/xen/xm_internal.c: Likewise. * src/xen/xm_internal.h: Likewise. * src/xen/xs_internal.c: Likewise. * src/xen/xs_internal.h: Likewise. * src/xen/xen_inotify.c: Likewise. * src/xen/xen_inotify.h: Likewise. * src/phyp/phyp_driver.c: Likewise. * src/openvz/openvz_driver.c: Likewise. * src/vmware/vmware_driver.c: Likewise. * src/vbox/vbox_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xenapi/xenapi_driver.c: Likewise. * src/esx/esx_driver.c: Likewise. * src/esx/esx_interface_driver.c: Likewise. * src/esx/esx_network_driver.c: Likewise. * src/esx/esx_storage_driver.c: Likewise. * src/esx/esx_device_monitor.c: Likewise. * src/esx/esx_secret_driver.c: Likewise. * src/esx/esx_nwfilter_driver.c: Likewise. * src/interface/netcf_driver.c: Likewise. * src/nwfilter/nwfilter_driver.c: Likewise. * src/libxl/libxl_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/secret/secret_driver.c: Likewise. * src/storage/storage_driver.c: Likewise. * src/node_device/node_device_hal.c: Likewise. * src/node_device/node_device_udev.c: Likewise. * src/remote_protocol-structs: Likewise.
2011-07-06 20:40:19 +00:00
libxlDomainCoreDump(virDomainPtr dom, const char *to, unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
virObjectEventPtr event = NULL;
bool paused = false;
int ret = -1;
virCheckFlags(VIR_DUMP_LIVE | VIR_DUMP_CRASH, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainCoreDumpEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
if (!(flags & VIR_DUMP_LIVE) &&
virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
if (libxl_domain_pause(priv->ctx, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Before dumping core, failed to suspend domain '%d'"
" with libxenlight"),
dom->id);
goto cleanup;
}
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_DUMP);
paused = true;
}
if (libxl_domain_core_dump(priv->ctx, dom->id, to, NULL) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to dump core of domain '%d' with libxenlight"),
dom->id);
goto cleanup_unpause;
}
if (flags & VIR_DUMP_CRASH) {
if (libxl_domain_destroy(priv->ctx, dom->id, NULL) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to destroy domain '%d'"), dom->id);
goto cleanup_unpause;
}
libxlVmCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_CRASHED);
if (!vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
}
ret = 0;
cleanup_unpause:
if (vm && virDomainObjIsActive(vm) && paused) {
if (libxl_domain_unpause(priv->ctx, dom->id) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("After dumping core, failed to resume domain '%d' with"
" libxenlight"), dom->id);
} else {
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
VIR_DOMAIN_RUNNING_UNPAUSED);
}
}
cleanup:
if (vm)
virObjectUnlock(vm);
if (event)
libxlDomainEventQueue(driver, event);
return ret;
}
static int
libxlDomainManagedSave(virDomainPtr dom, unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
virDomainObjPtr vm = NULL;
char *name = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainManagedSaveEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto cleanup;
}
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot do managed save for transient domain"));
goto cleanup;
}
name = libxlDomainManagedSavePath(driver, vm);
if (name == NULL)
goto cleanup;
VIR_INFO("Saving state to %s", name);
if (libxlDoDomainSave(driver, vm, name) < 0)
goto cleanup;
if (!vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
VIR_FREE(name);
return ret;
}
static int
libxlDomainManagedSaveLoad(virDomainObjPtr vm,
void *opaque)
{
libxlDriverPrivatePtr driver = opaque;
char *name;
int ret = -1;
virObjectLock(vm);
if (!(name = libxlDomainManagedSavePath(driver, vm)))
goto cleanup;
vm->hasManagedSave = virFileExists(name);
ret = 0;
cleanup:
virObjectUnlock(vm);
VIR_FREE(name);
return ret;
}
static int
libxlDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainHasManagedSaveImageEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
ret = vm->hasManagedSave;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
virDomainObjPtr vm = NULL;
int ret = -1;
char *name = NULL;
virCheckFlags(0, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainManagedSaveRemoveEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
name = libxlDomainManagedSavePath(driver, vm);
if (name == NULL)
goto cleanup;
ret = unlink(name);
vm->hasManagedSave = false;
cleanup:
VIR_FREE(name);
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainSetVcpusFlags(virDomainPtr dom, unsigned int nvcpus,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
libxlDomainObjPrivatePtr priv;
virDomainDefPtr def;
virDomainObjPtr vm;
libxl_bitmap map;
uint8_t *bitmask = NULL;
unsigned int maplen;
size_t i;
unsigned int pos;
int max;
int ret = -1;
virCheckFlags(VIR_DOMAIN_VCPU_LIVE |
VIR_DOMAIN_VCPU_CONFIG |
VIR_DOMAIN_VCPU_MAXIMUM, -1);
/* At least one of LIVE or CONFIG must be set. MAXIMUM cannot be
* mixed with LIVE. */
if ((flags & (VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_CONFIG)) == 0 ||
(flags & (VIR_DOMAIN_VCPU_MAXIMUM | VIR_DOMAIN_VCPU_LIVE)) ==
(VIR_DOMAIN_VCPU_MAXIMUM | VIR_DOMAIN_VCPU_LIVE)) {
virReportError(VIR_ERR_INVALID_ARG,
_("invalid flag combination: (0x%x)"), flags);
return -1;
}
if (!nvcpus) {
virReportError(VIR_ERR_INVALID_ARG, "%s", _("nvcpus is zero"));
return -1;
}
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainSetVcpusFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm) && (flags & VIR_DOMAIN_VCPU_LIVE)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot set vcpus on an inactive domain"));
goto cleanup;
}
if (!vm->persistent && (flags & VIR_DOMAIN_VCPU_CONFIG)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot change persistent config of a transient domain"));
goto cleanup;
}
if ((max = libxlConnectGetMaxVcpus(dom->conn, NULL)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not determine max vcpus for the domain"));
goto cleanup;
}
if (!(flags & VIR_DOMAIN_VCPU_MAXIMUM) && vm->def->maxvcpus < max) {
max = vm->def->maxvcpus;
}
if (nvcpus > max) {
virReportError(VIR_ERR_INVALID_ARG,
_("requested vcpus is greater than max allowable"
" vcpus for the domain: %d > %d"), nvcpus, max);
goto cleanup;
}
priv = vm->privateData;
if (!(def = virDomainObjGetPersistentDef(cfg->caps, driver->xmlopt, vm)))
goto cleanup;
maplen = VIR_CPU_MAPLEN(nvcpus);
if (VIR_ALLOC_N(bitmask, maplen) < 0)
goto cleanup;
for (i = 0; i < nvcpus; ++i) {
pos = i / 8;
bitmask[pos] |= 1 << (i % 8);
}
map.size = maplen;
map.map = bitmask;
switch (flags) {
case VIR_DOMAIN_VCPU_MAXIMUM | VIR_DOMAIN_VCPU_CONFIG:
def->maxvcpus = nvcpus;
if (nvcpus < def->vcpus)
def->vcpus = nvcpus;
break;
case VIR_DOMAIN_VCPU_CONFIG:
def->vcpus = nvcpus;
break;
case VIR_DOMAIN_VCPU_LIVE:
if (libxl_set_vcpuonline(priv->ctx, dom->id, &map) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to set vcpus for domain '%d'"
" with libxenlight"), dom->id);
goto cleanup;
}
break;
case VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_CONFIG:
if (libxl_set_vcpuonline(priv->ctx, dom->id, &map) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to set vcpus for domain '%d'"
" with libxenlight"), dom->id);
goto cleanup;
}
def->vcpus = nvcpus;
break;
}
ret = 0;
if (flags & VIR_DOMAIN_VCPU_CONFIG)
ret = virDomainSaveConfig(cfg->configDir, def);
cleanup:
VIR_FREE(bitmask);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus)
{
return libxlDomainSetVcpusFlags(dom, nvcpus, VIR_DOMAIN_VCPU_LIVE);
}
static int
libxlDomainGetVcpusFlags(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
virDomainDefPtr def;
int ret = -1;
bool active;
virCheckFlags(VIR_DOMAIN_VCPU_LIVE |
VIR_DOMAIN_VCPU_CONFIG |
VIR_DOMAIN_VCPU_MAXIMUM, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
api: require write permission for guest agent interaction I noticed that we allow virDomainGetVcpusFlags even for read-only connections, but that with a flag, it can require guest agent interaction. It is feasible that a malicious guest could intentionally abuse the replies it sends over the guest agent connection to possibly trigger a bug in libvirt's JSON parser, or withhold an answer so as to prevent the use of the agent in a later command such as a shutdown request. Although we don't know of any such exploits now (and therefore don't mind posting this patch publicly without trying to get a CVE assigned), it is better to err on the side of caution and explicitly require full access to any domain where the API requires guest interaction to operate correctly. I audited all commands that are marked as conditionally using a guest agent. Note that at least virDomainFSTrim is documented as needing a guest agent, but that such use is unconditional depending on the hypervisor (so the existing domain:fs_trim ACL should be sufficient there, rather than also requirng domain:write). But when designing future APIs, such as the plans for obtaining a domain's IP addresses, we should copy the approach of this patch in making interaction with the guest be specified via a flag, and use that flag to also require stricter access checks. * src/libvirt.c (virDomainGetVcpusFlags): Forbid guest interaction on read-only connection. (virDomainShutdownFlags, virDomainReboot): Improve docs on agent interaction. * src/remote/remote_protocol.x (REMOTE_PROC_DOMAIN_SNAPSHOT_CREATE_XML) (REMOTE_PROC_DOMAIN_SET_VCPUS_FLAGS) (REMOTE_PROC_DOMAIN_GET_VCPUS_FLAGS, REMOTE_PROC_DOMAIN_REBOOT) (REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS): Require domain:write for any conditional use of a guest agent. * src/xen/xen_driver.c: Fix clients. * src/libxl/libxl_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-21 17:37:29 +00:00
if (virDomainGetVcpusFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
active = virDomainObjIsActive(vm);
if ((flags & (VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_CONFIG)) == 0) {
if (active)
flags |= VIR_DOMAIN_VCPU_LIVE;
else
flags |= VIR_DOMAIN_VCPU_CONFIG;
}
if ((flags & VIR_DOMAIN_VCPU_LIVE) && (flags & VIR_DOMAIN_VCPU_CONFIG)) {
virReportError(VIR_ERR_INVALID_ARG,
_("invalid flag combination: (0x%x)"), flags);
return -1;
}
if (flags & VIR_DOMAIN_VCPU_LIVE) {
if (!active) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
def = vm->def;
} else {
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain is transient"));
goto cleanup;
}
def = vm->newDef ? vm->newDef : vm->def;
}
ret = (flags & VIR_DOMAIN_VCPU_MAXIMUM) ? def->maxvcpus : def->vcpus;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainPinVcpuFlags(virDomainPtr dom, unsigned int vcpu,
unsigned char *cpumap, int maplen,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainDefPtr targetDef = NULL;
virBitmapPtr pcpumap = NULL;
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainPinVcpuFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if ((flags & VIR_DOMAIN_AFFECT_LIVE) && !virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("domain is inactive"));
goto cleanup;
}
if (virDomainLiveConfigHelperMethod(cfg->caps, driver->xmlopt, vm,
&flags, &targetDef) < 0)
goto cleanup;
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
targetDef = vm->def;
}
/* Make sure coverity knows targetDef is valid at this point. */
sa_assert(targetDef);
pcpumap = virBitmapNewData(cpumap, maplen);
if (!pcpumap)
goto cleanup;
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
libxl_bitmap map = { .size = maplen, .map = cpumap };
libxlDomainObjPrivatePtr priv;
priv = vm->privateData;
if (libxl_set_vcpuaffinity(priv->ctx, dom->id, vcpu, &map) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to pin vcpu '%d' with libxenlight"),
vcpu);
goto cleanup;
}
}
/* full bitmap means reset the settings (if any). */
if (virBitmapIsAllSet(pcpumap)) {
if (virDomainVcpuPinDel(targetDef, vcpu) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to delete vcpupin xml for vcpu '%d'"),
vcpu);
goto cleanup;
}
goto out;
}
if (!targetDef->cputune.vcpupin) {
if (VIR_ALLOC(targetDef->cputune.vcpupin) < 0)
2012-08-21 09:18:28 +00:00
goto cleanup;
targetDef->cputune.nvcpupin = 0;
2012-08-21 09:18:28 +00:00
}
if (virDomainVcpuPinAdd(&targetDef->cputune.vcpupin,
&targetDef->cputune.nvcpupin,
2012-08-21 09:18:28 +00:00
cpumap,
maplen,
vcpu) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("failed to update or add vcpupin xml"));
goto cleanup;
}
out:
ret = 0;
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
ret = virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm);
} else if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
ret = virDomainSaveConfig(cfg->configDir, targetDef);
}
cleanup:
if (vm)
virObjectUnlock(vm);
virBitmapFree(pcpumap);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainPinVcpu(virDomainPtr dom, unsigned int vcpu, unsigned char *cpumap,
int maplen)
{
return libxlDomainPinVcpuFlags(dom, vcpu, cpumap, maplen,
VIR_DOMAIN_AFFECT_LIVE);
}
static int
libxlDomainGetVcpuPinInfo(virDomainPtr dom, int ncpumaps,
unsigned char *cpumaps, int maplen,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm = NULL;
virDomainDefPtr targetDef = NULL;
virDomainVcpuPinDefPtr *vcpupin_list;
virBitmapPtr cpumask = NULL;
int maxcpu, hostcpus, vcpu, pcpu, n, ret = -1;
unsigned char *cpumap;
bool pinned;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetVcpuPinInfoEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainLiveConfigHelperMethod(cfg->caps, driver->xmlopt, vm,
&flags, &targetDef) < 0)
goto cleanup;
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
targetDef = vm->def;
}
/* Make sure coverity knows targetDef is valid at this point. */
sa_assert(targetDef);
/* Clamp to actual number of vcpus */
if (ncpumaps > targetDef->vcpus)
ncpumaps = targetDef->vcpus;
/* we use cfg->ctx, as vm->privateData->ctx may be NULL if VM is down. */
if ((hostcpus = libxl_get_max_cpus(cfg->ctx)) < 0)
goto cleanup;
maxcpu = maplen * 8;
if (maxcpu > hostcpus)
maxcpu = hostcpus;
/* initialize cpumaps */
memset(cpumaps, 0xff, maplen * ncpumaps);
if (maxcpu % 8) {
for (vcpu = 0; vcpu < ncpumaps; vcpu++) {
cpumap = VIR_GET_CPUMAP(cpumaps, maplen, vcpu);
cpumap[maplen - 1] &= (1 << maxcpu % 8) - 1;
}
}
/* if vcpupin setting exists, there may be unused pcpus */
for (n = 0; n < targetDef->cputune.nvcpupin; n++) {
vcpupin_list = targetDef->cputune.vcpupin;
vcpu = vcpupin_list[n]->vcpuid;
cpumask = vcpupin_list[n]->cpumask;
cpumap = VIR_GET_CPUMAP(cpumaps, maplen, vcpu);
for (pcpu = 0; pcpu < maxcpu; pcpu++) {
if (virBitmapGetBit(cpumask, pcpu, &pinned) < 0)
goto cleanup;
if (!pinned)
VIR_UNUSE_CPU(cpumap, pcpu);
}
}
ret = ncpumaps;
cleanup:
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainGetVcpus(virDomainPtr dom, virVcpuInfoPtr info, int maxinfo,
unsigned char *cpumaps, int maplen)
{
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
int ret = -1;
libxl_vcpuinfo *vcpuinfo;
int maxcpu, hostcpus;
size_t i;
unsigned char *cpumap;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetVcpusEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
if ((vcpuinfo = libxl_list_vcpu(priv->ctx, dom->id, &maxcpu,
&hostcpus)) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to list vcpus for domain '%d' with libxenlight"),
dom->id);
goto cleanup;
}
if (cpumaps && maplen > 0)
memset(cpumaps, 0, maplen * maxinfo);
for (i = 0; i < maxcpu && i < maxinfo; ++i) {
info[i].number = vcpuinfo[i].vcpuid;
info[i].cpu = vcpuinfo[i].cpu;
info[i].cpuTime = vcpuinfo[i].vcpu_time;
if (vcpuinfo[i].running)
info[i].state = VIR_VCPU_RUNNING;
else if (vcpuinfo[i].blocked)
info[i].state = VIR_VCPU_BLOCKED;
else
info[i].state = VIR_VCPU_OFFLINE;
if (cpumaps && maplen > 0) {
cpumap = VIR_GET_CPUMAP(cpumaps, maplen, i);
memcpy(cpumap, vcpuinfo[i].cpumap.map,
MIN(maplen, vcpuinfo[i].cpumap.size));
}
libxl_vcpuinfo_dispose(&vcpuinfo[i]);
}
VIR_FREE(vcpuinfo);
ret = maxinfo;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static char *
drivers: prefer unsigned int for flags Now that the public APIs always use unsigned flags, the internal driver callbacks might as well do likewise. * src/driver.h (vrDrvOpen, virDrvDomainCoreDump) (virDrvDomainGetXMLDesc, virDrvNetworkGetXMLDesc) (virDrvNWFilterGetXMLDesc): Update type. * src/remote/remote_protocol.x (remote_open_args) (remote_domain_core_dump_args, remote_domain_get_xml_desc_args) (remote_network_get_xml_desc_args) (remote_nwfilter_get_xml_desc_args): Likewise. * src/test/test_driver.c: Update clients. * src/remote/remote_driver.c: Likewise. * src/xen/xen_hypervisor.c: Likewise. * src/xen/xen_hypervisor.h: Likewise. * src/xen/xen_driver.c: Likewise. * src/xen/xend_internal.c: Likewise. * src/xen/xend_internal.h: Likewise. * src/xen/xm_internal.c: Likewise. * src/xen/xm_internal.h: Likewise. * src/xen/xs_internal.c: Likewise. * src/xen/xs_internal.h: Likewise. * src/xen/xen_inotify.c: Likewise. * src/xen/xen_inotify.h: Likewise. * src/phyp/phyp_driver.c: Likewise. * src/openvz/openvz_driver.c: Likewise. * src/vmware/vmware_driver.c: Likewise. * src/vbox/vbox_driver.c: Likewise. * src/vbox/vbox_tmpl.c: Likewise. * src/xenapi/xenapi_driver.c: Likewise. * src/esx/esx_driver.c: Likewise. * src/esx/esx_interface_driver.c: Likewise. * src/esx/esx_network_driver.c: Likewise. * src/esx/esx_storage_driver.c: Likewise. * src/esx/esx_device_monitor.c: Likewise. * src/esx/esx_secret_driver.c: Likewise. * src/esx/esx_nwfilter_driver.c: Likewise. * src/interface/netcf_driver.c: Likewise. * src/nwfilter/nwfilter_driver.c: Likewise. * src/libxl/libxl_driver.c: Likewise. * src/qemu/qemu_driver.c: Likewise. * src/lxc/lxc_driver.c: Likewise. * src/uml/uml_driver.c: Likewise. * src/network/bridge_driver.c: Likewise. * src/secret/secret_driver.c: Likewise. * src/storage/storage_driver.c: Likewise. * src/node_device/node_device_hal.c: Likewise. * src/node_device/node_device_udev.c: Likewise. * src/remote_protocol-structs: Likewise.
2011-07-06 20:40:19 +00:00
libxlDomainGetXMLDesc(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
char *ret = NULL;
/* Flags checked by virDomainDefFormat */
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetXMLDescEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
ret = virDomainDefFormat(vm->def, flags);
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static char *
libxlConnectDomainXMLFromNative(virConnectPtr conn, const char * nativeFormat,
const char * nativeConfig,
unsigned int flags)
{
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainDefPtr def = NULL;
virConfPtr conf = NULL;
char *xml = NULL;
virCheckFlags(0, NULL);
if (virConnectDomainXMLFromNativeEnsureACL(conn) < 0)
goto cleanup;
if (STRNEQ(nativeFormat, LIBXL_CONFIG_FORMAT_XM)) {
virReportError(VIR_ERR_INVALID_ARG,
_("unsupported config type %s"), nativeFormat);
goto cleanup;
}
if (!(conf = virConfReadMem(nativeConfig, strlen(nativeConfig), 0)))
goto cleanup;
if (!(def = xenParseXM(conf,
cfg->verInfo->xen_version_major,
cfg->caps))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("parsing xm config failed"));
goto cleanup;
}
xml = virDomainDefFormat(def, VIR_DOMAIN_XML_INACTIVE);
cleanup:
virDomainDefFree(def);
if (conf)
virConfFree(conf);
virObjectUnref(cfg);
return xml;
}
#define MAX_CONFIG_SIZE (1024 * 65)
static char *
libxlConnectDomainXMLToNative(virConnectPtr conn, const char * nativeFormat,
const char * domainXml,
unsigned int flags)
{
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainDefPtr def = NULL;
virConfPtr conf = NULL;
int len = MAX_CONFIG_SIZE;
char *ret = NULL;
virCheckFlags(0, NULL);
if (virConnectDomainXMLToNativeEnsureACL(conn) < 0)
goto cleanup;
if (STRNEQ(nativeFormat, LIBXL_CONFIG_FORMAT_XM)) {
virReportError(VIR_ERR_INVALID_ARG,
_("unsupported config type %s"), nativeFormat);
goto cleanup;
}
if (!(def = virDomainDefParseString(domainXml,
cfg->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN, 0)))
goto cleanup;
if (!(conf = xenFormatXM(conn, def, cfg->verInfo->xen_version_major)))
goto cleanup;
if (VIR_ALLOC_N(ret, len) < 0)
goto cleanup;
if (virConfWriteMem(ret, &len, conf) < 0) {
VIR_FREE(ret);
goto cleanup;
}
cleanup:
virDomainDefFree(def);
if (conf)
virConfFree(conf);
virObjectUnref(cfg);
return ret;
}
static int
libxlConnectListDefinedDomains(virConnectPtr conn,
char **const names, int nnames)
{
libxlDriverPrivatePtr driver = conn->privateData;
int n;
if (virConnectListDefinedDomainsEnsureACL(conn) < 0)
return -1;
n = virDomainObjListGetInactiveNames(driver->domains, names, nnames,
virConnectListDefinedDomainsCheckACL, conn);
return n;
}
static int
libxlConnectNumOfDefinedDomains(virConnectPtr conn)
{
libxlDriverPrivatePtr driver = conn->privateData;
int n;
if (virConnectNumOfDefinedDomainsEnsureACL(conn) < 0)
return -1;
n = virDomainObjListNumOfDomains(driver->domains, false,
virConnectNumOfDefinedDomainsCheckACL,
conn);
return n;
}
static int
libxlDomainCreateWithFlags(virDomainPtr dom,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(VIR_DOMAIN_START_PAUSED, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainCreateWithFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is already running"));
goto cleanup;
}
ret = libxlVmStart(driver, vm, (flags & VIR_DOMAIN_START_PAUSED) != 0, -1);
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainCreate(virDomainPtr dom)
{
return libxlDomainCreateWithFlags(dom, 0);
}
static virDomainPtr
libxlDomainDefineXML(virConnectPtr conn, const char *xml)
{
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainDefPtr def = NULL;
virDomainObjPtr vm = NULL;
virDomainPtr dom = NULL;
virObjectEventPtr event = NULL;
virDomainDefPtr oldDef = NULL;
/* Lock the driver until the virDomainObj is created and locked */
libxlDriverLock(driver);
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup_unlock;
if (virDomainDefineXMLEnsureACL(conn, def) < 0)
goto cleanup_unlock;
if (!(vm = virDomainObjListAdd(driver->domains, def,
driver->xmlopt,
0,
&oldDef)))
goto cleanup_unlock;
def = NULL;
vm->persistent = 1;
libxlDriverUnlock(driver);
if (virDomainSaveConfig(cfg->configDir,
vm->newDef ? vm->newDef : vm->def) < 0) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
goto cleanup;
}
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_DEFINED,
!oldDef ?
VIR_DOMAIN_EVENT_DEFINED_ADDED :
VIR_DOMAIN_EVENT_DEFINED_UPDATED);
cleanup:
virDomainDefFree(def);
virDomainDefFree(oldDef);
if (vm)
virObjectUnlock(vm);
if (event)
libxlDomainEventQueue(driver, event);
virObjectUnref(cfg);
return dom;
cleanup_unlock:
libxlDriverUnlock(driver);
goto cleanup;
}
static int
libxlDomainUndefineFlags(virDomainPtr dom,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm;
virObjectEventPtr event = NULL;
char *name = NULL;
int ret = -1;
virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainUndefineFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot undefine transient domain"));
goto cleanup;
}
name = libxlDomainManagedSavePath(driver, vm);
if (name == NULL)
goto cleanup;
if (virFileExists(name)) {
if (flags & VIR_DOMAIN_UNDEFINE_MANAGED_SAVE) {
if (unlink(name) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to remove domain managed save image"));
goto cleanup;
}
} else {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Refusing to undefine while domain managed "
"save image exists"));
goto cleanup;
}
}
if (virDomainDeleteConfig(cfg->configDir, cfg->autostartDir, vm) < 0)
goto cleanup;
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_UNDEFINED,
VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
if (virDomainObjIsActive(vm)) {
vm->persistent = 0;
} else {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
ret = 0;
cleanup:
VIR_FREE(name);
if (vm)
virObjectUnlock(vm);
if (event)
libxlDomainEventQueue(driver, event);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainUndefine(virDomainPtr dom)
{
return libxlDomainUndefineFlags(dom, 0);
}
static int
libxlDomainChangeEjectableMedia(libxlDomainObjPrivatePtr priv,
virDomainObjPtr vm, virDomainDiskDefPtr disk)
{
virDomainDiskDefPtr origdisk = NULL;
libxl_device_disk x_disk;
size_t i;
int ret = -1;
for (i = 0; i < vm->def->ndisks; i++) {
if (vm->def->disks[i]->bus == disk->bus &&
STREQ(vm->def->disks[i]->dst, disk->dst)) {
origdisk = vm->def->disks[i];
break;
}
}
if (!origdisk) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("No device with bus '%s' and target '%s'"),
virDomainDiskBusTypeToString(disk->bus), disk->dst);
goto cleanup;
}
if (origdisk->device != VIR_DOMAIN_DISK_DEVICE_CDROM) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Removable media not supported for %s device"),
virDomainDiskDeviceTypeToString(disk->device));
return -1;
}
if (libxlMakeDisk(disk, &x_disk) < 0)
goto cleanup;
if ((ret = libxl_cdrom_insert(priv->ctx, vm->def->id, &x_disk, NULL)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to change media for disk '%s'"),
disk->dst);
goto cleanup;
}
VIR_FREE(origdisk->src);
origdisk->src = disk->src;
disk->src = NULL;
origdisk->type = disk->type;
virDomainDiskDefFree(disk);
ret = 0;
cleanup:
return ret;
}
static int
libxlDomainAttachDeviceDiskLive(libxlDomainObjPrivatePtr priv,
virDomainObjPtr vm, virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr l_disk = dev->data.disk;
libxl_device_disk x_disk;
int ret = -1;
switch (l_disk->device) {
case VIR_DOMAIN_DISK_DEVICE_CDROM:
ret = libxlDomainChangeEjectableMedia(priv, vm, l_disk);
break;
case VIR_DOMAIN_DISK_DEVICE_DISK:
if (l_disk->bus == VIR_DOMAIN_DISK_BUS_XEN) {
snapshot: also support disks by path I got confused when 'virsh domblkinfo dom disk' required the path to a disk (which can be ambiguous, since a single file can back multiple disks), rather than the unambiguous target device name that I was using in disk snapshots. So, in true developer fashion, I went for the best of both worlds - all interfaces that operate on a disk (aka block) now accept either the target name or the unambiguous path to the backing file used by the disk. * src/conf/domain_conf.h (virDomainDiskIndexByName): Add parameter. (virDomainDiskPathByName): New prototype. * src/libvirt_private.syms (domain_conf.h): Export it. * src/conf/domain_conf.c (virDomainDiskIndexByName): Also allow searching by path, and decide whether ambiguity is okay. (virDomainDiskPathByName): New function. (virDomainDiskRemoveByName, virDomainSnapshotAlignDisks): Update callers. * src/qemu/qemu_driver.c (qemudDomainBlockPeek) (qemuDomainAttachDeviceConfig, qemuDomainUpdateDeviceConfig) (qemuDomainGetBlockInfo, qemuDiskPathToAlias): Likewise. * src/qemu/qemu_process.c (qemuProcessFindDomainDiskByPath): Likewise. * src/libxl/libxl_driver.c (libxlDomainAttachDeviceDiskLive) (libxlDomainDetachDeviceDiskLive, libxlDomainAttachDeviceConfig) (libxlDomainUpdateDeviceConfig): Likewise. * src/uml/uml_driver.c (umlDomainBlockPeek): Likewise. * src/xen/xend_internal.c (xenDaemonDomainBlockPeek): Likewise. * docs/formatsnapshot.html.in: Update documentation. * tools/virsh.pod (domblkstat, domblkinfo): Likewise. * docs/schemas/domaincommon.rng (diskTarget): Tighten pattern on disk targets. * docs/schemas/domainsnapshot.rng (disksnapshot): Update to match. * tests/domainsnapshotxml2xmlin/disk_snapshot.xml: Update test.
2011-08-20 02:38:36 +00:00
if (virDomainDiskIndexByName(vm->def, l_disk->dst, true) >= 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("target %s already exists"), l_disk->dst);
goto cleanup;
}
if (!l_disk->src) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("disk source path is missing"));
goto cleanup;
}
if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0)
goto cleanup;
if (libxlMakeDisk(l_disk, &x_disk) < 0)
goto cleanup;
if ((ret = libxl_device_disk_add(priv->ctx, vm->def->id,
&x_disk, NULL)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to attach disk '%s'"),
l_disk->dst);
goto cleanup;
}
virDomainDiskInsertPreAlloced(vm->def, l_disk);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk bus '%s' cannot be hotplugged."),
virDomainDiskBusTypeToString(l_disk->bus));
}
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk device type '%s' cannot be hotplugged"),
virDomainDiskDeviceTypeToString(l_disk->device));
break;
}
cleanup:
return ret;
}
static int
libxlDomainDetachDeviceDiskLive(libxlDomainObjPrivatePtr priv,
virDomainObjPtr vm, virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr l_disk = NULL;
libxl_device_disk x_disk;
int idx;
int ret = -1;
switch (dev->data.disk->device) {
case VIR_DOMAIN_DISK_DEVICE_DISK:
if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_XEN) {
if ((idx = virDomainDiskIndexByName(vm->def,
dev->data.disk->dst,
false)) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("disk %s not found"), dev->data.disk->dst);
goto cleanup;
}
l_disk = vm->def->disks[idx];
if (libxlMakeDisk(l_disk, &x_disk) < 0)
goto cleanup;
if ((ret = libxl_device_disk_remove(priv->ctx, vm->def->id,
&x_disk, NULL)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to detach disk '%s'"),
l_disk->dst);
goto cleanup;
}
virDomainDiskRemove(vm->def, idx);
virDomainDiskDefFree(l_disk);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk bus '%s' cannot be hot unplugged."),
virDomainDiskBusTypeToString(dev->data.disk->bus));
}
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot hot unplugged"),
virDomainDiskDeviceTypeToString(dev->data.disk->device));
break;
}
cleanup:
return ret;
}
static int
libxlDomainAttachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
{
int ret = -1;
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
ret = libxlDomainAttachDeviceDiskLive(priv, vm, dev);
if (!ret)
dev->data.disk = NULL;
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot be attached"),
virDomainDeviceTypeToString(dev->type));
break;
}
return ret;
}
static int
libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr disk;
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
disk = dev->data.disk;
snapshot: also support disks by path I got confused when 'virsh domblkinfo dom disk' required the path to a disk (which can be ambiguous, since a single file can back multiple disks), rather than the unambiguous target device name that I was using in disk snapshots. So, in true developer fashion, I went for the best of both worlds - all interfaces that operate on a disk (aka block) now accept either the target name or the unambiguous path to the backing file used by the disk. * src/conf/domain_conf.h (virDomainDiskIndexByName): Add parameter. (virDomainDiskPathByName): New prototype. * src/libvirt_private.syms (domain_conf.h): Export it. * src/conf/domain_conf.c (virDomainDiskIndexByName): Also allow searching by path, and decide whether ambiguity is okay. (virDomainDiskPathByName): New function. (virDomainDiskRemoveByName, virDomainSnapshotAlignDisks): Update callers. * src/qemu/qemu_driver.c (qemudDomainBlockPeek) (qemuDomainAttachDeviceConfig, qemuDomainUpdateDeviceConfig) (qemuDomainGetBlockInfo, qemuDiskPathToAlias): Likewise. * src/qemu/qemu_process.c (qemuProcessFindDomainDiskByPath): Likewise. * src/libxl/libxl_driver.c (libxlDomainAttachDeviceDiskLive) (libxlDomainDetachDeviceDiskLive, libxlDomainAttachDeviceConfig) (libxlDomainUpdateDeviceConfig): Likewise. * src/uml/uml_driver.c (umlDomainBlockPeek): Likewise. * src/xen/xend_internal.c (xenDaemonDomainBlockPeek): Likewise. * docs/formatsnapshot.html.in: Update documentation. * tools/virsh.pod (domblkstat, domblkinfo): Likewise. * docs/schemas/domaincommon.rng (diskTarget): Tighten pattern on disk targets. * docs/schemas/domainsnapshot.rng (disksnapshot): Update to match. * tests/domainsnapshotxml2xmlin/disk_snapshot.xml: Update test.
2011-08-20 02:38:36 +00:00
if (virDomainDiskIndexByName(vmdef, disk->dst, true) >= 0) {
virReportError(VIR_ERR_INVALID_ARG,
_("target %s already exists."), disk->dst);
return -1;
}
if (virDomainDiskInsert(vmdef, disk))
return -1;
/* vmdef has the pointer. Generic codes for vmdef will do all jobs */
dev->data.disk = NULL;
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("persistent attach of device is not supported"));
return -1;
}
return 0;
}
static int
libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
{
int ret = -1;
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
ret = libxlDomainDetachDeviceDiskLive(priv, vm, dev);
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot be detached"),
virDomainDeviceTypeToString(dev->type));
break;
}
return ret;
}
static int
libxlDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev)
{
2012-03-06 23:06:14 +00:00
virDomainDiskDefPtr disk, detach;
int ret = -1;
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
disk = dev->data.disk;
2012-03-06 23:06:14 +00:00
if (!(detach = virDomainDiskRemoveByName(vmdef, disk->dst))) {
virReportError(VIR_ERR_INVALID_ARG,
_("no target device %s"), disk->dst);
break;
}
2012-03-06 23:06:14 +00:00
virDomainDiskDefFree(detach);
ret = 0;
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("persistent detach of device is not supported"));
break;
}
return ret;
}
static int
libxlDomainUpdateDeviceLive(libxlDomainObjPrivatePtr priv,
virDomainObjPtr vm, virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr disk;
int ret = -1;
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
disk = dev->data.disk;
switch (disk->device) {
case VIR_DOMAIN_DISK_DEVICE_CDROM:
ret = libxlDomainChangeEjectableMedia(priv, vm, disk);
if (ret == 0)
dev->data.disk = NULL;
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk bus '%s' cannot be updated."),
virDomainDiskBusTypeToString(disk->bus));
break;
}
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot be updated"),
virDomainDeviceTypeToString(dev->type));
break;
}
return ret;
}
static int
libxlDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr orig;
virDomainDiskDefPtr disk;
int idx;
int ret = -1;
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
disk = dev->data.disk;
if ((idx = virDomainDiskIndexByName(vmdef, disk->dst, false)) < 0) {
virReportError(VIR_ERR_INVALID_ARG,
_("target %s doesn't exist."), disk->dst);
goto cleanup;
}
orig = vmdef->disks[idx];
if (!(orig->device == VIR_DOMAIN_DISK_DEVICE_CDROM)) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("this disk doesn't support update"));
goto cleanup;
}
VIR_FREE(orig->src);
orig->src = disk->src;
orig->type = disk->type;
if (disk->driverName) {
VIR_FREE(orig->driverName);
orig->driverName = disk->driverName;
disk->driverName = NULL;
}
orig->format = disk->format;
disk->src = NULL;
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("persistent update of device is not supported"));
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int
libxlDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm = NULL;
virDomainDefPtr vmdef = NULL;
virDomainDeviceDefPtr dev = NULL;
libxlDomainObjPrivatePtr priv;
int ret = -1;
virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_LIVE |
VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainAttachDeviceFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (virDomainObjIsActive(vm)) {
if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT)
flags |= VIR_DOMAIN_DEVICE_MODIFY_LIVE;
} else {
if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT)
flags |= VIR_DOMAIN_DEVICE_MODIFY_CONFIG;
/* check consistency between flags and the vm state */
if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
}
if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot modify device on transient domain"));
goto cleanup;
}
priv = vm->privateData;
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
/* Make a copy for updated domain. */
if (!(vmdef = virDomainObjCopyPersistentDef(vm, cfg->caps,
driver->xmlopt)))
goto cleanup;
if ((ret = libxlDomainAttachDeviceConfig(vmdef, dev)) < 0)
goto cleanup;
} else {
ret = 0;
}
if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) {
/* If dev exists it was created to modify the domain config. Free it. */
virDomainDeviceDefFree(dev);
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
if ((ret = libxlDomainAttachDeviceLive(priv, vm, dev)) < 0)
goto cleanup;
/*
* update domain status forcibly because the domain status may be
* changed even if we attach the device failed.
*/
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
ret = -1;
}
/* Finally, if no error until here, we can save config. */
if (!ret && (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG)) {
ret = virDomainSaveConfig(cfg->configDir, vmdef);
if (!ret) {
virDomainObjAssignDef(vm, vmdef, false, NULL);
vmdef = NULL;
}
}
cleanup:
virDomainDefFree(vmdef);
virDomainDeviceDefFree(dev);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainAttachDevice(virDomainPtr dom, const char *xml)
{
return libxlDomainAttachDeviceFlags(dom, xml,
VIR_DOMAIN_DEVICE_MODIFY_LIVE);
}
static int
libxlDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm = NULL;
virDomainDefPtr vmdef = NULL;
virDomainDeviceDefPtr dev = NULL;
libxlDomainObjPrivatePtr priv;
int ret = -1;
virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_LIVE |
VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainDetachDeviceFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (virDomainObjIsActive(vm)) {
if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT)
flags |= VIR_DOMAIN_DEVICE_MODIFY_LIVE;
} else {
if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT)
flags |= VIR_DOMAIN_DEVICE_MODIFY_CONFIG;
/* check consistency between flags and the vm state */
if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
}
if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot modify device on transient domain"));
goto cleanup;
}
priv = vm->privateData;
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
/* Make a copy for updated domain. */
if (!(vmdef = virDomainObjCopyPersistentDef(vm, cfg->caps,
driver->xmlopt)))
goto cleanup;
if ((ret = libxlDomainDetachDeviceConfig(vmdef, dev)) < 0)
goto cleanup;
} else {
ret = 0;
}
if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) {
/* If dev exists it was created to modify the domain config. Free it. */
virDomainDeviceDefFree(dev);
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
if ((ret = libxlDomainDetachDeviceLive(priv, vm, dev)) < 0)
goto cleanup;
/*
* update domain status forcibly because the domain status may be
* changed even if we attach the device failed.
*/
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
ret = -1;
}
/* Finally, if no error until here, we can save config. */
if (!ret && (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG)) {
ret = virDomainSaveConfig(cfg->configDir, vmdef);
if (!ret) {
virDomainObjAssignDef(vm, vmdef, false, NULL);
vmdef = NULL;
}
}
cleanup:
virDomainDefFree(vmdef);
virDomainDeviceDefFree(dev);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
}
static int
libxlDomainDetachDevice(virDomainPtr dom, const char *xml)
{
return libxlDomainDetachDeviceFlags(dom, xml,
VIR_DOMAIN_DEVICE_MODIFY_LIVE);
}
static int
libxlDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm = NULL;
virDomainDefPtr vmdef = NULL;
virDomainDeviceDefPtr dev = NULL;
libxlDomainObjPrivatePtr priv;
int ret = -1;
virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_LIVE |
VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainUpdateDeviceFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (virDomainObjIsActive(vm)) {
if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT)
flags |= VIR_DOMAIN_DEVICE_MODIFY_LIVE;
} else {
if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT)
flags |= VIR_DOMAIN_DEVICE_MODIFY_CONFIG;
/* check consistency between flags and the vm state */
if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is not running"));
goto cleanup;
}
}
if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot modify device on transient domain"));
goto cleanup;
}
priv = vm->privateData;
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
/* Make a copy for updated domain. */
if (!(vmdef = virDomainObjCopyPersistentDef(vm, cfg->caps,
driver->xmlopt)))
goto cleanup;
if ((ret = libxlDomainUpdateDeviceConfig(vmdef, dev)) < 0)
goto cleanup;
} else {
ret = 0;
}
if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) {
/* If dev exists it was created to modify the domain config. Free it. */
virDomainDeviceDefFree(dev);
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt,
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
if ((ret = libxlDomainUpdateDeviceLive(priv, vm, dev)) < 0)
goto cleanup;
/*
* update domain status forcibly because the domain status may be
* changed even if we attach the device failed.
*/
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
ret = -1;
}
/* Finally, if no error until here, we can save config. */
if (!ret && (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG)) {
ret = virDomainSaveConfig(cfg->configDir, vmdef);
if (!ret) {
virDomainObjAssignDef(vm, vmdef, false, NULL);
vmdef = NULL;
}
}
cleanup:
virDomainDefFree(vmdef);
virDomainDeviceDefFree(dev);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
}
2011-03-21 09:35:53 +00:00
static unsigned long long
libxlNodeGetFreeMemory(virConnectPtr conn)
{
libxl_physinfo phy_info;
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
unsigned long long ret = 0;
2011-03-21 09:35:53 +00:00
if (virNodeGetFreeMemoryEnsureACL(conn) < 0)
goto cleanup;
if (libxl_get_physinfo(cfg->ctx, &phy_info)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxl_get_physinfo_info failed"));
goto cleanup;
2011-03-21 09:35:53 +00:00
}
ret = phy_info.free_pages * cfg->verInfo->pagesize;
cleanup:
virObjectUnref(cfg);
return ret;
2011-03-21 09:35:53 +00:00
}
static int
libxlNodeGetCellsFreeMemory(virConnectPtr conn,
unsigned long long *freeMems,
int startCell,
int maxCells)
{
int n, lastCell, numCells;
int ret = -1, nr_nodes = 0;
libxl_numainfo *numa_info = NULL;
libxlDriverPrivatePtr driver = conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
if (virNodeGetCellsFreeMemoryEnsureACL(conn) < 0)
goto cleanup;
numa_info = libxl_get_numainfo(cfg->ctx, &nr_nodes);
if (numa_info == NULL || nr_nodes == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxl_get_numainfo failed"));
goto cleanup;
}
/* Check/sanitize the cell range */
if (startCell >= nr_nodes) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("start cell %d out of range (0-%d)"),
startCell, nr_nodes - 1);
goto cleanup;
}
lastCell = startCell + maxCells - 1;
if (lastCell >= nr_nodes)
lastCell = nr_nodes - 1;
for (numCells = 0, n = startCell; n <= lastCell; n++) {
if (numa_info[n].size == LIBXL_NUMAINFO_INVALID_ENTRY)
freeMems[numCells++] = 0;
else
freeMems[numCells++] = numa_info[n].free;
}
ret = numCells;
cleanup:
libxl_numainfo_list_free(numa_info, nr_nodes);
virObjectUnref(cfg);
return ret;
}
static int
libxlConnectDomainEventRegister(virConnectPtr conn,
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
virConnectDomainEventCallback callback,
void *opaque,
virFreeCallback freecb)
{
libxlDriverPrivatePtr driver = conn->privateData;
if (virConnectDomainEventRegisterEnsureACL(conn) < 0)
return -1;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
if (virDomainEventStateRegister(conn,
driver->domainEventState,
callback, opaque, freecb) < 0)
return -1;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
return 0;
}
static int
libxlConnectDomainEventDeregister(virConnectPtr conn,
virConnectDomainEventCallback callback)
{
libxlDriverPrivatePtr driver = conn->privateData;
if (virConnectDomainEventDeregisterEnsureACL(conn) < 0)
return -1;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
if (virDomainEventStateDeregister(conn,
driver->domainEventState,
callback) < 0)
return -1;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
return 0;
}
static int
libxlDomainGetAutostart(virDomainPtr dom, int *autostart)
{
virDomainObjPtr vm;
int ret = -1;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetAutostartEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
*autostart = vm->autostart;
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainSetAutostart(virDomainPtr dom, int autostart)
{
libxlDriverPrivatePtr driver = dom->conn->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
virDomainObjPtr vm;
char *configFile = NULL, *autostartLink = NULL;
int ret = -1;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainSetAutostartEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cannot set autostart for transient domain"));
goto cleanup;
}
autostart = (autostart != 0);
if (vm->autostart != autostart) {
if (!(configFile = virDomainConfigFile(cfg->configDir, vm->def->name)))
goto cleanup;
if (!(autostartLink = virDomainConfigFile(cfg->autostartDir, vm->def->name)))
goto cleanup;
if (autostart) {
if (virFileMakePath(cfg->autostartDir) < 0) {
virReportSystemError(errno,
_("cannot create autostart directory %s"),
cfg->autostartDir);
goto cleanup;
}
if (symlink(configFile, autostartLink) < 0) {
virReportSystemError(errno,
_("Failed to create symlink '%s to '%s'"),
autostartLink, configFile);
goto cleanup;
}
} else {
if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
virReportSystemError(errno,
_("Failed to delete symlink '%s'"),
autostartLink);
goto cleanup;
}
}
vm->autostart = autostart;
}
ret = 0;
cleanup:
VIR_FREE(configFile);
VIR_FREE(autostartLink);
if (vm)
virObjectUnlock(vm);
virObjectUnref(cfg);
return ret;
}
static char *
libxlDomainGetSchedulerType(virDomainPtr dom, int *nparams)
{
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
char * ret = NULL;
const char *name = NULL;
libxl_scheduler sched_id;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetSchedulerTypeEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
sched_id = libxl_get_scheduler(priv->ctx);
if (nparams)
*nparams = 0;
switch (sched_id) {
case LIBXL_SCHEDULER_SEDF:
name = "sedf";
break;
case LIBXL_SCHEDULER_CREDIT:
name = "credit";
if (nparams)
*nparams = XEN_SCHED_CREDIT_NPARAM;
break;
case LIBXL_SCHEDULER_CREDIT2:
name = "credit2";
break;
case LIBXL_SCHEDULER_ARINC653:
name = "arinc653";
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to get scheduler id for domain '%d'"
" with libxenlight"), dom->id);
goto cleanup;
}
ignore_value(VIR_STRDUP(ret, name));
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainGetSchedulerParametersFlags(virDomainPtr dom,
virTypedParameterPtr params,
int *nparams,
unsigned int flags)
{
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
libxl_domain_sched_params sc_info;
libxl_scheduler sched_id;
int ret = -1;
virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
/* We don't return strings, and thus trivially support this flag. */
flags &= ~VIR_TYPED_PARAM_STRING_OKAY;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetSchedulerParametersFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
sched_id = libxl_get_scheduler(priv->ctx);
if (sched_id != LIBXL_SCHEDULER_CREDIT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Only 'credit' scheduler is supported"));
goto cleanup;
}
if (libxl_domain_sched_params_get(priv->ctx, dom->id, &sc_info) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to get scheduler parameters for domain '%d'"
" with libxenlight"), dom->id);
goto cleanup;
}
if (virTypedParameterAssign(&params[0], VIR_DOMAIN_SCHEDULER_WEIGHT,
VIR_TYPED_PARAM_UINT, sc_info.weight) < 0)
goto cleanup;
if (*nparams > 1) {
if (virTypedParameterAssign(&params[0], VIR_DOMAIN_SCHEDULER_CAP,
VIR_TYPED_PARAM_UINT, sc_info.cap) < 0)
goto cleanup;
}
if (*nparams > XEN_SCHED_CREDIT_NPARAM)
*nparams = XEN_SCHED_CREDIT_NPARAM;
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainGetSchedulerParameters(virDomainPtr dom, virTypedParameterPtr params,
int *nparams)
{
return libxlDomainGetSchedulerParametersFlags(dom, params, nparams, 0);
}
static int
libxlDomainSetSchedulerParametersFlags(virDomainPtr dom,
virTypedParameterPtr params,
int nparams,
unsigned int flags)
{
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
libxl_domain_sched_params sc_info;
int sched_id;
size_t i;
int ret = -1;
virCheckFlags(0, -1);
if (virTypedParamsValidate(params, nparams,
VIR_DOMAIN_SCHEDULER_WEIGHT,
VIR_TYPED_PARAM_UINT,
VIR_DOMAIN_SCHEDULER_CAP,
VIR_TYPED_PARAM_UINT,
NULL) < 0)
return -1;
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainSetSchedulerParametersFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
sched_id = libxl_get_scheduler(priv->ctx);
if (sched_id != LIBXL_SCHEDULER_CREDIT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Only 'credit' scheduler is supported"));
goto cleanup;
}
if (libxl_domain_sched_params_get(priv->ctx, dom->id, &sc_info) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to get scheduler parameters for domain '%d'"
" with libxenlight"), dom->id);
goto cleanup;
}
for (i = 0; i < nparams; ++i) {
virTypedParameterPtr param = &params[i];
if (STREQ(param->field, VIR_DOMAIN_SCHEDULER_WEIGHT))
sc_info.weight = params[i].value.ui;
else if (STREQ(param->field, VIR_DOMAIN_SCHEDULER_CAP))
sc_info.cap = params[i].value.ui;
}
if (libxl_domain_sched_params_set(priv->ctx, dom->id, &sc_info) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to set scheduler parameters for domain '%d'"
" with libxenlight"), dom->id);
goto cleanup;
}
ret = 0;
cleanup:
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainOpenConsole(virDomainPtr dom,
const char *dev_name,
virStreamPtr st,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
int ret = -1;
virDomainChrDefPtr chr = NULL;
libxlDomainObjPrivatePtr priv;
char *console = NULL;
virCheckFlags(VIR_DOMAIN_CONSOLE_FORCE, -1);
if (dev_name) {
/* XXX support device aliases in future */
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Named device aliases are not supported"));
goto cleanup;
}
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainOpenConsoleEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain is not running"));
goto cleanup;
}
priv = vm->privateData;
if (vm->def->nserials)
chr = vm->def->serials[0];
if (!chr) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find character device %s"),
NULLSTR(dev_name));
goto cleanup;
}
if (chr->source.type != VIR_DOMAIN_CHR_TYPE_PTY) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("character device %s is not using a PTY"),
NULLSTR(dev_name));
goto cleanup;
}
ret = libxl_primary_console_get_tty(priv->ctx, vm->def->id, &console);
if (ret)
goto cleanup;
if (VIR_STRDUP(chr->source.data.file.path, console) < 0)
goto cleanup;
/* handle mutually exclusive access to console devices */
ret = virChrdevOpen(priv->devs,
&chr->source,
st,
(flags & VIR_DOMAIN_CONSOLE_FORCE) != 0);
if (ret == 1) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Active console session exists for this domain"));
ret = -1;
}
cleanup:
VIR_FREE(console);
if (vm)
virObjectUnlock(vm);
return ret;
}
static int
libxlDomainSetSchedulerParameters(virDomainPtr dom, virTypedParameterPtr params,
int nparams)
{
return libxlDomainSetSchedulerParametersFlags(dom, params, nparams, 0);
}
/* NUMA node affinity information is available through libxl
* starting from Xen 4.3. */
#ifdef LIBXL_HAVE_DOMAIN_NODEAFFINITY
/* Number of Xen NUMA parameters */
# define LIBXL_NUMA_NPARAM 2
static int
libxlDomainGetNumaParameters(virDomainPtr dom,
virTypedParameterPtr params,
int *nparams,
unsigned int flags)
{
libxlDomainObjPrivatePtr priv;
virDomainObjPtr vm;
libxl_bitmap nodemap;
virBitmapPtr nodes = NULL;
char *nodeset = NULL;
int rc, ret = -1;
size_t i, j;
/* In Xen 4.3, it is possible to query the NUMA node affinity of a domain
* via libxl, but not to change it. We therefore only allow AFFECT_LIVE. */
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_TYPED_PARAM_STRING_OKAY, -1);
/* We blindly return a string, and let libvirt.c and remote_driver.c do
* the filtering on behalf of older clients that can't parse it. */
flags &= ~VIR_TYPED_PARAM_STRING_OKAY;
libxl_bitmap_init(&nodemap);
if (!(vm = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetNumaParametersEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not running"));
goto cleanup;
}
priv = vm->privateData;
if ((*nparams) == 0) {
*nparams = LIBXL_NUMA_NPARAM;
ret = 0;
goto cleanup;
}
for (i = 0; i < LIBXL_NUMA_NPARAM && i < *nparams; i++) {
virMemoryParameterPtr param = &params[i];
int numnodes;
switch (i) {
case 0:
/* NUMA mode */
/* Xen implements something that is really close to numactl's
* 'interleave' policy (see `man 8 numactl' for details). */
if (virTypedParameterAssign(param, VIR_DOMAIN_NUMA_MODE,
VIR_TYPED_PARAM_INT,
VIR_DOMAIN_NUMATUNE_MEM_INTERLEAVE) < 0)
goto cleanup;
break;
case 1:
/* Node affinity */
/* Let's allocate both libxl and libvirt bitmaps */
numnodes = libxl_get_max_nodes(priv->ctx);
if (numnodes <= 0)
goto cleanup;
if (libxl_node_bitmap_alloc(priv->ctx, &nodemap, 0) ||
!(nodes = virBitmapNew(numnodes))) {
virReportOOMError();
goto cleanup;
}
rc = libxl_domain_get_nodeaffinity(priv->ctx,
vm->def->id,
&nodemap);
if (rc != 0) {
virReportSystemError(-rc, "%s",
_("unable to get numa affinity"));
goto cleanup;
}
/* First, we convert libxl_bitmap into virBitmap. After that,
* we format virBitmap as a string that can be returned. */
virBitmapClearAll(nodes);
libxl_for_each_set_bit(j, nodemap) {
if (virBitmapSetBit(nodes, j)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Node %zu out of range"), j);
goto cleanup;
}
}
nodeset = virBitmapFormat(nodes);
if (!nodeset && VIR_STRDUP(nodeset, "") < 0)
goto cleanup;
if (virTypedParameterAssign(param, VIR_DOMAIN_NUMA_NODESET,
VIR_TYPED_PARAM_STRING, nodeset) < 0)
goto cleanup;
nodeset = NULL;
break;
}
}
if (*nparams > LIBXL_NUMA_NPARAM)
*nparams = LIBXL_NUMA_NPARAM;
ret = 0;
cleanup:
VIR_FREE(nodeset);
virBitmapFree(nodes);
libxl_bitmap_dispose(&nodemap);
if (vm)
virObjectUnlock(vm);
return ret;
}
#endif
static int
libxlDomainIsActive(virDomainPtr dom)
{
virDomainObjPtr obj;
int ret = -1;
if (!(obj = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainIsActiveEnsureACL(dom->conn, obj->def) < 0)
goto cleanup;
ret = virDomainObjIsActive(obj);
cleanup:
if (obj)
virObjectUnlock(obj);
return ret;
}
static int
libxlDomainIsPersistent(virDomainPtr dom)
{
virDomainObjPtr obj;
int ret = -1;
if (!(obj = libxlDomObjFromDomain(dom)))
goto cleanup;
if (virDomainIsPersistentEnsureACL(dom->conn, obj->def) < 0)
goto cleanup;
ret = obj->persistent;
cleanup:
if (obj)
virObjectUnlock(obj);
return ret;
}
2011-04-06 08:58:39 +00:00
static int
libxlDomainIsUpdated(virDomainPtr dom)
{
virDomainObjPtr vm;
int ret = -1;
if (!(vm = libxlDomObjFromDomain(dom)))
2011-04-06 08:58:39 +00:00
goto cleanup;
if (virDomainIsUpdatedEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
2011-04-06 08:58:39 +00:00
ret = vm->updated;
cleanup:
if (vm)
virObjectUnlock(vm);
2011-04-06 08:58:39 +00:00
return ret;
}
static int
libxlConnectDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eventID,
virConnectDomainEventGenericCallback callback,
void *opaque, virFreeCallback freecb)
{
libxlDriverPrivatePtr driver = conn->privateData;
int ret;
if (virConnectDomainEventRegisterAnyEnsureACL(conn) < 0)
return -1;
if (virDomainEventStateRegisterID(conn,
driver->domainEventState,
dom, eventID, callback, opaque,
freecb, &ret) < 0)
ret = -1;
return ret;
}
static int
libxlConnectDomainEventDeregisterAny(virConnectPtr conn, int callbackID)
{
libxlDriverPrivatePtr driver = conn->privateData;
if (virConnectDomainEventDeregisterAnyEnsureACL(conn) < 0)
return -1;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
if (virObjectEventStateDeregisterID(conn,
driver->domainEventState,
callbackID) < 0)
return -1;
event: make deregister return value match docs Ever since their introduction (commit 1509b80 in v0.5.0 for virConnectDomainEventRegister, commit 4445723 in v0.8.0 for virConnectDomainEventDeregisterAny), the event deregistration functions have been documented as returning 0 on success; likewise for older registration (only the newer RegisterAny must return a non-zero callbackID). And now that we are adding virConnectNetworkEventDeregisterAny for v1.2.1, it should have the same semantics. Fortunately, all of the stateful drivers have been obeying the docs and returning 0, thanks to the way the remote_driver tracks things (in fact, the RPC wire protocol is unable to send a return value for DomainEventRegisterAny, at least not without adding a new RPC number). Well, except for vbox, which was always failing deregistration, due to failure to set the return value to anything besides its initial -1. But for local drivers, such as test:///default, we've been returning non-zero numbers; worse, the non-zero numbers have differed over time. For example, in Fedora 12 (libvirt 0.8.2), calling Register twice would return 0 and 1 [the callbackID generated under the hood]; while in Fedora 20 (libvirt 1.1.3), it returns 1 and 2 [the number of callbacks registered for that event type]. Since we have changed the behavior over time, and since it differs by local vs. remote, we can safely argue that no one could have been reasonably relying on any particular behavior, so we might as well obey the docs, as well as prepare callers that might deal with older clients to not be surprised if the docs are not strictly followed. For consistency, this patch fixes the code for all drivers, even though it only makes an impact for vbox and for local drivers. By fixing all drivers, future copy and paste from a remote driver to a local driver is less likely to reintroduce the bug. Finally, update the testsuite to gain some coverage of the issue for local drivers, including the first test of old-style domain event registration via function pointer instead of event id. * src/libvirt.c (virConnectDomainEventRegister) (virConnectDomainEventDeregister) (virConnectDomainEventDeregisterAny): Clarify docs. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventDeregister) (libxlConnectDomainEventDeregisterAny): Match documentation. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventDeregister) (lxcConnectDomainEventDeregisterAny): Likewise. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventDeregister) (testConnectDomainEventDeregisterAny) (testConnectNetworkEventDeregisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventDeregister) (umlConnectDomainEventDeregisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxConnectDomainEventRegister) (vboxConnectDomainEventDeregister) (vboxConnectDomainEventDeregisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventDeregister) (xenUnifiedConnectDomainEventDeregisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventDeregisterAny): Likewise. * tests/objecteventtest.c (testDomainCreateXMLOld): New test. (mymain): Run it. (testDomainCreateXML): Check return values. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-01-03 21:21:17 +00:00
return 0;
}
static int
libxlConnectIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 1;
}
static int
libxlConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
libxlDriverPrivatePtr driver = conn->privateData;
int ret = -1;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
if (virConnectListAllDomainsEnsureACL(conn) < 0)
return -1;
ret = virDomainObjListExport(driver->domains, conn, domains,
virConnectListAllDomainsCheckACL, flags);
return ret;
}
/* Which features are supported by this driver? */
static int
libxlConnectSupportsFeature(virConnectPtr conn, int feature)
{
if (virConnectSupportsFeatureEnsureACL(conn) < 0)
return -1;
switch (feature) {
case VIR_DRV_FEATURE_TYPED_PARAM_STRING:
return 1;
default:
return 0;
}
}
static virDriver libxlDriver = {
.no = VIR_DRV_LIBXL,
.name = "xenlight",
.connectOpen = libxlConnectOpen, /* 0.9.0 */
.connectClose = libxlConnectClose, /* 0.9.0 */
.connectGetType = libxlConnectGetType, /* 0.9.0 */
.connectGetVersion = libxlConnectGetVersion, /* 0.9.0 */
.connectGetHostname = libxlConnectGetHostname, /* 0.9.0 */
.connectGetSysinfo = libxlConnectGetSysinfo, /* 1.1.0 */
.connectGetMaxVcpus = libxlConnectGetMaxVcpus, /* 0.9.0 */
.nodeGetInfo = libxlNodeGetInfo, /* 0.9.0 */
.connectGetCapabilities = libxlConnectGetCapabilities, /* 0.9.0 */
.connectListDomains = libxlConnectListDomains, /* 0.9.0 */
.connectNumOfDomains = libxlConnectNumOfDomains, /* 0.9.0 */
.connectListAllDomains = libxlConnectListAllDomains, /* 0.9.13 */
.domainCreateXML = libxlDomainCreateXML, /* 0.9.0 */
.domainLookupByID = libxlDomainLookupByID, /* 0.9.0 */
.domainLookupByUUID = libxlDomainLookupByUUID, /* 0.9.0 */
.domainLookupByName = libxlDomainLookupByName, /* 0.9.0 */
.domainSuspend = libxlDomainSuspend, /* 0.9.0 */
.domainResume = libxlDomainResume, /* 0.9.0 */
.domainShutdown = libxlDomainShutdown, /* 0.9.0 */
.domainShutdownFlags = libxlDomainShutdownFlags, /* 0.9.10 */
.domainReboot = libxlDomainReboot, /* 0.9.0 */
.domainDestroy = libxlDomainDestroy, /* 0.9.0 */
.domainDestroyFlags = libxlDomainDestroyFlags, /* 0.9.4 */
.domainGetOSType = libxlDomainGetOSType, /* 0.9.0 */
.domainGetMaxMemory = libxlDomainGetMaxMemory, /* 0.9.0 */
.domainSetMaxMemory = libxlDomainSetMaxMemory, /* 0.9.2 */
.domainSetMemory = libxlDomainSetMemory, /* 0.9.0 */
.domainSetMemoryFlags = libxlDomainSetMemoryFlags, /* 0.9.0 */
.domainGetInfo = libxlDomainGetInfo, /* 0.9.0 */
.domainGetState = libxlDomainGetState, /* 0.9.2 */
.domainSave = libxlDomainSave, /* 0.9.2 */
.domainSaveFlags = libxlDomainSaveFlags, /* 0.9.4 */
.domainRestore = libxlDomainRestore, /* 0.9.2 */
.domainRestoreFlags = libxlDomainRestoreFlags, /* 0.9.4 */
.domainCoreDump = libxlDomainCoreDump, /* 0.9.2 */
.domainSetVcpus = libxlDomainSetVcpus, /* 0.9.0 */
.domainSetVcpusFlags = libxlDomainSetVcpusFlags, /* 0.9.0 */
.domainGetVcpusFlags = libxlDomainGetVcpusFlags, /* 0.9.0 */
.domainPinVcpu = libxlDomainPinVcpu, /* 0.9.0 */
.domainPinVcpuFlags = libxlDomainPinVcpuFlags, /* 1.2.1 */
.domainGetVcpus = libxlDomainGetVcpus, /* 0.9.0 */
.domainGetVcpuPinInfo = libxlDomainGetVcpuPinInfo, /* 1.2.1 */
.domainGetXMLDesc = libxlDomainGetXMLDesc, /* 0.9.0 */
.connectDomainXMLFromNative = libxlConnectDomainXMLFromNative, /* 0.9.0 */
.connectDomainXMLToNative = libxlConnectDomainXMLToNative, /* 0.9.0 */
.connectListDefinedDomains = libxlConnectListDefinedDomains, /* 0.9.0 */
.connectNumOfDefinedDomains = libxlConnectNumOfDefinedDomains, /* 0.9.0 */
.domainCreate = libxlDomainCreate, /* 0.9.0 */
.domainCreateWithFlags = libxlDomainCreateWithFlags, /* 0.9.0 */
.domainDefineXML = libxlDomainDefineXML, /* 0.9.0 */
.domainUndefine = libxlDomainUndefine, /* 0.9.0 */
.domainUndefineFlags = libxlDomainUndefineFlags, /* 0.9.4 */
.domainAttachDevice = libxlDomainAttachDevice, /* 0.9.2 */
.domainAttachDeviceFlags = libxlDomainAttachDeviceFlags, /* 0.9.2 */
.domainDetachDevice = libxlDomainDetachDevice, /* 0.9.2 */
.domainDetachDeviceFlags = libxlDomainDetachDeviceFlags, /* 0.9.2 */
.domainUpdateDeviceFlags = libxlDomainUpdateDeviceFlags, /* 0.9.2 */
.domainGetAutostart = libxlDomainGetAutostart, /* 0.9.0 */
.domainSetAutostart = libxlDomainSetAutostart, /* 0.9.0 */
.domainGetSchedulerType = libxlDomainGetSchedulerType, /* 0.9.0 */
.domainGetSchedulerParameters = libxlDomainGetSchedulerParameters, /* 0.9.0 */
.domainGetSchedulerParametersFlags = libxlDomainGetSchedulerParametersFlags, /* 0.9.2 */
.domainSetSchedulerParameters = libxlDomainSetSchedulerParameters, /* 0.9.0 */
.domainSetSchedulerParametersFlags = libxlDomainSetSchedulerParametersFlags, /* 0.9.2 */
#ifdef LIBXL_HAVE_DOMAIN_NODEAFFINITY
.domainGetNumaParameters = libxlDomainGetNumaParameters, /* 1.1.1 */
#endif
.nodeGetFreeMemory = libxlNodeGetFreeMemory, /* 0.9.0 */
.nodeGetCellsFreeMemory = libxlNodeGetCellsFreeMemory, /* 1.1.1 */
.connectDomainEventRegister = libxlConnectDomainEventRegister, /* 0.9.0 */
.connectDomainEventDeregister = libxlConnectDomainEventDeregister, /* 0.9.0 */
.domainManagedSave = libxlDomainManagedSave, /* 0.9.2 */
.domainHasManagedSaveImage = libxlDomainHasManagedSaveImage, /* 0.9.2 */
.domainManagedSaveRemove = libxlDomainManagedSaveRemove, /* 0.9.2 */
.domainOpenConsole = libxlDomainOpenConsole, /* 1.1.2 */
.domainIsActive = libxlDomainIsActive, /* 0.9.0 */
.domainIsPersistent = libxlDomainIsPersistent, /* 0.9.0 */
.domainIsUpdated = libxlDomainIsUpdated, /* 0.9.0 */
.connectDomainEventRegisterAny = libxlConnectDomainEventRegisterAny, /* 0.9.0 */
.connectDomainEventDeregisterAny = libxlConnectDomainEventDeregisterAny, /* 0.9.0 */
.connectIsAlive = libxlConnectIsAlive, /* 0.9.8 */
.connectSupportsFeature = libxlConnectSupportsFeature, /* 1.1.1 */
};
static virStateDriver libxlStateDriver = {
.name = "LIBXL",
.stateInitialize = libxlStateInitialize,
.stateAutoStart = libxlStateAutoStart,
.stateCleanup = libxlStateCleanup,
.stateReload = libxlStateReload,
};
int
libxlRegister(void)
{
if (virRegisterDriver(&libxlDriver) < 0)
return -1;
if (virRegisterStateDriver(&libxlStateDriver) < 0)
return -1;
return 0;
}