mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-29 00:55:22 +00:00
6c98ac2c62
New line character in name of domain is now forbidden because it mess virsh output and can be confusing for users. Validation of name is done in drivers, after parsing XML to avoid problems with dissappeared domains which was already created with new-line char in name. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
6541 lines
192 KiB
C
6541 lines
192 KiB
C
/*
|
|
* libxl_driver.c: core driver methods for managing libxenlight domains
|
|
*
|
|
* Copyright (C) 2006-2015 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 <xenstore.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 "virhook.h"
|
|
#include "vircommand.h"
|
|
#include "libxl_domain.h"
|
|
#include "libxl_driver.h"
|
|
#include "libxl_conf.h"
|
|
#include "libxl_capabilities.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"
|
|
#include "locking/domain_lock.h"
|
|
#include "virstats.h"
|
|
#include "cpu/cpu.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_NB_TOTAL_CPU_STAT_PARAM 1
|
|
#define LIBXL_NB_TOTAL_BLK_STAT_PARAM 6
|
|
|
|
#define HYPERVISOR_CAPABILITIES "/proc/xen/capabilities"
|
|
#define HYPERVISOR_XENSTORED "/dev/xen/xenstored"
|
|
|
|
/* Number of Xen scheduler parameters */
|
|
#define XEN_SCHED_CREDIT_NPARAM 2
|
|
|
|
#define LIBXL_CHECK_DOM0_GOTO(name, label) \
|
|
do { \
|
|
if (STREQ_NULLABLE(name, "Domain-0")) { \
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s", \
|
|
_("Domain-0 does not support requested operation")); \
|
|
goto label; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
/* Object used to store disk statistics across multiple xen backends */
|
|
typedef struct _libxlBlockStats libxlBlockStats;
|
|
typedef libxlBlockStats *libxlBlockStatsPtr;
|
|
struct _libxlBlockStats {
|
|
long long rd_req;
|
|
long long rd_bytes;
|
|
long long wr_req;
|
|
long long wr_bytes;
|
|
long long f_req;
|
|
|
|
char *backend;
|
|
union {
|
|
struct {
|
|
long long ds_req;
|
|
long long oo_req;
|
|
} vbd;
|
|
} u;
|
|
};
|
|
|
|
/* 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 = virDomainObjListFindByUUIDRef(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;
|
|
int ret = -1;
|
|
|
|
virObjectRef(vm);
|
|
virObjectLock(vm);
|
|
virResetLastError();
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (vm->autostart && !virDomainObjIsActive(vm) &&
|
|
libxlDomainStartNew(driver, vm, false) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to autostart VM '%s': %s"),
|
|
vm->def->name, virGetLastErrorMessage());
|
|
goto endjob;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
cleanup:
|
|
virDomainObjEndAPI(&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;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
int rc;
|
|
libxl_dominfo d_info;
|
|
int len;
|
|
uint8_t *data = NULL;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
unsigned int hostdev_flags = VIR_HOSTDEV_SP_PCI;
|
|
int ret = -1;
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
hostdev_flags |= VIR_HOSTDEV_SP_USB;
|
|
#endif
|
|
|
|
virObjectRef(vm);
|
|
virObjectLock(vm);
|
|
|
|
libxl_dominfo_init(&d_info);
|
|
|
|
/* Does domain still exist? */
|
|
rc = libxl_domain_info(cfg->ctx, &d_info, vm->def->id);
|
|
if (rc == ERROR_INVAL) {
|
|
goto error;
|
|
} else if (rc != 0) {
|
|
VIR_DEBUG("libxl_domain_info failed (code %d), ignoring domain %d",
|
|
rc, vm->def->id);
|
|
goto error;
|
|
}
|
|
|
|
/* Is this a domain that was under libvirt control? */
|
|
if (libxl_userdata_retrieve(cfg->ctx, vm->def->id,
|
|
"libvirt-xml", &data, &len)) {
|
|
VIR_DEBUG("libxl_userdata_retrieve failed, ignoring domain %d", vm->def->id);
|
|
goto error;
|
|
}
|
|
|
|
/* Update domid in case it changed (e.g. reboot) while we were gone? */
|
|
vm->def->id = d_info.domid;
|
|
|
|
/* Update hostdev state */
|
|
if (virHostdevUpdateActiveDomainDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def, hostdev_flags) < 0)
|
|
goto error;
|
|
|
|
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
|
|
driver->inhibitCallback(true, driver->inhibitOpaque);
|
|
|
|
/* Enable domain death events */
|
|
libxl_evenable_domain_death(cfg->ctx, vm->def->id, 0, &priv->deathW);
|
|
|
|
/* now that we know it's reconnected call the hook if present */
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LIBXL) &&
|
|
STRNEQ("Domain-0", vm->def->name)) {
|
|
char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
|
|
int hookret;
|
|
|
|
/* we can't stop the operation even if the script raised an error */
|
|
hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
|
|
VIR_HOOK_LIBXL_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
|
|
NULL, xml, NULL);
|
|
VIR_FREE(xml);
|
|
if (hookret < 0) {
|
|
/* Stop the domain if the hook failed */
|
|
if (virDomainObjIsActive(vm)) {
|
|
libxlDomainDestroyInternal(driver, vm);
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_FAILED);
|
|
}
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
libxl_dominfo_dispose(&d_info);
|
|
virObjectUnlock(vm);
|
|
virObjectUnref(vm);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
|
|
error:
|
|
libxlDomainCleanup(driver, vm);
|
|
if (!vm->persistent) {
|
|
virDomainObjListRemoveLocked(driver->domains, vm);
|
|
|
|
/* virDomainObjListRemoveLocked leaves the object unlocked,
|
|
* lock it again to factorize more code. */
|
|
virObjectLock(vm);
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
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->reservedGraphicsPorts);
|
|
virObjectUnref(libxl_driver->migrationPorts);
|
|
virLockManagerPluginUnref(libxl_driver->lockManager);
|
|
|
|
virObjectUnref(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;
|
|
|
|
/* Don't load if non-root */
|
|
if (!privileged) {
|
|
VIR_INFO("Not running privileged, disabling libxenlight driver");
|
|
return ret;
|
|
}
|
|
|
|
if (virFileExists(HYPERVISOR_CAPABILITIES)) {
|
|
int status;
|
|
char *output = NULL;
|
|
/*
|
|
* 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;
|
|
}
|
|
} else if (!virFileExists(HYPERVISOR_XENSTORED)) {
|
|
VIR_INFO("Disabling driver as neither " HYPERVISOR_CAPABILITIES
|
|
" nor " HYPERVISOR_XENSTORED " exist");
|
|
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
|
|
libxlAddDom0(libxlDriverPrivatePtr driver)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainDefPtr def = NULL;
|
|
virDomainObjPtr vm = NULL;
|
|
virDomainDefPtr oldDef = NULL;
|
|
libxl_dominfo d_info;
|
|
int ret = -1;
|
|
|
|
libxl_dominfo_init(&d_info);
|
|
|
|
/* Ensure we have a dom0 */
|
|
if (libxl_domain_info(cfg->ctx, &d_info, 0) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("unable to get Domain-0 information from libxenlight"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(def = virDomainDefNew()))
|
|
goto cleanup;
|
|
|
|
def->id = 0;
|
|
def->virtType = VIR_DOMAIN_VIRT_XEN;
|
|
if (VIR_STRDUP(def->name, "Domain-0") < 0)
|
|
goto cleanup;
|
|
|
|
def->os.type = VIR_DOMAIN_OSTYPE_XEN;
|
|
|
|
if (virUUIDParse("00000000-0000-0000-0000-000000000000", def->uuid) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(vm = virDomainObjListAdd(driver->domains, def,
|
|
driver->xmlopt,
|
|
0,
|
|
&oldDef)))
|
|
goto cleanup;
|
|
|
|
def = NULL;
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
|
|
if (virDomainDefSetVcpusMax(vm->def, d_info.vcpu_max_id + 1, driver->xmlopt))
|
|
goto cleanup;
|
|
|
|
if (virDomainDefSetVcpus(vm->def, d_info.vcpu_online) < 0)
|
|
goto cleanup;
|
|
vm->def->mem.cur_balloon = d_info.current_memkb;
|
|
virDomainDefSetMemoryTotal(vm->def, d_info.max_memkb);
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
libxl_dominfo_dispose(&d_info);
|
|
virDomainDefFree(def);
|
|
virDomainDefFree(oldDef);
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlStateInitialize(bool privileged,
|
|
virStateInhibitCallback callback ATTRIBUTE_UNUSED,
|
|
void *opaque ATTRIBUTE_UNUSED)
|
|
{
|
|
libxlDriverConfigPtr cfg;
|
|
char *driverConf = NULL;
|
|
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->reservedGraphicsPorts =
|
|
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;
|
|
|
|
if (virAsprintf(&driverConf, "%s/libxl.conf", cfg->configBaseDir) < 0)
|
|
goto error;
|
|
|
|
if (libxlDriverConfigLoadFile(cfg, driverConf) < 0)
|
|
goto error;
|
|
VIR_FREE(driverConf);
|
|
|
|
/* 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;
|
|
}
|
|
if (virFileMakePath(cfg->channelDir) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to create channel dir '%s': %s"),
|
|
cfg->channelDir,
|
|
virStrerror(errno, ebuf, sizeof(ebuf)));
|
|
goto error;
|
|
}
|
|
|
|
if (!(libxl_driver->lockManager =
|
|
virLockManagerPluginNew(cfg->lockManagerName ?
|
|
cfg->lockManagerName : "nop",
|
|
"libxl",
|
|
cfg->configBaseDir,
|
|
0)))
|
|
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;
|
|
|
|
/* Add Domain-0 */
|
|
if (libxlAddDom0(libxl_driver) < 0)
|
|
goto error;
|
|
|
|
/* Load running domains first. */
|
|
if (virDomainObjListLoadAllConfigs(libxl_driver->domains,
|
|
cfg->stateDir,
|
|
cfg->autostartDir,
|
|
1,
|
|
cfg->caps,
|
|
libxl_driver->xmlopt,
|
|
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,
|
|
NULL, NULL) < 0)
|
|
goto error;
|
|
|
|
virDomainObjListForEach(libxl_driver->domains, libxlDomainManagedSaveLoad,
|
|
libxl_driver);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
VIR_FREE(driverConf);
|
|
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,
|
|
NULL, libxl_driver);
|
|
|
|
virDomainObjListForEach(libxl_driver->domains, libxlAutostartDomain,
|
|
libxl_driver);
|
|
|
|
virObjectUnref(cfg);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virDrvOpenStatus
|
|
libxlConnectOpen(virConnectPtr conn,
|
|
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
|
|
virConfPtr conf 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_SCHEMA;
|
|
|
|
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
|
|
NULL, parse_flags)))
|
|
goto cleanup;
|
|
|
|
if (virDomainCreateXMLEnsureACL(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;
|
|
virObjectRef(vm);
|
|
def = NULL;
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0) {
|
|
if (!vm->persistent) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
if (libxlDomainStartNew(driver, vm,
|
|
(flags & VIR_DOMAIN_START_PAUSED) != 0) < 0) {
|
|
if (!vm->persistent) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
goto cleanup;
|
|
}
|
|
goto endjob;
|
|
}
|
|
|
|
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
|
|
if (dom)
|
|
dom->id = vm->def->id;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainDefFree(def);
|
|
virDomainObjEndAPI(&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:
|
|
virDomainObjEndAPI(&vm);
|
|
return dom;
|
|
}
|
|
|
|
static int
|
|
libxlDomainSuspend(virDomainPtr dom)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
virObjectEventPtr event = NULL;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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;
|
|
}
|
|
|
|
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
|
|
if (libxl_domain_pause(cfg->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, cfg->caps) < 0)
|
|
goto endjob;
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&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;
|
|
virObjectEventPtr event = NULL;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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;
|
|
}
|
|
|
|
if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) {
|
|
if (libxl_domain_unpause(cfg->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, cfg->caps) < 0)
|
|
goto endjob;
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
if (event)
|
|
libxlDomainEventQueue(driver, event);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
int ret = -1;
|
|
|
|
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;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_SHUTDOWN_PARAVIRT) {
|
|
ret = libxl_domain_shutdown(cfg->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(cfg->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);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainShutdown(virDomainPtr dom)
|
|
{
|
|
return libxlDomainShutdownFlags(dom, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
libxlDomainReboot(virDomainPtr dom, unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_REBOOT_PARAVIRT, -1);
|
|
if (flags == 0)
|
|
flags = VIR_DOMAIN_REBOOT_PARAVIRT;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_REBOOT_PARAVIRT) {
|
|
ret = libxl_domain_reboot(cfg->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);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDestroyFlags(virDomainPtr dom,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
int ret = -1;
|
|
virObjectEventPtr event = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, cleanup);
|
|
|
|
if (virDomainDestroyFlagsEnsureACL(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 (libxlDomainDestroyInternal(driver, vm) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to destroy domain '%d'"), vm->def->id);
|
|
goto endjob;
|
|
}
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF,
|
|
VIR_DOMAIN_SHUTOFF_DESTROYED);
|
|
|
|
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
|
|
|
|
libxlDomainCleanup(driver, vm);
|
|
if (!vm->persistent)
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
if (event)
|
|
libxlDomainEventQueue(driver, event);
|
|
virObjectUnref(cfg);
|
|
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, virDomainOSTypeToString(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 = virDomainDefGetMemoryTotal(vm->def);
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Helper method for --current, --live, and --config options, and check
|
|
* whether domain is active or can get persistent domain configuration.
|
|
*
|
|
* Return 0 if success, also change the flags and get the persistent
|
|
* domain configuration if needed. Return -1 on error.
|
|
*/
|
|
static int
|
|
virDomainLiveConfigHelperMethod(virCapsPtr caps,
|
|
virDomainXMLOptionPtr xmlopt,
|
|
virDomainObjPtr dom,
|
|
unsigned int *flags,
|
|
virDomainDefPtr *persistentDef)
|
|
{
|
|
if (virDomainObjUpdateModificationImpact(dom, flags) < 0)
|
|
return -1;
|
|
|
|
if (*flags & VIR_DOMAIN_AFFECT_CONFIG) {
|
|
if (!(*persistentDef = virDomainObjGetPersistentDef(caps, xmlopt, dom))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Get persistent config failed"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
libxlDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
virDomainDefPtr persistentDef = NULL;
|
|
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;
|
|
|
|
if (virDomainLiveConfigHelperMethod(cfg->caps, driver->xmlopt, vm, &flags,
|
|
&persistentDef) < 0)
|
|
goto endjob;
|
|
|
|
if (flags & VIR_DOMAIN_MEM_MAXIMUM) {
|
|
/* resize the maximum memory */
|
|
|
|
if (flags & VIR_DOMAIN_MEM_LIVE) {
|
|
if (libxl_domain_setmaxmem(cfg->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);
|
|
virDomainDefSetMemoryTotal(persistentDef, newmem);
|
|
if (persistentDef->mem.cur_balloon > newmem)
|
|
persistentDef->mem.cur_balloon = newmem;
|
|
ret = virDomainSaveConfig(cfg->configDir, cfg->caps, persistentDef);
|
|
goto endjob;
|
|
}
|
|
|
|
} else {
|
|
/* resize the current memory */
|
|
|
|
if (newmem > virDomainDefGetMemoryTotal(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;
|
|
|
|
/* Unlock virDomainObj while ballooning memory */
|
|
virObjectUnlock(vm);
|
|
res = libxl_set_memory_target(cfg->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, cfg->caps, persistentDef);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&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)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
libxl_dominfo d_info;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetInfoEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
info->cpuTime = 0;
|
|
info->memory = vm->def->mem.cur_balloon;
|
|
info->maxMem = virDomainDefGetMemoryTotal(vm->def);
|
|
} else {
|
|
libxl_dominfo_init(&d_info);
|
|
|
|
if (libxl_domain_info(cfg->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;
|
|
|
|
libxl_dominfo_dispose(&d_info);
|
|
}
|
|
|
|
info->state = virDomainObjGetState(vm, NULL);
|
|
info->nrVirtCpu = virDomainDefGetVcpus(vm->def);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
virObjectUnref(cfg);
|
|
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)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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, cfg->caps, 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(cfg->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;
|
|
}
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF,
|
|
VIR_DOMAIN_SHUTOFF_SAVED);
|
|
|
|
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
|
|
VIR_DOMAIN_EVENT_STOPPED_SAVED);
|
|
|
|
if (libxlDomainDestroyInternal(driver, vm) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to destroy domain '%d'"), vm->def->id);
|
|
goto cleanup;
|
|
}
|
|
|
|
libxlDomainCleanup(driver, vm);
|
|
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);
|
|
virObjectUnref(cfg);
|
|
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;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
if (remove_dom && vm) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
virDomainObjEndAPI(&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)
|
|
goto cleanup;
|
|
|
|
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;
|
|
virObjectRef(vm);
|
|
def = NULL;
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0) {
|
|
if (!vm->persistent) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = libxlDomainStartRestore(driver, vm,
|
|
(flags & VIR_DOMAIN_SAVE_PAUSED) != 0,
|
|
fd, hdr.version);
|
|
if (ret < 0 && !vm->persistent)
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
if (VIR_CLOSE(fd) < 0)
|
|
virReportSystemError(errno, "%s", _("cannot close file"));
|
|
virDomainDefFree(def);
|
|
virDomainObjEndAPI(&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;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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;
|
|
}
|
|
|
|
if (!(flags & VIR_DUMP_LIVE) &&
|
|
virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
|
|
if (libxl_domain_pause(cfg->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(cfg->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 (libxlDomainDestroyInternal(driver, vm) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to destroy domain '%d'"), vm->def->id);
|
|
goto unpause;
|
|
}
|
|
|
|
libxlDomainCleanup(driver, vm);
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF,
|
|
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(cfg->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:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
if (remove_dom && vm) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
virDomainObjEndAPI(&vm);
|
|
if (event)
|
|
libxlDomainEventQueue(driver, event);
|
|
virObjectUnref(cfg);
|
|
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;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
if (remove_dom && vm) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
vm = NULL;
|
|
}
|
|
virDomainObjEndAPI(&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);
|
|
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) && virDomainDefGetVcpusMax(vm->def) < max)
|
|
max = virDomainDefGetVcpusMax(vm->def);
|
|
|
|
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;
|
|
}
|
|
|
|
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:
|
|
if (virDomainDefSetVcpusMax(def, nvcpus, driver->xmlopt) < 0)
|
|
goto cleanup;
|
|
break;
|
|
|
|
case VIR_DOMAIN_VCPU_CONFIG:
|
|
if (virDomainDefSetVcpus(def, nvcpus) < 0)
|
|
goto cleanup;
|
|
break;
|
|
|
|
case VIR_DOMAIN_VCPU_LIVE:
|
|
if (libxl_set_vcpuonline(cfg->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;
|
|
}
|
|
if (virDomainDefSetVcpus(vm->def, nvcpus) < 0)
|
|
goto endjob;
|
|
break;
|
|
|
|
case VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_CONFIG:
|
|
if (libxl_set_vcpuonline(cfg->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;
|
|
}
|
|
if (virDomainDefSetVcpus(vm->def, nvcpus) < 0 ||
|
|
virDomainDefSetVcpus(def, nvcpus) < 0)
|
|
goto endjob;
|
|
break;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
if (flags & VIR_DOMAIN_VCPU_LIVE) {
|
|
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, cfg->caps) < 0) {
|
|
VIR_WARN("Unable to save status on vm %s after changing vcpus",
|
|
vm->def->name);
|
|
}
|
|
}
|
|
if (flags & VIR_DOMAIN_VCPU_CONFIG) {
|
|
if (virDomainSaveConfig(cfg->configDir, cfg->caps, def) < 0) {
|
|
VIR_WARN("Unable to save configuration of vm %s after changing vcpus",
|
|
vm->def->name);
|
|
}
|
|
}
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
VIR_FREE(bitmask);
|
|
virDomainObjEndAPI(&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;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
|
|
ret = virDomainDefGetVcpusMax(def);
|
|
else
|
|
ret = virDomainDefGetVcpus(def);
|
|
|
|
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;
|
|
virDomainVcpuDefPtr vcpuinfo;
|
|
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 (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 (!(vcpuinfo = virDomainDefGetVcpu(targetDef, vcpu)) ||
|
|
!vcpuinfo->online) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("vcpu '%u' is not active"), vcpu);
|
|
goto endjob;
|
|
}
|
|
|
|
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
|
|
libxl_bitmap map = { .size = maplen, .map = cpumap };
|
|
if (libxl_set_vcpuaffinity(cfg->ctx, vm->def->id, vcpu, &map) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to pin vcpu '%d' with libxenlight"),
|
|
vcpu);
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
virBitmapFree(vcpuinfo->cpumask);
|
|
vcpuinfo->cpumask = pcpumap;
|
|
pcpumap = NULL;
|
|
|
|
ret = 0;
|
|
|
|
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
|
|
ret = virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, cfg->caps);
|
|
} else if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
|
|
ret = virDomainSaveConfig(cfg->configDir, cfg->caps, targetDef);
|
|
}
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&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;
|
|
int ret = -1;
|
|
|
|
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);
|
|
|
|
ret = virDomainDefGetVcpuPinInfoHelper(targetDef, maplen, ncpumaps, cpumaps,
|
|
libxl_get_max_cpus(cfg->ctx), NULL);
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainGetVcpus(virDomainPtr dom, virVcpuInfoPtr info, int maxinfo,
|
|
unsigned char *cpumaps, int maplen)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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;
|
|
}
|
|
|
|
if ((vcpuinfo = libxl_list_vcpu(cfg->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);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
libxlDomainGetXMLDesc(virDomainPtr dom, unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainObjPtr vm;
|
|
virDomainDefPtr def;
|
|
char *ret = NULL;
|
|
|
|
/* Flags checked by virDomainDefFormat */
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetXMLDescEnsureACL(dom->conn, vm->def, flags) < 0)
|
|
goto cleanup;
|
|
|
|
if ((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef)
|
|
def = vm->newDef;
|
|
else
|
|
def = vm->def;
|
|
|
|
ret = virDomainDefFormat(def, cfg->caps,
|
|
virDomainDefFormatConvertXMLFlags(flags));
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
virObjectUnref(cfg);
|
|
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, XEN_CONFIG_FORMAT_XL)) {
|
|
if (!(conf = virConfReadMem(nativeConfig, strlen(nativeConfig), 0)))
|
|
goto cleanup;
|
|
if (!(def = xenParseXL(conf,
|
|
cfg->caps,
|
|
driver->xmlopt)))
|
|
goto cleanup;
|
|
} else if (STREQ(nativeFormat, XEN_CONFIG_FORMAT_XM)) {
|
|
if (!(conf = virConfReadMem(nativeConfig, strlen(nativeConfig), 0)))
|
|
goto cleanup;
|
|
|
|
if (!(def = xenParseXM(conf,
|
|
cfg->caps,
|
|
driver->xmlopt)))
|
|
goto cleanup;
|
|
} else if (STREQ(nativeFormat, XEN_CONFIG_FORMAT_SEXPR)) {
|
|
/* only support latest xend config format */
|
|
if (!(def = xenParseSxprString(nativeConfig,
|
|
NULL,
|
|
-1,
|
|
cfg->caps,
|
|
driver->xmlopt))) {
|
|
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, cfg->caps, 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, NULL,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE)))
|
|
goto cleanup;
|
|
|
|
if (STREQ(nativeFormat, XEN_CONFIG_FORMAT_XL)) {
|
|
if (!(conf = xenFormatXL(def, conn)))
|
|
goto cleanup;
|
|
} else if (STREQ(nativeFormat, XEN_CONFIG_FORMAT_XM)) {
|
|
if (!(conf = xenFormatXM(conn, def)))
|
|
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 (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("Domain is already running"));
|
|
goto endjob;
|
|
}
|
|
|
|
ret = libxlDomainStartNew(driver, vm,
|
|
(flags & VIR_DOMAIN_START_PAUSED) != 0);
|
|
if (ret < 0)
|
|
goto endjob;
|
|
dom->id = vm->def->id;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&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_SCHEMA;
|
|
|
|
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
|
|
NULL, parse_flags)))
|
|
goto cleanup;
|
|
|
|
if (virXMLCheckIllegalChars("name", def->name, "\n") < 0)
|
|
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,
|
|
cfg->caps,
|
|
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(virDomainObjPtr vm, virDomainDiskDefPtr disk)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(libxl_driver);
|
|
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(cfg->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:
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachDeviceDiskLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(libxl_driver);
|
|
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(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 (virDomainLockDiskAttach(libxl_driver->lockManager,
|
|
"xen:///system",
|
|
vm, l_disk) < 0)
|
|
goto cleanup;
|
|
|
|
if ((ret = libxl_device_disk_add(cfg->ctx, vm->def->id,
|
|
&x_disk, NULL)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to attach disk '%s'"),
|
|
l_disk->dst);
|
|
if (virDomainLockDiskDetach(libxl_driver->lockManager,
|
|
vm, l_disk) < 0) {
|
|
VIR_WARN("Unable to release lock on %s",
|
|
virDomainDiskGetSource(l_disk));
|
|
}
|
|
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:
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachHostPCIDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
libxl_device_pci pcidev;
|
|
virDomainHostdevDefPtr found;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
|
|
int ret = -1;
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
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);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0)
|
|
goto cleanup;
|
|
|
|
if (virHostdevPreparePCIDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, vm->def->uuid,
|
|
&hostdev, 1, 0) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlMakePCI(hostdev, &pcidev) < 0)
|
|
goto error;
|
|
|
|
if (libxl_device_pci_add(cfg->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;
|
|
ret = 0;
|
|
goto cleanup;
|
|
|
|
error:
|
|
virHostdevReAttachPCIDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1, NULL);
|
|
|
|
cleanup:
|
|
virObjectUnref(cfg);
|
|
libxl_device_pci_dispose(&pcidev);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
static int
|
|
libxlDomainAttachControllerDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainControllerDefPtr controller)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
const char *type = virDomainControllerTypeToString(controller->type);
|
|
libxl_device_usbctrl usbctrl;
|
|
int ret = -1;
|
|
|
|
libxl_device_usbctrl_init(&usbctrl);
|
|
|
|
if (controller->type != VIR_DOMAIN_CONTROLLER_TYPE_USB) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
_("'%s' controller cannot be hot plugged."),
|
|
type);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (controller->idx == -1)
|
|
controller->idx = virDomainControllerFindUnusedIndex(vm->def,
|
|
controller->type);
|
|
|
|
if (controller->opts.usbopts.ports == -1)
|
|
controller->opts.usbopts.ports = 8;
|
|
|
|
if (virDomainControllerFind(vm->def, controller->type, controller->idx) >= 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("target %s:%d already exists"),
|
|
type, controller->idx);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers + 1) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlMakeUSBController(controller, &usbctrl) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxl_device_usbctrl_add(cfg->ctx, vm->def->id, &usbctrl, 0) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("libxenlight failed to attach USB controller"));
|
|
goto cleanup;
|
|
}
|
|
|
|
virDomainControllerInsertPreAlloced(vm->def, controller);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virObjectUnref(cfg);
|
|
libxl_device_usbctrl_dispose(&usbctrl);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachHostUSBDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
libxl_device_usbdev usbdev;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
int ret = -1;
|
|
size_t i;
|
|
int ports = 0, usbdevs = 0;
|
|
|
|
libxl_device_usbdev_init(&usbdev);
|
|
|
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
|
|
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
|
goto cleanup;
|
|
|
|
/* search for available controller:port */
|
|
for (i = 0; i < vm->def->ncontrollers; i++)
|
|
ports += vm->def->controllers[i]->opts.usbopts.ports;
|
|
|
|
for (i = 0; i < vm->def->nhostdevs; i++) {
|
|
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
|
|
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
|
usbdevs++;
|
|
}
|
|
|
|
if (ports <= usbdevs) {
|
|
/* no free ports, we will create a new usb controller */
|
|
virDomainControllerDefPtr controller;
|
|
|
|
if (!(controller = virDomainControllerDefNew(VIR_DOMAIN_CONTROLLER_TYPE_USB)))
|
|
goto cleanup;
|
|
|
|
controller->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2;
|
|
controller->idx = -1;
|
|
controller->opts.usbopts.ports = 8;
|
|
|
|
if (libxlDomainAttachControllerDevice(driver, vm, controller) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("No available USB controller and port, and "
|
|
"failed to attach a new one"));
|
|
virDomainControllerDefFree(controller);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0)
|
|
goto cleanup;
|
|
|
|
if (virHostdevPrepareUSBDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1, 0) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlMakeUSB(hostdev, &usbdev) < 0)
|
|
goto reattach;
|
|
|
|
if (libxl_device_usbdev_add(cfg->ctx, vm->def->id, &usbdev, 0) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to attach usb device Busnum:%3x, Devnum:%3x"),
|
|
hostdev->source.subsys.u.usb.bus,
|
|
hostdev->source.subsys.u.usb.device);
|
|
goto reattach;
|
|
}
|
|
|
|
vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
|
|
ret = 0;
|
|
goto cleanup;
|
|
|
|
reattach:
|
|
virHostdevReAttachUSBDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1);
|
|
|
|
cleanup:
|
|
virObjectUnref(cfg);
|
|
libxl_device_usbdev_dispose(&usbdev);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
libxlDomainAttachHostDevice(libxlDriverPrivatePtr driver,
|
|
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, vm, hostdev) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
|
|
if (libxlDomainAttachHostUSBDevice(driver, vm, hostdev) < 0)
|
|
return -1;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("hostdev subsys type '%s' not supported"),
|
|
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachDeviceDiskLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(libxl_driver);
|
|
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(cfg->ctx, vm->def->id,
|
|
&x_disk, NULL)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to detach disk '%s'"),
|
|
l_disk->dst);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainLockDiskDetach(libxl_driver->lockManager,
|
|
vm, l_disk) < 0)
|
|
VIR_WARN("Unable to release lock on %s",
|
|
virDomainDiskGetSource(l_disk));
|
|
|
|
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:
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachNetDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainNetDefPtr net)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainNetType actualType;
|
|
libxl_device_nic nic;
|
|
int ret = -1;
|
|
char mac[VIR_MAC_STRING_BUFLEN];
|
|
|
|
libxl_device_nic_init(&nic);
|
|
|
|
/* preallocate new slot for device */
|
|
if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets + 1) < 0)
|
|
goto cleanup;
|
|
|
|
/* 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)
|
|
goto cleanup;
|
|
|
|
actualType = virDomainNetGetActualType(net);
|
|
|
|
if (virDomainHasNet(vm->def, net)) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("network device with mac %s already exists"),
|
|
virMacAddrFormat(&net->mac, mac));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
|
|
virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net);
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
|
|
|
|
/* For those just allocated from a network pool whose backend is
|
|
* still VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, we need to set
|
|
* backend correctly.
|
|
*/
|
|
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
|
|
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
pcisrc->backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN;
|
|
|
|
/* 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 if successful.
|
|
*/
|
|
ret = libxlDomainAttachHostDevice(driver, vm, hostdev);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (libxlMakeNic(vm->def, net, &nic) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxl_device_nic_add(cfg->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);
|
|
if (!ret) {
|
|
vm->def->nets[vm->def->nnets++] = net;
|
|
} else {
|
|
virDomainNetRemoveHostdev(vm->def, net);
|
|
networkReleaseActualDevice(vm->def, net);
|
|
}
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainAttachDeviceLive(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainDeviceDefPtr dev)
|
|
{
|
|
int ret = -1;
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
ret = libxlDomainAttachDeviceDiskLive(vm, dev);
|
|
if (!ret)
|
|
dev->data.disk = NULL;
|
|
break;
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
case VIR_DOMAIN_DEVICE_CONTROLLER:
|
|
ret = libxlDomainAttachControllerDevice(driver, vm, dev->data.controller);
|
|
if (!ret)
|
|
dev->data.controller = NULL;
|
|
break;
|
|
#endif
|
|
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
ret = libxlDomainAttachNetDevice(driver, vm,
|
|
dev->data.net);
|
|
if (!ret)
|
|
dev->data.net = NULL;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
|
ret = libxlDomainAttachHostDevice(driver, 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;
|
|
virDomainControllerDefPtr controller;
|
|
virDomainHostdevDefPtr found;
|
|
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_CONTROLLER:
|
|
controller = dev->data.controller;
|
|
if (controller->idx != -1 &&
|
|
virDomainControllerFind(vmdef, controller->type,
|
|
controller->idx) >= 0) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Target already exists"));
|
|
return -1;
|
|
}
|
|
|
|
if (virDomainControllerInsert(vmdef, controller) < 0)
|
|
return -1;
|
|
dev->data.controller = 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;
|
|
|
|
switch (hostdev->source.subsys.type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
|
|
#ifndef LIBXL_HAVE_PVUSB
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
|
|
#endif
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
|
|
return -1;
|
|
}
|
|
|
|
if (virDomainHostdevFind(vmdef, hostdev, &found) >= 0) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("device is already in the domain configuration"));
|
|
return -1;
|
|
}
|
|
|
|
if (virDomainHostdevInsert(vmdef, hostdev) < 0)
|
|
return -1;
|
|
dev->data.hostdev = NULL;
|
|
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,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &subsys->u.pci;
|
|
libxl_device_pci pcidev;
|
|
virDomainHostdevDefPtr detach;
|
|
int idx;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
int ret = -1;
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
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);
|
|
goto cleanup;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
if (libxlMakePCI(detach, &pcidev) < 0)
|
|
goto error;
|
|
|
|
if (libxl_device_pci_remove(cfg->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;
|
|
}
|
|
|
|
|
|
virDomainHostdevRemove(vm->def, idx);
|
|
|
|
virHostdevReAttachPCIDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1, NULL);
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
virDomainHostdevDefFree(detach);
|
|
|
|
cleanup:
|
|
virObjectUnref(cfg);
|
|
libxl_device_pci_dispose(&pcidev);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
static int
|
|
libxlDomainDetachControllerDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainDeviceDefPtr dev)
|
|
{
|
|
int idx, ret = -1;
|
|
virDomainControllerDefPtr detach = NULL;
|
|
virDomainControllerDefPtr controller = dev->data.controller;
|
|
const char *type = virDomainControllerTypeToString(controller->type);
|
|
libxl_device_usbctrl usbctrl;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
|
|
libxl_device_usbctrl_init(&usbctrl);
|
|
|
|
if (controller->type != VIR_DOMAIN_CONTROLLER_TYPE_USB) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
_("'%s' controller cannot be hot plugged."),
|
|
type);
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((idx = virDomainControllerFind(vm->def,
|
|
controller->type,
|
|
controller->idx)) < 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("controller %s:%d not found"),
|
|
type, controller->idx);
|
|
goto cleanup;
|
|
}
|
|
|
|
detach = vm->def->controllers[idx];
|
|
|
|
if (libxlMakeUSBController(controller, &usbctrl) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxl_device_usbctrl_remove(cfg->ctx, vm->def->id, &usbctrl, 0) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("libxenlight failed to detach USB controller"));
|
|
goto cleanup;
|
|
}
|
|
|
|
virDomainControllerRemove(vm->def, idx);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainControllerDefFree(detach);
|
|
virObjectUnref(cfg);
|
|
libxl_device_usbctrl_dispose(&usbctrl);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachHostUSBDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainHostdevDefPtr hostdev)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys;
|
|
virDomainHostdevSubsysUSBPtr usbsrc = &subsys->u.usb;
|
|
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
|
|
libxl_device_usbdev usbdev;
|
|
libxl_device_usbdev *usbdevs = NULL;
|
|
int num = 0;
|
|
virDomainHostdevDefPtr detach;
|
|
int idx;
|
|
size_t i;
|
|
bool found = false;
|
|
int ret = -1;
|
|
|
|
libxl_device_usbdev_init(&usbdev);
|
|
|
|
idx = virDomainHostdevFind(vm->def, hostdev, &detach);
|
|
if (idx < 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("host USB device Busnum: %3x, Devnum: %3x not found"),
|
|
usbsrc->bus, usbsrc->device);
|
|
goto cleanup;
|
|
}
|
|
|
|
usbdevs = libxl_device_usbdev_list(cfg->ctx, vm->def->id, &num);
|
|
for (i = 0; i < num; i++) {
|
|
if (usbdevs[i].u.hostdev.hostbus == usbsrc->bus &&
|
|
usbdevs[i].u.hostdev.hostaddr == usbsrc->device) {
|
|
libxl_device_usbdev_copy(cfg->ctx, &usbdev, &usbdevs[i]);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
libxl_device_usbdev_list_free(usbdevs, num);
|
|
|
|
if (!found) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("host USB device Busnum: %3x, Devnum: %3x not found"),
|
|
usbsrc->bus, usbsrc->device);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (libxl_device_usbdev_remove(cfg->ctx, vm->def->id, &usbdev, 0) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to detach USB device\
|
|
Busnum: %3x, Devnum: %3x"),
|
|
usbsrc->bus, usbsrc->device);
|
|
goto cleanup;
|
|
}
|
|
|
|
virDomainHostdevRemove(vm->def, idx);
|
|
|
|
virHostdevReAttachUSBDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
|
|
vm->def->name, &hostdev, 1);
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainHostdevDefFree(detach);
|
|
virObjectUnref(cfg);
|
|
libxl_device_usbdev_dispose(&usbdev);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
libxlDomainDetachHostDevice(libxlDriverPrivatePtr driver,
|
|
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, vm, hostdev);
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
|
|
return libxlDomainDetachHostUSBDevice(driver, vm, hostdev);
|
|
#endif
|
|
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected hostdev type %d"), subsys->type);
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachNetDevice(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainNetDefPtr net)
|
|
{
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
int detachidx;
|
|
virDomainNetDefPtr detach = NULL;
|
|
libxl_device_nic nic;
|
|
char mac[VIR_MAC_STRING_BUFLEN];
|
|
int ret = -1;
|
|
|
|
libxl_device_nic_init(&nic);
|
|
|
|
if ((detachidx = virDomainNetFindIdx(vm->def, net)) < 0)
|
|
goto cleanup;
|
|
|
|
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, vm,
|
|
virDomainNetGetActualHostdev(detach));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (libxl_mac_to_device_nic(cfg->ctx, vm->def->id,
|
|
virMacAddrFormat(&detach->mac, mac), &nic))
|
|
goto cleanup;
|
|
|
|
if (libxl_device_nic_remove(cfg->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);
|
|
if (!ret) {
|
|
networkReleaseActualDevice(vm->def, detach);
|
|
virDomainNetRemove(vm->def, detachidx);
|
|
}
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainDetachDeviceLive(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virDomainDeviceDefPtr dev)
|
|
{
|
|
virDomainHostdevDefPtr hostdev;
|
|
int ret = -1;
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
ret = libxlDomainDetachDeviceDiskLive(vm, dev);
|
|
break;
|
|
|
|
#ifdef LIBXL_HAVE_PVUSB
|
|
case VIR_DOMAIN_DEVICE_CONTROLLER:
|
|
ret = libxlDomainDetachControllerDevice(driver, vm, dev);
|
|
break;
|
|
#endif
|
|
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
ret = libxlDomainDetachNetDevice(driver, vm,
|
|
dev->data.net);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
|
hostdev = dev->data.hostdev;
|
|
|
|
/* If this is a network hostdev, we need to use the higher-level
|
|
* detach function so that mac address / virtualport are reset
|
|
*/
|
|
if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET)
|
|
ret = libxlDomainDetachNetDevice(driver, vm,
|
|
hostdev->parent.data.net);
|
|
else
|
|
ret = libxlDomainDetachHostDevice(driver, vm, 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;
|
|
virDomainControllerDefPtr cont, det_cont;
|
|
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_CONTROLLER:
|
|
cont = dev->data.controller;
|
|
if ((idx = virDomainControllerFind(vmdef, cont->type,
|
|
cont->idx)) < 0) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("device not present in domain configuration"));
|
|
return -1;
|
|
}
|
|
det_cont = virDomainControllerRemove(vmdef, idx);
|
|
virDomainControllerDefFree(det_cont);
|
|
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(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(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 ret = -1;
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
disk = dev->data.disk;
|
|
if (!(orig = virDomainDiskByName(vmdef, disk->dst, false))) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("target %s doesn't exist."), disk->dst);
|
|
goto cleanup;
|
|
}
|
|
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;
|
|
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 (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
|
|
goto endjob;
|
|
|
|
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, 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, cfg->caps) < 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, cfg->caps, vmdef);
|
|
if (!ret) {
|
|
virDomainObjAssignDef(vm, vmdef, false, NULL);
|
|
vmdef = NULL;
|
|
}
|
|
}
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainDefFree(vmdef);
|
|
virDomainDeviceDefFree(dev);
|
|
virDomainObjEndAPI(&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;
|
|
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 (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
|
|
goto endjob;
|
|
|
|
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
|
|
if (!(dev = virDomainDeviceDefParse(xml, vm->def,
|
|
cfg->caps, driver->xmlopt,
|
|
VIR_DOMAIN_DEF_PARSE_INACTIVE |
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
|
|
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 |
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
|
|
goto endjob;
|
|
|
|
if (libxlDomainDetachDeviceLive(driver, 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, cfg->caps) < 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, cfg->caps, vmdef);
|
|
if (!ret) {
|
|
virDomainObjAssignDef(vm, vmdef, false, NULL);
|
|
vmdef = NULL;
|
|
}
|
|
}
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainDefFree(vmdef);
|
|
virDomainDeviceDefFree(dev);
|
|
virDomainObjEndAPI(&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;
|
|
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 (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
|
|
goto cleanup;
|
|
|
|
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(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, cfg->caps) < 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, cfg->caps, 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;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
VIR_FREE(configFile);
|
|
VIR_FREE(autostartLink);
|
|
virDomainObjEndAPI(&vm);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
libxlDomainGetSchedulerType(virDomainPtr dom, int *nparams)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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;
|
|
}
|
|
|
|
sched_id = libxl_get_scheduler(cfg->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);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainGetSchedulerParametersFlags(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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;
|
|
}
|
|
|
|
sched_id = libxl_get_scheduler(cfg->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(cfg->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[1], 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);
|
|
virObjectUnref(cfg);
|
|
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;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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;
|
|
}
|
|
|
|
sched_id = libxl_get_scheduler(cfg->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(cfg->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(cfg->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:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
virObjectUnref(cfg);
|
|
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 (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
LIBXL_CHECK_DOM0_GOTO(vm->def->name, 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 (dev_name) {
|
|
size_t i;
|
|
|
|
for (i = 0; !chr && i < vm->def->nserials; i++) {
|
|
if (STREQ(dev_name, vm->def->serials[i]->info.alias)) {
|
|
chr = vm->def->serials[i];
|
|
break;
|
|
}
|
|
}
|
|
} else if (vm->def->nconsoles) {
|
|
chr = vm->def->consoles[0];
|
|
if (chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL)
|
|
chr = vm->def->serials[0];
|
|
}
|
|
|
|
if (!chr) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot find character device %s"),
|
|
NULLSTR(dev_name));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (chr->source->type != VIR_DOMAIN_CHR_TYPE_PTY) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("character device %s is not using a PTY"),
|
|
dev_name ? dev_name : NULLSTR(chr->info.alias));
|
|
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)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
|
|
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;
|
|
}
|
|
|
|
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(cfg->ctx);
|
|
if (numnodes <= 0)
|
|
goto cleanup;
|
|
|
|
if (libxl_node_bitmap_alloc(cfg->ctx, &nodemap, 0)) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
if (!(nodes = virBitmapNew(numnodes)))
|
|
goto cleanup;
|
|
|
|
rc = libxl_domain_get_nodeaffinity(cfg->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);
|
|
virObjectUnref(cfg);
|
|
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
|
|
libxlDomainInterfaceStats(virDomainPtr dom,
|
|
const char *path,
|
|
virDomainInterfaceStatsPtr stats)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
virDomainObjPtr vm;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainInterfaceStatsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_QUERY) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("domain is not running"));
|
|
goto endjob;
|
|
}
|
|
|
|
/* Check the path is one of the domain's network interfaces. */
|
|
for (i = 0; i < vm->def->nnets; i++) {
|
|
if (vm->def->nets[i]->ifname &&
|
|
STREQ(vm->def->nets[i]->ifname, path)) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
ret = virNetInterfaceStats(path, stats);
|
|
else
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("'%s' is not a known interface"), path);
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainGetTotalCPUStats(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virTypedParameterPtr params,
|
|
unsigned int nparams)
|
|
{
|
|
libxlDriverConfigPtr cfg;
|
|
libxl_dominfo d_info;
|
|
int ret = -1;
|
|
|
|
if (nparams == 0)
|
|
return LIBXL_NB_TOTAL_CPU_STAT_PARAM;
|
|
|
|
libxl_dominfo_init(&d_info);
|
|
cfg = libxlDriverConfigGet(driver);
|
|
|
|
if (libxl_domain_info(cfg->ctx, &d_info, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxl_domain_info failed for domain '%d'"),
|
|
vm->def->id);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virTypedParameterAssign(¶ms[0], VIR_DOMAIN_CPU_STATS_CPUTIME,
|
|
VIR_TYPED_PARAM_ULLONG, d_info.cpu_time) < 0)
|
|
goto cleanup;
|
|
|
|
ret = nparams;
|
|
|
|
cleanup:
|
|
libxl_dominfo_dispose(&d_info);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainGetPerCPUStats(libxlDriverPrivatePtr driver,
|
|
virDomainObjPtr vm,
|
|
virTypedParameterPtr params,
|
|
unsigned int nparams,
|
|
int start_cpu,
|
|
unsigned int ncpus)
|
|
{
|
|
libxl_vcpuinfo *vcpuinfo;
|
|
int maxcpu, hostcpus;
|
|
size_t i;
|
|
libxlDriverConfigPtr cfg;
|
|
int ret = -1;
|
|
|
|
if (nparams == 0 && ncpus != 0)
|
|
return LIBXL_NB_TOTAL_CPU_STAT_PARAM;
|
|
else if (nparams == 0)
|
|
return virDomainDefGetVcpusMax(vm->def);
|
|
|
|
cfg = libxlDriverConfigGet(driver);
|
|
if ((vcpuinfo = libxl_list_vcpu(cfg->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;
|
|
}
|
|
|
|
for (i = start_cpu; i < maxcpu && i < ncpus; ++i) {
|
|
if (virTypedParameterAssign(¶ms[(i-start_cpu)],
|
|
VIR_DOMAIN_CPU_STATS_CPUTIME,
|
|
VIR_TYPED_PARAM_ULLONG,
|
|
vcpuinfo[i].vcpu_time) < 0)
|
|
goto cleanup;
|
|
}
|
|
ret = nparams;
|
|
|
|
cleanup:
|
|
if (vcpuinfo)
|
|
libxl_vcpuinfo_list_free(vcpuinfo, maxcpu);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainGetCPUStats(virDomainPtr dom,
|
|
virTypedParameterPtr params,
|
|
unsigned int nparams,
|
|
int start_cpu,
|
|
unsigned int ncpus,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
virDomainObjPtr vm = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetCPUStatsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("domain is not running"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (start_cpu == -1)
|
|
ret = libxlDomainGetTotalCPUStats(driver, vm, params, nparams);
|
|
else
|
|
ret = libxlDomainGetPerCPUStats(driver, vm, params, nparams,
|
|
start_cpu, ncpus);
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
#define LIBXL_SET_MEMSTAT(TAG, VAL) \
|
|
if (i < nr_stats) { \
|
|
stats[i].tag = TAG; \
|
|
stats[i].val = VAL; \
|
|
i++; \
|
|
}
|
|
|
|
static int
|
|
libxlDomainMemoryStats(virDomainPtr dom,
|
|
virDomainMemoryStatPtr stats,
|
|
unsigned int nr_stats,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
libxlDriverConfigPtr cfg;
|
|
virDomainObjPtr vm;
|
|
libxl_dominfo d_info;
|
|
unsigned mem, maxmem;
|
|
size_t i = 0;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
libxl_dominfo_init(&d_info);
|
|
cfg = libxlDriverConfigGet(driver);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainMemoryStatsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_QUERY) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("domain is not running"));
|
|
goto endjob;
|
|
}
|
|
|
|
if (libxl_domain_info(cfg->ctx, &d_info, vm->def->id) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxl_domain_info failed for domain '%d'"),
|
|
vm->def->id);
|
|
goto endjob;
|
|
}
|
|
mem = d_info.current_memkb;
|
|
maxmem = d_info.max_memkb;
|
|
|
|
LIBXL_SET_MEMSTAT(VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON, mem);
|
|
LIBXL_SET_MEMSTAT(VIR_DOMAIN_MEMORY_STAT_AVAILABLE, maxmem);
|
|
|
|
ret = i;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
libxl_dominfo_dispose(&d_info);
|
|
virDomainObjEndAPI(&vm);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
#undef LIBXL_SET_MEMSTAT
|
|
|
|
static int
|
|
libxlDomainGetJobInfo(virDomainPtr dom,
|
|
virDomainJobInfoPtr info)
|
|
{
|
|
libxlDomainObjPrivatePtr priv;
|
|
virDomainObjPtr vm;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetJobInfoEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
if (!priv->job.active) {
|
|
memset(info, 0, sizeof(*info));
|
|
info->type = VIR_DOMAIN_JOB_NONE;
|
|
ret = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* In libxl we don't have an estimated completion time
|
|
* thus we always set to unbounded and update time
|
|
* for the active job. */
|
|
if (libxlDomainJobUpdateTime(&priv->job) < 0)
|
|
goto cleanup;
|
|
|
|
memcpy(info, priv->job.current, sizeof(virDomainJobInfo));
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainGetJobStats(virDomainPtr dom,
|
|
int *type,
|
|
virTypedParameterPtr *params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
libxlDomainObjPrivatePtr priv;
|
|
virDomainObjPtr vm;
|
|
virDomainJobInfoPtr jobInfo;
|
|
int ret = -1;
|
|
int maxparams = 0;
|
|
|
|
/* VIR_DOMAIN_JOB_STATS_COMPLETED not supported yet */
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetJobStatsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
jobInfo = priv->job.current;
|
|
if (!priv->job.active) {
|
|
*type = VIR_DOMAIN_JOB_NONE;
|
|
*params = NULL;
|
|
*nparams = 0;
|
|
ret = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* In libxl we don't have an estimated completion time
|
|
* thus we always set to unbounded and update time
|
|
* for the active job. */
|
|
if (libxlDomainJobUpdateTime(&priv->job) < 0)
|
|
goto cleanup;
|
|
|
|
if (virTypedParamsAddULLong(params, nparams, &maxparams,
|
|
VIR_DOMAIN_JOB_TIME_ELAPSED,
|
|
jobInfo->timeElapsed) < 0)
|
|
goto cleanup;
|
|
|
|
*type = jobInfo->type;
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (vm)
|
|
virObjectUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
static int
|
|
libxlDiskPathToID(const char *virtpath)
|
|
{
|
|
static char const* drive_prefix[] = {"xvd", "hd", "sd"};
|
|
int disk, partition, chrused;
|
|
int fmt, id;
|
|
size_t i;
|
|
|
|
fmt = id = -1;
|
|
|
|
/* Find any disk prefixes we know about */
|
|
for (i = 0; i < ARRAY_CARDINALITY(drive_prefix); i++) {
|
|
if (STRPREFIX(virtpath, drive_prefix[i]) &&
|
|
!virDiskNameParse(virtpath, &disk, &partition)) {
|
|
fmt = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Handle it same way as xvd */
|
|
if (fmt < 0 &&
|
|
(sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2
|
|
&& chrused == strlen(virtpath)))
|
|
fmt = 0;
|
|
|
|
/* Test indexes ranges and calculate the device id */
|
|
switch (fmt) {
|
|
case 0: /* xvd */
|
|
if (disk <= 15 && partition <= 15)
|
|
id = (202 << 8) | (disk << 4) | partition;
|
|
else if ((disk <= ((1<<20)-1)) || partition <= 255)
|
|
id = (1 << 28) | (disk << 8) | partition;
|
|
break;
|
|
case 1: /* hd */
|
|
if (disk <= 3 && partition <= 63)
|
|
id = ((disk < 2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
|
|
break;
|
|
case 2: /* sd */
|
|
if (disk <= 15 && (partition <= 15))
|
|
id = (8 << 8) | (disk << 4) | partition;
|
|
break;
|
|
default: /* invalid */
|
|
break;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
# define LIBXL_VBD_SECTOR_SIZE 512
|
|
|
|
static int
|
|
libxlDiskSectorSize(int domid, int devno)
|
|
{
|
|
char *path, *val;
|
|
struct xs_handle *handle;
|
|
int ret = LIBXL_VBD_SECTOR_SIZE;
|
|
unsigned int len;
|
|
|
|
handle = xs_daemon_open_readonly();
|
|
if (!handle) {
|
|
VIR_WARN("cannot read sector size");
|
|
return ret;
|
|
}
|
|
|
|
path = val = NULL;
|
|
if (virAsprintf(&path, "/local/domain/%d/device/vbd/%d/backend",
|
|
domid, devno) < 0)
|
|
goto cleanup;
|
|
|
|
if ((val = xs_read(handle, XBT_NULL, path, &len)) == NULL)
|
|
goto cleanup;
|
|
|
|
VIR_FREE(path);
|
|
if (virAsprintf(&path, "%s/physical-sector-size", val) < 0)
|
|
goto cleanup;
|
|
|
|
VIR_FREE(val);
|
|
if ((val = xs_read(handle, XBT_NULL, path, &len)) == NULL)
|
|
goto cleanup;
|
|
|
|
if (sscanf(val, "%d", &ret) != 1)
|
|
ret = LIBXL_VBD_SECTOR_SIZE;
|
|
|
|
cleanup:
|
|
VIR_FREE(val);
|
|
VIR_FREE(path);
|
|
xs_daemon_close(handle);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainBlockStatsVBD(virDomainObjPtr vm,
|
|
const char *dev,
|
|
libxlBlockStatsPtr stats)
|
|
{
|
|
int ret = -1;
|
|
int devno = libxlDiskPathToID(dev);
|
|
int size;
|
|
char *path, *name, *val;
|
|
unsigned long long stat;
|
|
|
|
path = name = val = NULL;
|
|
if (devno < 0) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cannot find device number"));
|
|
return ret;
|
|
}
|
|
|
|
size = libxlDiskSectorSize(vm->def->id, devno);
|
|
|
|
if (VIR_STRDUP(stats->backend, "vbd") < 0)
|
|
return ret;
|
|
|
|
if (virAsprintf(&path, "/sys/bus/xen-backend/devices/vbd-%d-%d/statistics",
|
|
vm->def->id, devno) < 0)
|
|
return ret;
|
|
|
|
if (!virFileExists(path)) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
"%s", _("cannot open bus path"));
|
|
goto cleanup;
|
|
}
|
|
|
|
# define LIBXL_SET_VBDSTAT(FIELD, VAR, MUL) \
|
|
if ((virAsprintf(&name, "%s/"FIELD, path) < 0) || \
|
|
(virFileReadAll(name, 256, &val) < 0) || \
|
|
(sscanf(val, "%llu", &stat) != 1)) { \
|
|
virReportError(VIR_ERR_OPERATION_FAILED, \
|
|
_("cannot read %s"), name); \
|
|
goto cleanup; \
|
|
} \
|
|
VAR += (stat * MUL); \
|
|
VIR_FREE(name); \
|
|
VIR_FREE(val);
|
|
|
|
LIBXL_SET_VBDSTAT("f_req", stats->f_req, 1)
|
|
LIBXL_SET_VBDSTAT("wr_req", stats->wr_req, 1)
|
|
LIBXL_SET_VBDSTAT("rd_req", stats->rd_req, 1)
|
|
LIBXL_SET_VBDSTAT("wr_sect", stats->wr_bytes, size)
|
|
LIBXL_SET_VBDSTAT("rd_sect", stats->rd_bytes, size)
|
|
|
|
LIBXL_SET_VBDSTAT("ds_req", stats->u.vbd.ds_req, size)
|
|
LIBXL_SET_VBDSTAT("oo_req", stats->u.vbd.oo_req, 1)
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(name);
|
|
VIR_FREE(path);
|
|
VIR_FREE(val);
|
|
|
|
# undef LIBXL_SET_VBDSTAT
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
static int
|
|
libxlDomainBlockStatsVBD(virDomainObjPtr vm ATTRIBUTE_UNUSED,
|
|
const char *dev ATTRIBUTE_UNUSED,
|
|
libxlBlockStatsPtr stats ATTRIBUTE_UNUSED)
|
|
{
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
"%s", _("platform unsupported"));
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
libxlDomainBlockStatsGatherSingle(virDomainObjPtr vm,
|
|
const char *path,
|
|
libxlBlockStatsPtr stats)
|
|
{
|
|
virDomainDiskDefPtr disk;
|
|
const char *disk_drv;
|
|
int ret = -1, disk_fmt;
|
|
|
|
if (!(disk = virDomainDiskByName(vm->def, path, false))) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("invalid path: %s"), path);
|
|
return ret;
|
|
}
|
|
|
|
disk_fmt = virDomainDiskGetFormat(disk);
|
|
if (!(disk_drv = virDomainDiskGetDriver(disk)))
|
|
disk_drv = "qemu";
|
|
|
|
if (STREQ(disk_drv, "phy")) {
|
|
if (disk_fmt != VIR_STORAGE_FILE_RAW &&
|
|
disk_fmt != VIR_STORAGE_FILE_NONE) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
_("unsupported format %s"),
|
|
virStorageFileFormatTypeToString(disk_fmt));
|
|
return ret;
|
|
}
|
|
|
|
ret = libxlDomainBlockStatsVBD(vm, disk->dst, stats);
|
|
} else {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
_("unsupported disk driver %s"),
|
|
disk_drv);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainBlockStatsGather(virDomainObjPtr vm,
|
|
const char *path,
|
|
libxlBlockStatsPtr stats)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (*path) {
|
|
if (libxlDomainBlockStatsGatherSingle(vm, path, stats) < 0)
|
|
return ret;
|
|
} else {
|
|
size_t i;
|
|
|
|
for (i = 0; i < vm->def->ndisks; ++i) {
|
|
if (libxlDomainBlockStatsGatherSingle(vm, vm->def->disks[i]->dst,
|
|
stats) < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlDomainBlockStats(virDomainPtr dom,
|
|
const char *path,
|
|
virDomainBlockStatsPtr stats)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
virDomainObjPtr vm;
|
|
libxlBlockStats blkstats;
|
|
int ret = -1;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainBlockStatsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_QUERY) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("domain is not running"));
|
|
goto endjob;
|
|
}
|
|
|
|
memset(&blkstats, 0, sizeof(libxlBlockStats));
|
|
if ((ret = libxlDomainBlockStatsGather(vm, path, &blkstats)) < 0)
|
|
goto endjob;
|
|
|
|
stats->rd_req = blkstats.rd_req;
|
|
stats->rd_bytes = blkstats.rd_bytes;
|
|
stats->wr_req = blkstats.wr_req;
|
|
stats->wr_bytes = blkstats.wr_bytes;
|
|
if (STREQ_NULLABLE(blkstats.backend, "vbd"))
|
|
stats->errs = blkstats.u.vbd.oo_req;
|
|
else
|
|
stats->errs = -1;
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
libxlDomainBlockStatsFlags(virDomainPtr dom,
|
|
const char *path,
|
|
virTypedParameterPtr params,
|
|
int *nparams,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = dom->conn->privateData;
|
|
virDomainObjPtr vm;
|
|
libxlBlockStats blkstats;
|
|
int nstats;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1);
|
|
|
|
flags &= ~VIR_TYPED_PARAM_STRING_OKAY;
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainBlockStatsFlagsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_QUERY) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("domain is not running"));
|
|
goto endjob;
|
|
}
|
|
|
|
/* return count of supported stats */
|
|
if (*nparams == 0) {
|
|
*nparams = LIBXL_NB_TOTAL_BLK_STAT_PARAM;
|
|
ret = 0;
|
|
goto endjob;
|
|
}
|
|
|
|
memset(&blkstats, 0, sizeof(libxlBlockStats));
|
|
if ((ret = libxlDomainBlockStatsGather(vm, path, &blkstats)) < 0)
|
|
goto endjob;
|
|
|
|
nstats = 0;
|
|
|
|
#define LIBXL_BLKSTAT_ASSIGN_PARAM(VAR, NAME) \
|
|
if (nstats < *nparams && (blkstats.VAR) != -1) { \
|
|
if (virTypedParameterAssign(params + nstats, NAME, \
|
|
VIR_TYPED_PARAM_LLONG, (blkstats.VAR)) < 0) \
|
|
goto endjob; \
|
|
nstats++; \
|
|
}
|
|
|
|
LIBXL_BLKSTAT_ASSIGN_PARAM(wr_bytes, VIR_DOMAIN_BLOCK_STATS_WRITE_BYTES);
|
|
LIBXL_BLKSTAT_ASSIGN_PARAM(wr_req, VIR_DOMAIN_BLOCK_STATS_WRITE_REQ);
|
|
|
|
LIBXL_BLKSTAT_ASSIGN_PARAM(rd_bytes, VIR_DOMAIN_BLOCK_STATS_READ_BYTES);
|
|
LIBXL_BLKSTAT_ASSIGN_PARAM(rd_req, VIR_DOMAIN_BLOCK_STATS_READ_REQ);
|
|
|
|
LIBXL_BLKSTAT_ASSIGN_PARAM(f_req, VIR_DOMAIN_BLOCK_STATS_FLUSH_REQ);
|
|
|
|
if (STREQ_NULLABLE(blkstats.backend, "vbd"))
|
|
LIBXL_BLKSTAT_ASSIGN_PARAM(u.vbd.oo_req, VIR_DOMAIN_BLOCK_STATS_ERRS);
|
|
|
|
*nparams = nstats;
|
|
|
|
#undef LIBXL_BLKSTAT_ASSIGN_PARAM
|
|
|
|
endjob:
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&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_MIGRATION_V3:
|
|
case VIR_DRV_FEATURE_TYPED_PARAM_STRING:
|
|
case VIR_DRV_FEATURE_MIGRATION_PARAMS:
|
|
case VIR_DRV_FEATURE_MIGRATION_P2P:
|
|
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->data.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")) {
|
|
virPCIDeviceSetStubDriver(pci, VIR_PCI_STUB_DRIVER_XEN);
|
|
} 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,
|
|
int *cookieoutlen,
|
|
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 (STREQ_NULLABLE(vm->def->name, "Domain-0")) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain-0 cannot be migrated"));
|
|
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,
|
|
cookieout, cookieoutlen);
|
|
}
|
|
|
|
static int
|
|
libxlDomainMigratePrepare3Params(virConnectPtr dconn,
|
|
virTypedParameterPtr params,
|
|
int nparams,
|
|
const char *cookiein,
|
|
int cookieinlen,
|
|
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,
|
|
cookiein, cookieinlen, 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 (flags & VIR_MIGRATE_PEER2PEER) {
|
|
if (libxlDomainMigrationPerformP2P(driver, vm, dom->conn, dom_xml,
|
|
dconnuri, uri, dname, flags) < 0)
|
|
goto cleanup;
|
|
} else {
|
|
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) {
|
|
virDomainObjEndAPI(&vm);
|
|
return NULL;
|
|
}
|
|
|
|
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0) {
|
|
virDomainObjEndAPI(&vm);
|
|
return NULL;
|
|
}
|
|
|
|
ret = libxlDomainMigrationFinish(dconn, vm, flags, cancelled);
|
|
|
|
libxlDomainObjEndJob(driver, vm);
|
|
|
|
virDomainObjEndAPI(&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 int libxlNodeGetSecurityModel(virConnectPtr conn,
|
|
virSecurityModelPtr secmodel)
|
|
{
|
|
memset(secmodel, 0, sizeof(*secmodel));
|
|
|
|
if (virNodeGetSecurityModelEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* Currently the libxl driver does not support security model.
|
|
* Similar to the qemu driver, treat this as success and simply
|
|
* return no data in secmodel. Avoids spamming the libvirt log
|
|
* with "this function is not supported by the connection driver:
|
|
* virNodeGetSecurityModel"
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlGetDHCPInterfaces(virDomainPtr dom,
|
|
virDomainObjPtr vm,
|
|
virDomainInterfacePtr **ifaces)
|
|
{
|
|
int rv = -1;
|
|
int n_leases = 0;
|
|
size_t i, j;
|
|
size_t ifaces_count = 0;
|
|
virNetworkPtr network = NULL;
|
|
char macaddr[VIR_MAC_STRING_BUFLEN];
|
|
virDomainInterfacePtr iface = NULL;
|
|
virNetworkDHCPLeasePtr *leases = NULL;
|
|
virDomainInterfacePtr *ifaces_ret = NULL;
|
|
|
|
if (!dom->conn->networkDriver ||
|
|
!dom->conn->networkDriver->networkGetDHCPLeases) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Network driver does not support DHCP lease query"));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nnets; i++) {
|
|
if (vm->def->nets[i]->type != VIR_DOMAIN_NET_TYPE_NETWORK)
|
|
continue;
|
|
|
|
virMacAddrFormat(&(vm->def->nets[i]->mac), macaddr);
|
|
virObjectUnref(network);
|
|
network = virNetworkLookupByName(dom->conn,
|
|
vm->def->nets[i]->data.network.name);
|
|
|
|
if ((n_leases = virNetworkGetDHCPLeases(network, macaddr,
|
|
&leases, 0)) < 0)
|
|
goto error;
|
|
|
|
if (n_leases) {
|
|
if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0)
|
|
goto error;
|
|
|
|
if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0)
|
|
goto error;
|
|
|
|
iface = ifaces_ret[ifaces_count - 1];
|
|
/* Assuming each lease corresponds to a separate IP */
|
|
iface->naddrs = n_leases;
|
|
|
|
if (VIR_ALLOC_N(iface->addrs, iface->naddrs) < 0)
|
|
goto error;
|
|
|
|
if (VIR_STRDUP(iface->name, vm->def->nets[i]->ifname) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_STRDUP(iface->hwaddr, macaddr) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
for (j = 0; j < n_leases; j++) {
|
|
virNetworkDHCPLeasePtr lease = leases[j];
|
|
virDomainIPAddressPtr ip_addr = &iface->addrs[j];
|
|
|
|
if (VIR_STRDUP(ip_addr->addr, lease->ipaddr) < 0)
|
|
goto cleanup;
|
|
|
|
ip_addr->type = lease->type;
|
|
ip_addr->prefix = lease->prefix;
|
|
}
|
|
|
|
for (j = 0; j < n_leases; j++)
|
|
virNetworkDHCPLeaseFree(leases[j]);
|
|
|
|
VIR_FREE(leases);
|
|
}
|
|
|
|
*ifaces = ifaces_ret;
|
|
ifaces_ret = NULL;
|
|
rv = ifaces_count;
|
|
|
|
cleanup:
|
|
virObjectUnref(network);
|
|
if (leases) {
|
|
for (i = 0; i < n_leases; i++)
|
|
virNetworkDHCPLeaseFree(leases[i]);
|
|
}
|
|
VIR_FREE(leases);
|
|
|
|
return rv;
|
|
|
|
error:
|
|
if (ifaces_ret) {
|
|
for (i = 0; i < ifaces_count; i++)
|
|
virDomainInterfaceFree(ifaces_ret[i]);
|
|
}
|
|
VIR_FREE(ifaces_ret);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
static int
|
|
libxlDomainInterfaceAddresses(virDomainPtr dom,
|
|
virDomainInterfacePtr **ifaces,
|
|
unsigned int source,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObjPtr vm = NULL;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = libxlDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainInterfaceAddressesEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("domain is not running"));
|
|
goto cleanup;
|
|
}
|
|
|
|
switch (source) {
|
|
case VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE:
|
|
ret = libxlGetDHCPInterfaces(dom, vm, ifaces);
|
|
break;
|
|
|
|
default:
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
_("Unsupported IP address data source %d"),
|
|
source);
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
libxlConnectGetDomainCapabilities(virConnectPtr conn,
|
|
const char *emulatorbin,
|
|
const char *arch_str,
|
|
const char *machine,
|
|
const char *virttype_str,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = conn->privateData;
|
|
libxlDriverConfigPtr cfg;
|
|
char *ret = NULL;
|
|
int virttype = VIR_DOMAIN_VIRT_XEN;
|
|
virDomainCapsPtr domCaps = NULL;
|
|
int arch = virArchFromHost(); /* virArch */
|
|
|
|
virCheckFlags(0, ret);
|
|
|
|
if (virConnectGetDomainCapabilitiesEnsureACL(conn) < 0)
|
|
return ret;
|
|
|
|
cfg = libxlDriverConfigGet(driver);
|
|
|
|
if (virttype_str &&
|
|
(virttype = virDomainVirtTypeFromString(virttype_str)) < 0) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unknown virttype: %s"),
|
|
virttype_str);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virttype != VIR_DOMAIN_VIRT_XEN) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unknown virttype: %s"),
|
|
virttype_str);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (arch_str && (arch = virArchFromString(arch_str)) == VIR_ARCH_NONE) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unknown architecture: %s"),
|
|
arch_str);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (emulatorbin == NULL)
|
|
emulatorbin = "/usr/bin/qemu-system-x86_64";
|
|
|
|
if (machine) {
|
|
if (STRNEQ(machine, "xenpv") && STRNEQ(machine, "xenfv")) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Xen only supports 'xenpv' and 'xenfv' machines"));
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
machine = "xenpv";
|
|
}
|
|
|
|
if (!(domCaps = virDomainCapsNew(emulatorbin, machine, arch, virttype)))
|
|
goto cleanup;
|
|
|
|
if (libxlMakeDomainCapabilities(domCaps, cfg->firmwares,
|
|
cfg->nfirmwares) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virDomainCapsFormat(domCaps);
|
|
|
|
cleanup:
|
|
virObjectUnref(domCaps);
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
libxlConnectCompareCPU(virConnectPtr conn,
|
|
const char *xmlDesc,
|
|
unsigned int flags)
|
|
{
|
|
libxlDriverPrivatePtr driver = conn->privateData;
|
|
libxlDriverConfigPtr cfg;
|
|
int ret = VIR_CPU_COMPARE_ERROR;
|
|
bool failIncompatible;
|
|
|
|
virCheckFlags(VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE,
|
|
VIR_CPU_COMPARE_ERROR);
|
|
|
|
if (virConnectCompareCPUEnsureACL(conn) < 0)
|
|
return ret;
|
|
|
|
failIncompatible = !!(flags & VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE);
|
|
|
|
cfg = libxlDriverConfigGet(driver);
|
|
|
|
ret = virCPUCompareXML(cfg->caps->host.arch, cfg->caps->host.cpu,
|
|
xmlDesc, failIncompatible);
|
|
|
|
virObjectUnref(cfg);
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
libxlConnectBaselineCPU(virConnectPtr conn,
|
|
const char **xmlCPUs,
|
|
unsigned int ncpus,
|
|
unsigned int flags)
|
|
{
|
|
char *cpu = NULL;
|
|
|
|
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES |
|
|
VIR_CONNECT_BASELINE_CPU_MIGRATABLE, NULL);
|
|
|
|
if (virConnectBaselineCPUEnsureACL(conn) < 0)
|
|
goto cleanup;
|
|
|
|
cpu = cpuBaselineXML(xmlCPUs, ncpus, NULL, 0, flags);
|
|
|
|
cleanup:
|
|
return cpu;
|
|
}
|
|
|
|
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 */
|
|
.domainGetJobInfo = libxlDomainGetJobInfo, /* 1.3.1 */
|
|
.domainGetJobStats = libxlDomainGetJobStats, /* 1.3.1 */
|
|
.domainMemoryStats = libxlDomainMemoryStats, /* 1.3.0 */
|
|
.domainGetCPUStats = libxlDomainGetCPUStats, /* 1.3.0 */
|
|
.domainInterfaceStats = libxlDomainInterfaceStats, /* 1.3.2 */
|
|
.domainBlockStats = libxlDomainBlockStats, /* 2.1.0 */
|
|
.domainBlockStatsFlags = libxlDomainBlockStatsFlags, /* 2.1.0 */
|
|
.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 */
|
|
.nodeGetSecurityModel = libxlNodeGetSecurityModel, /* 1.2.16 */
|
|
.domainInterfaceAddresses = libxlDomainInterfaceAddresses, /* 1.3.5 */
|
|
.connectGetDomainCapabilities = libxlConnectGetDomainCapabilities, /* 2.0.0 */
|
|
.connectCompareCPU = libxlConnectCompareCPU, /* 2.3.0 */
|
|
.connectBaselineCPU = libxlConnectBaselineCPU, /* 2.3.0 */
|
|
};
|
|
|
|
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;
|
|
}
|