diff options
author | Matthias Bolte <matthias.bolte@googlemail.com> | 2011-07-13 17:16:47 +0200 |
---|---|---|
committer | Matthias Bolte <matthias.bolte@googlemail.com> | 2011-08-26 17:52:55 +0200 |
commit | 5e3b0f8b5718f0a8631e60e46cb107cbb1fe53bf (patch) | |
tree | bac276a8637500a92f3407f55a0ece0b538c0379 /src/hyperv | |
parent | hyperv: Add OpenWSMAN based client for the Hyper-V WMI API (diff) | |
download | libvirt-5e3b0f8b5718f0a8631e60e46cb107cbb1fe53bf.tar.gz libvirt-5e3b0f8b5718f0a8631e60e46cb107cbb1fe53bf.tar.bz2 libvirt-5e3b0f8b5718f0a8631e60e46cb107cbb1fe53bf.zip |
hyperv: Add basic driver for Microsoft Hyper-V
Domain listing, basic information retrieval and domain life cycle
management is implemented. But currently the domain XML output
lacks the complete devices section.
The driver uses OpenWSMAN to directly communicate with a Hyper-V
server over its WS-Management interface exposed via Microsoft WinRM.
The driver is based on the work of Michael Sievers. This started in
the same master program project group at the University of Paderborn
as the ESX driver.
See Michael's blog for details: http://hyperv4libvirt.wordpress.com/
Diffstat (limited to 'src/hyperv')
-rw-r--r-- | src/hyperv/hyperv_driver.c | 1199 | ||||
-rw-r--r-- | src/hyperv/hyperv_private.h | 2 | ||||
-rw-r--r-- | src/hyperv/hyperv_util.c | 129 | ||||
-rw-r--r-- | src/hyperv/hyperv_util.h | 40 |
4 files changed, 1368 insertions, 2 deletions
diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 301aeb21a..b022fee7f 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -39,14 +39,42 @@ #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; + 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 not hyperv */ @@ -69,28 +97,1192 @@ hypervOpen(virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags) return VIR_DRV_OPEN_ERROR; } - return VIR_DRV_OPEN_SUCCESS; + /* 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 = virRequestUsername(auth, "administrator", conn->uri->server); + + if (username == NULL) { + HYPERV_ERROR(VIR_ERR_AUTH_FAILED, "%s", _("Username request failed")); + goto cleanup; + } + } + + password = virRequestPassword(auth, username, conn->uri->server); + + if (password == NULL) { + HYPERV_ERROR(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) { + HYPERV_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not create openwsman client")); + goto cleanup; + } + + if (wsmc_transport_init(priv->client, NULL) != 0) { + HYPERV_ERROR(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) { + HYPERV_ERROR(VIR_ERR_INTERNAL_ERROR, + _("%s is not a Hyper-V server"), conn->uri->server); + goto cleanup; + } + + conn->privateData = priv; + + result = VIR_DRV_OPEN_SUCCESS; + + cleanup: + if (result == VIR_DRV_OPEN_ERROR) { + 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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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 -hypervClose(virConnectPtr conn ATTRIBUTE_UNUSED) +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)) { + HYPERV_ERROR(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 +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) { + HYPERV_ERROR(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) { + HYPERV_ERROR(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; +} + + + 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 */ + .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 */ }; +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) { @@ -104,5 +1296,8 @@ hypervRegister(void) return -1; } + /* Forward openwsman errors and warnings to libvirt's logging */ + debug_add_handler(hypervDebugHandler, DEBUG_LEVEL_WARNING, NULL); + return 0; } diff --git a/src/hyperv/hyperv_private.h b/src/hyperv/hyperv_private.h index 0d5370e8e..ebddf5daa 100644 --- a/src/hyperv/hyperv_private.h +++ b/src/hyperv/hyperv_private.h @@ -26,6 +26,7 @@ # include "internal.h" # include "virterror_internal.h" +# include "hyperv_util.h" # include "openwsman.h" # define HYPERV_ERROR(code, ...) \ @@ -35,6 +36,7 @@ typedef struct _hypervPrivate hypervPrivate; struct _hypervPrivate { + hypervParsedUri *parsedUri; WsManClient *client; }; diff --git a/src/hyperv/hyperv_util.c b/src/hyperv/hyperv_util.c new file mode 100644 index 000000000..298cfe0ee --- /dev/null +++ b/src/hyperv/hyperv_util.c @@ -0,0 +1,129 @@ + +/* + * hyperv_util.c: utility functions for the Microsoft Hyper-V driver + * + * Copyright (C) 2011 Matthias Bolte <matthias.bolte@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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + +#include "internal.h" +#include "datatypes.h" +#include "qparams.h" +#include "util.h" +#include "memory.h" +#include "logging.h" +#include "uuid.h" +#include "hyperv_private.h" +#include "hyperv_util.h" + +#define VIR_FROM_THIS VIR_FROM_HYPERV + + + +int +hypervParseUri(hypervParsedUri **parsedUri, xmlURIPtr uri) +{ + int result = -1; + struct qparam_set *queryParamSet = NULL; + struct qparam *queryParam = NULL; + int i; + + if (parsedUri == NULL || *parsedUri != NULL) { + HYPERV_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (VIR_ALLOC(*parsedUri) < 0) { + virReportOOMError(); + return -1; + } + +#ifdef HAVE_XMLURI_QUERY_RAW + queryParamSet = qparam_query_parse(uri->query_raw); +#else + queryParamSet = qparam_query_parse(uri->query); +#endif + + if (queryParamSet == NULL) { + goto cleanup; + } + + for (i = 0; i < queryParamSet->n; i++) { + queryParam = &queryParamSet->p[i]; + + if (STRCASEEQ(queryParam->name, "transport")) { + VIR_FREE((*parsedUri)->transport); + + (*parsedUri)->transport = strdup(queryParam->value); + + if ((*parsedUri)->transport == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (STRNEQ((*parsedUri)->transport, "http") && + STRNEQ((*parsedUri)->transport, "https")) { + HYPERV_ERROR(VIR_ERR_INVALID_ARG, + _("Query parameter 'transport' has unexpected value " + "'%s' (should be http|https)"), + (*parsedUri)->transport); + goto cleanup; + } + } else { + VIR_WARN("Ignoring unexpected query parameter '%s'", + queryParam->name); + } + } + + if ((*parsedUri)->transport == NULL) { + (*parsedUri)->transport = strdup("https"); + + if ((*parsedUri)->transport == NULL) { + virReportOOMError(); + goto cleanup; + } + } + + result = 0; + + cleanup: + if (result < 0) { + hypervFreeParsedUri(parsedUri); + } + + if (queryParamSet != NULL) { + free_qparam_set(queryParamSet); + } + + return result; +} + + + +void +hypervFreeParsedUri(hypervParsedUri **parsedUri) +{ + if (parsedUri == NULL || *parsedUri == NULL) { + return; + } + + VIR_FREE((*parsedUri)->transport); + + VIR_FREE(*parsedUri); +} diff --git a/src/hyperv/hyperv_util.h b/src/hyperv/hyperv_util.h new file mode 100644 index 000000000..9057f55c1 --- /dev/null +++ b/src/hyperv/hyperv_util.h @@ -0,0 +1,40 @@ + +/* + * hyperv_util.h: utility functions for the Microsoft Hyper-V driver + * + * Copyright (C) 2011 Matthias Bolte <matthias.bolte@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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __HYPERV_UTIL_H__ +# define __HYPERV_UTIL_H__ + +# include <libxml/uri.h> + +# include "internal.h" + +typedef struct _hypervParsedUri hypervParsedUri; + +struct _hypervParsedUri { + char *transport; +}; + +int hypervParseUri(hypervParsedUri **parsedUri, xmlURIPtr uri); + +void hypervFreeParsedUri(hypervParsedUri **parsedUri); + +#endif /* __HYPERV_UTIL_H__ */ |