/* * hyperv_driver.c: core driver functions for managing Microsoft Hyper-V hosts * * Copyright (C) 2011 Matthias Bolte * Copyright (C) 2009 Michael Sievers * * 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 * . * */ #include #include "internal.h" #include "datatypes.h" #include "domain_conf.h" #include "virauth.h" #include "util.h" #include "memory.h" #include "logging.h" #include "uuid.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; 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) { 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 (!(flags & VIR_CONNECT_LIST_DOMAINS_ACTIVE && flags & VIR_CONNECT_LIST_DOMAINS_INACTIVE)) { if (flags & VIR_CONNECT_LIST_DOMAINS_ACTIVE) { virBufferAddLit(&query, "and "); virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_ACTIVE); } if (flags & 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; 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) { if (doms[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; }