diff --git a/po/POTFILES.in b/po/POTFILES.in index 4ba478eb54..5ce35ae60f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -29,6 +29,7 @@ src/esx/esx_vi_methods.c src/esx/esx_vi_types.c src/fdstream.c src/hyperv/hyperv_driver.c +src/hyperv/hyperv_util.c src/hyperv/hyperv_wmi.c src/interface/netcf_driver.c src/internal.h diff --git a/src/Makefile.am b/src/Makefile.am index ba9ab462ad..322c900a3c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -412,6 +412,7 @@ HYPERV_DRIVER_SOURCES = \ hyperv/hyperv_device_monitor.c hyperv/hyperv_device_monitor.h \ hyperv/hyperv_secret_driver.c hyperv/hyperv_secret_driver.h \ hyperv/hyperv_nwfilter_driver.c hyperv/hyperv_nwfilter_driver.h \ + hyperv/hyperv_util.c hyperv/hyperv_util.h \ hyperv/hyperv_wmi.c hyperv/hyperv_wmi.h \ hyperv/hyperv_wmi_classes.c hyperv/hyperv_wmi_classes.h \ hyperv/openwsman.h diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 301aeb21a8..b022fee7f2 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 ATTRIBUTE_UNUSED) +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 +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 0d5370e8ef..ebddf5daa8 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 0000000000..298cfe0eef --- /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 + * + * 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 + +#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 0000000000..9057f55c14 --- /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 + * + * 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 + +# 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__ */