libvirt/src/hyperv/hyperv_driver.c

1477 lines
41 KiB
C

/*
* hyperv_driver.c: core driver functions for managing Microsoft Hyper-V hosts
*
* Copyright (C) 2011-2013 Matthias Bolte <matthias.bolte@googlemail.com>
* Copyright (C) 2009 Michael Sievers <msievers83@googlemail.com>
*
* 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 "internal.h"
#include "datatypes.h"
#include "domain_conf.h"
#include "virauth.h"
#include "virutil.h"
#include "viralloc.h"
#include "virlog.h"
#include "viruuid.h"
#include "hyperv_driver.h"
#include "hyperv_interface_driver.h"
#include "hyperv_network_driver.h"
#include "hyperv_storage_driver.h"
#include "hyperv_device_monitor.h"
#include "hyperv_secret_driver.h"
#include "hyperv_nwfilter_driver.h"
#include "hyperv_private.h"
#include "hyperv_util.h"
#include "hyperv_wmi.h"
#include "openwsman.h"
#define VIR_FROM_THIS VIR_FROM_HYPERV
static void
hypervFreePrivate(hypervPrivate **priv)
{
if (priv == NULL || *priv == NULL) {
return;
}
if ((*priv)->client != NULL) {
/* FIXME: This leaks memory due to bugs in openwsman <= 2.2.6 */
wsmc_release((*priv)->client);
}
hypervFreeParsedUri(&(*priv)->parsedUri);
VIR_FREE(*priv);
}
static virDrvOpenStatus
hypervOpen(virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags)
{
virDrvOpenStatus result = VIR_DRV_OPEN_ERROR;
char *plus;
hypervPrivate *priv = NULL;
char *username = NULL;
char *password = NULL;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystem = NULL;
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
/* Decline if the URI is NULL or the scheme is NULL */
if (conn->uri == NULL || conn->uri->scheme == NULL) {
return VIR_DRV_OPEN_DECLINED;
}
/* Decline if the scheme is not hyperv */
plus = strchr(conn->uri->scheme, '+');
if (plus == NULL) {
if (STRCASENEQ(conn->uri->scheme, "hyperv")) {
return VIR_DRV_OPEN_DECLINED;
}
} else {
if (plus - conn->uri->scheme != 6 ||
STRCASENEQLEN(conn->uri->scheme, "hyperv", 6)) {
return VIR_DRV_OPEN_DECLINED;
}
virReportError(VIR_ERR_INVALID_ARG,
_("Transport '%s' in URI scheme is not supported, try again "
"without the transport part"), plus + 1);
return VIR_DRV_OPEN_ERROR;
}
/* Require server part */
if (conn->uri->server == NULL) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("URI is missing the server part"));
return VIR_DRV_OPEN_ERROR;
}
/* Require auth */
if (auth == NULL || auth->cb == NULL) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Missing or invalid auth pointer"));
return VIR_DRV_OPEN_ERROR;
}
/* Allocate per-connection private data */
if (VIR_ALLOC(priv) < 0) {
virReportOOMError();
goto cleanup;
}
if (hypervParseUri(&priv->parsedUri, conn->uri) < 0) {
goto cleanup;
}
/* Set the port dependent on the transport protocol if no port is
* specified. This allows us to rely on the port parameter being
* correctly set when building URIs later on, without the need to
* distinguish between the situations port == 0 and port != 0 */
if (conn->uri->port == 0) {
if (STRCASEEQ(priv->parsedUri->transport, "https")) {
conn->uri->port = 5986;
} else {
conn->uri->port = 5985;
}
}
/* Request credentials */
if (conn->uri->user != NULL) {
username = strdup(conn->uri->user);
if (username == NULL) {
virReportOOMError();
goto cleanup;
}
} else {
username = virAuthGetUsername(conn, auth, "hyperv", "administrator", conn->uri->server);
if (username == NULL) {
virReportError(VIR_ERR_AUTH_FAILED, "%s", _("Username request failed"));
goto cleanup;
}
}
password = virAuthGetPassword(conn, auth, "hyperv", username, conn->uri->server);
if (password == NULL) {
virReportError(VIR_ERR_AUTH_FAILED, "%s", _("Password request failed"));
goto cleanup;
}
/* Initialize the openwsman connection */
priv->client = wsmc_create(conn->uri->server, conn->uri->port, "/wsman",
priv->parsedUri->transport, username, password);
if (priv->client == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not create openwsman client"));
goto cleanup;
}
if (wsmc_transport_init(priv->client, NULL) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not initialize openwsman transport"));
goto cleanup;
}
/* FIXME: Currently only basic authentication is supported */
wsman_transport_set_auth_method(priv->client, "basic");
/* Check if the connection can be established and if the server has the
* Hyper-V role installed. If the call to hypervGetMsvmComputerSystemList
* succeeds than the connection has been established. If the returned list
* is empty than the server isn't a Hyper-V server. */
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_PHYSICAL);
if (hypervGetMsvmComputerSystemList(priv, &query, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s is not a Hyper-V server"), conn->uri->server);
goto cleanup;
}
conn->privateData = priv;
priv = NULL;
result = VIR_DRV_OPEN_SUCCESS;
cleanup:
hypervFreePrivate(&priv);
VIR_FREE(username);
VIR_FREE(password);
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervClose(virConnectPtr conn)
{
hypervPrivate *priv = conn->privateData;
hypervFreePrivate(&priv);
conn->privateData = NULL;
return 0;
}
static const char *
hypervGetType(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return "Hyper-V";
}
static char *
hypervGetHostname(virConnectPtr conn)
{
char *hostname = NULL;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Win32_ComputerSystem *computerSystem = NULL;
virBufferAddLit(&query, WIN32_COMPUTERSYSTEM_WQL_SELECT);
if (hypervGetWin32ComputerSystemList(priv, &query, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s"),
"Win32_ComputerSystem");
goto cleanup;
}
hostname = strdup(computerSystem->data->DNSHostName);
if (hostname == NULL) {
virReportOOMError();
goto cleanup;
}
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return hostname;
}
static int
hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
{
int result = -1;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Win32_ComputerSystem *computerSystem = NULL;
Win32_Processor *processorList = NULL;
Win32_Processor *processor = NULL;
char *tmp;
memset(info, 0, sizeof(*info));
virBufferAddLit(&query, WIN32_COMPUTERSYSTEM_WQL_SELECT);
/* Get Win32_ComputerSystem */
if (hypervGetWin32ComputerSystemList(priv, &query, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s"),
"Win32_ComputerSystem");
goto cleanup;
}
/* Get Win32_Processor list */
virBufferAsprintf(&query,
"associators of "
"{Win32_ComputerSystem.Name=\"%s\"} "
"where AssocClass = Win32_ComputerSystemProcessor "
"ResultClass = Win32_Processor",
computerSystem->data->Name);
if (hypervGetWin32ProcessorList(priv, &query, &processorList) < 0) {
goto cleanup;
}
if (processorList == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s"),
"Win32_Processor");
goto cleanup;
}
/* Strip the string to fit more relevant information in 32 chars */
tmp = processorList->data->Name;
while (*tmp != '\0') {
if (STRPREFIX(tmp, " ")) {
memmove(tmp, tmp + 1, strlen(tmp + 1) + 1);
continue;
} else if (STRPREFIX(tmp, "(R)") || STRPREFIX(tmp, "(C)")) {
memmove(tmp, tmp + 3, strlen(tmp + 3) + 1);
continue;
} else if (STRPREFIX(tmp, "(TM)")) {
memmove(tmp, tmp + 4, strlen(tmp + 4) + 1);
continue;
}
++tmp;
}
/* Fill struct */
if (virStrncpy(info->model, processorList->data->Name,
sizeof(info->model) - 1, sizeof(info->model)) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("CPU model %s too long for destination"),
processorList->data->Name);
goto cleanup;
}
info->memory = computerSystem->data->TotalPhysicalMemory / 1024; /* byte to kilobyte */
info->mhz = processorList->data->MaxClockSpeed;
info->nodes = 1;
info->sockets = 0;
for (processor = processorList; processor != NULL;
processor = processor->next) {
++info->sockets;
}
info->cores = processorList->data->NumberOfCores;
info->threads = info->cores / processorList->data->NumberOfLogicalProcessors;
info->cpus = info->sockets * info->cores;
result = 0;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
hypervFreeObject(priv, (hypervObject *)processorList);
return result;
}
static int
hypervListDomains(virConnectPtr conn, int *ids, int maxids)
{
bool success = false;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystemList = NULL;
Msvm_ComputerSystem *computerSystem = NULL;
int count = 0;
if (maxids == 0) {
return 0;
}
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAddLit(&query, "and ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_ACTIVE);
if (hypervGetMsvmComputerSystemList(priv, &query,
&computerSystemList) < 0) {
goto cleanup;
}
for (computerSystem = computerSystemList; computerSystem != NULL;
computerSystem = computerSystem->next) {
ids[count++] = computerSystem->data->ProcessID;
if (count >= maxids) {
break;
}
}
success = true;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystemList);
return success ? count : -1;
}
static int
hypervNumberOfDomains(virConnectPtr conn)
{
bool success = false;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystemList = NULL;
Msvm_ComputerSystem *computerSystem = NULL;
int count = 0;
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAddLit(&query, "and ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_ACTIVE);
if (hypervGetMsvmComputerSystemList(priv, &query,
&computerSystemList) < 0) {
goto cleanup;
}
for (computerSystem = computerSystemList; computerSystem != NULL;
computerSystem = computerSystem->next) {
++count;
}
success = true;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystemList);
return success ? count : -1;
}
static virDomainPtr
hypervDomainLookupByID(virConnectPtr conn, int id)
{
virDomainPtr domain = NULL;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystem = NULL;
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAsprintf(&query, "and ProcessID = %d", id);
if (hypervGetMsvmComputerSystemList(priv, &query, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem == NULL) {
virReportError(VIR_ERR_NO_DOMAIN, _("No domain with ID %d"), id);
goto cleanup;
}
hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return domain;
}
static virDomainPtr
hypervDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
{
virDomainPtr domain = NULL;
hypervPrivate *priv = conn->privateData;
char uuid_string[VIR_UUID_STRING_BUFLEN];
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystem = NULL;
virUUIDFormat(uuid, uuid_string);
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAsprintf(&query, "and Name = \"%s\"", uuid_string);
if (hypervGetMsvmComputerSystemList(priv, &query, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem == NULL) {
virReportError(VIR_ERR_NO_DOMAIN,
_("No domain with UUID %s"), uuid_string);
goto cleanup;
}
hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return domain;
}
static virDomainPtr
hypervDomainLookupByName(virConnectPtr conn, const char *name)
{
virDomainPtr domain = NULL;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystem = NULL;
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAsprintf(&query, "and ElementName = \"%s\"", name);
if (hypervGetMsvmComputerSystemList(priv, &query, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem == NULL) {
virReportError(VIR_ERR_NO_DOMAIN,
_("No domain with name %s"), name);
goto cleanup;
}
hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return domain;
}
static int
hypervDomainSuspend(virDomainPtr domain)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem->data->EnabledState !=
MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not active"));
goto cleanup;
}
result = hypervInvokeMsvmComputerSystemRequestStateChange
(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_PAUSED);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainResume(virDomainPtr domain)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem->data->EnabledState !=
MSVM_COMPUTERSYSTEM_ENABLEDSTATE_PAUSED) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not paused"));
goto cleanup;
}
result = hypervInvokeMsvmComputerSystemRequestStateChange
(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_ENABLED);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainDestroyFlags(virDomainPtr domain, unsigned int flags)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
bool in_transition = false;
virCheckFlags(0, -1);
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
in_transition) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not active or is in state transition"));
goto cleanup;
}
result = hypervInvokeMsvmComputerSystemRequestStateChange
(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_DISABLED);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainDestroy(virDomainPtr domain)
{
return hypervDomainDestroyFlags(domain, 0);
}
static char *
hypervDomainGetOSType(virDomainPtr domain ATTRIBUTE_UNUSED)
{
char *osType = strdup("hvm");
if (osType == NULL) {
virReportOOMError();
return NULL;
}
return osType;
}
static int
hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
char uuid_string[VIR_UUID_STRING_BUFLEN];
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystem = NULL;
Msvm_VirtualSystemSettingData *virtualSystemSettingData = NULL;
Msvm_ProcessorSettingData *processorSettingData = NULL;
Msvm_MemorySettingData *memorySettingData = NULL;
memset(info, 0, sizeof(*info));
virUUIDFormat(domain->uuid, uuid_string);
/* Get Msvm_ComputerSystem */
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
/* Get Msvm_VirtualSystemSettingData */
virBufferAsprintf(&query,
"associators of "
"{Msvm_ComputerSystem.CreationClassName=\"Msvm_ComputerSystem\","
"Name=\"%s\"} "
"where AssocClass = Msvm_SettingsDefineState "
"ResultClass = Msvm_VirtualSystemSettingData",
uuid_string);
if (hypervGetMsvmVirtualSystemSettingDataList(priv, &query,
&virtualSystemSettingData) < 0) {
goto cleanup;
}
if (virtualSystemSettingData == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s for domain %s"),
"Msvm_VirtualSystemSettingData",
computerSystem->data->ElementName);
goto cleanup;
}
/* Get Msvm_ProcessorSettingData */
virBufferAsprintf(&query,
"associators of "
"{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
"where AssocClass = Msvm_VirtualSystemSettingDataComponent "
"ResultClass = Msvm_ProcessorSettingData",
virtualSystemSettingData->data->InstanceID);
if (hypervGetMsvmProcessorSettingDataList(priv, &query,
&processorSettingData) < 0) {
goto cleanup;
}
if (processorSettingData == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s for domain %s"),
"Msvm_ProcessorSettingData",
computerSystem->data->ElementName);
goto cleanup;
}
/* Get Msvm_MemorySettingData */
virBufferAsprintf(&query,
"associators of "
"{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
"where AssocClass = Msvm_VirtualSystemSettingDataComponent "
"ResultClass = Msvm_MemorySettingData",
virtualSystemSettingData->data->InstanceID);
if (hypervGetMsvmMemorySettingDataList(priv, &query,
&memorySettingData) < 0) {
goto cleanup;
}
if (memorySettingData == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s for domain %s"),
"Msvm_MemorySettingData",
computerSystem->data->ElementName);
goto cleanup;
}
/* Fill struct */
info->state = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
info->maxMem = memorySettingData->data->Limit * 1024; /* megabyte to kilobyte */
info->memory = memorySettingData->data->VirtualQuantity * 1024; /* megabyte to kilobyte */
info->nrVirtCpu = processorSettingData->data->VirtualQuantity;
info->cpuTime = 0;
result = 0;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
hypervFreeObject(priv, (hypervObject *)virtualSystemSettingData);
hypervFreeObject(priv, (hypervObject *)processorSettingData);
hypervFreeObject(priv, (hypervObject *)memorySettingData);
return result;
}
static int
hypervDomainGetState(virDomainPtr domain, int *state, int *reason,
unsigned int flags)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
virCheckFlags(0, -1);
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
*state = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
if (reason != NULL) {
*reason = 0;
}
result = 0;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static char *
hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
char *xml = NULL;
hypervPrivate *priv = domain->conn->privateData;
virDomainDefPtr def = NULL;
char uuid_string[VIR_UUID_STRING_BUFLEN];
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystem = NULL;
Msvm_VirtualSystemSettingData *virtualSystemSettingData = NULL;
Msvm_ProcessorSettingData *processorSettingData = NULL;
Msvm_MemorySettingData *memorySettingData = NULL;
/* Flags checked by virDomainDefFormat */
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
goto cleanup;
}
virUUIDFormat(domain->uuid, uuid_string);
/* Get Msvm_ComputerSystem */
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
/* Get Msvm_VirtualSystemSettingData */
virBufferAsprintf(&query,
"associators of "
"{Msvm_ComputerSystem.CreationClassName=\"Msvm_ComputerSystem\","
"Name=\"%s\"} "
"where AssocClass = Msvm_SettingsDefineState "
"ResultClass = Msvm_VirtualSystemSettingData",
uuid_string);
if (hypervGetMsvmVirtualSystemSettingDataList(priv, &query,
&virtualSystemSettingData) < 0) {
goto cleanup;
}
if (virtualSystemSettingData == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s for domain %s"),
"Msvm_VirtualSystemSettingData",
computerSystem->data->ElementName);
goto cleanup;
}
/* Get Msvm_ProcessorSettingData */
virBufferAsprintf(&query,
"associators of "
"{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
"where AssocClass = Msvm_VirtualSystemSettingDataComponent "
"ResultClass = Msvm_ProcessorSettingData",
virtualSystemSettingData->data->InstanceID);
if (hypervGetMsvmProcessorSettingDataList(priv, &query,
&processorSettingData) < 0) {
goto cleanup;
}
if (processorSettingData == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s for domain %s"),
"Msvm_ProcessorSettingData",
computerSystem->data->ElementName);
goto cleanup;
}
/* Get Msvm_MemorySettingData */
virBufferAsprintf(&query,
"associators of "
"{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
"where AssocClass = Msvm_VirtualSystemSettingDataComponent "
"ResultClass = Msvm_MemorySettingData",
virtualSystemSettingData->data->InstanceID);
if (hypervGetMsvmMemorySettingDataList(priv, &query,
&memorySettingData) < 0) {
goto cleanup;
}
if (memorySettingData == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not lookup %s for domain %s"),
"Msvm_MemorySettingData",
computerSystem->data->ElementName);
goto cleanup;
}
/* Fill struct */
def->virtType = VIR_DOMAIN_VIRT_HYPERV;
if (hypervIsMsvmComputerSystemActive(computerSystem, NULL)) {
def->id = computerSystem->data->ProcessID;
} else {
def->id = -1;
}
if (virUUIDParse(computerSystem->data->Name, def->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not parse UUID from string '%s'"),
computerSystem->data->Name);
return NULL;
}
def->name = strdup(computerSystem->data->ElementName);
if (def->name == NULL) {
virReportOOMError();
goto cleanup;
}
if (virtualSystemSettingData->data->Notes != NULL) {
def->description = strdup(virtualSystemSettingData->data->Notes);
if (def->description == NULL) {
virReportOOMError();
goto cleanup;
}
}
def->mem.max_balloon = memorySettingData->data->Limit * 1024; /* megabyte to kilobyte */
def->mem.cur_balloon = memorySettingData->data->VirtualQuantity * 1024; /* megabyte to kilobyte */
def->vcpus = processorSettingData->data->VirtualQuantity;
def->maxvcpus = processorSettingData->data->VirtualQuantity;
def->os.type = strdup("hvm");
if (def->os.type == NULL) {
virReportOOMError();
goto cleanup;
}
/* FIXME: devices section is totally missing */
xml = virDomainDefFormat(def, flags);
cleanup:
virDomainDefFree(def);
hypervFreeObject(priv, (hypervObject *)computerSystem);
hypervFreeObject(priv, (hypervObject *)virtualSystemSettingData);
hypervFreeObject(priv, (hypervObject *)processorSettingData);
hypervFreeObject(priv, (hypervObject *)memorySettingData);
return xml;
}
static int
hypervListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
{
bool success = false;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystemList = NULL;
Msvm_ComputerSystem *computerSystem = NULL;
int count = 0;
int i;
if (maxnames == 0) {
return 0;
}
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAddLit(&query, "and ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_INACTIVE);
if (hypervGetMsvmComputerSystemList(priv, &query,
&computerSystemList) < 0) {
goto cleanup;
}
for (computerSystem = computerSystemList; computerSystem != NULL;
computerSystem = computerSystem->next) {
names[count] = strdup(computerSystem->data->ElementName);
if (names[count] == NULL) {
virReportOOMError();
goto cleanup;
}
++count;
if (count >= maxnames) {
break;
}
}
success = true;
cleanup:
if (!success) {
for (i = 0; i < count; ++i) {
VIR_FREE(names[i]);
}
count = -1;
}
hypervFreeObject(priv, (hypervObject *)computerSystemList);
return count;
}
static int
hypervNumberOfDefinedDomains(virConnectPtr conn)
{
bool success = false;
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystemList = NULL;
Msvm_ComputerSystem *computerSystem = NULL;
int count = 0;
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
virBufferAddLit(&query, "and ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_INACTIVE);
if (hypervGetMsvmComputerSystemList(priv, &query,
&computerSystemList) < 0) {
goto cleanup;
}
for (computerSystem = computerSystemList; computerSystem != NULL;
computerSystem = computerSystem->next) {
++count;
}
success = true;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystemList);
return success ? count : -1;
}
static int
hypervDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
virCheckFlags(0, -1);
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
if (hypervIsMsvmComputerSystemActive(computerSystem, NULL)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is already active or is in state transition"));
goto cleanup;
}
result = hypervInvokeMsvmComputerSystemRequestStateChange
(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_ENABLED);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainCreate(virDomainPtr domain)
{
return hypervDomainCreateWithFlags(domain, 0);
}
static int
hypervIsEncrypted(virConnectPtr conn)
{
hypervPrivate *priv = conn->privateData;
if (STRCASEEQ(priv->parsedUri->transport, "https")) {
return 1;
} else {
return 0;
}
}
static int
hypervIsSecure(virConnectPtr conn)
{
hypervPrivate *priv = conn->privateData;
if (STRCASEEQ(priv->parsedUri->transport, "https")) {
return 1;
} else {
return 0;
}
}
static int
hypervIsAlive(virConnectPtr conn)
{
hypervPrivate *priv = conn->privateData;
/* XXX we should be able to do something better than this is simple, safe,
* and good enough for now. In worst case, the function will return true
* even though the connection is not alive.
*/
if (priv->client)
return 1;
else
return 0;
}
static int
hypervDomainIsActive(virDomainPtr domain)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
result = hypervIsMsvmComputerSystemActive(computerSystem, NULL) ? 1 : 0;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainIsPersistent(virDomainPtr domain ATTRIBUTE_UNUSED)
{
/* Hyper-V has no concept of transient domains, so all of them are persistent */
return 1;
}
static int
hypervDomainIsUpdated(virDomainPtr domain ATTRIBUTE_UNUSED)
{
return 0;
}
static int
hypervDomainManagedSave(virDomainPtr domain, unsigned int flags)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
bool in_transition = false;
virCheckFlags(0, -1);
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
in_transition) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain is not active or is in state transition"));
goto cleanup;
}
result = hypervInvokeMsvmComputerSystemRequestStateChange
(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_SUSPENDED);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
virCheckFlags(0, -1);
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
result = computerSystem->data->EnabledState ==
MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED ? 1 : 0;
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
static int
hypervDomainManagedSaveRemove(virDomainPtr domain, unsigned int flags)
{
int result = -1;
hypervPrivate *priv = domain->conn->privateData;
Msvm_ComputerSystem *computerSystem = NULL;
virCheckFlags(0, -1);
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0) {
goto cleanup;
}
if (computerSystem->data->EnabledState !=
MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("Domain has no managed save image"));
goto cleanup;
}
result = hypervInvokeMsvmComputerSystemRequestStateChange
(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_DISABLED);
cleanup:
hypervFreeObject(priv, (hypervObject *)computerSystem);
return result;
}
#define MATCH(FLAG) (flags & (FLAG))
static int
hypervListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
hypervPrivate *priv = conn->privateData;
virBuffer query = VIR_BUFFER_INITIALIZER;
Msvm_ComputerSystem *computerSystemList = NULL;
Msvm_ComputerSystem *computerSystem = NULL;
size_t ndoms;
virDomainPtr domain;
virDomainPtr *doms = NULL;
int count = 0;
int ret = -1;
int i;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
/* check for filter combinations that return no results:
* persistent: all hyperv guests are persistent
* snapshot: the driver does not support snapshot management
* autostart: the driver does not support autostarting guests
*/
if ((MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT))) {
if (domains &&
VIR_ALLOC_N(*domains, 1) < 0)
goto no_memory;
ret = 0;
goto cleanup;
}
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
virBufferAddLit(&query, "where ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
/* construct query with filter depending on flags */
if (!(MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) &&
MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE))) {
if (MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE)) {
virBufferAddLit(&query, "and ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_ACTIVE);
}
if (MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE)) {
virBufferAddLit(&query, "and ");
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_INACTIVE);
}
}
if (hypervGetMsvmComputerSystemList(priv, &query,
&computerSystemList) < 0)
goto cleanup;
if (domains) {
if (VIR_ALLOC_N(doms, 1) < 0)
goto no_memory;
ndoms = 1;
}
for (computerSystem = computerSystemList; computerSystem != NULL;
computerSystem = computerSystem->next) {
/* filter by domain state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE)) {
int st = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
st == VIR_DOMAIN_RUNNING) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
st == VIR_DOMAIN_PAUSED) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
st == VIR_DOMAIN_SHUTOFF) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
(st != VIR_DOMAIN_RUNNING &&
st != VIR_DOMAIN_PAUSED &&
st != VIR_DOMAIN_SHUTOFF))))
continue;
}
/* managed save filter */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_MANAGEDSAVE)) {
bool mansave = computerSystem->data->EnabledState ==
MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED;
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) && mansave) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE) && !mansave)))
continue;
}
if (!doms) {
count++;
continue;
}
if (VIR_RESIZE_N(doms, ndoms, count, 2) < 0)
goto no_memory;
domain = NULL;
if (hypervMsvmComputerSystemToDomain(conn, computerSystem,
&domain) < 0)
goto cleanup;
doms[count++] = domain;
}
if (doms)
*domains = doms;
doms = NULL;
ret = count;
cleanup:
if (doms) {
for (i = 0; i < count; ++i) {
virDomainFree(doms[i]);
}
VIR_FREE(doms);
}
hypervFreeObject(priv, (hypervObject *)computerSystemList);
return ret;
no_memory:
virReportOOMError();
goto cleanup;
}
#undef MATCH
static virDriver hypervDriver = {
.no = VIR_DRV_HYPERV,
.name = "Hyper-V",
.open = hypervOpen, /* 0.9.5 */
.close = hypervClose, /* 0.9.5 */
.type = hypervGetType, /* 0.9.5 */
.getHostname = hypervGetHostname, /* 0.9.5 */
.nodeGetInfo = hypervNodeGetInfo, /* 0.9.5 */
.listDomains = hypervListDomains, /* 0.9.5 */
.numOfDomains = hypervNumberOfDomains, /* 0.9.5 */
.listAllDomains = hypervListAllDomains, /* 0.10.2 */
.domainLookupByID = hypervDomainLookupByID, /* 0.9.5 */
.domainLookupByUUID = hypervDomainLookupByUUID, /* 0.9.5 */
.domainLookupByName = hypervDomainLookupByName, /* 0.9.5 */
.domainSuspend = hypervDomainSuspend, /* 0.9.5 */
.domainResume = hypervDomainResume, /* 0.9.5 */
.domainDestroy = hypervDomainDestroy, /* 0.9.5 */
.domainDestroyFlags = hypervDomainDestroyFlags, /* 0.9.5 */
.domainGetOSType = hypervDomainGetOSType, /* 0.9.5 */
.domainGetInfo = hypervDomainGetInfo, /* 0.9.5 */
.domainGetState = hypervDomainGetState, /* 0.9.5 */
.domainGetXMLDesc = hypervDomainGetXMLDesc, /* 0.9.5 */
.listDefinedDomains = hypervListDefinedDomains, /* 0.9.5 */
.numOfDefinedDomains = hypervNumberOfDefinedDomains, /* 0.9.5 */
.domainCreate = hypervDomainCreate, /* 0.9.5 */
.domainCreateWithFlags = hypervDomainCreateWithFlags, /* 0.9.5 */
.isEncrypted = hypervIsEncrypted, /* 0.9.5 */
.isSecure = hypervIsSecure, /* 0.9.5 */
.domainIsActive = hypervDomainIsActive, /* 0.9.5 */
.domainIsPersistent = hypervDomainIsPersistent, /* 0.9.5 */
.domainIsUpdated = hypervDomainIsUpdated, /* 0.9.5 */
.domainManagedSave = hypervDomainManagedSave, /* 0.9.5 */
.domainHasManagedSaveImage = hypervDomainHasManagedSaveImage, /* 0.9.5 */
.domainManagedSaveRemove = hypervDomainManagedSaveRemove, /* 0.9.5 */
.isAlive = hypervIsAlive, /* 0.9.8 */
};
static void
hypervDebugHandler(const char *message, debug_level_e level,
void *user_data ATTRIBUTE_UNUSED)
{
switch (level) {
case DEBUG_LEVEL_ERROR:
case DEBUG_LEVEL_CRITICAL:
VIR_ERROR(_("openwsman error: %s"), message);
break;
case DEBUG_LEVEL_WARNING:
VIR_WARN("openwsman warning: %s", message);
break;
default:
/* Ignore the rest */
break;
}
}
int
hypervRegister(void)
{
if (virRegisterDriver(&hypervDriver) < 0 ||
hypervInterfaceRegister() < 0 ||
hypervNetworkRegister() < 0 ||
hypervStorageRegister() < 0 ||
hypervDeviceRegister() < 0 ||
hypervSecretRegister() < 0 ||
hypervNWFilterRegister() < 0) {
return -1;
}
/* Forward openwsman errors and warnings to libvirt's logging */
debug_add_handler(hypervDebugHandler, DEBUG_LEVEL_WARNING, NULL);
return 0;
}