mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-24 05:25:18 +00:00
02dfea6693
Use the appropriate type for the variable and refactor the XML parser to parse it correctly using virXMLPropEnum. Changes to other places using switch statements were required. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
3791 lines
121 KiB
C
3791 lines
121 KiB
C
/*
|
|
* hyperv_driver.c: core driver functions for managing Microsoft Hyper-V hosts
|
|
*
|
|
* Copyright (C) 2011-2013 Matthias Bolte <matthias.bolte@googlemail.com>
|
|
* Copyright (C) 2009 Michael Sievers <msievers83@googlemail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include "internal.h"
|
|
#include "datatypes.h"
|
|
#include "virdomainobjlist.h"
|
|
#include "virauth.h"
|
|
#include "viralloc.h"
|
|
#include "virlog.h"
|
|
#include "viruuid.h"
|
|
#include "virutil.h"
|
|
#include "hyperv_driver.h"
|
|
#include "hyperv_network_driver.h"
|
|
#include "hyperv_private.h"
|
|
#include "hyperv_util.h"
|
|
#include "hyperv_wmi.h"
|
|
#include "virstring.h"
|
|
#include "virkeycode.h"
|
|
#include "domain_conf.h"
|
|
#include "virfdstream.h"
|
|
#include "virfile.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_HYPERV
|
|
|
|
VIR_LOG_INIT("hyperv.hyperv_driver");
|
|
|
|
/*
|
|
* WMI utility functions
|
|
*
|
|
* wrapper functions for commonly-accessed WMI objects and interfaces.
|
|
*/
|
|
|
|
static int
|
|
hypervGetProcessorsByName(hypervPrivate *priv, const char *name,
|
|
Win32_Processor **processorList)
|
|
{
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
virBufferEscapeSQL(&query,
|
|
"ASSOCIATORS OF {Win32_ComputerSystem.Name='%s'} "
|
|
"WHERE AssocClass = Win32_ComputerSystemProcessor "
|
|
"ResultClass = Win32_Processor",
|
|
name);
|
|
|
|
if (hypervGetWmiClass(Win32_Processor, processorList) < 0)
|
|
return -1;
|
|
|
|
if (!processorList) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not look up processor(s) on '%s'"),
|
|
name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervGetActiveVirtualSystemList(hypervPrivate *priv,
|
|
Msvm_ComputerSystem **computerSystemList)
|
|
{
|
|
g_auto(virBuffer) query = { g_string_new(MSVM_COMPUTERSYSTEM_WQL_SELECT
|
|
"WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
|
|
"AND " MSVM_COMPUTERSYSTEM_WQL_ACTIVE), 0 };
|
|
|
|
if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
|
|
return -1;
|
|
|
|
if (!*computerSystemList) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not look up active virtual machines"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* gets all the vms including the ones that are marked inactive. */
|
|
static int
|
|
hypervGetInactiveVirtualSystemList(hypervPrivate *priv,
|
|
Msvm_ComputerSystem **computerSystemList)
|
|
{
|
|
g_auto(virBuffer) query = { g_string_new(MSVM_COMPUTERSYSTEM_WQL_SELECT
|
|
"WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
|
|
"AND " MSVM_COMPUTERSYSTEM_WQL_INACTIVE), 0 };
|
|
|
|
if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
|
|
return -1;
|
|
|
|
if (!*computerSystemList) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not look up inactive virtual machines"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervGetPhysicalSystemList(hypervPrivate *priv,
|
|
Win32_ComputerSystem **computerSystemList)
|
|
{
|
|
g_auto(virBuffer) query = { g_string_new(WIN32_COMPUTERSYSTEM_WQL_SELECT), 0 };
|
|
|
|
if (hypervGetWmiClass(Win32_ComputerSystem, computerSystemList) < 0)
|
|
return -1;
|
|
|
|
if (!*computerSystemList) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not look up Win32_ComputerSystem"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervGetVirtualSystemByID(hypervPrivate *priv, int id,
|
|
Msvm_ComputerSystem **computerSystemList)
|
|
{
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
virBufferAsprintf(&query,
|
|
MSVM_COMPUTERSYSTEM_WQL_SELECT
|
|
"WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
|
|
"AND ProcessID = %d",
|
|
id);
|
|
|
|
if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
|
|
return -1;
|
|
|
|
if (*computerSystemList == NULL) {
|
|
virReportError(VIR_ERR_NO_DOMAIN, _("No domain with ID %d"), id);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervGetVirtualSystemByName(hypervPrivate *priv, const char *name,
|
|
Msvm_ComputerSystem **computerSystemList)
|
|
{
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
virBufferEscapeSQL(&query,
|
|
MSVM_COMPUTERSYSTEM_WQL_SELECT
|
|
"WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
|
|
"AND ElementName = '%s'",
|
|
name);
|
|
|
|
if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
|
|
return -1;
|
|
|
|
if (*computerSystemList == NULL) {
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
_("No domain with name %s"), name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervGetOperatingSystem(hypervPrivate *priv, Win32_OperatingSystem **operatingSystem)
|
|
{
|
|
g_auto(virBuffer) query = { g_string_new(WIN32_OPERATINGSYSTEM_WQL_SELECT), 0 };
|
|
|
|
if (hypervGetWmiClass(Win32_OperatingSystem, operatingSystem) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervRequestStateChange(virDomainPtr domain, int state)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not active"));
|
|
return -1;
|
|
}
|
|
|
|
return hypervInvokeMsvmComputerSystemRequestStateChange(domain, state);
|
|
}
|
|
|
|
|
|
/*
|
|
* API-specific utility functions
|
|
*/
|
|
|
|
static int
|
|
hypervParseVersionString(const char *str, unsigned int *major,
|
|
unsigned int *minor, unsigned int *micro)
|
|
{
|
|
char *suffix = NULL;
|
|
|
|
if (virStrToLong_ui(str, &suffix, 10, major) < 0)
|
|
return -1;
|
|
|
|
if (virStrToLong_ui(suffix + 1, &suffix, 10, minor) < 0)
|
|
return -1;
|
|
|
|
if (virStrToLong_ui(suffix + 1, NULL, 10, micro) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervLookupHostSystemBiosUuid(hypervPrivate *priv, unsigned char *uuid)
|
|
{
|
|
g_autoptr(Win32_ComputerSystemProduct) computerSystem = NULL;
|
|
g_auto(virBuffer) query = { g_string_new(WIN32_COMPUTERSYSTEMPRODUCT_WQL_SELECT), 0 };
|
|
|
|
if (hypervGetWmiClass(Win32_ComputerSystemProduct, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (virUUIDParse(computerSystem->data->UUID, uuid) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse UUID from string '%s'"),
|
|
computerSystem->data->UUID);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virCaps *
|
|
hypervCapsInit(hypervPrivate *priv)
|
|
{
|
|
virCaps *caps = NULL;
|
|
virCapsGuest *guest = NULL;
|
|
|
|
caps = virCapabilitiesNew(VIR_ARCH_X86_64, 1, 1);
|
|
|
|
if (!caps)
|
|
return NULL;
|
|
|
|
if (hypervLookupHostSystemBiosUuid(priv, caps->host.host_uuid) < 0)
|
|
goto error;
|
|
|
|
/* i686 caps */
|
|
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM, VIR_ARCH_I686,
|
|
NULL, NULL, 0, NULL);
|
|
if (!guest)
|
|
goto error;
|
|
|
|
if (!virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_HYPERV, NULL, NULL, 0, NULL))
|
|
goto error;
|
|
|
|
/* x86_64 caps */
|
|
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM, VIR_ARCH_X86_64,
|
|
NULL, NULL, 0, NULL);
|
|
if (!guest)
|
|
goto error;
|
|
|
|
if (!virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_HYPERV, NULL, NULL, 0, NULL))
|
|
goto error;
|
|
|
|
return caps;
|
|
|
|
error:
|
|
virObjectUnref(caps);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervGetVideoResolution(hypervPrivate *priv,
|
|
char *vm_uuid,
|
|
int *xRes,
|
|
int *yRes,
|
|
bool fallback)
|
|
{
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_S3DisplayController) s3Display = NULL;
|
|
g_autoptr(Msvm_SyntheticDisplayController) synthetic = NULL;
|
|
g_autoptr(Msvm_VideoHead) heads = NULL;
|
|
const char *wmiClass = NULL;
|
|
char *deviceId = NULL;
|
|
g_autofree char *enabledStateString = NULL;
|
|
|
|
if (fallback) {
|
|
wmiClass = "Msvm_S3DisplayController";
|
|
|
|
virBufferEscapeSQL(&query,
|
|
MSVM_S3DISPLAYCONTROLLER_WQL_SELECT "WHERE SystemName = '%s'",
|
|
vm_uuid);
|
|
|
|
if (hypervGetWmiClass(Msvm_S3DisplayController, &s3Display) < 0 || !s3Display)
|
|
return -1;
|
|
|
|
deviceId = s3Display->data->DeviceID;
|
|
} else {
|
|
wmiClass = "Msvm_SyntheticDisplayController";
|
|
|
|
virBufferEscapeSQL(&query,
|
|
MSVM_SYNTHETICDISPLAYCONTROLLER_WQL_SELECT "WHERE SystemName = '%s'",
|
|
vm_uuid);
|
|
|
|
if (hypervGetWmiClass(Msvm_SyntheticDisplayController, &synthetic) < 0 || !synthetic)
|
|
return -1;
|
|
|
|
deviceId = synthetic->data->DeviceID;
|
|
}
|
|
|
|
virBufferFreeAndReset(&query);
|
|
|
|
virBufferAsprintf(&query,
|
|
"ASSOCIATORS OF {%s."
|
|
"CreationClassName='%s',"
|
|
"DeviceID='%s',"
|
|
"SystemCreationClassName='Msvm_ComputerSystem',"
|
|
"SystemName='%s'"
|
|
"} WHERE AssocClass = Msvm_VideoHeadOnController "
|
|
"ResultClass = Msvm_VideoHead",
|
|
wmiClass, wmiClass, deviceId, vm_uuid);
|
|
|
|
if (hypervGetWmiClass(Msvm_VideoHead, &heads) < 0)
|
|
return -1;
|
|
|
|
enabledStateString = g_strdup_printf("%d", CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_ENABLED);
|
|
if (heads && STREQ(heads->data->EnabledState, enabledStateString)) {
|
|
*xRes = heads->data->CurrentHorizontalResolution;
|
|
*yRes = heads->data->CurrentVerticalResolution;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Virtual device functions
|
|
*/
|
|
static int
|
|
hypervGetDeviceParentRasdFromDeviceId(const char *parentDeviceId,
|
|
Msvm_ResourceAllocationSettingData *list,
|
|
Msvm_ResourceAllocationSettingData **out)
|
|
{
|
|
Msvm_ResourceAllocationSettingData *entry = list;
|
|
*out = NULL;
|
|
|
|
while (entry) {
|
|
g_autofree char *escapedDeviceId = virStringReplace(entry->data->InstanceID, "\\", "\\\\");
|
|
g_autofree char *expectedSuffix = g_strdup_printf("%s\"", escapedDeviceId);
|
|
|
|
if (g_str_has_suffix(parentDeviceId, expectedSuffix)) {
|
|
*out = entry;
|
|
break;
|
|
}
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
if (*out)
|
|
return 0;
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to locate parent device with ID '%s'"),
|
|
parentDeviceId);
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static char *
|
|
hypervGetInstanceIDFromXMLResponse(WsXmlDocH response)
|
|
{
|
|
WsXmlNodeH envelope = NULL;
|
|
char *instanceId = NULL;
|
|
|
|
envelope = ws_xml_get_soap_envelope(response);
|
|
if (!envelope) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid XML response"));
|
|
return NULL;
|
|
}
|
|
|
|
instanceId = ws_xml_get_xpath_value(response, (char *)"//w:Selector[@Name='InstanceID']");
|
|
|
|
if (!instanceId) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not find selectors in method response"));
|
|
return NULL;
|
|
}
|
|
|
|
return instanceId;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainCreateSCSIController(virDomainPtr domain, virDomainControllerDef *def)
|
|
{
|
|
g_autoptr(GHashTable) scsiResource = NULL;
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
if (def->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT &&
|
|
def->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Unsupported SCSI controller model '%d'"), def->model);
|
|
return -1;
|
|
}
|
|
|
|
if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Unsupported SCSI controller address type '%d'"), def->info.type);
|
|
return -1;
|
|
}
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA);
|
|
|
|
VIR_DEBUG("Attaching SCSI Controller");
|
|
|
|
/* prepare embedded param */
|
|
scsiResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!scsiResource)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(scsiResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(scsiResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Synthetic SCSI Controller") < 0)
|
|
return -1;
|
|
|
|
/* perform the settings change */
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &scsiResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAddVirtualDiskParent(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *controller,
|
|
const char *hostname,
|
|
WsXmlDocH *response)
|
|
{
|
|
g_autoptr(GHashTable) controllerResource = NULL;
|
|
g_autofree char *parentInstanceIDEscaped = NULL;
|
|
g_autofree char *parent__PATH = NULL;
|
|
g_autofree char *addressString = g_strdup_printf("%u", disk->info.addr.drive.unit);
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_DISK_DRIVE);
|
|
|
|
controllerResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!controllerResource)
|
|
return -1;
|
|
|
|
parentInstanceIDEscaped = virStringReplace(controller->data->InstanceID, "\\", "\\\\");
|
|
parent__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
|
|
hostname, parentInstanceIDEscaped);
|
|
if (!parent__PATH)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(controllerResource, "Parent", parent__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(controllerResource, "AddressOnParent", addressString) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(controllerResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(controllerResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Synthetic Disk Drive") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &controllerResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo,
|
|
response) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAddVirtualHardDisk(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
const char *hostname,
|
|
char *parentInstanceID)
|
|
{
|
|
g_autoptr(GHashTable) volumeResource = NULL;
|
|
g_autofree char *vhdInstanceIdEscaped = NULL;
|
|
g_autofree char *vhd__PATH = NULL;
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_LOGICAL_DISK);
|
|
|
|
volumeResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!volumeResource)
|
|
return -1;
|
|
|
|
vhdInstanceIdEscaped = virStringReplace(parentInstanceID, "\\", "\\\\");
|
|
vhd__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
|
|
hostname, vhdInstanceIdEscaped);
|
|
|
|
if (!vhd__PATH)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "Parent", vhd__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "HostResource", disk->src->path) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Virtual Hard Disk") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &volumeResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachVirtualDisk(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *controller,
|
|
const char *hostname)
|
|
{
|
|
g_autofree char *parentInstanceID = NULL;
|
|
g_auto(WsXmlDocH) response = NULL;
|
|
|
|
VIR_DEBUG("Now attaching disk image '%s' with address %d to bus %d of type %d",
|
|
disk->src->path, disk->info.addr.drive.unit, disk->info.addr.drive.controller, disk->bus);
|
|
|
|
if (hypervDomainAddVirtualDiskParent(domain, disk, controller, hostname, &response) < 0)
|
|
return -1;
|
|
|
|
if (!response) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add virtual disk parent"));
|
|
return -1;
|
|
}
|
|
|
|
parentInstanceID = hypervGetInstanceIDFromXMLResponse(response);
|
|
if (!parentInstanceID)
|
|
return -1;
|
|
|
|
if (hypervDomainAddVirtualHardDisk(domain, disk, hostname, parentInstanceID) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachPhysicalDisk(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *controller,
|
|
const char *hostname)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autofree char *hostResource = NULL;
|
|
g_autofree char *controller__PATH = NULL;
|
|
g_auto(GStrv) matches = NULL;
|
|
ssize_t found = 0;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_ResourceAllocationSettingData) diskdefault = NULL;
|
|
g_autofree char *controllerInstanceIdEscaped = NULL;
|
|
g_autoptr(GHashTable) diskResource = NULL;
|
|
g_autofree char *addressString = g_strdup_printf("%u", disk->info.addr.drive.unit);
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_DISK_DRIVE);
|
|
|
|
if (strstr(disk->src->path, "NODRIVE")) {
|
|
/* Hyper-V doesn't let you define LUNs with no connection */
|
|
VIR_DEBUG("Skipping empty LUN '%s' with address %d on bus %d of type %d",
|
|
disk->src->path, disk->info.addr.drive.unit,
|
|
disk->info.addr.drive.controller, disk->bus);
|
|
return 0;
|
|
}
|
|
|
|
VIR_DEBUG("Now attaching LUN '%s' with address %d to bus %d of type %d",
|
|
disk->src->path, disk->info.addr.drive.unit,
|
|
disk->info.addr.drive.controller, disk->bus);
|
|
|
|
/* prepare HostResource */
|
|
|
|
/* get Msvm_DiskDrive root device ID */
|
|
virBufferAddLit(&query,
|
|
MSVM_RESOURCEALLOCATIONSETTINGDATA_WQL_SELECT
|
|
"WHERE ResourceSubType = 'Microsoft:Hyper-V:Physical Disk Drive' "
|
|
"AND InstanceID LIKE '%%Default%%'");
|
|
|
|
if (hypervGetWmiClass(Msvm_ResourceAllocationSettingData, &diskdefault) < 0)
|
|
return -1;
|
|
|
|
if (!diskdefault) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not retrieve default Msvm_DiskDrive object"));
|
|
return -1;
|
|
}
|
|
|
|
found = virStringSearch(diskdefault->data->InstanceID,
|
|
"([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})",
|
|
1, &matches);
|
|
|
|
if (found < 1) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get Msvm_DiskDrive default InstanceID"));
|
|
return -1;
|
|
}
|
|
|
|
hostResource = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_DiskDrive.CreationClassName=\"Msvm_DiskDrive\","
|
|
"DeviceID=\"Microsoft:%s\\\\%s\","
|
|
"SystemCreationClassName=\"Msvm_ComputerSystem\","
|
|
"SystemName=\"%s\"",
|
|
hostname, matches[0], disk->src->path, hostname);
|
|
|
|
/* create embedded param */
|
|
diskResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!diskResource)
|
|
return -1;
|
|
|
|
controllerInstanceIdEscaped = virStringReplace(controller->data->InstanceID, "\\", "\\\\");
|
|
controller__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
|
|
hostname, controllerInstanceIdEscaped);
|
|
if (!controller__PATH)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(diskResource, "Parent", controller__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(diskResource, "AddressOnParent", addressString) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(diskResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(diskResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Physical Disk Drive") < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(diskResource, "HostResource", hostResource) < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &diskResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAddOpticalDrive(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *controller,
|
|
const char *hostname,
|
|
WsXmlDocH *response)
|
|
{
|
|
g_autoptr(GHashTable) driveResource = NULL;
|
|
g_autofree char *parentInstanceIDEscaped = NULL;
|
|
g_autofree char *parent__PATH = NULL;
|
|
g_autofree char *addressString = g_strdup_printf("%u", disk->info.addr.drive.unit);
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_DVD_DRIVE);
|
|
|
|
driveResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!driveResource)
|
|
return -1;
|
|
|
|
parentInstanceIDEscaped = virStringReplace(controller->data->InstanceID, "\\", "\\\\");
|
|
parent__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
|
|
hostname, parentInstanceIDEscaped);
|
|
if (!parent__PATH)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(driveResource, "Parent", parent__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(driveResource, "AddressOnParent", addressString) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(driveResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(driveResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Synthetic DVD Drive") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &driveResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo, response) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAddOpticalDisk(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
const char *hostname,
|
|
char *driveInstanceID)
|
|
{
|
|
g_autoptr(GHashTable) volumeResource = NULL;
|
|
g_autofree char *vhdInstanceIdEscaped = NULL;
|
|
g_autofree char *vhd__PATH = NULL;
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_LOGICAL_DISK);
|
|
|
|
volumeResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!volumeResource)
|
|
return -1;
|
|
|
|
vhdInstanceIdEscaped = virStringReplace(driveInstanceID, "\\", "\\\\");
|
|
vhd__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
|
|
hostname, vhdInstanceIdEscaped);
|
|
if (!vhd__PATH)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "Parent", vhd__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "HostResource", disk->src->path) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Virtual CD/DVD Disk") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &volumeResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachCDROM(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *controller,
|
|
const char *hostname)
|
|
{
|
|
g_auto(WsXmlDocH) response = NULL;
|
|
g_autofree char *driveInstanceID = NULL;
|
|
|
|
VIR_DEBUG("Now attaching CD/DVD '%s' with address %d to bus %d of type %d",
|
|
disk->src->path, disk->info.addr.drive.unit,
|
|
disk->info.addr.drive.controller, disk->bus);
|
|
|
|
if (hypervDomainAddOpticalDrive(domain, disk, controller, hostname, &response) < 0)
|
|
return -1;
|
|
|
|
driveInstanceID = hypervGetInstanceIDFromXMLResponse(response);
|
|
if (!driveInstanceID)
|
|
return -1;
|
|
|
|
if (hypervDomainAddOpticalDisk(domain, disk, hostname, driveInstanceID) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachFloppy(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *driveSettings,
|
|
const char *hostname)
|
|
{
|
|
g_autoptr(GHashTable) volumeResource = NULL;
|
|
g_autofree char *vhdInstanceIdEscaped = NULL;
|
|
g_autofree char *vfd__PATH = NULL;
|
|
g_autofree char *resourceType = NULL;
|
|
|
|
resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_LOGICAL_DISK);
|
|
|
|
volumeResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
|
|
if (!volumeResource)
|
|
return -1;
|
|
|
|
vhdInstanceIdEscaped = virStringReplace(driveSettings->data->InstanceID, "\\", "\\\\");
|
|
vfd__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
|
|
hostname, vhdInstanceIdEscaped);
|
|
|
|
if (!vfd__PATH)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "Parent", vfd__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "HostResource", disk->src->path) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(volumeResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Virtual Floppy Disk") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &volumeResource,
|
|
Msvm_ResourceAllocationSettingData_WmiInfo,
|
|
NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachStorageVolume(virDomainPtr domain,
|
|
virDomainDiskDef *disk,
|
|
Msvm_ResourceAllocationSettingData *controller,
|
|
const char *hostname)
|
|
{
|
|
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported disk address type"));
|
|
return -1;
|
|
}
|
|
|
|
switch (disk->device) {
|
|
case VIR_DOMAIN_DISK_DEVICE_DISK:
|
|
if (disk->src->type == VIR_STORAGE_TYPE_FILE)
|
|
return hypervDomainAttachVirtualDisk(domain, disk, controller, hostname);
|
|
else if (disk->src->type == VIR_STORAGE_TYPE_BLOCK)
|
|
return hypervDomainAttachPhysicalDisk(domain, disk, controller, hostname);
|
|
else
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported disk type"));
|
|
break;
|
|
case VIR_DOMAIN_DISK_DEVICE_CDROM:
|
|
return hypervDomainAttachCDROM(domain, disk, controller, hostname);
|
|
case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
|
|
return hypervDomainAttachFloppy(domain, disk, controller, hostname);
|
|
case VIR_DOMAIN_DISK_DEVICE_LUN:
|
|
case VIR_DOMAIN_DISK_DEVICE_LAST:
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported disk bus"));
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachStorage(virDomainPtr domain, virDomainDef *def, const char *hostname)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
size_t i = 0;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
int num_scsi_controllers = 0;
|
|
int ctrlr_idx = -1;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
|
|
Msvm_ResourceAllocationSettingData *entry = NULL;
|
|
Msvm_ResourceAllocationSettingData *ideChannels[HYPERV_MAX_IDE_CHANNELS];
|
|
Msvm_ResourceAllocationSettingData *scsiControllers[HYPERV_MAX_SCSI_CONTROLLERS];
|
|
Msvm_ResourceAllocationSettingData *floppySettings = NULL;
|
|
|
|
/* start with attaching scsi controllers */
|
|
for (i = 0; i < def->ncontrollers; i++) {
|
|
if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
|
|
continue;
|
|
|
|
if (hypervDomainCreateSCSIController(domain, def->controllers[i]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* filter through all the rasd entries and isolate our controllers */
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0)
|
|
return -1;
|
|
|
|
entry = rasd;
|
|
while (entry) {
|
|
if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER)
|
|
ideChannels[entry->data->Address[0] - '0'] = entry;
|
|
else if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA)
|
|
scsiControllers[num_scsi_controllers++] = entry;
|
|
else if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE)
|
|
floppySettings = entry;
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
/* now we loop through and attach all the disks */
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
switch (def->disks[i]->bus) {
|
|
case VIR_DOMAIN_DISK_BUS_IDE:
|
|
ctrlr_idx = def->disks[i]->info.addr.drive.bus;
|
|
if (hypervDomainAttachStorageVolume(domain, def->disks[i],
|
|
ideChannels[ctrlr_idx], hostname) < 0) {
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_SCSI:
|
|
ctrlr_idx = def->disks[i]->info.addr.drive.controller;
|
|
if (hypervDomainAttachStorageVolume(domain, def->disks[i],
|
|
scsiControllers[ctrlr_idx], hostname) < 0) {
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_FDC:
|
|
if (hypervDomainAttachFloppy(domain, def->disks[i], floppySettings, hostname) < 0)
|
|
return -1;
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_NONE:
|
|
case VIR_DOMAIN_DISK_BUS_VIRTIO:
|
|
case VIR_DOMAIN_DISK_BUS_XEN:
|
|
case VIR_DOMAIN_DISK_BUS_USB:
|
|
case VIR_DOMAIN_DISK_BUS_UML:
|
|
case VIR_DOMAIN_DISK_BUS_SATA:
|
|
case VIR_DOMAIN_DISK_BUS_SD:
|
|
case VIR_DOMAIN_DISK_BUS_LAST:
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported controller type"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachSerial(virDomainPtr domain, virDomainChrDef *serial)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autofree char *com_string = g_strdup_printf("COM %d", serial->target.port);
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
|
|
g_autoptr(Msvm_SerialPortSettingData) spsd = NULL;
|
|
hypervWmiClassInfo *classInfo = NULL;
|
|
Msvm_ResourceAllocationSettingData *current = NULL;
|
|
Msvm_ResourceAllocationSettingData *entry = NULL;
|
|
g_autoptr(GHashTable) serialResource = NULL;
|
|
const char *connectionValue = NULL;
|
|
g_autofree const char *resourceType = NULL;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (g_str_has_prefix(priv->version, "6.")) {
|
|
if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0)
|
|
return -1;
|
|
|
|
current = rasd;
|
|
classInfo = Msvm_ResourceAllocationSettingData_WmiInfo;
|
|
} else {
|
|
if (hypervGetSerialPortSD(priv, vssd->data->InstanceID, &spsd) < 0)
|
|
return -1;
|
|
|
|
current = (Msvm_ResourceAllocationSettingData *)spsd;
|
|
classInfo = Msvm_SerialPortSettingData_WmiInfo;
|
|
}
|
|
|
|
while (current) {
|
|
if (current->data->ResourceType == MSVM_RASD_RESOURCETYPE_SERIAL_PORT &&
|
|
STREQ(current->data->ElementName, com_string)) {
|
|
/* found our com port */
|
|
entry = current;
|
|
break;
|
|
}
|
|
current = current->next;
|
|
}
|
|
|
|
if (!entry)
|
|
return -1;
|
|
|
|
if (STRNEQ(serial->source->data.file.path, "-1"))
|
|
connectionValue = serial->source->data.file.path;
|
|
else
|
|
connectionValue = "";
|
|
|
|
resourceType = g_strdup_printf("%d", entry->data->ResourceType);
|
|
|
|
serialResource = hypervCreateEmbeddedParam(classInfo);
|
|
if (!serialResource)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(serialResource, "Connection", connectionValue) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(serialResource, "InstanceID", entry->data->InstanceID) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(serialResource, "ResourceType", resourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(serialResource, "ResourceSubType",
|
|
entry->data->ResourceSubType) < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSModifyResourceSettings(priv, &serialResource, classInfo) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachSyntheticEthernetAdapter(virDomainPtr domain,
|
|
virDomainNetDef *net,
|
|
char *hostname)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autofree char *portResourceType = NULL;
|
|
unsigned char vsiGuid[VIR_UUID_BUFLEN];
|
|
char guidString[VIR_UUID_STRING_BUFLEN];
|
|
g_autofree char *virtualSystemIdentifiers = NULL;
|
|
char macString[VIR_MAC_STRING_BUFLEN];
|
|
g_autofree char *macAddressNoColons = NULL;
|
|
g_autoptr(GHashTable) portResource = NULL;
|
|
g_auto(WsXmlDocH) sepsdResponse = NULL;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_VirtualEthernetSwitch) vSwitch = NULL;
|
|
g_autofree char *enabledState = NULL;
|
|
g_autofree char *switch__PATH = NULL;
|
|
g_autofree char *sepsd__PATH = NULL;
|
|
g_autofree char *sepsdInstanceID = NULL;
|
|
g_autofree char *sepsdInstanceEscaped = NULL;
|
|
g_autofree char *connectionResourceType = NULL;
|
|
g_autoptr(GHashTable) connectionResource = NULL;
|
|
|
|
/*
|
|
* Step 1: Create the Msvm_SyntheticEthernetPortSettingData object
|
|
* that holds half the settings for the new adapter we are creating
|
|
*/
|
|
portResourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_ETHERNET_ADAPTER);
|
|
|
|
if (virUUIDGenerate(vsiGuid) < 0)
|
|
return -1;
|
|
|
|
virUUIDFormat(vsiGuid, guidString);
|
|
virtualSystemIdentifiers = g_strdup_printf("{%s}", guidString);
|
|
|
|
virMacAddrFormat(&net->mac, macString);
|
|
macAddressNoColons = virStringReplace(macString, ":", "");
|
|
|
|
/* prepare embedded param */
|
|
portResource = hypervCreateEmbeddedParam(Msvm_SyntheticEthernetPortSettingData_WmiInfo);
|
|
if (!portResource)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(portResource, "ResourceType", portResourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(portResource, "ResourceSubType",
|
|
"Microsoft:Hyper-V:Synthetic Ethernet Port") < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(portResource,
|
|
"VirtualSystemIdentifiers", virtualSystemIdentifiers) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(portResource, "Address", macAddressNoColons) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(portResource, "StaticMacAddress", "true") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &portResource,
|
|
Msvm_SyntheticEthernetPortSettingData_WmiInfo,
|
|
&sepsdResponse) < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* Step 2: Get the Msvm_VirtualEthernetSwitch object
|
|
*/
|
|
virBufferAsprintf(&query,
|
|
MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE Name='%s'",
|
|
net->data.bridge.brname);
|
|
|
|
if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &vSwitch) < 0)
|
|
return -1;
|
|
|
|
if (!vSwitch)
|
|
return -1;
|
|
|
|
/*
|
|
* Step 3: Create the Msvm_EthernetPortAllocationSettingData object that
|
|
* holds the other half of the network configuration
|
|
*/
|
|
enabledState = g_strdup_printf("%d", MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_ENABLED);
|
|
|
|
/* build the two __PATH variables */
|
|
switch__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
|
|
"Msvm_VirtualEthernetSwitch.CreationClassName=\"Msvm_VirtualEthernetSwitch\","
|
|
"Name=\"%s\"",
|
|
hostname, vSwitch->data->Name);
|
|
|
|
/* Get the sepsd instance ID out of the XML response */
|
|
sepsdInstanceID = hypervGetInstanceIDFromXMLResponse(sepsdResponse);
|
|
sepsdInstanceEscaped = virStringReplace(sepsdInstanceID, "\\", "\\\\");
|
|
sepsd__PATH = g_strdup_printf("\\\\%s\\root\\virtualization\\v2:"
|
|
"Msvm_SyntheticEthernetPortSettingData.InstanceID=\"%s\"",
|
|
hostname, sepsdInstanceEscaped);
|
|
|
|
connectionResourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_ETHERNET_CONNECTION);
|
|
|
|
connectionResource = hypervCreateEmbeddedParam(Msvm_EthernetPortAllocationSettingData_WmiInfo);
|
|
if (!connectionResource)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(connectionResource, "EnabledState", enabledState) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(connectionResource, "HostResource", switch__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(connectionResource, "Parent", sepsd__PATH) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(connectionResource, "ResourceType", connectionResourceType) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(connectionResource,
|
|
"ResourceSubType", "Microsoft:Hyper-V:Ethernet Connection") < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSAddResourceSettings(domain, &connectionResource,
|
|
Msvm_EthernetPortAllocationSettingData_WmiInfo, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Functions for deserializing device entries
|
|
*/
|
|
static int
|
|
hypervDomainDefAppendController(virDomainDef *def,
|
|
int idx,
|
|
virDomainControllerType controllerType)
|
|
{
|
|
virDomainControllerDef *controller = NULL;
|
|
|
|
if (!(controller = virDomainControllerDefNew(controllerType)))
|
|
return -1;
|
|
|
|
controller->idx = idx;
|
|
|
|
if (VIR_APPEND_ELEMENT(def->controllers, def->ncontrollers, controller) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefAppendIDEController(virDomainDef *def)
|
|
{
|
|
return hypervDomainDefAppendController(def, 0, VIR_DOMAIN_CONTROLLER_TYPE_IDE);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefAppendSCSIController(virDomainDef *def, int idx)
|
|
{
|
|
return hypervDomainDefAppendController(def, idx, VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefAppendDisk(virDomainDef *def,
|
|
virDomainDiskDef *disk,
|
|
virDomainDiskBus busType,
|
|
int diskNameOffset,
|
|
const char *diskNamePrefix,
|
|
int maxControllers,
|
|
Msvm_ResourceAllocationSettingData **controllers,
|
|
Msvm_ResourceAllocationSettingData *diskParent,
|
|
Msvm_ResourceAllocationSettingData *diskController)
|
|
{
|
|
size_t i = 0;
|
|
int ctrlr_idx = -1;
|
|
int addr = -1;
|
|
|
|
if (virStrToLong_i(diskParent->data->AddressOnParent, NULL, 10, &addr) < 0)
|
|
return -1;
|
|
|
|
if (addr < 0)
|
|
return -1;
|
|
|
|
/* Find controller index */
|
|
for (i = 0; i < maxControllers; i++) {
|
|
if (diskController == controllers[i]) {
|
|
ctrlr_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ctrlr_idx < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find controller for disk!"));
|
|
return -1;
|
|
}
|
|
|
|
disk->bus = busType;
|
|
disk->dst = virIndexToDiskName(ctrlr_idx * diskNameOffset + addr, diskNamePrefix);
|
|
if (busType == VIR_DOMAIN_DISK_BUS_IDE) {
|
|
disk->info.addr.drive.controller = 0;
|
|
disk->info.addr.drive.bus = ctrlr_idx;
|
|
} else {
|
|
disk->info.addr.drive.controller = ctrlr_idx;
|
|
disk->info.addr.drive.bus = 0;
|
|
}
|
|
disk->info.addr.drive.target = 0;
|
|
disk->info.addr.drive.unit = addr;
|
|
|
|
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefParseFloppyStorageExtent(virDomainDef *def, virDomainDiskDef *disk)
|
|
{
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
|
|
disk->dst = g_strdup("fda");
|
|
|
|
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefParseVirtualExtent(hypervPrivate *priv,
|
|
virDomainDef *def,
|
|
Msvm_StorageAllocationSettingData *disk_entry,
|
|
Msvm_ResourceAllocationSettingData *rasd,
|
|
Msvm_ResourceAllocationSettingData **ideChannels,
|
|
Msvm_ResourceAllocationSettingData **scsiControllers)
|
|
{
|
|
Msvm_ResourceAllocationSettingData *diskParent = NULL;
|
|
Msvm_ResourceAllocationSettingData *controller = NULL;
|
|
virDomainDiskDef *disk = NULL;
|
|
int result = -1;
|
|
|
|
if (disk_entry->data->HostResource.count < 1)
|
|
goto cleanup;
|
|
|
|
if (!(disk = virDomainDiskDefNew(priv->xmlopt))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not allocate disk definition"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* get disk associated with storage extent */
|
|
if (hypervGetDeviceParentRasdFromDeviceId(disk_entry->data->Parent, rasd, &diskParent) < 0)
|
|
goto cleanup;
|
|
|
|
/* get associated controller */
|
|
if (hypervGetDeviceParentRasdFromDeviceId(diskParent->data->Parent, rasd, &controller) < 0)
|
|
goto cleanup;
|
|
|
|
/* common fields first */
|
|
disk->src->type = VIR_STORAGE_TYPE_FILE;
|
|
disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
|
|
|
|
/* note if it's a CDROM disk */
|
|
if (STREQ(disk_entry->data->ResourceSubType, "Microsoft:Hyper-V:Virtual CD/DVD Disk"))
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
|
|
else
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
|
|
|
|
/* copy in the source path */
|
|
virDomainDiskSetSource(disk, *(char **)disk_entry->data->HostResource.data);
|
|
|
|
/* controller-specific fields */
|
|
if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA) {
|
|
if (hypervDomainDefAppendDisk(def, disk, VIR_DOMAIN_DISK_BUS_SCSI,
|
|
64, "sd", HYPERV_MAX_SCSI_CONTROLLERS,
|
|
scsiControllers, diskParent, controller) < 0) {
|
|
goto cleanup;
|
|
}
|
|
} else if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER) {
|
|
if (hypervDomainDefAppendDisk(def, disk, VIR_DOMAIN_DISK_BUS_IDE,
|
|
2, "hd", HYPERV_MAX_IDE_CHANNELS,
|
|
ideChannels, diskParent, controller) < 0) {
|
|
goto cleanup;
|
|
}
|
|
} else if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_OTHER &&
|
|
diskParent->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE) {
|
|
if (hypervDomainDefParseFloppyStorageExtent(def, disk) < 0)
|
|
goto cleanup;
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unrecognized controller type %d"),
|
|
controller->data->ResourceType);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (result != 0 && disk)
|
|
virDomainDiskDefFree(disk);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefParsePhysicalDisk(hypervPrivate *priv,
|
|
virDomainDef *def,
|
|
Msvm_ResourceAllocationSettingData *entry,
|
|
Msvm_ResourceAllocationSettingData *rasd,
|
|
Msvm_ResourceAllocationSettingData **ideChannels,
|
|
Msvm_ResourceAllocationSettingData **scsiControllers)
|
|
{
|
|
int result = -1;
|
|
Msvm_ResourceAllocationSettingData *controller = NULL;
|
|
g_autoptr(Msvm_DiskDrive) diskdrive = NULL;
|
|
virDomainDiskDef *disk = NULL;
|
|
char **hostResource = entry->data->HostResource.data;
|
|
g_autofree char *hostEscaped = NULL;
|
|
g_autofree char *driveNumberStr = NULL;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
int addr = -1, ctrlr_idx = -1;
|
|
size_t i = 0;
|
|
|
|
if (virStrToLong_i(entry->data->AddressOnParent, NULL, 10, &addr) < 0)
|
|
return -1;
|
|
|
|
if (addr < 0)
|
|
return -1;
|
|
|
|
if (hypervGetDeviceParentRasdFromDeviceId(entry->data->Parent, rasd, &controller) < 0)
|
|
goto cleanup;
|
|
|
|
/* create disk definition */
|
|
if (!(disk = virDomainDiskDefNew(priv->xmlopt))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not allocate disk def"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Query Msvm_DiskDrive for the DriveNumber */
|
|
hostEscaped = virStringReplace(*hostResource, "\\\"", "\"");
|
|
hostEscaped = virStringReplace(hostEscaped, "\\", "\\\\");
|
|
|
|
/* quotes must be preserved, so virBufferEscapeSQL can't be used */
|
|
virBufferAsprintf(&query,
|
|
MSVM_DISKDRIVE_WQL_SELECT "WHERE __PATH='%s'",
|
|
hostEscaped);
|
|
|
|
if (hypervGetWmiClass(Msvm_DiskDrive, &diskdrive) < 0)
|
|
goto cleanup;
|
|
|
|
if (!diskdrive) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find Msvm_DiskDrive object"));
|
|
goto cleanup;
|
|
}
|
|
|
|
driveNumberStr = g_strdup_printf("%u", diskdrive->data->DriveNumber);
|
|
virDomainDiskSetSource(disk, driveNumberStr);
|
|
|
|
if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA) {
|
|
for (i = 0; i < HYPERV_MAX_SCSI_CONTROLLERS; i++) {
|
|
if (controller == scsiControllers[i]) {
|
|
ctrlr_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
|
|
disk->dst = virIndexToDiskName(ctrlr_idx * 64 + addr, "sd");
|
|
disk->info.addr.drive.unit = addr;
|
|
disk->info.addr.drive.controller = ctrlr_idx;
|
|
disk->info.addr.drive.bus = 0;
|
|
} else if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER) {
|
|
for (i = 0; i < HYPERV_MAX_IDE_CHANNELS; i++) {
|
|
if (controller == ideChannels[i]) {
|
|
ctrlr_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
|
|
disk->dst = virIndexToDiskName(ctrlr_idx * 4 + addr, "hd");
|
|
disk->info.addr.drive.unit = addr;
|
|
disk->info.addr.drive.controller = 0;
|
|
disk->info.addr.drive.bus = ctrlr_idx;
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid controller type for LUN"));
|
|
goto cleanup;
|
|
}
|
|
|
|
disk->info.addr.drive.target = 0;
|
|
virDomainDiskSetType(disk, VIR_STORAGE_TYPE_BLOCK);
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
|
|
|
|
disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
|
|
|
|
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
|
|
goto cleanup;
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (result != 0 && disk)
|
|
virDomainDiskDefFree(disk);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefParseStorage(hypervPrivate *priv,
|
|
virDomainDef *def,
|
|
Msvm_ResourceAllocationSettingData *rasd,
|
|
Msvm_StorageAllocationSettingData *sasd)
|
|
{
|
|
Msvm_ResourceAllocationSettingData *entry = rasd;
|
|
Msvm_StorageAllocationSettingData *disk_entry = sasd;
|
|
Msvm_ResourceAllocationSettingData *ideChannels[HYPERV_MAX_IDE_CHANNELS];
|
|
Msvm_ResourceAllocationSettingData *scsiControllers[HYPERV_MAX_SCSI_CONTROLLERS];
|
|
bool hasIdeController = false;
|
|
int channel = -1;
|
|
int scsi_idx = 0;
|
|
|
|
/* first pass: populate storage controllers */
|
|
while (entry) {
|
|
if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER) {
|
|
channel = entry->data->Address[0] - '0';
|
|
ideChannels[channel] = entry;
|
|
if (!hasIdeController) {
|
|
/* Hyper-V represents its PIIX4 controller's two channels as separate objects. */
|
|
if (hypervDomainDefAppendIDEController(def) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add IDE controller"));
|
|
return -1;
|
|
}
|
|
hasIdeController = true;
|
|
}
|
|
} else if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA) {
|
|
scsiControllers[scsi_idx++] = entry;
|
|
if (hypervDomainDefAppendSCSIController(def, scsi_idx - 1) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not parse SCSI controller"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
/* second pass: populate physical disks */
|
|
entry = rasd;
|
|
while (entry) {
|
|
if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISK_DRIVE &&
|
|
entry->data->HostResource.count > 0) {
|
|
char **hostResource = entry->data->HostResource.data;
|
|
|
|
if (strstr(*hostResource, "NODRIVE")) {
|
|
/* Hyper-V doesn't let you define LUNs with no connection */
|
|
VIR_DEBUG("Skipping empty LUN '%s'", *hostResource);
|
|
entry = entry->next;
|
|
continue;
|
|
}
|
|
|
|
if (hypervDomainDefParsePhysicalDisk(priv, def, entry, rasd,
|
|
ideChannels, scsiControllers) < 0)
|
|
return -1;
|
|
}
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
/* third pass: populate virtual disks */
|
|
while (disk_entry) {
|
|
if (hypervDomainDefParseVirtualExtent(priv, def, disk_entry, rasd,
|
|
ideChannels, scsiControllers) < 0)
|
|
return -1;
|
|
|
|
disk_entry = disk_entry->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefParseSerial(virDomainDef *def, Msvm_ResourceAllocationSettingData *rasd)
|
|
{
|
|
for (; rasd; rasd = rasd->next) {
|
|
int port_num = 0;
|
|
char **conn = NULL;
|
|
const char *srcPath = NULL;
|
|
virDomainChrDef *serial = NULL;
|
|
|
|
if (rasd->data->ResourceType != MSVM_RASD_RESOURCETYPE_SERIAL_PORT)
|
|
continue;
|
|
|
|
/* get port number */
|
|
port_num = rasd->data->ElementName[4] - '0';
|
|
if (port_num < 1) {
|
|
continue;
|
|
}
|
|
|
|
serial = virDomainChrDefNew(NULL);
|
|
|
|
serial->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
|
|
serial->source->type = VIR_DOMAIN_CHR_TYPE_PIPE;
|
|
serial->target.port = port_num;
|
|
|
|
/* set up source */
|
|
if (rasd->data->Connection.count < 1) {
|
|
srcPath = "-1";
|
|
} else {
|
|
conn = rasd->data->Connection.data;
|
|
if (!*conn)
|
|
srcPath = "-1";
|
|
else
|
|
srcPath = *conn;
|
|
}
|
|
|
|
serial->source->data.file.path = g_strdup(srcPath);
|
|
|
|
if (VIR_APPEND_ELEMENT(def->serials, def->nserials, serial) < 0) {
|
|
virDomainChrDefFree(serial);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDefParseEthernetAdapter(virDomainDef *def,
|
|
Msvm_EthernetPortAllocationSettingData *net,
|
|
hypervPrivate *priv)
|
|
{
|
|
g_autoptr(virDomainNetDef) ndef = g_new0(virDomainNetDef, 1);
|
|
g_autoptr(Msvm_SyntheticEthernetPortSettingData) sepsd = NULL;
|
|
g_autoptr(Msvm_VirtualEthernetSwitch) vSwitch = NULL;
|
|
char **switchConnection = NULL;
|
|
g_autofree char *switchConnectionEscaped = NULL;
|
|
char *sepsdPATH = NULL;
|
|
g_autofree char *sepsdEscaped = NULL;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
|
|
VIR_DEBUG("Parsing ethernet adapter '%s'", net->data->InstanceID);
|
|
|
|
ndef->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
|
|
|
|
/*
|
|
* If there's no switch port connection or the EnabledState is disabled,
|
|
* then the adapter isn't hooked up to anything and we don't have to
|
|
* do anything more.
|
|
*/
|
|
switchConnection = net->data->HostResource.data;
|
|
if (net->data->HostResource.count < 1 || !*switchConnection ||
|
|
net->data->EnabledState == MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_DISABLED) {
|
|
VIR_DEBUG("Adapter not connected to switch");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Now we retrieve the associated Msvm_SyntheticEthernetPortSettingData and
|
|
* Msvm_VirtualEthernetSwitch objects and use them to build the XML definition.
|
|
*/
|
|
|
|
/* begin by getting the Msvm_SyntheticEthernetPortSettingData object */
|
|
sepsdPATH = net->data->Parent;
|
|
sepsdEscaped = virStringReplace(sepsdPATH, "\\", "\\\\");
|
|
virBufferAsprintf(&query,
|
|
MSVM_SYNTHETICETHERNETPORTSETTINGDATA_WQL_SELECT "WHERE __PATH = '%s'",
|
|
sepsdEscaped);
|
|
|
|
if (hypervGetWmiClass(Msvm_SyntheticEthernetPortSettingData, &sepsd) < 0)
|
|
return -1;
|
|
|
|
if (!sepsd) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve NIC settings"));
|
|
return -1;
|
|
}
|
|
|
|
/* set mac address */
|
|
if (virMacAddrParseHex(sepsd->data->Address, &ndef->mac) < 0)
|
|
return -1;
|
|
|
|
/* now we get the Msvm_VirtualEthernetSwitch */
|
|
virBufferFreeAndReset(&query);
|
|
switchConnectionEscaped = virStringReplace(*switchConnection, "\\", "\\\\");
|
|
virBufferAsprintf(&query,
|
|
MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE __PATH = '%s'",
|
|
switchConnectionEscaped);
|
|
|
|
if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &vSwitch) < 0)
|
|
return -1;
|
|
|
|
if (!vSwitch) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve virtual switch"));
|
|
return -1;
|
|
}
|
|
|
|
/* get bridge name */
|
|
ndef->data.bridge.brname = g_strdup(vSwitch->data->Name);
|
|
|
|
if (VIR_APPEND_ELEMENT(def->nets, def->nnets, ndef) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not append definition to domain"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hypervDomainDefParseEthernet(virDomainPtr domain,
|
|
virDomainDef *def,
|
|
Msvm_EthernetPortAllocationSettingData *nets)
|
|
{
|
|
Msvm_EthernetPortAllocationSettingData *entry = nets;
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
|
|
while (entry) {
|
|
if (hypervDomainDefParseEthernetAdapter(def, entry, priv) < 0)
|
|
return -1;
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Driver functions
|
|
*/
|
|
|
|
static void
|
|
hypervFreePrivate(hypervPrivate **priv)
|
|
{
|
|
if (priv == NULL || *priv == NULL)
|
|
return;
|
|
|
|
if ((*priv)->client != NULL)
|
|
wsmc_release((*priv)->client);
|
|
|
|
if ((*priv)->caps)
|
|
virObjectUnref((*priv)->caps);
|
|
|
|
if ((*priv)->xmlopt)
|
|
virObjectUnref((*priv)->xmlopt);
|
|
|
|
if ((*priv)->version)
|
|
g_free((*priv)->version);
|
|
|
|
hypervFreeParsedUri(&(*priv)->parsedUri);
|
|
VIR_FREE(*priv);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervInitConnection(virConnectPtr conn, hypervPrivate *priv,
|
|
char *username, char *password)
|
|
{
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
if (wsmc_transport_init(priv->client, NULL) != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not initialize openwsman transport"));
|
|
return -1;
|
|
}
|
|
|
|
/* FIXME: Currently only basic authentication is supported */
|
|
wsman_transport_set_auth_method(priv->client, "basic");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
virDomainDefParserConfig hypervDomainDefParserConfig;
|
|
|
|
static virDrvOpenStatus
|
|
hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
|
|
virConf *conf G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
virDrvOpenStatus result = VIR_DRV_OPEN_ERROR;
|
|
hypervPrivate *priv = NULL;
|
|
g_autofree char *username = NULL;
|
|
g_autofree char *password = NULL;
|
|
g_autoptr(Win32_OperatingSystem) os = NULL;
|
|
|
|
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
|
|
|
|
/* Allocate per-connection private data */
|
|
priv = g_new0(hypervPrivate, 1);
|
|
|
|
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 = g_strdup(conn->uri->user);
|
|
} else {
|
|
if (!(username = virAuthGetUsername(conn, auth, "hyperv",
|
|
"administrator",
|
|
conn->uri->server)))
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(password = virAuthGetPassword(conn, auth, "hyperv", username,
|
|
conn->uri->server)))
|
|
goto cleanup;
|
|
|
|
if (hypervInitConnection(conn, priv, username, password) < 0)
|
|
goto cleanup;
|
|
|
|
/* set up capabilities */
|
|
priv->caps = hypervCapsInit(priv);
|
|
if (!priv->caps)
|
|
goto cleanup;
|
|
|
|
/* init xmlopt for domain XML */
|
|
priv->xmlopt = virDomainXMLOptionNew(&hypervDomainDefParserConfig, NULL, NULL, NULL, NULL);
|
|
|
|
if (hypervGetOperatingSystem(priv, &os) < 0)
|
|
goto cleanup;
|
|
|
|
if (!os) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get version information for host %s"),
|
|
conn->uri->server);
|
|
goto cleanup;
|
|
}
|
|
|
|
priv->version = g_strdup(os->data->Version);
|
|
|
|
conn->privateData = priv;
|
|
priv = NULL;
|
|
result = VIR_DRV_OPEN_SUCCESS;
|
|
|
|
cleanup:
|
|
hypervFreePrivate(&priv);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectClose(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
|
|
hypervFreePrivate(&priv);
|
|
|
|
conn->privateData = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const char *
|
|
hypervConnectGetType(virConnectPtr conn G_GNUC_UNUSED)
|
|
{
|
|
return "Hyper-V";
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectGetVersion(virConnectPtr conn, unsigned long *version)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
unsigned int major, minor, micro;
|
|
|
|
if (hypervParseVersionString(priv->version, &major, &minor, µ) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse version from '%s'"),
|
|
priv->version);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Pack the version into an unsigned long while retaining all the digits.
|
|
*
|
|
* Since Microsoft's build numbers are almost always over 1000, this driver
|
|
* needs to pack the value differently compared to the format defined by
|
|
* virConnectGetVersion().
|
|
*
|
|
* This results in `virsh version` producing unexpected output.
|
|
*
|
|
* For example...
|
|
* 2008: 6.0.6001 => 600.6.1
|
|
* 2008 R2: 6.1.7600 => 601.7.600
|
|
* 2012: 6.2.9200 => 602.9.200
|
|
* 2012 R2: 6.3.9600 => 603.9.600
|
|
* 2016: 10.0.14393 => 1000.14.393
|
|
* 2019: 10.0.17763 => 1000.17.763
|
|
*/
|
|
if (major > 99 || minor > 99 || micro > 999999) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not produce packed version number from '%s'"),
|
|
priv->version);
|
|
return -1;
|
|
}
|
|
|
|
*version = major * 100000000 + minor * 1000000 + micro;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *
|
|
hypervConnectGetHostname(virConnectPtr conn)
|
|
{
|
|
g_autoptr(Win32_ComputerSystem) computerSystem = NULL;
|
|
|
|
if (hypervGetPhysicalSystemList((hypervPrivate *)conn->privateData, &computerSystem) < 0)
|
|
return NULL;
|
|
|
|
return g_strdup(computerSystem->data->DNSHostName);
|
|
}
|
|
|
|
|
|
static char*
|
|
hypervConnectGetCapabilities(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
|
|
return virCapabilitiesFormatXML(priv->caps);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectGetMaxVcpus(virConnectPtr conn, const char *type G_GNUC_UNUSED)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_ProcessorSettingData) processorSettingData = NULL;
|
|
|
|
/* Get max processors definition */
|
|
virBufferAddLit(&query,
|
|
MSVM_PROCESSORSETTINGDATA_WQL_SELECT
|
|
"WHERE InstanceID LIKE 'Microsoft:Definition%Maximum'");
|
|
|
|
if (hypervGetWmiClass(Msvm_ProcessorSettingData, &processorSettingData) < 0)
|
|
return -1;
|
|
|
|
if (!processorSettingData) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get maximum definition of Msvm_ProcessorSettingData for host %s"),
|
|
conn->uri->server);
|
|
return -1;
|
|
}
|
|
|
|
return processorSettingData->data->VirtualQuantity;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Win32_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Win32_Processor) processorList = NULL;
|
|
Win32_Processor *processor = NULL;
|
|
char *tmp;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
if (hypervGetPhysicalSystemList(priv, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetProcessorsByName(priv, computerSystem->data->Name, &processorList) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* 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;
|
|
} else if (STRPREFIX(tmp, " @ ")) {
|
|
/* Remove " @ X.YZGHz" from the end. */
|
|
*tmp = '\0';
|
|
break;
|
|
}
|
|
|
|
++tmp;
|
|
}
|
|
|
|
/* Fill struct */
|
|
if (virStrcpyStatic(info->model, processorList->data->Name) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("CPU model %s too long for destination"),
|
|
processorList->data->Name);
|
|
return -1;
|
|
}
|
|
|
|
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 = processorList->data->NumberOfLogicalProcessors / info->cores;
|
|
info->cpus = info->sockets * info->cores;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectListDomains(virConnectPtr conn, int *ids, int maxids)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
|
|
Msvm_ComputerSystem *computerSystem = NULL;
|
|
int count = 0;
|
|
|
|
if (maxids == 0)
|
|
return 0;
|
|
|
|
if (hypervGetActiveVirtualSystemList(priv, &computerSystemList) < 0)
|
|
return -1;
|
|
|
|
for (computerSystem = computerSystemList; computerSystem != NULL;
|
|
computerSystem = computerSystem->next) {
|
|
ids[count++] = computerSystem->data->ProcessID;
|
|
|
|
if (count >= maxids)
|
|
break;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectNumOfDomains(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
|
|
Msvm_ComputerSystem *computerSystem = NULL;
|
|
int count = 0;
|
|
|
|
if (hypervGetActiveVirtualSystemList(priv, &computerSystemList) < 0)
|
|
return -1;
|
|
|
|
for (computerSystem = computerSystemList; computerSystem != NULL;
|
|
computerSystem = computerSystem->next) {
|
|
++count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static virDomainPtr
|
|
hypervDomainLookupByID(virConnectPtr conn, int id)
|
|
{
|
|
virDomainPtr domain = NULL;
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
if (hypervGetVirtualSystemByID(priv, id, &computerSystem) < 0)
|
|
return NULL;
|
|
|
|
hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
|
|
|
|
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];
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
virUUIDFormat(uuid, uuid_string);
|
|
|
|
if (hypervMsvmComputerSystemFromUUID(priv, uuid_string, &computerSystem) < 0)
|
|
return NULL;
|
|
|
|
hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
|
|
|
|
return domain;
|
|
}
|
|
|
|
|
|
static virDomainPtr
|
|
hypervDomainLookupByName(virConnectPtr conn, const char *name)
|
|
{
|
|
virDomainPtr domain = NULL;
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
if (hypervGetVirtualSystemByName(priv, name, &computerSystem) < 0)
|
|
return NULL;
|
|
|
|
if (computerSystem->next) {
|
|
virReportError(VIR_ERR_MULTIPLE_DOMAINS,
|
|
_("Multiple domains exist with the name '%s': repeat the request using a UUID"),
|
|
name);
|
|
return NULL;
|
|
}
|
|
|
|
hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
|
|
|
|
return domain;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSuspend(virDomainPtr domain)
|
|
{
|
|
return hypervRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_QUIESCE);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainResume(virDomainPtr domain)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_QUIESCE) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not paused"));
|
|
return -1;
|
|
}
|
|
|
|
return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
|
|
MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_ENABLED);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainShutdownFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Msvm_ShutdownComponent) shutdown = NULL;
|
|
bool in_transition = false;
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
g_autofree char *selector = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virUUIDFormat(domain->uuid, uuid);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
|
|
in_transition) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not active or in state transition"));
|
|
return -1;
|
|
}
|
|
|
|
virBufferEscapeSQL(&query, MSVM_SHUTDOWNCOMPONENT_WQL_SELECT "WHERE SystemName = '%s'", uuid);
|
|
|
|
if (hypervGetWmiClass(Msvm_ShutdownComponent, &shutdown) < 0 ||
|
|
!shutdown) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Could not get Msvm_ShutdownComponent for domain with UUID '%s'"),
|
|
uuid);
|
|
return -1;
|
|
}
|
|
|
|
selector = g_strdup_printf("CreationClassName=\"Msvm_ShutdownComponent\"&DeviceID=\"%s\"&"
|
|
"SystemCreationClassName=\"Msvm_ComputerSystem\"&SystemName=\"%s\"",
|
|
shutdown->data->DeviceID, uuid);
|
|
|
|
params = hypervCreateInvokeParamsList("InitiateShutdown", selector,
|
|
Msvm_ShutdownComponent_WmiInfo);
|
|
if (!params)
|
|
return -1;
|
|
|
|
hypervAddSimpleParam(params, "Force", "False");
|
|
|
|
/* "Reason" is not translated because the Hyper-V administrator may not
|
|
* know the libvirt user's language. They may not know English, either,
|
|
* but this makes it consistent, at least. */
|
|
hypervAddSimpleParam(params, "Reason", "Planned shutdown via libvirt");
|
|
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainShutdown(virDomainPtr domain)
|
|
{
|
|
return hypervDomainShutdownFlags(domain, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainReboot(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
virCheckFlags(0, -1);
|
|
return hypervRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_REBOOT);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainReset(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
virCheckFlags(0, -1);
|
|
return hypervRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_RESET);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDestroyFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
bool in_transition = false;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
|
|
in_transition) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not active or is in state transition"));
|
|
return -1;
|
|
}
|
|
|
|
return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
|
|
MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_DISABLED);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainDestroy(virDomainPtr domain)
|
|
{
|
|
return hypervDomainDestroyFlags(domain, 0);
|
|
}
|
|
|
|
|
|
static char *
|
|
hypervDomainGetOSType(virDomainPtr domain G_GNUC_UNUSED)
|
|
{
|
|
char *osType;
|
|
|
|
osType = g_strdup("hvm");
|
|
return osType;
|
|
}
|
|
|
|
|
|
static unsigned long long
|
|
hypervDomainGetMaxMemory(virDomainPtr domain)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(Msvm_MemorySettingData) mem_sd = NULL;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return 0;
|
|
|
|
if (hypervGetMemorySD(priv, vssd->data->InstanceID, &mem_sd) < 0)
|
|
return 0;
|
|
|
|
return mem_sd->data->Limit * 1024;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetMemoryProperty(virDomainPtr domain,
|
|
unsigned long memory,
|
|
const char* propertyName)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(Msvm_MemorySettingData) memsd = NULL;
|
|
g_autoptr(GHashTable) memResource = NULL;
|
|
g_autofree char *memory_str = g_strdup_printf("%lu", VIR_ROUND_UP(VIR_DIV_UP(memory, 1024), 2));
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetMemorySD(priv, vssd->data->InstanceID, &memsd) < 0)
|
|
return -1;
|
|
|
|
memResource = hypervCreateEmbeddedParam(Msvm_MemorySettingData_WmiInfo);
|
|
if (!memResource)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(memResource, propertyName, memory_str) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(memResource, "InstanceID", memsd->data->InstanceID) < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSModifyResourceSettings(priv, &memResource,
|
|
Msvm_MemorySettingData_WmiInfo) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetMaxMemory(virDomainPtr domain, unsigned long memory)
|
|
{
|
|
return hypervDomainSetMemoryProperty(domain, memory, "Limit");
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetMemoryFlags(virDomainPtr domain, unsigned long memory, unsigned int flags)
|
|
{
|
|
virCheckFlags(0, -1);
|
|
return hypervDomainSetMemoryProperty(domain, memory, "VirtualQuantity");
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetMemory(virDomainPtr domain, unsigned long memory)
|
|
{
|
|
return hypervDomainSetMemoryFlags(domain, memory, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) virtualSystemSettingData = NULL;
|
|
g_autoptr(Msvm_ProcessorSettingData) processorSettingData = NULL;
|
|
g_autoptr(Msvm_MemorySettingData) memorySettingData = NULL;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* Get Msvm_ComputerSystem */
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string,
|
|
&virtualSystemSettingData) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetProcessorSD(priv,
|
|
virtualSystemSettingData->data->InstanceID,
|
|
&processorSettingData) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetMemorySD(priv,
|
|
virtualSystemSettingData->data->InstanceID,
|
|
&memorySettingData) < 0)
|
|
return -1;
|
|
|
|
/* 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;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetState(virDomainPtr domain, int *state, int *reason,
|
|
unsigned int flags)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
*state = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
|
|
|
|
if (reason != NULL)
|
|
*reason = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *
|
|
hypervDomainScreenshot(virDomainPtr domain,
|
|
virStreamPtr stream,
|
|
unsigned int screen G_GNUC_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
g_auto(WsXmlDocH) ret_doc = NULL;
|
|
int xRes = 640;
|
|
int yRes = 480;
|
|
g_autofree char *width = NULL;
|
|
g_autofree char *height = NULL;
|
|
g_autofree char *imageDataText = NULL;
|
|
g_autofree unsigned char *imageDataBuffer = NULL;
|
|
size_t imageDataBufferSize;
|
|
const char *temporaryDirectory = NULL;
|
|
g_autofree char *temporaryFile = NULL;
|
|
g_autofree uint8_t *ppmBuffer = NULL;
|
|
char *result = NULL;
|
|
const char *xpath = "/s:Envelope/s:Body/p:GetVirtualSystemThumbnailImage_OUTPUT/p:ImageData";
|
|
int pixelCount;
|
|
int pixelByteCount;
|
|
size_t i = 0;
|
|
int fd = -1;
|
|
bool unlinkTemporaryFile = false;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* Hyper-V Generation 1 VMs use two video heads:
|
|
* - S3DisplayController is used for early boot screens.
|
|
* - SyntheticDisplayController takes over when the guest OS initializes its video driver.
|
|
*
|
|
* This attempts to get the resolution from the SyntheticDisplayController first.
|
|
* If that fails, it falls back to S3DisplayController. */
|
|
if (hypervGetVideoResolution(priv, uuid_string, &xRes, &yRes, false) < 0) {
|
|
if (hypervGetVideoResolution(priv, uuid_string, &xRes, &yRes, true) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
/* prepare params */
|
|
params = hypervCreateInvokeParamsList("GetVirtualSystemThumbnailImage",
|
|
MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
|
|
Msvm_VirtualSystemManagementService_WmiInfo);
|
|
if (!params)
|
|
goto cleanup;
|
|
|
|
width = g_strdup_printf("%d", xRes);
|
|
hypervAddSimpleParam(params, "WidthPixels", width);
|
|
|
|
height = g_strdup_printf("%d", yRes);
|
|
hypervAddSimpleParam(params, "HeightPixels", height);
|
|
|
|
virBufferAsprintf(&query,
|
|
"ASSOCIATORS OF "
|
|
"{Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='%s'} "
|
|
"WHERE ResultClass = Msvm_VirtualSystemSettingData",
|
|
uuid_string);
|
|
hypervAddEprParam(params, "TargetSystem", &query, Msvm_VirtualSystemSettingData_WmiInfo);
|
|
|
|
/* capture and parse the screenshot */
|
|
if (hypervInvokeMethod(priv, ¶ms, &ret_doc) < 0)
|
|
goto cleanup;
|
|
|
|
if (!ws_xml_get_soap_envelope(ret_doc)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve screenshot"));
|
|
goto cleanup;
|
|
}
|
|
|
|
imageDataText = ws_xml_get_xpath_value(ret_doc, (char *)xpath);
|
|
|
|
if (!imageDataText) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to retrieve image data"));
|
|
goto cleanup;
|
|
}
|
|
|
|
imageDataBuffer = g_base64_decode(imageDataText, &imageDataBufferSize);
|
|
|
|
pixelCount = imageDataBufferSize / 2;
|
|
pixelByteCount = pixelCount * 3;
|
|
|
|
ppmBuffer = g_new0(uint8_t, pixelByteCount);
|
|
|
|
/* convert rgb565 to rgb888 */
|
|
for (i = 0; i < pixelCount; i++) {
|
|
const uint16_t pixel = imageDataBuffer[i * 2] + (imageDataBuffer[i * 2 + 1] << 8);
|
|
const uint16_t redMask = 0xF800;
|
|
const uint16_t greenMask = 0x7E0;
|
|
const uint16_t blueMask = 0x1F;
|
|
const uint8_t redFive = (pixel & redMask) >> 11;
|
|
const uint8_t greenSix = (pixel & greenMask) >> 5;
|
|
const uint8_t blueFive = pixel & blueMask;
|
|
ppmBuffer[i * 3] = (redFive * 527 + 23) >> 6;
|
|
ppmBuffer[i * 3 + 1] = (greenSix * 259 + 33) >> 6;
|
|
ppmBuffer[i * 3 + 2] = (blueFive * 527 + 23) >> 6;
|
|
}
|
|
|
|
temporaryDirectory = getenv("TMPDIR");
|
|
if (!temporaryDirectory)
|
|
temporaryDirectory = "/tmp";
|
|
temporaryFile = g_strdup_printf("%s/libvirt.hyperv.screendump.XXXXXX", temporaryDirectory);
|
|
if ((fd = g_mkstemp_full(temporaryFile, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR)) == -1) {
|
|
virReportSystemError(errno, _("g_mkstemp(\"%s\") failed"), temporaryFile);
|
|
goto cleanup;
|
|
}
|
|
unlinkTemporaryFile = true;
|
|
|
|
/* write image data */
|
|
dprintf(fd, "P6\n%d %d\n255\n", xRes, yRes);
|
|
if (safewrite(fd, ppmBuffer, pixelByteCount) != pixelByteCount) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to write pixel data"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VIR_CLOSE(fd) < 0)
|
|
virReportSystemError(errno, "%s", _("failed to close screenshot file"));
|
|
|
|
if (virFDStreamOpenFile(stream, temporaryFile, 0, 0, O_RDONLY) < 0)
|
|
goto cleanup;
|
|
|
|
result = g_strdup("image/x-portable-pixmap");
|
|
|
|
cleanup:
|
|
VIR_FORCE_CLOSE(fd);
|
|
if (unlinkTemporaryFile)
|
|
unlink(temporaryFile);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetVcpusFlags(virDomainPtr domain,
|
|
unsigned int nvcpus,
|
|
unsigned int flags)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(Msvm_ProcessorSettingData) proc_sd = NULL;
|
|
g_autoptr(GHashTable) vcpuResource = NULL;
|
|
g_autofree char *nvcpus_str = g_strdup_printf("%u", nvcpus);
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetProcessorSD(priv, vssd->data->InstanceID, &proc_sd) < 0)
|
|
return -1;
|
|
|
|
vcpuResource = hypervCreateEmbeddedParam(Msvm_ProcessorSettingData_WmiInfo);
|
|
if (!vcpuResource)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(vcpuResource, "VirtualQuantity", nvcpus_str) < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(vcpuResource, "InstanceID", proc_sd->data->InstanceID) < 0)
|
|
return -1;
|
|
|
|
if (hypervMsvmVSMSModifyResourceSettings(priv, &vcpuResource,
|
|
Msvm_ProcessorSettingData_WmiInfo) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetVcpus(virDomainPtr domain, unsigned int nvcpus)
|
|
{
|
|
return hypervDomainSetVcpusFlags(domain, nvcpus, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Msvm_ProcessorSettingData) proc_sd = NULL;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_VCPU_LIVE |
|
|
VIR_DOMAIN_VCPU_CONFIG |
|
|
VIR_DOMAIN_VCPU_MAXIMUM, -1);
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* Start by getting the Msvm_ComputerSystem */
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
/* Check @flags to see if we are to query a running domain, and fail
|
|
* if that domain is not running */
|
|
if (flags & VIR_DOMAIN_VCPU_LIVE &&
|
|
computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not active"));
|
|
return -1;
|
|
}
|
|
|
|
/* Check @flags to see if we are to return the maximum vCPU limit */
|
|
if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
|
|
return hypervConnectGetMaxVcpus(domain->conn, NULL);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetProcessorSD(priv, vssd->data->InstanceID, &proc_sd) < 0)
|
|
return -1;
|
|
|
|
return proc_sd->data->VirtualQuantity;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetVcpus(virDomainPtr domain,
|
|
virVcpuInfoPtr info,
|
|
int maxinfo,
|
|
unsigned char *cpumaps,
|
|
int maplen)
|
|
{
|
|
int count = 0;
|
|
int vcpu_number;
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor) vproc = NULL;
|
|
|
|
/* Hyper-V does not allow setting CPU affinity: all cores will be used */
|
|
if (cpumaps && maplen > 0)
|
|
memset(cpumaps, 0xFF, maxinfo * maplen);
|
|
|
|
for (vcpu_number = 0; vcpu_number < maxinfo; vcpu_number++) {
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
|
|
/* Name format: <domain_name>:Hv VP <vCPU_number> */
|
|
g_autofree char *vcpu_name = g_strdup_printf("%s:Hv VP %d", domain->name, vcpu_number);
|
|
|
|
/* try to free objects from previous iteration */
|
|
hypervFreeObject((hypervObject *)vproc);
|
|
vproc = NULL;
|
|
|
|
/* get the info */
|
|
virBufferEscapeSQL(&query,
|
|
WIN32_PERFRAWDATA_HVSTATS_HYPERVHYPERVISORVIRTUALPROCESSOR_WQL_SELECT
|
|
"WHERE Name = '%s'",
|
|
vcpu_name);
|
|
|
|
if (hypervGetWmiClass(Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor, &vproc) < 0)
|
|
continue;
|
|
|
|
/* fill structure info */
|
|
info[vcpu_number].number = vcpu_number;
|
|
if (vproc) {
|
|
info[vcpu_number].state = VIR_VCPU_RUNNING;
|
|
info[vcpu_number].cpuTime = vproc->data->PercentTotalRunTime * 100;
|
|
info[vcpu_number].cpu = VIR_VCPU_INFO_CPU_UNAVAILABLE;
|
|
} else {
|
|
info[vcpu_number].state = VIR_VCPU_OFFLINE;
|
|
info[vcpu_number].cpuTime = 0LLU;
|
|
info[vcpu_number].cpu = VIR_VCPU_INFO_CPU_OFFLINE;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static char *
|
|
hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(virDomainDef) def = NULL;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) virtualSystemSettingData = NULL;
|
|
g_autoptr(Msvm_ProcessorSettingData) processorSettingData = NULL;
|
|
g_autoptr(Msvm_MemorySettingData) memorySettingData = NULL;
|
|
g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
|
|
g_autoptr(Msvm_StorageAllocationSettingData) sasd = NULL;
|
|
g_autoptr(Msvm_SerialPortSettingData) spsd = NULL;
|
|
Msvm_ResourceAllocationSettingData *serialDevices = NULL;
|
|
g_autoptr(Msvm_EthernetPortAllocationSettingData) nets = NULL;
|
|
|
|
virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
|
|
|
|
if (!(def = virDomainDefNew()))
|
|
return NULL;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* Get Msvm_ComputerSystem */
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return NULL;
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string,
|
|
&virtualSystemSettingData) < 0)
|
|
return NULL;
|
|
|
|
if (hypervGetProcessorSD(priv,
|
|
virtualSystemSettingData->data->InstanceID,
|
|
&processorSettingData) < 0)
|
|
return NULL;
|
|
|
|
if (hypervGetMemorySD(priv,
|
|
virtualSystemSettingData->data->InstanceID,
|
|
&memorySettingData) < 0)
|
|
return NULL;
|
|
|
|
if (hypervGetResourceAllocationSD(priv,
|
|
virtualSystemSettingData->data->InstanceID,
|
|
&rasd) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (hypervGetStorageAllocationSD(priv,
|
|
virtualSystemSettingData->data->InstanceID,
|
|
&sasd) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (hypervGetSerialPortSD(priv, virtualSystemSettingData->data->InstanceID, &spsd) < 0)
|
|
return NULL;
|
|
|
|
if (hypervGetEthernetPortAllocationSD(priv,
|
|
virtualSystemSettingData->data->InstanceID, &nets) < 0)
|
|
return NULL;
|
|
|
|
/* 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 = g_strdup(computerSystem->data->ElementName);
|
|
|
|
if (virtualSystemSettingData->data->Notes.data) {
|
|
char **notes = (char **)virtualSystemSettingData->data->Notes.data;
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
size_t i = 0;
|
|
|
|
/* in practice Notes has 1 element */
|
|
for (i = 0; i < virtualSystemSettingData->data->Notes.count; i++) {
|
|
/* but if there's more than 1, separate by double new line */
|
|
if (virBufferUse(&buf) > 0)
|
|
virBufferAddLit(&buf, "\n\n");
|
|
|
|
virBufferAdd(&buf, *notes, -1);
|
|
notes++;
|
|
}
|
|
|
|
def->description = virBufferContentAndReset(&buf);
|
|
}
|
|
|
|
/* mebibytes to kibibytes */
|
|
def->mem.max_memory = memorySettingData->data->Limit * 1024;
|
|
def->mem.cur_balloon = memorySettingData->data->VirtualQuantity * 1024;
|
|
virDomainDefSetMemoryTotal(def, memorySettingData->data->VirtualQuantity * 1024);
|
|
|
|
if (virDomainDefSetVcpusMax(def, processorSettingData->data->VirtualQuantity, NULL) < 0)
|
|
return NULL;
|
|
|
|
if (virDomainDefSetVcpus(def, processorSettingData->data->VirtualQuantity) < 0)
|
|
return NULL;
|
|
|
|
def->os.type = VIR_DOMAIN_OSTYPE_HVM;
|
|
|
|
/* Allocate space for all potential devices */
|
|
|
|
/* 256 scsi drives + 4 ide drives */
|
|
def->disks = g_new0(virDomainDiskDef *,
|
|
HYPERV_MAX_SCSI_CONTROLLERS * HYPERV_MAX_DRIVES_PER_SCSI_CONTROLLER +
|
|
HYPERV_MAX_IDE_CHANNELS * HYPERV_MAX_DRIVES_PER_IDE_CHANNEL);
|
|
def->ndisks = 0;
|
|
|
|
/* 1 ide & 4 scsi controllers */
|
|
def->controllers = g_new0(virDomainControllerDef *, 5);
|
|
def->ncontrollers = 0;
|
|
|
|
/* 8 synthetic + 4 legacy NICs */
|
|
def->nets = g_new0(virDomainNetDef *, 12);
|
|
def->nnets = 0;
|
|
|
|
if (hypervDomainDefParseStorage(priv, def, rasd, sasd) < 0)
|
|
return NULL;
|
|
|
|
if (g_str_has_prefix(priv->version, "6."))
|
|
serialDevices = rasd;
|
|
else
|
|
serialDevices = (Msvm_ResourceAllocationSettingData *)spsd;
|
|
|
|
if (hypervDomainDefParseSerial(def, serialDevices) < 0)
|
|
return NULL;
|
|
|
|
if (hypervDomainDefParseEthernet(domain, def, nets) < 0)
|
|
return NULL;
|
|
|
|
/* XXX xmlopts must be non-NULL */
|
|
return virDomainDefFormat(def, NULL, virDomainDefFormatConvertXMLFlags(flags));
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
|
|
{
|
|
bool success = false;
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
|
|
Msvm_ComputerSystem *computerSystem = NULL;
|
|
int count = 0;
|
|
size_t i;
|
|
|
|
if (maxnames == 0)
|
|
return 0;
|
|
|
|
if (hypervGetInactiveVirtualSystemList(priv, &computerSystemList) < 0)
|
|
goto cleanup;
|
|
|
|
for (computerSystem = computerSystemList; computerSystem != NULL;
|
|
computerSystem = computerSystem->next) {
|
|
names[count] = g_strdup(computerSystem->data->ElementName);
|
|
|
|
++count;
|
|
|
|
if (count >= maxnames)
|
|
break;
|
|
}
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
if (!success) {
|
|
for (i = 0; i < count; ++i)
|
|
VIR_FREE(names[i]);
|
|
|
|
count = -1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectNumOfDefinedDomains(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
|
|
Msvm_ComputerSystem *computerSystem = NULL;
|
|
int count = 0;
|
|
|
|
if (hypervGetInactiveVirtualSystemList(priv, &computerSystemList) < 0)
|
|
return -1;
|
|
|
|
for (computerSystem = computerSystemList; computerSystem != NULL;
|
|
computerSystem = computerSystem->next) {
|
|
++count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (hypervIsMsvmComputerSystemActive(computerSystem, NULL)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is already active or is in state transition"));
|
|
return -1;
|
|
}
|
|
|
|
return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
|
|
MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_ENABLED);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainCreate(virDomainPtr domain)
|
|
{
|
|
return hypervDomainCreateWithFlags(domain, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainUndefineFlags(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
g_auto(virBuffer) eprQuery = VIR_BUFFER_INITIALIZER;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* prepare params */
|
|
params = hypervCreateInvokeParamsList("DestroySystem",
|
|
MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
|
|
Msvm_VirtualSystemManagementService_WmiInfo);
|
|
|
|
if (!params)
|
|
return -1;
|
|
|
|
virBufferEscapeSQL(&eprQuery, MSVM_COMPUTERSYSTEM_WQL_SELECT "WHERE Name = '%s'", uuid_string);
|
|
|
|
if (hypervAddEprParam(params, "AffectedSystem", &eprQuery, Msvm_ComputerSystem_WmiInfo) < 0)
|
|
return -1;
|
|
|
|
/* actually destroy the VM */
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainUndefine(virDomainPtr domain)
|
|
{
|
|
return hypervDomainUndefineFlags(domain, 0);
|
|
}
|
|
|
|
|
|
static virDomainPtr
|
|
hypervDomainDefineXML(virConnectPtr conn, const char *xml)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autofree char *hostname = hypervConnectGetHostname(conn);
|
|
g_autoptr(virDomainDef) def = NULL;
|
|
virDomainPtr domain = NULL;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
g_autoptr(GHashTable) defineSystemParam = NULL;
|
|
size_t i = 0;
|
|
|
|
/* parse xml */
|
|
def = virDomainDefParseString(xml, priv->xmlopt, NULL,
|
|
1 << VIR_DOMAIN_VIRT_HYPERV | VIR_DOMAIN_XML_INACTIVE);
|
|
|
|
if (!def)
|
|
goto error;
|
|
|
|
/* abort if a domain with this UUID already exists */
|
|
if ((domain = hypervDomainLookupByUUID(conn, def->uuid))) {
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
virReportError(VIR_ERR_DOM_EXIST, _("Domain already exists with UUID '%s'"), uuid_string);
|
|
|
|
// Don't use the 'exit' label, since we don't want to delete the existing domain.
|
|
virObjectUnref(domain);
|
|
return NULL;
|
|
}
|
|
|
|
/* prepare params: only set the VM's name for now */
|
|
params = hypervCreateInvokeParamsList("DefineSystem",
|
|
MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
|
|
Msvm_VirtualSystemManagementService_WmiInfo);
|
|
|
|
if (!params)
|
|
goto error;
|
|
|
|
defineSystemParam = hypervCreateEmbeddedParam(Msvm_VirtualSystemSettingData_WmiInfo);
|
|
|
|
if (hypervSetEmbeddedProperty(defineSystemParam, "ElementName", def->name) < 0)
|
|
goto error;
|
|
|
|
if (hypervAddEmbeddedParam(params, "SystemSettings",
|
|
&defineSystemParam, Msvm_VirtualSystemSettingData_WmiInfo) < 0)
|
|
goto error;
|
|
|
|
/* create the VM */
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
goto error;
|
|
|
|
/* populate a domain ptr so that we can edit it */
|
|
domain = hypervDomainLookupByName(conn, def->name);
|
|
|
|
/* set domain vcpus */
|
|
if (def->vcpus && hypervDomainSetVcpus(domain, def->maxvcpus) < 0)
|
|
goto error;
|
|
|
|
/* set VM maximum memory */
|
|
if (def->mem.max_memory > 0 && hypervDomainSetMaxMemory(domain, def->mem.max_memory) < 0)
|
|
goto error;
|
|
|
|
/* set VM memory */
|
|
if (def->mem.cur_balloon > 0 && hypervDomainSetMemory(domain, def->mem.cur_balloon) < 0)
|
|
goto error;
|
|
|
|
/* attach all storage */
|
|
if (hypervDomainAttachStorage(domain, def, hostname) < 0)
|
|
goto error;
|
|
|
|
/* Attach serials */
|
|
for (i = 0; i < def->nserials; i++) {
|
|
if (hypervDomainAttachSerial(domain, def->serials[i]) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not attach serial port %zu"), i);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Attach networks */
|
|
for (i = 0; i < def->nnets; i++) {
|
|
if (hypervDomainAttachSyntheticEthernetAdapter(domain, def->nets[i], hostname) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not attach network %zu"), i);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return domain;
|
|
|
|
error:
|
|
VIR_DEBUG("Domain creation failed, rolling back");
|
|
if (domain)
|
|
hypervDomainUndefine(domain);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(virDomainDef) def = NULL;
|
|
g_autoptr(virDomainDeviceDef) dev = NULL;
|
|
g_autoptr(Win32_ComputerSystem) host = NULL;
|
|
char *hostname = NULL;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
Msvm_ResourceAllocationSettingData *controller = NULL;
|
|
g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
|
|
Msvm_ResourceAllocationSettingData *entry = NULL;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
int num_scsi = 0;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
/* get domain definition */
|
|
if (!(def = virDomainDefNew()))
|
|
return -1;
|
|
|
|
/* get domain device definition */
|
|
dev = virDomainDeviceDefParse(xml, def, priv->xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE);
|
|
if (!dev)
|
|
return -1;
|
|
|
|
/* get the host computer system */
|
|
if (hypervGetPhysicalSystemList(priv, &host) < 0)
|
|
return -1;
|
|
|
|
hostname = host->data->Name;
|
|
|
|
switch (dev->type) {
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
/* get our controller */
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0)
|
|
return -1;
|
|
|
|
entry = rasd;
|
|
switch (dev->data.disk->bus) {
|
|
case VIR_DOMAIN_DISK_BUS_IDE:
|
|
while (entry) {
|
|
if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER &&
|
|
(entry->data->Address[0] - '0') == dev->data.disk->info.addr.drive.controller) {
|
|
controller = entry;
|
|
break;
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
if (!entry)
|
|
return -1;
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_SCSI:
|
|
while (entry) {
|
|
if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA &&
|
|
num_scsi++ == dev->data.disk->info.addr.drive.controller) {
|
|
controller = entry;
|
|
break;
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
if (!entry)
|
|
return -1;
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_FDC:
|
|
while (entry) {
|
|
if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE) {
|
|
controller = entry;
|
|
break;
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
if (!entry)
|
|
return -1;
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_NONE:
|
|
case VIR_DOMAIN_DISK_BUS_VIRTIO:
|
|
case VIR_DOMAIN_DISK_BUS_XEN:
|
|
case VIR_DOMAIN_DISK_BUS_USB:
|
|
case VIR_DOMAIN_DISK_BUS_UML:
|
|
case VIR_DOMAIN_DISK_BUS_SATA:
|
|
case VIR_DOMAIN_DISK_BUS_SD:
|
|
case VIR_DOMAIN_DISK_BUS_LAST:
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid disk bus in definition"));
|
|
return -1;
|
|
}
|
|
|
|
if (hypervDomainAttachStorageVolume(domain, dev->data.disk, controller, hostname) < 0)
|
|
return -1;
|
|
break;
|
|
case VIR_DOMAIN_DEVICE_CHR:
|
|
if (hypervDomainAttachSerial(domain, dev->data.chr) < 0)
|
|
return -1;
|
|
break;
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
if (hypervDomainAttachSyntheticEthernetAdapter(domain, dev->data.net, hostname) < 0)
|
|
return -1;
|
|
break;
|
|
default:
|
|
/* unsupported device type */
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Attaching devices of type %d is not implemented"), dev->type);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainAttachDevice(virDomainPtr domain, const char *xml)
|
|
{
|
|
return hypervDomainAttachDeviceFlags(domain, xml, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetAutostart(virDomainPtr domain, int *autostart)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
*autostart = vssd->data->AutomaticStartupAction == 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainSetAutostart(virDomainPtr domain, int autostart)
|
|
{
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
g_autoptr(GHashTable) autostartParam = NULL;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
params = hypervCreateInvokeParamsList("ModifySystemSettings",
|
|
MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
|
|
Msvm_VirtualSystemManagementService_WmiInfo);
|
|
|
|
if (!params)
|
|
return -1;
|
|
|
|
autostartParam = hypervCreateEmbeddedParam(Msvm_VirtualSystemSettingData_WmiInfo);
|
|
|
|
if (hypervSetEmbeddedProperty(autostartParam, "AutomaticStartupAction",
|
|
autostart ? "4" : "2") < 0)
|
|
return -1;
|
|
|
|
if (hypervSetEmbeddedProperty(autostartParam, "InstanceID", vssd->data->InstanceID) < 0)
|
|
return -1;
|
|
|
|
if (hypervAddEmbeddedParam(params, "SystemSettings",
|
|
&autostartParam, Msvm_VirtualSystemSettingData_WmiInfo) < 0)
|
|
return -1;
|
|
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *
|
|
hypervDomainGetSchedulerType(virDomainPtr domain G_GNUC_UNUSED, int *nparams)
|
|
{
|
|
if (nparams)
|
|
*nparams = 3; /* reservation, limit, weight */
|
|
|
|
return g_strdup("allocation");
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetSchedulerParametersFlags(virDomainPtr domain,
|
|
virTypedParameterPtr params,
|
|
int *nparams, unsigned int flags)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(Msvm_ProcessorSettingData) proc_sd = NULL;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
int saved_nparams = 0;
|
|
|
|
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
/* get info from host */
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
if (hypervGetProcessorSD(priv, vssd->data->InstanceID, &proc_sd) < 0)
|
|
return -1;
|
|
|
|
/* parse it all out */
|
|
if (virTypedParameterAssign(¶ms[0], VIR_DOMAIN_SCHEDULER_LIMIT,
|
|
VIR_TYPED_PARAM_LLONG, proc_sd->data->Limit) < 0)
|
|
return -1;
|
|
saved_nparams++;
|
|
|
|
if (*nparams > saved_nparams) {
|
|
if (virTypedParameterAssign(¶ms[1], VIR_DOMAIN_SCHEDULER_RESERVATION,
|
|
VIR_TYPED_PARAM_LLONG, proc_sd->data->Reservation) < 0)
|
|
return -1;
|
|
saved_nparams++;
|
|
}
|
|
|
|
if (*nparams > saved_nparams) {
|
|
if (virTypedParameterAssign(¶ms[2], VIR_DOMAIN_SCHEDULER_WEIGHT,
|
|
VIR_TYPED_PARAM_UINT, proc_sd->data->Weight) < 0)
|
|
return -1;
|
|
saved_nparams++;
|
|
}
|
|
|
|
*nparams = saved_nparams;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetSchedulerParameters(virDomainPtr domain,
|
|
virTypedParameterPtr params,
|
|
int *nparams)
|
|
{
|
|
return hypervDomainGetSchedulerParametersFlags(domain, params, nparams,
|
|
VIR_DOMAIN_AFFECT_CURRENT);
|
|
}
|
|
|
|
|
|
static unsigned long long
|
|
hypervNodeGetFreeMemory(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_autoptr(Win32_OperatingSystem) operatingSystem = NULL;
|
|
|
|
if (hypervGetOperatingSystem(priv, &operatingSystem) < 0)
|
|
return 0;
|
|
|
|
if (!operatingSystem) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get free memory for host %s"),
|
|
conn->uri->server);
|
|
return 0;
|
|
}
|
|
|
|
return operatingSystem->data->FreePhysicalMemory * 1024;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectIsEncrypted(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
|
|
if (STRCASEEQ(priv->parsedUri->transport, "https")) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectIsSecure(virConnectPtr conn)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
|
|
if (STRCASEEQ(priv->parsedUri->transport, "https")) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
hypervConnectIsAlive(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)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
return hypervIsMsvmComputerSystemActive(computerSystem, NULL) ? 1 : 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainGetMaxVcpus(virDomainPtr dom)
|
|
{
|
|
if (hypervDomainIsActive(dom))
|
|
return hypervDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_MAXIMUM));
|
|
else
|
|
return hypervConnectGetMaxVcpus(dom->conn, NULL);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainIsPersistent(virDomainPtr domain G_GNUC_UNUSED)
|
|
{
|
|
/* Hyper-V has no concept of transient domains, so all of them are persistent */
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainIsUpdated(virDomainPtr domain G_GNUC_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainManagedSave(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
bool in_transition = false;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
|
|
in_transition) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain is not active or is in state transition"));
|
|
return -1;
|
|
}
|
|
|
|
return hypervInvokeMsvmComputerSystemRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_OFFLINE);
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
return computerSystem->data->EnabledState == MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED ? 1 : 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervDomainManagedSaveRemove(virDomainPtr domain, unsigned int flags)
|
|
{
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
_("Domain has no managed save image"));
|
|
return -1;
|
|
}
|
|
|
|
return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
|
|
MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_DISABLED);
|
|
}
|
|
|
|
|
|
#define MATCH(FLAG) (flags & (FLAG))
|
|
static int
|
|
hypervConnectListAllDomains(virConnectPtr conn,
|
|
virDomainPtr **domains,
|
|
unsigned int flags)
|
|
{
|
|
hypervPrivate *priv = conn->privateData;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
|
|
Msvm_ComputerSystem *computerSystem = NULL;
|
|
size_t ndoms;
|
|
virDomainPtr domain;
|
|
virDomainPtr *doms = NULL;
|
|
int count = 0;
|
|
int ret = -1;
|
|
size_t 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)
|
|
*domains = g_new0(virDomainPtr, 1);
|
|
|
|
ret = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
virBufferAddLit(&query,
|
|
MSVM_COMPUTERSYSTEM_WQL_SELECT
|
|
"WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
|
|
|
|
/* construct query with filter depending on flags */
|
|
if (!(MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) &&
|
|
MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE))) {
|
|
if (MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE)) {
|
|
virBufferAddLit(&query, "AND " MSVM_COMPUTERSYSTEM_WQL_ACTIVE);
|
|
}
|
|
|
|
if (MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE)) {
|
|
virBufferAddLit(&query, "AND " MSVM_COMPUTERSYSTEM_WQL_INACTIVE);
|
|
}
|
|
}
|
|
|
|
if (hypervGetWmiClass(Msvm_ComputerSystem, &computerSystemList) < 0)
|
|
goto cleanup;
|
|
|
|
if (domains) {
|
|
doms = g_new0(virDomainPtr, 1);
|
|
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;
|
|
}
|
|
|
|
VIR_RESIZE_N(doms, ndoms, count, 2);
|
|
|
|
domain = NULL;
|
|
|
|
if (hypervMsvmComputerSystemToDomain(conn, computerSystem,
|
|
&domain) < 0)
|
|
goto cleanup;
|
|
|
|
doms[count++] = domain;
|
|
}
|
|
|
|
if (doms)
|
|
*domains = doms;
|
|
doms = NULL;
|
|
ret = count;
|
|
|
|
cleanup:
|
|
if (doms) {
|
|
for (i = 0; i < count; ++i)
|
|
virObjectUnref(doms[i]);
|
|
|
|
VIR_FREE(doms);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#undef MATCH
|
|
|
|
|
|
static int
|
|
hypervDomainSendKey(virDomainPtr domain, unsigned int codeset,
|
|
unsigned int holdtime, unsigned int *keycodes, int nkeycodes,
|
|
unsigned int flags)
|
|
{
|
|
size_t i = 0;
|
|
int keycode = 0;
|
|
g_autofree int *translatedKeycodes = NULL;
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
g_autofree char *selector = NULL;
|
|
g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
|
|
g_autoptr(Msvm_Keyboard) keyboard = NULL;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
char keycodeStr[VIR_INT64_STR_BUFLEN];
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
|
|
return -1;
|
|
|
|
virBufferEscapeSQL(&query,
|
|
"ASSOCIATORS OF {Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='%s'} "
|
|
"WHERE ResultClass = Msvm_Keyboard",
|
|
uuid_string);
|
|
|
|
if (hypervGetWmiClass(Msvm_Keyboard, &keyboard) < 0)
|
|
return -1;
|
|
|
|
translatedKeycodes = g_new0(int, nkeycodes);
|
|
|
|
/* translate keycodes to win32 and generate keyup scancodes. */
|
|
for (i = 0; i < nkeycodes; i++) {
|
|
if (codeset != VIR_KEYCODE_SET_WIN32) {
|
|
keycode = virKeycodeValueTranslate(codeset,
|
|
VIR_KEYCODE_SET_WIN32,
|
|
keycodes[i]);
|
|
|
|
if (keycode < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not translate keycode"));
|
|
return -1;
|
|
}
|
|
translatedKeycodes[i] = keycode;
|
|
}
|
|
}
|
|
|
|
selector = g_strdup_printf("CreationClassName=Msvm_Keyboard&DeviceID=%s&"
|
|
"SystemCreationClassName=Msvm_ComputerSystem&"
|
|
"SystemName=%s", keyboard->data->DeviceID, uuid_string);
|
|
|
|
/* press the keys */
|
|
for (i = 0; i < nkeycodes; i++) {
|
|
g_snprintf(keycodeStr, sizeof(keycodeStr), "%d", translatedKeycodes[i]);
|
|
|
|
params = hypervCreateInvokeParamsList("PressKey", selector,
|
|
Msvm_Keyboard_WmiInfo);
|
|
|
|
if (!params)
|
|
return -1;
|
|
|
|
if (hypervAddSimpleParam(params, "keyCode", keycodeStr) < 0)
|
|
return -1;
|
|
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
return -1;
|
|
}
|
|
|
|
/* simulate holdtime by sleeping */
|
|
if (holdtime > 0)
|
|
g_usleep(holdtime * 1000);
|
|
|
|
/* release the keys */
|
|
for (i = 0; i < nkeycodes; i++) {
|
|
g_snprintf(keycodeStr, sizeof(keycodeStr), "%d", translatedKeycodes[i]);
|
|
params = hypervCreateInvokeParamsList("ReleaseKey", selector,
|
|
Msvm_Keyboard_WmiInfo);
|
|
|
|
if (!params)
|
|
return -1;
|
|
|
|
if (hypervAddSimpleParam(params, "keyCode", keycodeStr) < 0)
|
|
return -1;
|
|
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virHypervisorDriver hypervHypervisorDriver = {
|
|
.name = "Hyper-V",
|
|
.connectOpen = hypervConnectOpen, /* 0.9.5 */
|
|
.connectClose = hypervConnectClose, /* 0.9.5 */
|
|
.connectGetType = hypervConnectGetType, /* 0.9.5 */
|
|
.connectGetVersion = hypervConnectGetVersion, /* 6.9.0 */
|
|
.connectGetHostname = hypervConnectGetHostname, /* 0.9.5 */
|
|
.connectGetMaxVcpus = hypervConnectGetMaxVcpus, /* 6.9.0 */
|
|
.nodeGetInfo = hypervNodeGetInfo, /* 0.9.5 */
|
|
.connectGetCapabilities = hypervConnectGetCapabilities, /* 6.9.0 */
|
|
.connectListDomains = hypervConnectListDomains, /* 0.9.5 */
|
|
.connectNumOfDomains = hypervConnectNumOfDomains, /* 0.9.5 */
|
|
.connectListAllDomains = hypervConnectListAllDomains, /* 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 */
|
|
.domainShutdown = hypervDomainShutdown, /* 6.9.0 */
|
|
.domainShutdownFlags = hypervDomainShutdownFlags, /* 6.9.0 */
|
|
.domainReboot = hypervDomainReboot, /* 6.9.0 */
|
|
.domainReset = hypervDomainReset, /* 6.9.0 */
|
|
.domainDestroy = hypervDomainDestroy, /* 0.9.5 */
|
|
.domainDestroyFlags = hypervDomainDestroyFlags, /* 0.9.5 */
|
|
.domainGetOSType = hypervDomainGetOSType, /* 0.9.5 */
|
|
.domainGetMaxMemory = hypervDomainGetMaxMemory, /* 6.10.0 */
|
|
.domainSetMaxMemory = hypervDomainSetMaxMemory, /* 6.10.0 */
|
|
.domainSetMemory = hypervDomainSetMemory, /* 3.6.0 */
|
|
.domainSetMemoryFlags = hypervDomainSetMemoryFlags, /* 3.6.0 */
|
|
.domainGetInfo = hypervDomainGetInfo, /* 0.9.5 */
|
|
.domainGetState = hypervDomainGetState, /* 0.9.5 */
|
|
.domainScreenshot = hypervDomainScreenshot, /* 7.1.0 */
|
|
.domainSetVcpus = hypervDomainSetVcpus, /* 6.10.0 */
|
|
.domainSetVcpusFlags = hypervDomainSetVcpusFlags, /* 6.10.0 */
|
|
.domainGetVcpusFlags = hypervDomainGetVcpusFlags, /* 6.10.0 */
|
|
.domainGetVcpus = hypervDomainGetVcpus, /* 6.10.0 */
|
|
.domainGetMaxVcpus = hypervDomainGetMaxVcpus, /* 6.10.0 */
|
|
.domainGetXMLDesc = hypervDomainGetXMLDesc, /* 0.9.5 */
|
|
.connectListDefinedDomains = hypervConnectListDefinedDomains, /* 0.9.5 */
|
|
.connectNumOfDefinedDomains = hypervConnectNumOfDefinedDomains, /* 0.9.5 */
|
|
.domainCreate = hypervDomainCreate, /* 0.9.5 */
|
|
.domainCreateWithFlags = hypervDomainCreateWithFlags, /* 0.9.5 */
|
|
.domainDefineXML = hypervDomainDefineXML, /* 7.1.0 */
|
|
.domainUndefine = hypervDomainUndefine, /* 7.1.0 */
|
|
.domainUndefineFlags = hypervDomainUndefineFlags, /* 7.1.0 */
|
|
.domainAttachDevice = hypervDomainAttachDevice, /* 7.1.0 */
|
|
.domainAttachDeviceFlags = hypervDomainAttachDeviceFlags, /* 7.1.0 */
|
|
.domainGetAutostart = hypervDomainGetAutostart, /* 6.9.0 */
|
|
.domainSetAutostart = hypervDomainSetAutostart, /* 6.9.0 */
|
|
.domainGetSchedulerType = hypervDomainGetSchedulerType, /* 6.10.0 */
|
|
.domainGetSchedulerParameters = hypervDomainGetSchedulerParameters, /* 6.10.0 */
|
|
.domainGetSchedulerParametersFlags = hypervDomainGetSchedulerParametersFlags, /* 6.10.0 */
|
|
.nodeGetFreeMemory = hypervNodeGetFreeMemory, /* 6.9.0 */
|
|
.connectIsEncrypted = hypervConnectIsEncrypted, /* 0.9.5 */
|
|
.connectIsSecure = hypervConnectIsSecure, /* 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 */
|
|
.domainSendKey = hypervDomainSendKey, /* 3.6.0 */
|
|
.connectIsAlive = hypervConnectIsAlive, /* 0.9.8 */
|
|
};
|
|
|
|
|
|
virDomainDefParserConfig hypervDomainDefParserConfig = {
|
|
.features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG,
|
|
};
|
|
|
|
|
|
static void
|
|
hypervDebugHandler(const char *message, debug_level_e level,
|
|
void *user_data G_GNUC_UNUSED)
|
|
{
|
|
switch (level) {
|
|
case DEBUG_LEVEL_ERROR:
|
|
case DEBUG_LEVEL_CRITICAL:
|
|
case DEBUG_LEVEL_ALWAYS:
|
|
VIR_ERROR(_("openwsman: %s"), message);
|
|
break;
|
|
|
|
case DEBUG_LEVEL_WARNING:
|
|
VIR_WARN("openwsman: %s", message);
|
|
break;
|
|
|
|
case DEBUG_LEVEL_MESSAGE:
|
|
VIR_INFO("openwsman: %s", message);
|
|
break;
|
|
|
|
case DEBUG_LEVEL_INFO:
|
|
VIR_INFO("openwsman: %s", message);
|
|
break;
|
|
|
|
case DEBUG_LEVEL_DEBUG:
|
|
VIR_DEBUG("openwsman: %s", message);
|
|
break;
|
|
|
|
case DEBUG_LEVEL_NONE:
|
|
default:
|
|
/* Ignore the rest */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static virConnectDriver hypervConnectDriver = {
|
|
.remoteOnly = true,
|
|
.uriSchemes = (const char *[]){ "hyperv", NULL },
|
|
.hypervisorDriver = &hypervHypervisorDriver,
|
|
.networkDriver = &hypervNetworkDriver,
|
|
};
|
|
|
|
int
|
|
hypervRegister(void)
|
|
{
|
|
/* Forward openwsman errors and warnings to libvirt's logging */
|
|
debug_add_handler(hypervDebugHandler, DEBUG_LEVEL_WARNING, NULL);
|
|
|
|
return virRegisterConnectDriver(&hypervConnectDriver,
|
|
false);
|
|
}
|