mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-27 06:55:18 +00:00
105dace22c
This reverts commit 938382b60ae5bd1f83b5cb09e1ce68b9a88f679a. Turns out, the commit did more harm than good. It changed semantics on some public APIs. For instance, while qemuDomainGetInfo() previously did not returned an error it does now. While the calls to virProcessGetStatInfo() is guarded with virDomainObjIsActive() it doesn't necessarily mean that QEMU's PID is still alive. QEMU might be gone but we just haven't realized it (e.g. because the eof handler thread is waiting for a job). Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2041610 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Andrea Bolognani <abologna@redhat.com>
1195 lines
31 KiB
C
1195 lines
31 KiB
C
/*
|
|
* Copyright Intel Corp. 2020-2021
|
|
*
|
|
* ch_driver.c: Core Cloud-Hypervisor driver functions
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "ch_conf.h"
|
|
#include "ch_domain.h"
|
|
#include "ch_driver.h"
|
|
#include "ch_monitor.h"
|
|
#include "ch_process.h"
|
|
#include "datatypes.h"
|
|
#include "driver.h"
|
|
#include "viraccessapicheck.h"
|
|
#include "viralloc.h"
|
|
#include "virbuffer.h"
|
|
#include "virchrdev.h"
|
|
#include "vircommand.h"
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "virlog.h"
|
|
#include "virnetdevtap.h"
|
|
#include "virobject.h"
|
|
#include "virstring.h"
|
|
#include "virtypedparam.h"
|
|
#include "viruri.h"
|
|
#include "virutil.h"
|
|
#include "viruuid.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_CH
|
|
|
|
VIR_LOG_INIT("ch.ch_driver");
|
|
|
|
virCHDriver *ch_driver = NULL;
|
|
|
|
static virDomainObj *
|
|
chDomObjFromDomain(virDomain *domain)
|
|
{
|
|
virDomainObj *vm;
|
|
virCHDriver *driver = domain->conn->privateData;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
vm = virDomainObjListFindByUUID(driver->domains, domain->uuid);
|
|
if (!vm) {
|
|
virUUIDFormat(domain->uuid, uuidstr);
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
_("no domain with matching uuid '%s' (%s)"),
|
|
uuidstr, domain->name);
|
|
return NULL;
|
|
}
|
|
|
|
return vm;
|
|
}
|
|
|
|
/* Functions */
|
|
static int
|
|
chConnectURIProbe(char **uri)
|
|
{
|
|
if (ch_driver == NULL)
|
|
return 0;
|
|
|
|
*uri = g_strdup("ch:///system");
|
|
return 1;
|
|
}
|
|
|
|
static virDrvOpenStatus chConnectOpen(virConnectPtr conn,
|
|
virConnectAuthPtr auth G_GNUC_UNUSED,
|
|
virConf *conf G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
|
|
|
|
/* URI was good, but driver isn't active */
|
|
if (ch_driver == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Cloud-Hypervisor state driver is not active"));
|
|
return VIR_DRV_OPEN_ERROR;
|
|
}
|
|
|
|
if (virConnectOpenEnsureACL(conn) < 0)
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
conn->privateData = ch_driver;
|
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
}
|
|
|
|
static int chConnectClose(virConnectPtr conn)
|
|
{
|
|
conn->privateData = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static const char *chConnectGetType(virConnectPtr conn)
|
|
{
|
|
if (virConnectGetTypeEnsureACL(conn) < 0)
|
|
return NULL;
|
|
|
|
return "CH";
|
|
}
|
|
|
|
static int chConnectGetVersion(virConnectPtr conn,
|
|
unsigned long *version)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
|
|
if (virConnectGetVersionEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
chDriverLock(driver);
|
|
*version = driver->version;
|
|
chDriverUnlock(driver);
|
|
return 0;
|
|
}
|
|
|
|
static char *chConnectGetHostname(virConnectPtr conn)
|
|
{
|
|
if (virConnectGetHostnameEnsureACL(conn) < 0)
|
|
return NULL;
|
|
|
|
return virGetHostname();
|
|
}
|
|
|
|
static int chConnectNumOfDomains(virConnectPtr conn)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
|
|
if (virConnectNumOfDomainsEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virDomainObjListNumOfDomains(driver->domains, true,
|
|
virConnectNumOfDomainsCheckACL, conn);
|
|
}
|
|
|
|
static int chConnectListDomains(virConnectPtr conn, int *ids, int nids)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
|
|
if (virConnectListDomainsEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virDomainObjListGetActiveIDs(driver->domains, ids, nids,
|
|
virConnectListDomainsCheckACL, conn);
|
|
}
|
|
|
|
static int
|
|
chConnectListAllDomains(virConnectPtr conn,
|
|
virDomainPtr **domains,
|
|
unsigned int flags)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
|
|
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
|
|
|
|
if (virConnectListAllDomainsEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virDomainObjListExport(driver->domains, conn, domains,
|
|
virConnectListAllDomainsCheckACL, flags);
|
|
}
|
|
|
|
static int chNodeGetInfo(virConnectPtr conn,
|
|
virNodeInfoPtr nodeinfo)
|
|
{
|
|
if (virNodeGetInfoEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virCapabilitiesGetNodeInfo(nodeinfo);
|
|
}
|
|
|
|
static char *chConnectGetCapabilities(virConnectPtr conn)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
g_autoptr(virCaps) caps = NULL;
|
|
char *xml;
|
|
|
|
if (virConnectGetCapabilitiesEnsureACL(conn) < 0)
|
|
return NULL;
|
|
|
|
if (!(caps = virCHDriverGetCapabilities(driver, true)))
|
|
return NULL;
|
|
|
|
xml = virCapabilitiesFormatXML(caps);
|
|
|
|
return xml;
|
|
}
|
|
|
|
/**
|
|
* chDomainCreateXML:
|
|
* @conn: pointer to connection
|
|
* @xml: XML definition of domain
|
|
* @flags: bitwise-OR of supported virDomainCreateFlags
|
|
*
|
|
* Creates a domain based on xml and starts it
|
|
*
|
|
* Returns a new domain object or NULL in case of failure.
|
|
*/
|
|
static virDomainPtr
|
|
chDomainCreateXML(virConnectPtr conn,
|
|
const char *xml,
|
|
unsigned int flags)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
g_autoptr(virDomainDef) vmdef = NULL;
|
|
virDomainObj *vm = NULL;
|
|
virDomainPtr dom = NULL;
|
|
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
|
|
|
|
virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL);
|
|
|
|
if (flags & VIR_DOMAIN_START_VALIDATE)
|
|
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
|
|
|
|
|
|
if ((vmdef = virDomainDefParseString(xml, driver->xmlopt,
|
|
NULL, parse_flags)) == NULL)
|
|
goto cleanup;
|
|
|
|
if (virDomainCreateXMLEnsureACL(conn, vmdef) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(vm = virDomainObjListAdd(driver->domains,
|
|
&vmdef,
|
|
driver->xmlopt,
|
|
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
|
|
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
|
|
NULL)))
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED) < 0)
|
|
goto cleanup;
|
|
|
|
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
|
|
|
|
virCHDomainObjEndJob(vm);
|
|
|
|
cleanup:
|
|
if (vm && !dom) {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
}
|
|
virDomainObjEndAPI(&vm);
|
|
chDriverUnlock(driver);
|
|
return dom;
|
|
}
|
|
|
|
static int
|
|
chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
|
|
{
|
|
virCHDriver *driver = dom->conn->privateData;
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainCreateWithFlagsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
|
|
|
|
virCHDomainObjEndJob(vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainCreate(virDomainPtr dom)
|
|
{
|
|
return chDomainCreateWithFlags(dom, 0);
|
|
}
|
|
|
|
static virDomainPtr
|
|
chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
g_autoptr(virDomainDef) vmdef = NULL;
|
|
virDomainObj *vm = NULL;
|
|
virDomainPtr dom = NULL;
|
|
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
|
|
|
|
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
|
|
|
|
if (flags & VIR_DOMAIN_START_VALIDATE)
|
|
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
|
|
|
|
if ((vmdef = virDomainDefParseString(xml, driver->xmlopt,
|
|
NULL, parse_flags)) == NULL)
|
|
goto cleanup;
|
|
|
|
if (virXMLCheckIllegalChars("name", vmdef->name, "\n") < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainDefineXMLFlagsEnsureACL(conn, vmdef) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(vm = virDomainObjListAdd(driver->domains, &vmdef,
|
|
driver->xmlopt,
|
|
0, NULL)))
|
|
goto cleanup;
|
|
|
|
vm->persistent = 1;
|
|
|
|
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return dom;
|
|
}
|
|
|
|
static virDomainPtr
|
|
chDomainDefineXML(virConnectPtr conn, const char *xml)
|
|
{
|
|
return chDomainDefineXMLFlags(conn, xml, 0);
|
|
}
|
|
|
|
static int
|
|
chDomainUndefineFlags(virDomainPtr dom,
|
|
unsigned int flags)
|
|
{
|
|
virCHDriver *driver = dom->conn->privateData;
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(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;
|
|
}
|
|
|
|
if (virDomainObjIsActive(vm)) {
|
|
vm->persistent = 0;
|
|
} else {
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainUndefine(virDomainPtr dom)
|
|
{
|
|
return chDomainUndefineFlags(dom, 0);
|
|
}
|
|
|
|
static int chDomainIsActive(virDomainPtr dom)
|
|
{
|
|
virCHDriver *driver = dom->conn->privateData;
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
chDriverLock(driver);
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainIsActiveEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virDomainObjIsActive(vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
chDriverUnlock(driver);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainShutdownFlags(virDomainPtr dom,
|
|
unsigned int flags)
|
|
{
|
|
virCHDomainObjPrivate *priv;
|
|
virDomainObj *vm;
|
|
virDomainState state;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto endjob;
|
|
|
|
state = virDomainObjGetState(vm, NULL);
|
|
if (state != VIR_DOMAIN_RUNNING && state != VIR_DOMAIN_PAUSED) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("only can shutdown running/paused domain"));
|
|
goto endjob;
|
|
} else {
|
|
if (virCHMonitorShutdownVM(priv->monitor) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("failed to shutdown guest VM"));
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTDOWN, VIR_DOMAIN_SHUTDOWN_USER);
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
virCHDomainObjEndJob(vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainShutdown(virDomainPtr dom)
|
|
{
|
|
return chDomainShutdownFlags(dom, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
chDomainReboot(virDomainPtr dom, unsigned int flags)
|
|
{
|
|
virCHDomainObjPrivate *priv;
|
|
virDomainObj *vm;
|
|
virDomainState state;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_REBOOT_ACPI_POWER_BTN, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (virDomainRebootEnsureACL(dom->conn, vm->def, flags) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto endjob;
|
|
|
|
state = virDomainObjGetState(vm, NULL);
|
|
if (state != VIR_DOMAIN_RUNNING && state != VIR_DOMAIN_PAUSED) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
_("only can reboot running/paused domain"));
|
|
goto endjob;
|
|
} else {
|
|
if (virCHMonitorRebootVM(priv->monitor) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("failed to reboot domain"));
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
if (state == VIR_DOMAIN_RUNNING)
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
|
|
else
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
virCHDomainObjEndJob(vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainSuspend(virDomainPtr dom)
|
|
{
|
|
virCHDomainObjPrivate *priv;
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (virDomainSuspendEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto endjob;
|
|
|
|
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
_("only can suspend running domain"));
|
|
goto endjob;
|
|
} else {
|
|
if (virCHMonitorSuspendVM(priv->monitor) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("failed to suspend domain"));
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
virCHDomainObjEndJob(vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainResume(virDomainPtr dom)
|
|
{
|
|
virCHDomainObjPrivate *priv;
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (virDomainResumeEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto endjob;
|
|
|
|
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
_("only can resume paused domain"));
|
|
goto endjob;
|
|
} else {
|
|
if (virCHMonitorResumeVM(priv->monitor) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("failed to resume domain"));
|
|
goto endjob;
|
|
}
|
|
}
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
|
|
|
|
ret = 0;
|
|
|
|
endjob:
|
|
virCHDomainObjEndJob(vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* chDomainDestroyFlags:
|
|
* @dom: pointer to domain to destroy
|
|
* @flags: extra flags; not used yet.
|
|
*
|
|
* Sends SIGKILL to Cloud-Hypervisor process to terminate it
|
|
*
|
|
* Returns 0 on success or -1 in case of error
|
|
*/
|
|
static int
|
|
chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
|
|
{
|
|
virCHDriver *driver = dom->conn->privateData;
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainDestroyFlagsEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virCHDomainObjBeginJob(vm, CH_JOB_DESTROY) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto endjob;
|
|
|
|
ret = virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
|
|
|
|
endjob:
|
|
virCHDomainObjEndJob(vm);
|
|
if (!vm->persistent)
|
|
virDomainObjListRemove(driver->domains, vm);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainDestroy(virDomainPtr dom)
|
|
{
|
|
return chDomainDestroyFlags(dom, 0);
|
|
}
|
|
|
|
static virDomainPtr chDomainLookupByID(virConnectPtr conn,
|
|
int id)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
virDomainObj *vm;
|
|
virDomainPtr dom = NULL;
|
|
|
|
chDriverLock(driver);
|
|
vm = virDomainObjListFindByID(driver->domains, id);
|
|
chDriverUnlock(driver);
|
|
|
|
if (!vm) {
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
_("no domain with matching id '%d'"), id);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainLookupByIDEnsureACL(conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return dom;
|
|
}
|
|
|
|
static virDomainPtr chDomainLookupByName(virConnectPtr conn,
|
|
const char *name)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
virDomainObj *vm;
|
|
virDomainPtr dom = NULL;
|
|
|
|
chDriverLock(driver);
|
|
vm = virDomainObjListFindByName(driver->domains, name);
|
|
chDriverUnlock(driver);
|
|
|
|
if (!vm) {
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
_("no domain with matching name '%s'"), name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainLookupByNameEnsureACL(conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return dom;
|
|
}
|
|
|
|
static virDomainPtr chDomainLookupByUUID(virConnectPtr conn,
|
|
const unsigned char *uuid)
|
|
{
|
|
virCHDriver *driver = conn->privateData;
|
|
virDomainObj *vm;
|
|
virDomainPtr dom = NULL;
|
|
|
|
chDriverLock(driver);
|
|
vm = virDomainObjListFindByUUID(driver->domains, uuid);
|
|
chDriverUnlock(driver);
|
|
|
|
if (!vm) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(uuid, uuidstr);
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
_("no domain with matching uuid '%s'"), uuidstr);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainLookupByUUIDEnsureACL(conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return dom;
|
|
}
|
|
|
|
static int
|
|
chDomainGetState(virDomainPtr dom,
|
|
int *state,
|
|
int *reason,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetStateEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
*state = virDomainObjGetState(vm, reason);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static char *chDomainGetXMLDesc(virDomainPtr dom,
|
|
unsigned int flags)
|
|
{
|
|
virCHDriver *driver = dom->conn->privateData;
|
|
virDomainObj *vm;
|
|
char *ret = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetXMLDescEnsureACL(dom->conn, vm->def, flags) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virDomainDefFormat(vm->def, driver->xmlopt,
|
|
virDomainDefFormatConvertXMLFlags(flags));
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int chDomainGetInfo(virDomainPtr dom,
|
|
virDomainInfoPtr info)
|
|
{
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetInfoEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
info->state = virDomainObjGetState(vm, NULL);
|
|
|
|
info->cpuTime = 0;
|
|
|
|
info->maxMem = virDomainDefGetMemoryTotal(vm->def);
|
|
info->memory = vm->def->mem.cur_balloon;
|
|
info->nrVirtCpu = virDomainDefGetVcpus(vm->def);
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainOpenConsole(virDomainPtr dom,
|
|
const char *dev_name,
|
|
virStreamPtr st,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
int ret = -1;
|
|
size_t i;
|
|
virDomainChrDef *chr = NULL;
|
|
virCHDomainObjPrivate *priv;
|
|
|
|
virCheckFlags(VIR_DOMAIN_CONSOLE_SAFE | VIR_DOMAIN_CONSOLE_FORCE, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainOpenConsoleEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0)
|
|
goto cleanup;
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (dev_name) {
|
|
for (i = 0; !chr && i < vm->def->nconsoles; i++) {
|
|
if (vm->def->consoles[i]->info.alias &&
|
|
STREQ(dev_name, vm->def->consoles[i]->info.alias))
|
|
chr = vm->def->consoles[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];
|
|
}
|
|
} else {
|
|
if (vm->def->nconsoles &&
|
|
vm->def->consoles[0]->source->type == VIR_DOMAIN_CHR_TYPE_PTY)
|
|
chr = vm->def->consoles[0];
|
|
else if (vm->def->nserials &&
|
|
vm->def->serials[0]->source->type == VIR_DOMAIN_CHR_TYPE_PTY)
|
|
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->chrdevs, 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:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int chStateCleanup(void)
|
|
{
|
|
if (ch_driver == NULL)
|
|
return -1;
|
|
|
|
virObjectUnref(ch_driver->domains);
|
|
virObjectUnref(ch_driver->xmlopt);
|
|
virObjectUnref(ch_driver->caps);
|
|
virObjectUnref(ch_driver->config);
|
|
virMutexDestroy(&ch_driver->lock);
|
|
g_free(ch_driver);
|
|
ch_driver = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int chStateInitialize(bool privileged,
|
|
const char *root,
|
|
virStateInhibitCallback callback G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
int ret = VIR_DRV_STATE_INIT_ERROR;
|
|
int rv;
|
|
|
|
if (root != NULL) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("Driver does not support embedded mode"));
|
|
return -1;
|
|
}
|
|
|
|
ch_driver = g_new0(virCHDriver, 1);
|
|
|
|
if (virMutexInit(&ch_driver->lock) < 0) {
|
|
g_free(ch_driver);
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
|
}
|
|
|
|
if (!(ch_driver->domains = virDomainObjListNew()))
|
|
goto cleanup;
|
|
|
|
if (!(ch_driver->caps = virCHDriverCapsInit()))
|
|
goto cleanup;
|
|
|
|
if (!(ch_driver->xmlopt = chDomainXMLConfInit(ch_driver)))
|
|
goto cleanup;
|
|
|
|
if (!(ch_driver->config = virCHDriverConfigNew(privileged)))
|
|
goto cleanup;
|
|
|
|
if ((rv = chExtractVersion(ch_driver)) < 0) {
|
|
if (rv == -2)
|
|
ret = VIR_DRV_STATE_INIT_SKIPPED;
|
|
goto cleanup;
|
|
}
|
|
|
|
ch_driver->privileged = privileged;
|
|
ret = VIR_DRV_STATE_INIT_COMPLETE;
|
|
|
|
cleanup:
|
|
if (ret != VIR_DRV_STATE_INIT_COMPLETE)
|
|
chStateCleanup();
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainGetVcpusFlags(virDomainPtr dom,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm;
|
|
virDomainDef *def;
|
|
int ret = -1;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_AFFECT_CONFIG |
|
|
VIR_DOMAIN_VCPU_MAXIMUM | VIR_DOMAIN_VCPU_GUEST, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
return -1;
|
|
|
|
if (virDomainGetVcpusFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDef(vm, flags)))
|
|
goto cleanup;
|
|
|
|
if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
|
|
ret = virDomainDefGetVcpusMax(def);
|
|
else
|
|
ret = virDomainDefGetVcpus(def);
|
|
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chDomainGetMaxVcpus(virDomainPtr dom)
|
|
{
|
|
return chDomainGetVcpusFlags(dom,
|
|
(VIR_DOMAIN_AFFECT_LIVE |
|
|
VIR_DOMAIN_VCPU_MAXIMUM));
|
|
}
|
|
|
|
static int
|
|
chDomainGetVcpuPinInfo(virDomain *dom,
|
|
int ncpumaps,
|
|
unsigned char *cpumaps,
|
|
int maplen,
|
|
unsigned int flags)
|
|
{
|
|
virDomainObj *vm = NULL;
|
|
virDomainDef *def;
|
|
bool live;
|
|
int ret = -1;
|
|
|
|
g_autoptr(virBitmap) hostcpus = NULL;
|
|
virBitmap *autoCpuset = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetVcpuPinInfoEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (!(def = virDomainObjGetOneDefState(vm, flags, &live)))
|
|
goto cleanup;
|
|
|
|
if (!(hostcpus = virHostCPUGetAvailableCPUsBitmap()))
|
|
goto cleanup;
|
|
|
|
if (live)
|
|
autoCpuset = CH_DOMAIN_PRIVATE(vm)->autoCpuset;
|
|
|
|
ret = virDomainDefGetVcpuPinInfoHelper(def, maplen, ncpumaps, cpumaps,
|
|
hostcpus, autoCpuset);
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chNodeGetCPUMap(virConnectPtr conn,
|
|
unsigned char **cpumap,
|
|
unsigned int *online, unsigned int flags)
|
|
{
|
|
if (virNodeGetCPUMapEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return virHostCPUGetMap(cpumap, online, flags);
|
|
}
|
|
|
|
|
|
static int
|
|
chDomainHelperGetVcpus(virDomainObj *vm,
|
|
virVcpuInfoPtr info,
|
|
unsigned long long *cpuwait,
|
|
int maxinfo,
|
|
unsigned char *cpumaps,
|
|
int maplen)
|
|
{
|
|
size_t ncpuinfo = 0;
|
|
size_t i;
|
|
|
|
if (maxinfo == 0)
|
|
return 0;
|
|
|
|
if (!virCHDomainHasVcpuPids(vm)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
"%s", _("cpu affinity is not supported"));
|
|
return -1;
|
|
}
|
|
|
|
if (info)
|
|
memset(info, 0, sizeof(*info) * maxinfo);
|
|
|
|
if (cpumaps)
|
|
memset(cpumaps, 0, sizeof(*cpumaps) * maxinfo);
|
|
|
|
for (i = 0; i < virDomainDefGetVcpusMax(vm->def) && ncpuinfo < maxinfo; i++) {
|
|
virDomainVcpuDef *vcpu = virDomainDefGetVcpu(vm->def, i);
|
|
pid_t vcpupid = virCHDomainGetVcpuPid(vm, i);
|
|
virVcpuInfoPtr vcpuinfo = info + ncpuinfo;
|
|
|
|
if (!vcpu->online)
|
|
continue;
|
|
|
|
if (info) {
|
|
vcpuinfo->number = i;
|
|
vcpuinfo->state = VIR_VCPU_RUNNING;
|
|
if (virProcessGetStatInfo(&vcpuinfo->cpuTime,
|
|
&vcpuinfo->cpu, NULL,
|
|
vm->pid, vcpupid) < 0) {
|
|
virReportSystemError(errno, "%s",
|
|
_("cannot get vCPU placement & pCPU time"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (cpumaps) {
|
|
unsigned char *cpumap = VIR_GET_CPUMAP(cpumaps, maplen, ncpuinfo);
|
|
g_autoptr(virBitmap) map = NULL;
|
|
|
|
if (!(map = virProcessGetAffinity(vcpupid)))
|
|
return -1;
|
|
|
|
virBitmapToDataBuf(map, cpumap, maplen);
|
|
}
|
|
|
|
if (cpuwait) {
|
|
if (virProcessGetSchedInfo(&(cpuwait[ncpuinfo]), vm->pid, vcpupid) < 0)
|
|
return -1;
|
|
}
|
|
|
|
ncpuinfo++;
|
|
}
|
|
|
|
return ncpuinfo;
|
|
}
|
|
|
|
static int
|
|
chDomainGetVcpus(virDomainPtr dom,
|
|
virVcpuInfoPtr info,
|
|
int maxinfo,
|
|
unsigned char *cpumaps,
|
|
int maplen)
|
|
{
|
|
virDomainObj *vm;
|
|
int ret = -1;
|
|
|
|
if (!(vm = chDomObjFromDomain(dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetVcpusEnsureACL(dom->conn, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (virDomainObjCheckActive(vm) < 0) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("cannot retrieve vcpu information for inactive domain"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = chDomainHelperGetVcpus(vm, info, NULL, maxinfo, cpumaps, maplen);
|
|
|
|
cleanup:
|
|
virDomainObjEndAPI(&vm);
|
|
return ret;
|
|
}
|
|
|
|
/* Function Tables */
|
|
static virHypervisorDriver chHypervisorDriver = {
|
|
.name = "CH",
|
|
.connectURIProbe = chConnectURIProbe,
|
|
.connectOpen = chConnectOpen, /* 7.5.0 */
|
|
.connectClose = chConnectClose, /* 7.5.0 */
|
|
.connectGetType = chConnectGetType, /* 7.5.0 */
|
|
.connectGetVersion = chConnectGetVersion, /* 7.5.0 */
|
|
.connectGetHostname = chConnectGetHostname, /* 7.5.0 */
|
|
.connectNumOfDomains = chConnectNumOfDomains, /* 7.5.0 */
|
|
.connectListAllDomains = chConnectListAllDomains, /* 7.5.0 */
|
|
.connectListDomains = chConnectListDomains, /* 7.5.0 */
|
|
.connectGetCapabilities = chConnectGetCapabilities, /* 7.5.0 */
|
|
.domainCreateXML = chDomainCreateXML, /* 7.5.0 */
|
|
.domainCreate = chDomainCreate, /* 7.5.0 */
|
|
.domainCreateWithFlags = chDomainCreateWithFlags, /* 7.5.0 */
|
|
.domainShutdown = chDomainShutdown, /* 7.5.0 */
|
|
.domainShutdownFlags = chDomainShutdownFlags, /* 7.5.0 */
|
|
.domainReboot = chDomainReboot, /* 7.5.0 */
|
|
.domainSuspend = chDomainSuspend, /* 7.5.0 */
|
|
.domainResume = chDomainResume, /* 7.5.0 */
|
|
.domainDestroy = chDomainDestroy, /* 7.5.0 */
|
|
.domainDestroyFlags = chDomainDestroyFlags, /* 7.5.0 */
|
|
.domainDefineXML = chDomainDefineXML, /* 7.5.0 */
|
|
.domainDefineXMLFlags = chDomainDefineXMLFlags, /* 7.5.0 */
|
|
.domainUndefine = chDomainUndefine, /* 7.5.0 */
|
|
.domainUndefineFlags = chDomainUndefineFlags, /* 7.5.0 */
|
|
.domainLookupByID = chDomainLookupByID, /* 7.5.0 */
|
|
.domainLookupByUUID = chDomainLookupByUUID, /* 7.5.0 */
|
|
.domainLookupByName = chDomainLookupByName, /* 7.5.0 */
|
|
.domainGetState = chDomainGetState, /* 7.5.0 */
|
|
.domainGetXMLDesc = chDomainGetXMLDesc, /* 7.5.0 */
|
|
.domainGetInfo = chDomainGetInfo, /* 7.5.0 */
|
|
.domainIsActive = chDomainIsActive, /* 7.5.0 */
|
|
.domainOpenConsole = chDomainOpenConsole, /* 7.8.0 */
|
|
.nodeGetInfo = chNodeGetInfo, /* 7.5.0 */
|
|
.domainGetVcpus = chDomainGetVcpus, /* 8.0.0 */
|
|
.domainGetVcpusFlags = chDomainGetVcpusFlags, /* 8.0.0 */
|
|
.domainGetMaxVcpus = chDomainGetMaxVcpus, /* 8.0.0 */
|
|
.domainGetVcpuPinInfo = chDomainGetVcpuPinInfo, /* 8.0.0 */
|
|
.nodeGetCPUMap = chNodeGetCPUMap, /* 8.0.0 */
|
|
};
|
|
|
|
static virConnectDriver chConnectDriver = {
|
|
.localOnly = true,
|
|
.uriSchemes = (const char *[]){"ch", NULL},
|
|
.hypervisorDriver = &chHypervisorDriver,
|
|
};
|
|
|
|
static virStateDriver chStateDriver = {
|
|
.name = "cloud-hypervisor",
|
|
.stateInitialize = chStateInitialize,
|
|
.stateCleanup = chStateCleanup,
|
|
};
|
|
|
|
int chRegister(void)
|
|
{
|
|
if (virRegisterConnectDriver(&chConnectDriver, true) < 0)
|
|
return -1;
|
|
if (virRegisterStateDriver(&chStateDriver) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|