mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-01 19:01:14 +00:00
1cca1d25a0
This function now only enables domain death events. Simply call libxl_evenable_domain_death() instead of an unnecessary wrapper. Signed-off-by: Jim Fehlig <jfehlig@suse.com>
5099 lines
149 KiB
C
5099 lines
149 KiB
C
/*
|
|
* libxl_driver.c: core driver methods for managing libxenlight domains
|
|
*
|
|
* Copyright (C) 2006-2014 Red Hat, Inc.
|
|
* Copyright (C) 2011-2015 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"
|
|
#include "virlog.h"
|
|
#include "virerror.h"
|
|
#include "virconf.h"
|
|
#include "datatypes.h"
|
|
#include "virfile.h"
|
|
#include "viralloc.h"
|
|
#include "viruuid.h"
|
|
#include "vircommand.h"
|
|
#include "libxl_domain.h"
|
|
#include "libxl_driver.h"
|
|
#include "libxl_conf.h"
|
|
#include "libxl_migration.h"
|
|
#include "xen_xm.h"
|
|
#include "xen_sxpr.h"
|
|
#include "xen_xl.h"
|
|
#include "virtypedparam.h"
|
|
#include "viruri.h"
|
|
#include "virstring.h"
|
|
#include "virsysinfo.h"
|
|
#include "viraccessapicheck.h"
|
|
#include "viratomic.h"
|
|
#include "virhostdev.h"
|
|
#include "network/bridge_driver.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_LIBXL
|
|
|
|
VIR_LOG_INIT("libxl.libxl_driver");
|
|
|
|
#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_XL "xen-xl"
|
|
#define LIBXL_CONFIG_FORMAT_XM "xen-xm"
|
|
#define LIBXL_CONFIG_FORMAT_SEXPR "xen-sxpr"
|
|
|
|
#define HYPERVISOR_CAPABILITIES "/proc/xen/capabilities"
|
|
|
|
/* Number of Xen scheduler parameters */
|
|
#define XEN_SCHED_CREDIT_NPARAM 2
|
|
|
|
|
|
static libxlDriverPrivatePtr libxl_driver;
|
|
|
|
/* Object used to store info related to libxl event registrations */
|
|
typedef struct _libxlOSEventHookInfo libxlOSEventHookInfo;
|
|
typedef libxlOSEventHookInfo *libxlOSEventHookInfoPtr;
|
|
struct _libxlOSEventHookInfo {
|
|
libxl_ctx *ctx;
|
|
void *xl_priv;
|
|
int id;
|
|
};
|
|
|
|
/* Function declarations */
|
|
static int
|
|
libxlDomainManagedSaveLoad(virDomainObjPtr vm,
|
|
void *opaque);
|
|
|
|
|
|
/* Function definitions */
|
|
static void
|
|
libxlOSEventHookInfoFree(void *obj)
|
|
{
|
|
VIR_FREE(obj);
|
|
}
|
|
|
|
static void
|
|
libxlFDEventCallback(int watch ATTRIBUTE_UNUSED,
|
|
int fd,
|
|
int vir_events,
|
|
void *fd_info)
|
|
{
|
|
libxlOSEventHookInfoPtr info = fd_info;
|
|
int events = 0;
|
|
|
|
if (vir_events & VIR_EVENT_HANDLE_READABLE)
|
|
events |= POLLIN;
|
|
if (vir_events & VIR_EVENT_HANDLE_WRITABLE)
|
|
events |= POLLOUT;
|
|
if (vir_events & VIR_EVENT_HANDLE_ERROR)
|
|
events |= POLLERR;
|
|
if (vir_events & VIR_EVENT_HANDLE_HANGUP)
|
|
events |= POLLHUP;
|
|
|
|
libxl_osevent_occurred_fd(info->ctx, info->xl_priv, fd, 0, events);
|
|
}
|
|
|
|
static int
|
|
libxlFDRegisterEventHook(void *priv,
|
|
int fd,
|
|
void **hndp,
|
|
short events,
|
|
void *xl_priv)
|
|
{
|
|
int vir_events = VIR_EVENT_HANDLE_ERROR;
|
|
libxlOSEventHookInfoPtr info;
|
|
|
|
if (VIR_ALLOC(info) < 0)
|
|
return -1;
|
|
|
|
info->ctx = priv;
|
|
info->xl_priv = xl_priv;
|
|
|
|
if (events & POLLIN)
|
|
vir_events |= VIR_EVENT_HANDLE_READABLE;
|
|
if (events & POLLOUT)
|
|
vir_events |= VIR_EVENT_HANDLE_WRITABLE;
|
|
|
|
info->id = virEventAddHandle(fd, vir_events, libxlFDEventCallback,
|
|
info, libxlOSEventHookInfoFree);
|
|
if (info->id < 0) {
|
|
VIR_FREE(info);
|
|
return -1;
|
|
}
|
|
|
|
*hndp = info;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlFDModifyEventHook(void *priv ATTRIBUTE_UNUSED,
|
|
int fd ATTRIBUTE_UNUSED,
|
|
void **hndp,
|
|
short events)
|
|
{
|
|
libxlOSEventHookInfoPtr info = *hndp;
|
|
int vir_events = VIR_EVENT_HANDLE_ERROR;
|
|
|
|
if (events & POLLIN)
|
|
vir_events |= VIR_EVENT_HANDLE_READABLE;
|
|
if (events & POLLOUT)
|
|
vir_events |= VIR_EVENT_HANDLE_WRITABLE;
|
|
|
|
virEventUpdateHandle(info->id, vir_events);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
libxlFDDeregisterEventHook(void *priv ATTRIBUTE_UNUSED,
|
|
int fd ATTRIBUTE_UNUSED,
|
|
void *hnd)
|
|
{
|
|
libxlOSEventHookInfoPtr info = hnd;
|
|
|
|
virEventRemoveHandle(info->id);
|
|
}
|
|
|
|
static void
|
|
libxlTimerCallback(int timer ATTRIBUTE_UNUSED, void *timer_info)
|
|
{
|
|
libxlOSEventHookInfoPtr info = timer_info;
|
|
|
|
/*
|
|
* libxl expects the event to be deregistered when calling
|
|
* libxl_osevent_occurred_timeout, but we dont want the event info
|
|
* destroyed. Disable the timeout and only remove it after returning
|
|
* from libxl.
|
|
*/
|
|
virEventUpdateTimeout(info->id, -1);
|
|
libxl_osevent_occurred_timeout(info->ctx, info->xl_priv);
|
|
virEventRemoveTimeout(info->id);
|
|
}
|
|
|
|
static int
|
|
libxlTimeoutRegisterEventHook(void *priv,
|
|
void **hndp,
|
|
struct timeval abs_t,
|
|
void *xl_priv)
|
|
{
|
|
libxlOSEventHookInfoPtr info;
|
|
struct timeval now;
|
|
struct timeval res;
|
|
static struct timeval zero;
|
|
int timeout;
|
|
|
|
if (VIR_ALLOC(info) < 0)
|
|
return -1;
|
|
|
|
info->ctx = priv;
|
|
info->xl_priv = xl_priv;
|
|
|
|
gettimeofday(&now, NULL);
|
|
timersub(&abs_t, &now, &res);
|
|
/* Ensure timeout is not overflowed */
|
|
if (timercmp(&res, &zero, <)) {
|
|
timeout = 0;
|
|
} else if (res.tv_sec > INT_MAX / 1000) {
|
|
timeout = INT_MAX;
|
|
} else {
|
|
timeout = res.tv_sec * 1000 + (res.tv_usec + 999) / 1000;
|
|
}
|
|
info->id = virEventAddTimeout(timeout, libxlTimerCallback,
|
|
info, libxlOSEventHookInfoFree);
|
|
if (info->id < 0) {
|
|
VIR_FREE(info);
|
|
return -1;
|
|
}
|
|
|
|
*hndp = info;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Note: There are two changes wrt timeouts starting with xen-unstable
|
|
* changeset 26469:
|
|
*
|
|
* 1. Timeout modify callbacks will only be invoked with an abs_t of {0,0},
|
|
* i.e. make the timeout fire immediately. Prior to this commit, timeout
|
|
* modify callbacks were never invoked.
|
|
*
|
|
* 2. Timeout deregister hooks will no longer be called.
|
|
*/
|
|
static int
|
|
libxlTimeoutModifyEventHook(void *priv ATTRIBUTE_UNUSED,
|
|
void **hndp,
|
|
struct timeval abs_t ATTRIBUTE_UNUSED)
|
|
{
|
|
libxlOSEventHookInfoPtr info = *hndp;
|
|
|
|
/* Make the timeout fire */
|
|
virEventUpdateTimeout(info->id, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
libxlTimeoutDeregisterEventHook(void *priv ATTRIBUTE_UNUSED,
|
|
void *hnd)
|
|
{
|
|
libxlOSEventHookInfoPtr info = hnd;
|
|
|
|
virEventRemoveTimeout(info->id);
|
|
}
|
|
|
|
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 int
|
|
libxlAutostartDomain(virDomainObjPtr vm,
|
|
void *opaque)
|
|
{
|
|
libxlDriverPrivatePtr driver = opaque;
|
|
virErrorPtr err;
|
|
int ret = -1;
|
|
|
|
virObjectLock(vm);
|
|
virResetLastError();
|
|
|
|
if (vm->autostart && !virDomainObjIsActive(vm) &&
|
|
libxlDomainStart(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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
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;
|
|
|
|
/* Update hostdev state */
|
|
if (virHostdevUpdateDomainActiveDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def, VIR_HOSTDEV_SP_PCI) < 0)
|
|
goto out;
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNKNOWN);
|
|
|
|
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
|
|
driver->inhibitCallback(true, driver->inhibitOpaque);
|
|
|
|
/* Enable domain death events */
|
|
libxl_evenable_domain_death(priv->ctx, vm->def->id, 0, &priv->deathW);
|
|
|
|
virObjectUnlock(vm);
|
|
return 0;
|
|
|
|
out:
|
|
libxlDomainCleanup(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->hostdevMgr);
|
|
virObjectUnref(libxl_driver->config);
|
|
virObjectUnref(libxl_driver->xmlopt);
|
|
virObjectUnref(libxl_driver->domains);
|
|
virObjectUnref(libxl_driver->reservedVNCPorts);
|
|
virObjectUnref(libxl_driver->migrationPorts);
|
|
|
|
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;
|
|
int status;
|
|
char *output = NULL;
|
|
|
|
/* Don't load if non-root */
|
|
if (!privileged) {
|
|
VIR_INFO("Not running privileged, disabling libxenlight driver");
|
|
return ret;
|
|
}
|
|
|
|
if (!virFileExists(HYPERVISOR_CAPABILITIES)) {
|
|
VIR_INFO("Disabling driver as " HYPERVISOR_CAPABILITIES
|
|
" does not exist");
|
|
return ret;
|
|
}
|
|
/*
|
|
* Don't load if not running on a Xen control domain (dom0). It is not
|
|
* sufficient to check for the file to exist as any guest can mount
|
|
* xenfs to /proc/xen.
|
|
*/
|
|
status = virFileReadAll(HYPERVISOR_CAPABILITIES, 10, &output);
|
|
if (status >= 0)
|
|
status = strncmp(output, "control_d", 9);
|
|
VIR_FREE(output);
|
|
if (status) {
|
|
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 */
|
|
if (virFileExists("/usr/sbin/xend")) {
|
|
virCommandPtr cmd;
|
|
|
|
cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL);
|
|
if (virCommandRun(cmd, NULL) == 0) {
|
|
VIR_INFO("Legacy xen tool stack seems to be in use, disabling "
|
|
"libxenlight driver.");
|
|
} else {
|
|
ret = true;
|
|
}
|
|
virCommandFree(cmd);
|
|
} else {
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Callbacks wrapping libvirt's event loop interface */
|
|
static const libxl_osevent_hooks libxl_osevent_callbacks = {
|
|
.fd_register = libxlFDRegisterEventHook,
|
|
.fd_modify = libxlFDModifyEventHook,
|
|
.fd_deregister = libxlFDDeregisterEventHook,
|
|
.timeout_register = libxlTimeoutRegisterEventHook,
|
|
.timeout_modify = libxlTimeoutModifyEventHook,
|
|
.timeout_deregister = libxlTimeoutDeregisterEventHook,
|
|
};
|
|
|
|
static const libxl_childproc_hooks libxl_child_hooks = {
|
|
#ifdef LIBXL_HAVE_SIGCHLD_OWNER_SELECTIVE_REAP
|
|
.chldowner = libxl_sigchld_owner_libxl_always_selective_reap,
|
|
#else
|
|
.chldowner = libxl_sigchld_owner_libxl,
|
|
#endif
|
|
};
|
|
|
|
const struct libxl_event_hooks ev_hooks = {
|
|
.event_occurs_mask = LIBXL_EVENTMASK_ALL,
|
|
.event_occurs = libxlDomainEventHandler,
|
|
.disaster = NULL,
|
|
};
|
|
|
|
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,
|
|
0)))
|
|
goto error;
|
|
|
|
/* Allocate bitmap for migration port reservation */
|
|
if (!(libxl_driver->migrationPorts =
|
|
virPortAllocatorNew(_("migration"),
|
|
LIBXL_MIGRATION_PORT_MIN,
|
|
LIBXL_MIGRATION_PORT_MAX, 0)))
|
|
goto error;
|
|
|
|
if (!(libxl_driver->domains = virDomainObjListNew()))
|
|
goto error;
|
|
|
|
if (!(libxl_driver->hostdevMgr = virHostdevManagerGetDefault()))
|
|
goto error;
|
|
|
|
if (!(cfg = libxlDriverConfigNew()))
|
|
goto error;
|
|
|
|
/* Register the callbacks providing access to libvirt's event loop */
|
|
libxl_osevent_register_hooks(cfg->ctx, &libxl_osevent_callbacks, cfg->ctx);
|
|
|
|
/* Setup child process handling. See $xen-src/tools/libxl/libxl_event.h */
|
|
libxl_childproc_setmode(cfg->ctx, &libxl_child_hooks, cfg->ctx);
|
|
|
|
/* Register callback to handle domain events */
|
|
libxl_event_register_callbacks(cfg->ctx, &ev_hooks, libxl_driver);
|
|
|
|
libxl_driver->config = cfg;
|
|
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;
|
|
}
|
|
if (virFileMakePath(cfg->autoDumpDir) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to create dump dir '%s': %s"),
|
|
cfg->autoDumpDir,
|
|
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 = libxlCreateXMLConf()))
|
|
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 (virBufferCheckError(&buf) < 0)
|
|
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 libxlDriverNodeGetInfo(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);
|
|
xml = virCapabilitiesFormatXML(cfg->caps);
|
|
|
|
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);
|
|
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
|
|
|
|
virCheckFlags(VIR_DOMAIN_START_PAUSED |
|
|
VIR_DOMAIN_START_VALIDATE, NULL);
|
|
|
|
if (flags & VIR_DOMAIN_START_VALIDATE)
|
|
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE;
|
|
|
|
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
|
|
1 << VIR_DOMAIN_VIRT_XEN,
|
|
parse_flags)))
|
|
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 (libxlDomainStart(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, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to suspend domain '%d' with libxenlight"),
|
|
vm->def->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, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to resume domain '%d' with libxenlight"),
|
|
vm->def->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(VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN |
|
|
VIR_DOMAIN_SHUTDOWN_PARAVIRT, -1);
|
|
if (flags == 0)
|
|
flags = VIR_DOMAIN_SHUTDOWN_PARAVIRT |
|
|
VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
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 (flags & VIR_DOMAIN_SHUTDOWN_PARAVIRT) {
|
|
ret = libxl_domain_shutdown(priv->ctx, vm->def->id);
|
|
if (ret == 0)
|
|
goto cleanup;
|
|
|
|
if (ret != ERROR_NOPARAVIRT) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to shutdown domain '%d' with libxenlight"),
|
|
vm->def->id);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
ret = -1;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN) {
|
|
ret = libxl_send_trigger(priv->ctx, vm->def->id,
|
|
LIBXL_TRIGGER_POWER, 0);
|
|
if (ret == 0)
|
|
goto cleanup;
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to shutdown domain '%d' with libxenlight"),
|
|
vm->def->id);
|
|
ret = -1;
|
|
}
|
|
|
|
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(VIR_DOMAIN_REBOOT_PARAVIRT, -1);
|
|
if (flags == 0)
|
|
flags = VIR_DOMAIN_REBOOT_PARAVIRT;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
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 (flags & VIR_DOMAIN_REBOOT_PARAVIRT) {
|
|
ret = libxl_domain_reboot(priv->ctx, vm->def->id);
|
|
if (ret == 0)
|
|
goto cleanup;
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to reboot domain '%d' with libxenlight"),
|
|
vm->def->id);
|
|
ret = -1;
|
|
}
|
|
|
|
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'"), vm->def->id);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (libxlDomainCleanupJob(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;
|
|
}
|
|
|
|
static unsigned long long
|
|
libxlDomainGetMaxMemory(virDomainPtr dom)
|
|
{
|
|
virDomainObjPtr vm;
|
|
unsigned long long ret = 0;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetMaxMemoryEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virDomainDefGetMemoryActual(vm->def);
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
libxlDomainObjPrivatePtr priv;
|
|
virDomainObjPtr vm;
|
|
virDomainDefPtr persistentDef = NULL;
|
|
bool isActive;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_MEM_LIVE |
|
|
VIR_DOMAIN_MEM_CONFIG |
|
|
VIR_DOMAIN_MEM_MAXIMUM, -1);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
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;
|
|
}
|
|
|
|
if (!isActive && (flags & VIR_DOMAIN_MEM_LIVE)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("cannot set memory on an inactive domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (!(persistentDef = virDomainObjGetPersistentDef(cfg->caps,
|
|
driver->xmlopt,
|
|
vm)))
|
|
goto endjob;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_MEM_MAXIMUM) {
|
|
/* resize the maximum memory */
|
|
|
|
if (flags & VIR_DOMAIN_MEM_LIVE) {
|
|
priv = vm->privateData;
|
|
if (libxl_domain_setmaxmem(priv->ctx, vm->def->id, newmem) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to set maximum memory for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_MEM_CONFIG) {
|
|
/* Help clang 2.8 decipher the logic flow. */
|
|
sa_assert(persistentDef);
|
|
virDomainDefSetMemoryInitial(persistentDef, newmem);
|
|
if (persistentDef->mem.cur_balloon > newmem)
|
|
persistentDef->mem.cur_balloon = newmem;
|
|
ret = virDomainSaveConfig(cfg->configDir, persistentDef);
|
|
goto endjob;
|
|
}
|
|
|
|
} else {
|
|
/* resize the current memory */
|
|
|
|
if (newmem > virDomainDefGetMemoryActual(vm->def)) {
|
|
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, vm->def->id, newmem, 0,
|
|
/* force */ 1);
|
|
virObjectLock(vm);
|
|
if (res < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to set memory for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_MEM_CONFIG) {
|
|
sa_assert(persistentDef);
|
|
persistentDef->mem.cur_balloon = newmem;
|
|
ret = virDomainSaveConfig(cfg->configDir, persistentDef);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
virObjectUnref(cfg);
|
|
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 = virDomainDefGetMemoryActual(vm->def);
|
|
} else {
|
|
if (libxl_domain_info(priv->ctx, &d_info, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxl_domain_info failed for domain '%d'"),
|
|
vm->def->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,
|
|
-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;
|
|
}
|
|
|
|
/* Unlock virDomainObj while saving domain */
|
|
virObjectUnlock(vm);
|
|
ret = libxl_domain_suspend(priv->ctx, vm->def->id, fd, 0, NULL);
|
|
virObjectLock(vm);
|
|
|
|
if (ret != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to save domain '%d' with libxenlight"),
|
|
vm->def->id);
|
|
ret = -1;
|
|
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;
|
|
}
|
|
|
|
libxlDomainCleanup(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;
|
|
bool remove_dom = false;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return -1;
|
|
#endif
|
|
|
|
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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not running"));
|
|
goto endjob;
|
|
}
|
|
|
|
if (libxlDoDomainSave(driver, vm, to) < 0)
|
|
goto endjob;
|
|
|
|
if (!vm->persistent)
|
|
remove_dom = true;
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
cleanup:
|
|
if (remove_dom && vm) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
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;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return -1;
|
|
#endif
|
|
|
|
virCheckFlags(VIR_DOMAIN_SAVE_PAUSED, -1);
|
|
if (dxml) {
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
_("xml modification unsupported"));
|
|
return -1;
|
|
}
|
|
|
|
fd = libxlDomainSaveImageOpen(driver, cfg, from, &def, &hdr);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
if (virDomainRestoreFlagsEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(vm = virDomainObjListAdd(driver->domains, def,
|
|
driver->xmlopt,
|
|
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
|
|
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
|
|
NULL)))
|
|
goto cleanup;
|
|
|
|
def = NULL;
|
|
|
|
ret = libxlDomainStart(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;
|
|
}
|
|
|
|
static int
|
|
libxlDomainRestore(virConnectPtr conn, const char *from)
|
|
{
|
|
return libxlDomainRestoreFlags(conn, from, NULL, 0);
|
|
}
|
|
|
|
static int
|
|
libxlDomainCoreDump(virDomainPtr dom, const char *to, unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDomainObjPrivatePtr priv;
|
|
virDomainObjPtr vm;
|
|
virObjectEventPtr event = NULL;
|
|
bool remove_dom = false;
|
|
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 (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 (!(flags & VIR_DUMP_LIVE) &&
|
|
virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
|
|
if (libxl_domain_pause(priv->ctx, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Before dumping core, failed to suspend domain '%d'"
|
|
" with libxenlight"),
|
|
vm->def->id);
|
|
goto endjob;
|
|
}
|
|
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_DUMP);
|
|
paused = true;
|
|
}
|
|
|
|
/* Unlock virDomainObj while dumping core */
|
|
virObjectUnlock(vm);
|
|
ret = libxl_domain_core_dump(priv->ctx, vm->def->id, to, NULL);
|
|
virObjectLock(vm);
|
|
if (ret != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to dump core of domain '%d' with libxenlight"),
|
|
vm->def->id);
|
|
ret = -1;
|
|
goto unpause;
|
|
}
|
|
|
|
if (flags & VIR_DUMP_CRASH) {
|
|
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 unpause;
|
|
}
|
|
|
|
libxlDomainCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED);
|
|
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_CRASHED);
|
|
if (!vm->persistent)
|
|
remove_dom = true;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
unpause:
|
|
if (virDomainObjIsActive(vm) && paused) {
|
|
if (libxl_domain_unpause(priv->ctx, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("After dumping core, failed to resume domain '%d' with"
|
|
" libxenlight"), vm->def->id);
|
|
} else {
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
|
|
VIR_DOMAIN_RUNNING_UNPAUSED);
|
|
}
|
|
}
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
cleanup:
|
|
if (remove_dom && vm) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
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;
|
|
bool remove_dom = false;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainManagedSaveEnsureACL(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;
|
|
}
|
|
if (!vm->persistent) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("cannot do managed save for transient domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
name = libxlDomainManagedSavePath(driver, vm);
|
|
if (name == NULL)
|
|
goto endjob;
|
|
|
|
VIR_INFO("Saving state to %s", name);
|
|
|
|
if (libxlDoDomainSave(driver, vm, name) < 0)
|
|
goto endjob;
|
|
|
|
if (!vm->persistent)
|
|
remove_dom = true;
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
cleanup:
|
|
if (remove_dom && vm) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 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 endjob;
|
|
}
|
|
|
|
if (!vm->persistent && (flags & VIR_DOMAIN_VCPU_CONFIG)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("cannot change persistent config of a transient domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
if ((max = libxlConnectGetMaxVcpus(dom->conn, NULL)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("could not determine max vcpus for the domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
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 endjob;
|
|
}
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (!(def = virDomainObjGetPersistentDef(cfg->caps, driver->xmlopt, vm)))
|
|
goto endjob;
|
|
|
|
maplen = VIR_CPU_MAPLEN(nvcpus);
|
|
if (VIR_ALLOC_N(bitmask, maplen) < 0)
|
|
goto endjob;
|
|
|
|
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, vm->def->id, &map) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to set vcpus for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_CONFIG:
|
|
if (libxl_set_vcpuonline(priv->ctx, vm->def->id, &map) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to set vcpus for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
def->vcpus = nvcpus;
|
|
break;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
if (flags & VIR_DOMAIN_VCPU_CONFIG)
|
|
ret = virDomainSaveConfig(cfg->configDir, def);
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
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;
|
|
|
|
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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if ((flags & VIR_DOMAIN_AFFECT_LIVE) && !virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("domain is inactive"));
|
|
goto endjob;
|
|
}
|
|
|
|
if (virDomainLiveConfigHelperMethod(cfg->caps, driver->xmlopt, vm,
|
|
&flags, &targetDef) < 0)
|
|
goto endjob;
|
|
|
|
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 endjob;
|
|
|
|
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
|
|
libxl_bitmap map = { .size = maplen, .map = cpumap };
|
|
libxlDomainObjPrivatePtr priv;
|
|
|
|
priv = vm->privateData;
|
|
if (libxl_set_vcpuaffinity(priv->ctx, vm->def->id, vcpu, &map) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to pin vcpu '%d' with libxenlight"),
|
|
vcpu);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
/* full bitmap means reset the settings (if any). */
|
|
if (virBitmapIsAllSet(pcpumap)) {
|
|
virDomainPinDel(&targetDef->cputune.vcpupin,
|
|
&targetDef->cputune.nvcpupin,
|
|
vcpu);
|
|
goto done;
|
|
}
|
|
|
|
if (!targetDef->cputune.vcpupin) {
|
|
if (VIR_ALLOC(targetDef->cputune.vcpupin) < 0)
|
|
goto endjob;
|
|
targetDef->cputune.nvcpupin = 0;
|
|
}
|
|
if (virDomainPinAdd(&targetDef->cputune.vcpupin,
|
|
&targetDef->cputune.nvcpupin,
|
|
cpumap,
|
|
maplen,
|
|
vcpu) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("failed to update or add vcpupin xml"));
|
|
goto endjob;
|
|
}
|
|
|
|
done:
|
|
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);
|
|
}
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
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;
|
|
virDomainPinDefPtr *vcpupin_list;
|
|
virBitmapPtr cpumask = NULL;
|
|
int maxcpu, hostcpus, vcpu, pcpu, n, ret = -1;
|
|
unsigned char *cpumap;
|
|
|
|
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]->id;
|
|
cpumask = vcpupin_list[n]->cpumask;
|
|
cpumap = VIR_GET_CPUMAP(cpumaps, maplen, vcpu);
|
|
for (pcpu = 0; pcpu < maxcpu; pcpu++) {
|
|
if (!virBitmapIsBitSet(cpumask, pcpu))
|
|
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, vm->def->id, &maxcpu,
|
|
&hostcpus)) == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to list vcpus for domain '%d' with libxenlight"),
|
|
vm->def->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 *
|
|
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,
|
|
virDomainDefFormatConvertXMLFlags(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 (STREQ(nativeFormat, LIBXL_CONFIG_FORMAT_XL)) {
|
|
if (!(conf = virConfReadMem(nativeConfig, strlen(nativeConfig), 0)))
|
|
goto cleanup;
|
|
if (!(def = xenParseXL(conf,
|
|
cfg->caps,
|
|
cfg->verInfo->xen_version_major)))
|
|
goto cleanup;
|
|
} else if (STREQ(nativeFormat, LIBXL_CONFIG_FORMAT_XM)) {
|
|
if (!(conf = virConfReadMem(nativeConfig, strlen(nativeConfig), 0)))
|
|
goto cleanup;
|
|
|
|
if (!(def = xenParseXM(conf,
|
|
cfg->verInfo->xen_version_major,
|
|
cfg->caps)))
|
|
goto cleanup;
|
|
} else if (STREQ(nativeFormat, LIBXL_CONFIG_FORMAT_SEXPR)) {
|
|
/* only support latest xend config format */
|
|
if (!(def = xenParseSxprString(nativeConfig,
|
|
XEND_CONFIG_VERSION_3_1_0,
|
|
NULL,
|
|
-1))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("parsing sxpr config failed"));
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unsupported config type %s"), nativeFormat);
|
|
goto cleanup;
|
|
}
|
|
|
|
xml = virDomainDefFormat(def, VIR_DOMAIN_DEF_FORMAT_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 (!(def = virDomainDefParseString(domainXml,
|
|
cfg->caps, driver->xmlopt,
|
|
1 << VIR_DOMAIN_VIRT_XEN,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE)))
|
|
goto cleanup;
|
|
|
|
if (STREQ(nativeFormat, LIBXL_CONFIG_FORMAT_XL)) {
|
|
if (!(conf = xenFormatXL(def, conn, cfg->verInfo->xen_version_major)))
|
|
goto cleanup;
|
|
} else if (STREQ(nativeFormat, LIBXL_CONFIG_FORMAT_XM)) {
|
|
if (!(conf = xenFormatXM(conn, def, cfg->verInfo->xen_version_major)))
|
|
goto cleanup;
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unsupported config type %s"), nativeFormat);
|
|
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 = libxlDomainStart(driver, vm, (flags & VIR_DOMAIN_START_PAUSED) != 0, -1);
|
|
if (ret < 0)
|
|
goto cleanup;
|
|
dom->id = vm->def->id;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainCreate(virDomainPtr dom)
|
|
{
|
|
return libxlDomainCreateWithFlags(dom, 0);
|
|
}
|
|
|
|
static virDomainPtr
|
|
libxlDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainDefPtr def = NULL;
|
|
virDomainObjPtr vm = NULL;
|
|
virDomainPtr dom = NULL;
|
|
virObjectEventPtr event = NULL;
|
|
virDomainDefPtr oldDef = NULL;
|
|
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
|
|
|
|
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
|
|
|
|
if (flags & VIR_DOMAIN_DEFINE_VALIDATE)
|
|
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE;
|
|
|
|
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
|
|
1 << VIR_DOMAIN_VIRT_XEN,
|
|
parse_flags)))
|
|
goto cleanup;
|
|
|
|
if (virDomainDefineXMLFlagsEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(vm = virDomainObjListAdd(driver->domains, def,
|
|
driver->xmlopt,
|
|
0,
|
|
&oldDef)))
|
|
goto cleanup;
|
|
|
|
def = NULL;
|
|
vm->persistent = 1;
|
|
|
|
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;
|
|
}
|
|
|
|
static virDomainPtr
|
|
libxlDomainDefineXML(virConnectPtr conn, const char *xml)
|
|
{
|
|
return libxlDomainDefineXMLFlags(conn, xml, 0);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (virDomainDiskSetSource(origdisk, virDomainDiskGetSource(disk)) < 0)
|
|
goto cleanup;
|
|
virDomainDiskSetType(origdisk, virDomainDiskGetType(disk));
|
|
|
|
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) {
|
|
if (virDomainDiskIndexByName(vm->def, l_disk->dst, true) >= 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("target %s already exists"), l_disk->dst);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!virDomainDiskGetSource(l_disk)) {
|
|
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
|
|
libxlDomainAttachHostPCIDevice(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
libxl_device_pci pcidev;
|
|
virDomainHostdevDefPtr found;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
|
|
|
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
return -1;
|
|
|
|
if (virDomainHostdevFind(vm->def, hostdev, &found) >= 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("target pci device %.4x:%.2x:%.2x.%.1x already exists"),
|
|
pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0)
|
|
return -1;
|
|
|
|
if (virHostdevPreparePCIDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, vm->def->uuid,
|
|
&hostdev, 1, 0) < 0)
|
|
return -1;
|
|
|
|
if (libxlMakePCI(hostdev, &pcidev) < 0)
|
|
goto error;
|
|
|
|
if (libxl_device_pci_add(priv->ctx, vm->def->id, &pcidev, 0) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to attach pci device %.4x:%.2x:%.2x.%.1x"),
|
|
pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
goto error;
|
|
}
|
|
|
|
vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
|
|
return 0;
|
|
|
|
error:
|
|
virHostdevReAttachPCIDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1, NULL);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachHostDevice(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("hostdev mode '%s' not supported"),
|
|
virDomainHostdevModeTypeToString(hostdev->mode));
|
|
return -1;
|
|
}
|
|
|
|
switch (hostdev->source.subsys.type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
|
|
if (libxlDomainAttachHostPCIDevice(driver, priv, vm, hostdev) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("hostdev subsys type '%s' not supported"),
|
|
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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
|
|
libxlDomainAttachNetDevice(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainNetDefPtr net)
|
|
{
|
|
int actualType;
|
|
libxl_device_nic nic;
|
|
int ret = -1;
|
|
char mac[VIR_MAC_STRING_BUFLEN];
|
|
|
|
/* preallocate new slot for device */
|
|
if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets + 1) < 0)
|
|
return -1;
|
|
|
|
/* If appropriate, grab a physical device from the configured
|
|
* network's pool of devices, or resolve bridge device name
|
|
* to the one defined in the network definition.
|
|
*/
|
|
if (networkAllocateActualDevice(vm->def, net) < 0)
|
|
return -1;
|
|
|
|
actualType = virDomainNetGetActualType(net);
|
|
|
|
if (virDomainHasNet(vm->def, net)) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("network device with mac %s already exists"),
|
|
virMacAddrFormat(&net->mac, mac));
|
|
return -1;
|
|
}
|
|
|
|
if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
|
|
/* This is really a "smart hostdev", so it should be attached
|
|
* as a hostdev (the hostdev code will reach over into the
|
|
* netdev-specific code as appropriate), then also added to
|
|
* the nets list (see out:) if successful.
|
|
*/
|
|
ret = libxlDomainAttachHostDevice(driver, priv, vm,
|
|
virDomainNetGetActualHostdev(net));
|
|
goto out;
|
|
}
|
|
|
|
libxl_device_nic_init(&nic);
|
|
if (libxlMakeNic(vm->def, net, &nic) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxl_device_nic_add(priv->ctx, vm->def->id, &nic, 0)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("libxenlight failed to attach network device"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
libxl_device_nic_dispose(&nic);
|
|
out:
|
|
if (!ret)
|
|
vm->def->nets[vm->def->nnets++] = net;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachDeviceLive(libxlDriverPrivatePtr driver,
|
|
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;
|
|
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
ret = libxlDomainAttachNetDevice(driver, priv, vm,
|
|
dev->data.net);
|
|
if (!ret)
|
|
dev->data.net = NULL;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
|
ret = libxlDomainAttachHostDevice(driver, priv, vm,
|
|
dev->data.hostdev);
|
|
if (!ret)
|
|
dev->data.hostdev = 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;
|
|
virDomainNetDefPtr net;
|
|
virDomainHostdevDefPtr hostdev;
|
|
virDomainHostdevDefPtr found;
|
|
virDomainHostdevSubsysPCIPtr pcisrc;
|
|
char mac[VIR_MAC_STRING_BUFLEN];
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
disk = dev->data.disk;
|
|
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;
|
|
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
net = dev->data.net;
|
|
if (virDomainHasNet(vmdef, net)) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("network device with mac %s already exists"),
|
|
virMacAddrFormat(&net->mac, mac));
|
|
return -1;
|
|
}
|
|
if (virDomainNetInsert(vmdef, net))
|
|
return -1;
|
|
dev->data.net = NULL;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
|
hostdev = dev->data.hostdev;
|
|
|
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
return -1;
|
|
|
|
if (virDomainHostdevFind(vmdef, hostdev, &found) >= 0) {
|
|
pcisrc = &hostdev->source.subsys.u.pci;
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("target pci device %.4x:%.2x:%.2x.%.1x\
|
|
already exists"),
|
|
pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
return -1;
|
|
}
|
|
|
|
if (virDomainHostdevInsert(vmdef, hostdev) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("persistent attach of device is not supported"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
|
virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
|
|
virDomainDeviceInfoPtr info1,
|
|
void *opaque)
|
|
{
|
|
virDomainDeviceInfoPtr info2 = opaque;
|
|
|
|
if (info1->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI ||
|
|
info2->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
|
|
return 0;
|
|
|
|
if (info1->addr.pci.domain == info2->addr.pci.domain &&
|
|
info1->addr.pci.bus == info2->addr.pci.bus &&
|
|
info1->addr.pci.slot == info2->addr.pci.slot &&
|
|
info1->addr.pci.function != info2->addr.pci.function)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
libxlIsMultiFunctionDevice(virDomainDefPtr def,
|
|
virDomainDeviceInfoPtr dev)
|
|
{
|
|
if (virDomainDeviceInfoIterate(def, libxlComparePCIDevice, dev) < 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachHostPCIDevice(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &subsys->u.pci;
|
|
libxl_device_pci pcidev;
|
|
virDomainHostdevDefPtr detach;
|
|
int idx;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
return -1;
|
|
|
|
idx = virDomainHostdevFind(vm->def, hostdev, &detach);
|
|
if (idx < 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("host pci device %.4x:%.2x:%.2x.%.1x not found"),
|
|
pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
return -1;
|
|
}
|
|
|
|
if (libxlIsMultiFunctionDevice(vm->def, detach->info)) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"),
|
|
pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
goto error;
|
|
}
|
|
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
if (libxlMakePCI(detach, &pcidev) < 0)
|
|
goto error;
|
|
|
|
if (libxl_device_pci_remove(priv->ctx, vm->def->id, &pcidev, 0) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to detach pci device\
|
|
%.4x:%.2x:%.2x.%.1x"),
|
|
pcisrc->addr.domain, pcisrc->addr.bus,
|
|
pcisrc->addr.slot, pcisrc->addr.function);
|
|
goto error;
|
|
}
|
|
|
|
libxl_device_pci_dispose(&pcidev);
|
|
|
|
virDomainHostdevRemove(vm->def, idx);
|
|
|
|
virHostdevReAttachPCIDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1, NULL);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
virDomainHostdevDefFree(detach);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachHostDevice(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys;
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("hostdev mode '%s' not supported"),
|
|
virDomainHostdevModeTypeToString(hostdev->mode));
|
|
return -1;
|
|
}
|
|
|
|
switch (subsys->type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
|
|
return libxlDomainDetachHostPCIDevice(driver, priv, vm, hostdev);
|
|
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected hostdev type %d"), subsys->type);
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachNetDevice(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainNetDefPtr net)
|
|
{
|
|
int detachidx;
|
|
virDomainNetDefPtr detach = NULL;
|
|
libxl_device_nic nic;
|
|
char mac[VIR_MAC_STRING_BUFLEN];
|
|
int ret = -1;
|
|
|
|
if ((detachidx = virDomainNetFindIdx(vm->def, net)) < 0)
|
|
return -1;
|
|
|
|
detach = vm->def->nets[detachidx];
|
|
|
|
if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
|
|
/* This is really a "smart hostdev", so it should be attached as a
|
|
* hostdev, then also removed from nets list (see out:) if successful.
|
|
*/
|
|
ret = libxlDomainDetachHostDevice(driver, priv, vm,
|
|
virDomainNetGetActualHostdev(detach));
|
|
goto out;
|
|
}
|
|
|
|
libxl_device_nic_init(&nic);
|
|
if (libxl_mac_to_device_nic(priv->ctx, vm->def->id,
|
|
virMacAddrFormat(&detach->mac, mac), &nic))
|
|
goto cleanup;
|
|
|
|
if (libxl_device_nic_remove(priv->ctx, vm->def->id, &nic, 0)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("libxenlight failed to detach network device"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
libxl_device_nic_dispose(&nic);
|
|
out:
|
|
if (!ret)
|
|
virDomainNetRemove(vm->def, detachidx);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachDeviceLive(libxlDriverPrivatePtr driver,
|
|
libxlDomainObjPrivatePtr priv,
|
|
virDomainObjPtr vm,
|
|
virDomainDeviceDefPtr dev)
|
|
{
|
|
int ret = -1;
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
ret = libxlDomainDetachDeviceDiskLive(priv, vm, dev);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
ret = libxlDomainDetachNetDevice(driver, priv, vm,
|
|
dev->data.net);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
|
ret = libxlDomainDetachHostDevice(driver, priv, vm,
|
|
dev->data.hostdev);
|
|
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)
|
|
{
|
|
virDomainDiskDefPtr disk, detach;
|
|
virDomainHostdevDefPtr hostdev, det_hostdev;
|
|
virDomainNetDefPtr net;
|
|
int idx;
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
disk = dev->data.disk;
|
|
if (!(detach = virDomainDiskRemoveByName(vmdef, disk->dst))) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("no target device %s"), disk->dst);
|
|
return -1;
|
|
}
|
|
virDomainDiskDefFree(detach);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
net = dev->data.net;
|
|
if ((idx = virDomainNetFindIdx(vmdef, net)) < 0)
|
|
return -1;
|
|
|
|
/* this is guaranteed to succeed */
|
|
virDomainNetDefFree(virDomainNetRemove(vmdef, idx));
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV: {
|
|
hostdev = dev->data.hostdev;
|
|
if ((idx = virDomainHostdevFind(vmdef, hostdev, &det_hostdev)) < 0) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("device not present in domain configuration"));
|
|
return -1;
|
|
}
|
|
virDomainHostdevRemove(vmdef, idx);
|
|
virDomainHostdevDefFree(det_hostdev);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("persistent detach of device is not supported"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (virDomainDiskSetSource(orig, virDomainDiskGetSource(disk)) < 0)
|
|
goto cleanup;
|
|
virDomainDiskSetType(orig, virDomainDiskGetType(disk));
|
|
virDomainDiskSetFormat(orig, virDomainDiskGetFormat(disk));
|
|
if (virDomainDiskSetDriver(orig, virDomainDiskGetDriver(disk)) < 0)
|
|
goto cleanup;
|
|
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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 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 endjob;
|
|
}
|
|
}
|
|
|
|
if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cannot modify device on transient domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
|
|
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
|
|
cfg->caps, driver->xmlopt,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE)))
|
|
goto endjob;
|
|
|
|
/* Make a copy for updated domain. */
|
|
if (!(vmdef = virDomainObjCopyPersistentDef(vm, cfg->caps,
|
|
driver->xmlopt)))
|
|
goto endjob;
|
|
|
|
if (libxlDomainAttachDeviceConfig(vmdef, dev) < 0)
|
|
goto endjob;
|
|
}
|
|
|
|
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_DEF_PARSE_INACTIVE)))
|
|
goto endjob;
|
|
|
|
if (libxlDomainAttachDeviceLive(driver, priv, vm, dev) < 0)
|
|
goto endjob;
|
|
|
|
/*
|
|
* 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)
|
|
goto endjob;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
/* Finally, if no error until here, we can save config. */
|
|
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
|
|
ret = virDomainSaveConfig(cfg->configDir, vmdef);
|
|
if (!ret) {
|
|
virDomainObjAssignDef(vm, vmdef, false, NULL);
|
|
vmdef = NULL;
|
|
}
|
|
}
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = 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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 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 endjob;
|
|
}
|
|
}
|
|
|
|
if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cannot modify device on transient domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
|
|
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
|
|
cfg->caps, driver->xmlopt,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE)))
|
|
goto endjob;
|
|
|
|
/* Make a copy for updated domain. */
|
|
if (!(vmdef = virDomainObjCopyPersistentDef(vm, cfg->caps,
|
|
driver->xmlopt)))
|
|
goto endjob;
|
|
|
|
if (libxlDomainDetachDeviceConfig(vmdef, dev) < 0)
|
|
goto endjob;
|
|
}
|
|
|
|
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_DEF_PARSE_INACTIVE)))
|
|
goto endjob;
|
|
|
|
if (libxlDomainDetachDeviceLive(driver, priv, vm, dev) < 0)
|
|
goto endjob;
|
|
|
|
/*
|
|
* 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)
|
|
goto endjob;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
/* Finally, if no error until here, we can save config. */
|
|
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
|
|
ret = virDomainSaveConfig(cfg->configDir, vmdef);
|
|
if (!ret) {
|
|
virDomainObjAssignDef(vm, vmdef, false, NULL);
|
|
vmdef = NULL;
|
|
}
|
|
}
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = 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_DEF_PARSE_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_DEF_PARSE_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;
|
|
}
|
|
|
|
static unsigned long long
|
|
libxlNodeGetFreeMemory(virConnectPtr conn)
|
|
{
|
|
libxl_physinfo phy_info;
|
|
libxlDriverPrivatePtr driver = conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
unsigned long long ret = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
ret = phy_info.free_pages * cfg->verInfo->pagesize;
|
|
|
|
cleanup:
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
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,
|
|
virConnectDomainEventCallback callback,
|
|
void *opaque,
|
|
virFreeCallback freecb)
|
|
{
|
|
libxlDriverPrivatePtr driver = conn->privateData;
|
|
|
|
if (virConnectDomainEventRegisterEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
if (virDomainEventStateRegister(conn,
|
|
driver->domainEventState,
|
|
callback, opaque, freecb) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
libxlConnectDomainEventDeregister(virConnectPtr conn,
|
|
virConnectDomainEventCallback callback)
|
|
{
|
|
libxlDriverPrivatePtr driver = conn->privateData;
|
|
|
|
if (virConnectDomainEventDeregisterEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
if (virDomainEventStateDeregister(conn,
|
|
driver->domainEventState,
|
|
callback) < 0)
|
|
return -1;
|
|
|
|
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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (!vm->persistent) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cannot set autostart for transient domain"));
|
|
goto endjob;
|
|
}
|
|
|
|
autostart = (autostart != 0);
|
|
|
|
if (vm->autostart != autostart) {
|
|
if (!(configFile = virDomainConfigFile(cfg->configDir, vm->def->name)))
|
|
goto endjob;
|
|
if (!(autostartLink = virDomainConfigFile(cfg->autostartDir, vm->def->name)))
|
|
goto endjob;
|
|
|
|
if (autostart) {
|
|
if (virFileMakePath(cfg->autostartDir) < 0) {
|
|
virReportSystemError(errno,
|
|
_("cannot create autostart directory %s"),
|
|
cfg->autostartDir);
|
|
goto endjob;
|
|
}
|
|
|
|
if (symlink(configFile, autostartLink) < 0) {
|
|
virReportSystemError(errno,
|
|
_("Failed to create symlink '%s to '%s'"),
|
|
autostartLink, configFile);
|
|
goto endjob;
|
|
}
|
|
} else {
|
|
if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
|
|
virReportSystemError(errno,
|
|
_("Failed to delete symlink '%s'"),
|
|
autostartLink);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
vm->autostart = autostart;
|
|
}
|
|
ret = 0;
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
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"), vm->def->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, vm->def->id, &sc_info) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to get scheduler parameters for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virTypedParameterAssign(¶ms[0], VIR_DOMAIN_SCHEDULER_WEIGHT,
|
|
VIR_TYPED_PARAM_UINT, sc_info.weight) < 0)
|
|
goto cleanup;
|
|
|
|
if (*nparams > 1) {
|
|
if (virTypedParameterAssign(¶ms[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)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
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 (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;
|
|
|
|
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 endjob;
|
|
}
|
|
|
|
if (libxl_domain_sched_params_get(priv->ctx, vm->def->id, &sc_info) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to get scheduler parameters for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
|
|
for (i = 0; i < nparams; ++i) {
|
|
virTypedParameterPtr param = ¶ms[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, vm->def->id, &sc_info) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to set scheduler parameters for domain '%d'"
|
|
" with libxenlight"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
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;
|
|
|
|
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->nconsoles)
|
|
chr = vm->def->consoles[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;
|
|
}
|
|
|
|
/* 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:
|
|
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 = ¶ms[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)) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
if (!(nodes = virBitmapNew(numnodes)))
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!(nodeset = virBitmapFormat(nodes)))
|
|
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;
|
|
}
|
|
|
|
static int
|
|
libxlDomainIsUpdated(virDomainPtr dom)
|
|
{
|
|
virDomainObjPtr vm;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainIsUpdatedEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = vm->updated;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
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;
|
|
|
|
if (virObjectEventStateDeregisterID(conn,
|
|
driver->domainEventState,
|
|
callbackID) < 0)
|
|
return -1;
|
|
|
|
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:
|
|
case VIR_DRV_FEATURE_MIGRATION_PARAMS:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
libxlNodeDeviceGetPCIInfo(virNodeDeviceDefPtr def,
|
|
unsigned *domain,
|
|
unsigned *bus,
|
|
unsigned *slot,
|
|
unsigned *function)
|
|
{
|
|
virNodeDevCapsDefPtr cap;
|
|
|
|
cap = def->caps;
|
|
while (cap) {
|
|
if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) {
|
|
*domain = cap->data.pci_dev.domain;
|
|
*bus = cap->data.pci_dev.bus;
|
|
*slot = cap->data.pci_dev.slot;
|
|
*function = cap->data.pci_dev.function;
|
|
break;
|
|
}
|
|
|
|
cap = cap->next;
|
|
}
|
|
|
|
if (!cap) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("device %s is not a PCI device"), def->name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlNodeDeviceDetachFlags(virNodeDevicePtr dev,
|
|
const char *driverName,
|
|
unsigned int flags)
|
|
{
|
|
virPCIDevicePtr pci = NULL;
|
|
unsigned domain = 0, bus = 0, slot = 0, function = 0;
|
|
int ret = -1;
|
|
virNodeDeviceDefPtr def = NULL;
|
|
char *xml = NULL;
|
|
libxlDriverPrivatePtr driver = dev->conn->privateData;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
xml = virNodeDeviceGetXMLDesc(dev, 0);
|
|
if (!xml)
|
|
goto cleanup;
|
|
|
|
def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL);
|
|
if (!def)
|
|
goto cleanup;
|
|
|
|
if (virNodeDeviceDetachFlagsEnsureACL(dev->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlNodeDeviceGetPCIInfo(def, &domain, &bus, &slot, &function) < 0)
|
|
goto cleanup;
|
|
|
|
pci = virPCIDeviceNew(domain, bus, slot, function);
|
|
if (!pci)
|
|
goto cleanup;
|
|
|
|
if (!driverName || STREQ(driverName, "xen")) {
|
|
if (virPCIDeviceSetStubDriver(pci, "pciback") < 0)
|
|
goto cleanup;
|
|
} else {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unsupported driver name '%s'"), driverName);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virHostdevPCINodeDeviceDetach(hostdev_mgr, pci) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virPCIDeviceFree(pci);
|
|
virNodeDeviceDefFree(def);
|
|
VIR_FREE(xml);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlNodeDeviceDettach(virNodeDevicePtr dev)
|
|
{
|
|
return libxlNodeDeviceDetachFlags(dev, NULL, 0);
|
|
}
|
|
|
|
static int
|
|
libxlNodeDeviceReAttach(virNodeDevicePtr dev)
|
|
{
|
|
virPCIDevicePtr pci = NULL;
|
|
unsigned domain = 0, bus = 0, slot = 0, function = 0;
|
|
int ret = -1;
|
|
virNodeDeviceDefPtr def = NULL;
|
|
char *xml = NULL;
|
|
libxlDriverPrivatePtr driver = dev->conn->privateData;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
xml = virNodeDeviceGetXMLDesc(dev, 0);
|
|
if (!xml)
|
|
goto cleanup;
|
|
|
|
def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL);
|
|
if (!def)
|
|
goto cleanup;
|
|
|
|
if (virNodeDeviceReAttachEnsureACL(dev->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlNodeDeviceGetPCIInfo(def, &domain, &bus, &slot, &function) < 0)
|
|
goto cleanup;
|
|
|
|
pci = virPCIDeviceNew(domain, bus, slot, function);
|
|
if (!pci)
|
|
goto cleanup;
|
|
|
|
if (virHostdevPCINodeDeviceReAttach(hostdev_mgr, pci) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virPCIDeviceFree(pci);
|
|
virNodeDeviceDefFree(def);
|
|
VIR_FREE(xml);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlNodeDeviceReset(virNodeDevicePtr dev)
|
|
{
|
|
virPCIDevicePtr pci = NULL;
|
|
unsigned domain = 0, bus = 0, slot = 0, function = 0;
|
|
int ret = -1;
|
|
virNodeDeviceDefPtr def = NULL;
|
|
char *xml = NULL;
|
|
libxlDriverPrivatePtr driver = dev->conn->privateData;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
|
|
xml = virNodeDeviceGetXMLDesc(dev, 0);
|
|
if (!xml)
|
|
goto cleanup;
|
|
|
|
def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL);
|
|
if (!def)
|
|
goto cleanup;
|
|
|
|
if (virNodeDeviceResetEnsureACL(dev->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlNodeDeviceGetPCIInfo(def, &domain, &bus, &slot, &function) < 0)
|
|
goto cleanup;
|
|
|
|
pci = virPCIDeviceNew(domain, bus, slot, function);
|
|
if (!pci)
|
|
goto cleanup;
|
|
|
|
if (virHostdevPCINodeDeviceReset(hostdev_mgr, pci) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virPCIDeviceFree(pci);
|
|
virNodeDeviceDefFree(def);
|
|
VIR_FREE(xml);
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
libxlDomainMigrateBegin3Params(virDomainPtr domain,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
char **cookieout ATTRIBUTE_UNUSED,
|
|
int *cookieoutlen ATTRIBUTE_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
const char *xmlin = NULL;
|
|
virDomainObjPtr vm = NULL;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return NULL;
|
|
#endif
|
|
|
|
virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL);
|
|
if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
|
|
return NULL;
|
|
|
|
if (virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_DEST_XML,
|
|
&xmlin) < 0)
|
|
return NULL;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(domain)))
|
|
return NULL;
|
|
|
|
if (virDomainMigrateBegin3ParamsEnsureACL(domain->conn, vm->def) < 0) {
|
|
virObjectUnlock(vm);
|
|
return NULL;
|
|
}
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("domain is not running"));
|
|
virObjectUnlock(vm);
|
|
return NULL;
|
|
}
|
|
|
|
return libxlDomainMigrationBegin(domain->conn, vm, xmlin);
|
|
}
|
|
|
|
static int
|
|
libxlDomainMigratePrepare3Params(virConnectPtr dconn,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
const char *cookiein ATTRIBUTE_UNUSED,
|
|
int cookieinlen ATTRIBUTE_UNUSED,
|
|
char **cookieout ATTRIBUTE_UNUSED,
|
|
int *cookieoutlen ATTRIBUTE_UNUSED,
|
|
char **uri_out,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dconn->privateData;
|
|
virDomainDefPtr def = NULL;
|
|
const char *dom_xml = NULL;
|
|
const char *dname = NULL;
|
|
const char *uri_in = NULL;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return -1;
|
|
#endif
|
|
|
|
virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
|
|
if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
|
|
goto error;
|
|
|
|
if (virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_DEST_XML,
|
|
&dom_xml) < 0 ||
|
|
virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_DEST_NAME,
|
|
&dname) < 0 ||
|
|
virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_URI,
|
|
&uri_in) < 0)
|
|
|
|
goto error;
|
|
|
|
if (!(def = libxlDomainMigrationPrepareDef(driver, dom_xml, dname)))
|
|
goto error;
|
|
|
|
if (virDomainMigratePrepare3ParamsEnsureACL(dconn, def) < 0)
|
|
goto error;
|
|
|
|
if (libxlDomainMigrationPrepare(dconn, &def, uri_in, uri_out, flags) < 0)
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
virDomainDefFree(def);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlDomainMigratePerform3Params(virDomainPtr dom,
|
|
const char *dconnuri,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
const char *cookiein ATTRIBUTE_UNUSED,
|
|
int cookieinlen ATTRIBUTE_UNUSED,
|
|
char **cookieout ATTRIBUTE_UNUSED,
|
|
int *cookieoutlen ATTRIBUTE_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
virDomainObjPtr vm = NULL;
|
|
const char *dom_xml = NULL;
|
|
const char *dname = NULL;
|
|
const char *uri = NULL;
|
|
int ret = -1;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return -1;
|
|
#endif
|
|
|
|
virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
|
|
if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
|
|
goto cleanup;
|
|
|
|
if (virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_DEST_XML,
|
|
&dom_xml) < 0 ||
|
|
virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_DEST_NAME,
|
|
&dname) < 0 ||
|
|
virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_URI,
|
|
&uri) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainMigratePerform3ParamsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlDomainMigrationPerform(driver, vm, dom_xml, dconnuri,
|
|
uri, dname, flags) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
static virDomainPtr
|
|
libxlDomainMigrateFinish3Params(virConnectPtr dconn,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
const char *cookiein ATTRIBUTE_UNUSED,
|
|
int cookieinlen ATTRIBUTE_UNUSED,
|
|
char **cookieout ATTRIBUTE_UNUSED,
|
|
int *cookieoutlen ATTRIBUTE_UNUSED,
|
|
unsigned int flags,
|
|
int cancelled)
|
|
{
|
|
libxlDriverPrivatePtr driver = dconn->privateData;
|
|
virDomainObjPtr vm = NULL;
|
|
const char *dname = NULL;
|
|
virDomainPtr ret = NULL;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return NULL;
|
|
#endif
|
|
|
|
virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL);
|
|
if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
|
|
return NULL;
|
|
|
|
if (virTypedParamsGetString(params, nparams,
|
|
VIR_MIGRATE_PARAM_DEST_NAME,
|
|
&dname) < 0)
|
|
return NULL;
|
|
|
|
if (!dname ||
|
|
!(vm = virDomainObjListFindByName(driver->domains, dname))) {
|
|
/* Migration obviously failed if the domain doesn't exist */
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Migration failed. No domain on destination host "
|
|
"with matching name '%s'"),
|
|
NULLSTR(dname));
|
|
return NULL;
|
|
}
|
|
|
|
if (virDomainMigrateFinish3ParamsEnsureACL(dconn, vm->def) < 0) {
|
|
virObjectUnlock(vm);
|
|
return NULL;
|
|
}
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0) {
|
|
virObjectUnlock(vm);
|
|
return NULL;
|
|
}
|
|
|
|
ret = libxlDomainMigrationFinish(dconn, vm, flags, cancelled);
|
|
|
|
if (!libxlDomainObjEndJob(driver, vm))
|
|
vm = NULL;
|
|
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainMigrateConfirm3Params(virDomainPtr domain,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
const char *cookiein ATTRIBUTE_UNUSED,
|
|
int cookieinlen ATTRIBUTE_UNUSED,
|
|
unsigned int flags,
|
|
int cancelled)
|
|
{
|
|
libxlDriverPrivatePtr driver = domain->conn->privateData;
|
|
virDomainObjPtr vm = NULL;
|
|
|
|
#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
|
|
virReportUnsupportedError();
|
|
return -1;
|
|
#endif
|
|
|
|
virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
|
|
if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
|
|
return -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(domain)))
|
|
return -1;
|
|
|
|
if (virDomainMigrateConfirm3ParamsEnsureACL(domain->conn, vm->def) < 0) {
|
|
virObjectUnlock(vm);
|
|
return -1;
|
|
}
|
|
|
|
return libxlDomainMigrationConfirm(driver, vm, flags, cancelled);
|
|
}
|
|
|
|
|
|
static virHypervisorDriver libxlHypervisorDriver = {
|
|
.name = LIBXL_DRIVER_NAME,
|
|
.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 */
|
|
.domainDefineXMLFlags = libxlDomainDefineXMLFlags, /* 1.2.12 */
|
|
.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 */
|
|
.nodeDeviceDettach = libxlNodeDeviceDettach, /* 1.2.3 */
|
|
.nodeDeviceDetachFlags = libxlNodeDeviceDetachFlags, /* 1.2.3 */
|
|
.nodeDeviceReAttach = libxlNodeDeviceReAttach, /* 1.2.3 */
|
|
.nodeDeviceReset = libxlNodeDeviceReset, /* 1.2.3 */
|
|
.domainMigrateBegin3Params = libxlDomainMigrateBegin3Params, /* 1.2.6 */
|
|
.domainMigratePrepare3Params = libxlDomainMigratePrepare3Params, /* 1.2.6 */
|
|
.domainMigratePerform3Params = libxlDomainMigratePerform3Params, /* 1.2.6 */
|
|
.domainMigrateFinish3Params = libxlDomainMigrateFinish3Params, /* 1.2.6 */
|
|
.domainMigrateConfirm3Params = libxlDomainMigrateConfirm3Params, /* 1.2.6 */
|
|
};
|
|
|
|
static virConnectDriver libxlConnectDriver = {
|
|
.hypervisorDriver = &libxlHypervisorDriver,
|
|
};
|
|
|
|
static virStateDriver libxlStateDriver = {
|
|
.name = "LIBXL",
|
|
.stateInitialize = libxlStateInitialize,
|
|
.stateAutoStart = libxlStateAutoStart,
|
|
.stateCleanup = libxlStateCleanup,
|
|
.stateReload = libxlStateReload,
|
|
};
|
|
|
|
|
|
int
|
|
libxlRegister(void)
|
|
{
|
|
if (virRegisterConnectDriver(&libxlConnectDriver,
|
|
true) < 0)
|
|
return -1;
|
|
if (virRegisterStateDriver(&libxlStateDriver) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|