1 /*
2  * hyperv_driver.c: core driver functions for managing Microsoft Hyper-V hosts
3  *
4  * Copyright (C) 2011-2013 Matthias Bolte <matthias.bolte@googlemail.com>
5  * Copyright (C) 2009 Michael Sievers <msievers83@googlemail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include <config.h>
24 
25 #include <fcntl.h>
26 
27 #include "internal.h"
28 #include "datatypes.h"
29 #include "virdomainobjlist.h"
30 #include "virauth.h"
31 #include "viralloc.h"
32 #include "virlog.h"
33 #include "viruuid.h"
34 #include "virutil.h"
35 #include "hyperv_driver.h"
36 #include "hyperv_network_driver.h"
37 #include "hyperv_private.h"
38 #include "hyperv_util.h"
39 #include "hyperv_wmi.h"
40 #include "virstring.h"
41 #include "virkeycode.h"
42 #include "domain_conf.h"
43 #include "virfdstream.h"
44 #include "virfile.h"
45 
46 #define VIR_FROM_THIS VIR_FROM_HYPERV
47 
48 VIR_LOG_INIT("hyperv.hyperv_driver");
49 
50 /*
51  * WMI utility functions
52  *
53  * wrapper functions for commonly-accessed WMI objects and interfaces.
54  */
55 
56 static int
hypervGetProcessorsByName(hypervPrivate * priv,const char * name,Win32_Processor ** processorList)57 hypervGetProcessorsByName(hypervPrivate *priv, const char *name,
58                           Win32_Processor **processorList)
59 {
60     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
61     virBufferEscapeSQL(&query,
62                        "ASSOCIATORS OF {Win32_ComputerSystem.Name='%s'} "
63                        "WHERE AssocClass = Win32_ComputerSystemProcessor "
64                        "ResultClass = Win32_Processor",
65                        name);
66 
67     if (hypervGetWmiClass(Win32_Processor, processorList) < 0)
68         return -1;
69 
70     if (!processorList) {
71         virReportError(VIR_ERR_INTERNAL_ERROR,
72                        _("Could not look up processor(s) on '%s'"),
73                        name);
74         return -1;
75     }
76 
77     return 0;
78 }
79 
80 
81 static int
hypervGetActiveVirtualSystemList(hypervPrivate * priv,Msvm_ComputerSystem ** computerSystemList)82 hypervGetActiveVirtualSystemList(hypervPrivate *priv,
83                                  Msvm_ComputerSystem **computerSystemList)
84 {
85     g_auto(virBuffer) query = { g_string_new(MSVM_COMPUTERSYSTEM_WQL_SELECT
86                                              "WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
87                                              "AND " MSVM_COMPUTERSYSTEM_WQL_ACTIVE), 0 };
88 
89     if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
90         return -1;
91 
92     if (!*computerSystemList) {
93         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
94                        _("Could not look up active virtual machines"));
95         return -1;
96     }
97 
98     return 0;
99 }
100 
101 
102 /* gets all the vms including the ones that are marked inactive. */
103 static int
hypervGetInactiveVirtualSystemList(hypervPrivate * priv,Msvm_ComputerSystem ** computerSystemList)104 hypervGetInactiveVirtualSystemList(hypervPrivate *priv,
105                                    Msvm_ComputerSystem **computerSystemList)
106 {
107     g_auto(virBuffer) query = { g_string_new(MSVM_COMPUTERSYSTEM_WQL_SELECT
108                                              "WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
109                                              "AND " MSVM_COMPUTERSYSTEM_WQL_INACTIVE), 0 };
110 
111     if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
112         return -1;
113 
114     if (!*computerSystemList) {
115         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
116                        _("Could not look up inactive virtual machines"));
117         return -1;
118     }
119 
120     return 0;
121 }
122 
123 
124 static int
hypervGetPhysicalSystemList(hypervPrivate * priv,Win32_ComputerSystem ** computerSystemList)125 hypervGetPhysicalSystemList(hypervPrivate *priv,
126                             Win32_ComputerSystem **computerSystemList)
127 {
128     g_auto(virBuffer) query = { g_string_new(WIN32_COMPUTERSYSTEM_WQL_SELECT), 0 };
129 
130     if (hypervGetWmiClass(Win32_ComputerSystem, computerSystemList) < 0)
131         return -1;
132 
133     if (!*computerSystemList) {
134         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
135                        _("Could not look up Win32_ComputerSystem"));
136         return -1;
137     }
138 
139     return 0;
140 }
141 
142 
143 static int
hypervGetVirtualSystemByID(hypervPrivate * priv,int id,Msvm_ComputerSystem ** computerSystemList)144 hypervGetVirtualSystemByID(hypervPrivate *priv, int id,
145                            Msvm_ComputerSystem **computerSystemList)
146 {
147     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
148     virBufferAsprintf(&query,
149                       MSVM_COMPUTERSYSTEM_WQL_SELECT
150                       "WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
151                       "AND ProcessID = %d",
152                       id);
153 
154     if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
155         return -1;
156 
157     if (*computerSystemList == NULL) {
158         virReportError(VIR_ERR_NO_DOMAIN, _("No domain with ID %d"), id);
159         return -1;
160     }
161 
162     return 0;
163 }
164 
165 
166 static int
hypervGetVirtualSystemByName(hypervPrivate * priv,const char * name,Msvm_ComputerSystem ** computerSystemList)167 hypervGetVirtualSystemByName(hypervPrivate *priv, const char *name,
168                              Msvm_ComputerSystem **computerSystemList)
169 {
170     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
171     virBufferEscapeSQL(&query,
172                        MSVM_COMPUTERSYSTEM_WQL_SELECT
173                        "WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
174                        "AND ElementName = '%s'",
175                        name);
176 
177     if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystemList) < 0)
178         return -1;
179 
180     if (*computerSystemList == NULL) {
181         virReportError(VIR_ERR_NO_DOMAIN,
182                        _("No domain with name %s"), name);
183         return -1;
184     }
185 
186     return 0;
187 }
188 
189 
190 static int
hypervGetOperatingSystem(hypervPrivate * priv,Win32_OperatingSystem ** operatingSystem)191 hypervGetOperatingSystem(hypervPrivate *priv, Win32_OperatingSystem **operatingSystem)
192 {
193     g_auto(virBuffer) query = { g_string_new(WIN32_OPERATINGSYSTEM_WQL_SELECT), 0 };
194 
195     if (hypervGetWmiClass(Win32_OperatingSystem, operatingSystem) < 0)
196         return -1;
197 
198     return 0;
199 }
200 
201 
202 static int
hypervRequestStateChange(virDomainPtr domain,int state)203 hypervRequestStateChange(virDomainPtr domain, int state)
204 {
205     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
206 
207     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
208         return -1;
209 
210     if (computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED) {
211         virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not active"));
212         return -1;
213     }
214 
215     return hypervInvokeMsvmComputerSystemRequestStateChange(domain, state);
216 }
217 
218 
219 /*
220  * API-specific utility functions
221  */
222 
223 static int
hypervParseVersionString(const char * str,unsigned int * major,unsigned int * minor,unsigned int * micro)224 hypervParseVersionString(const char *str, unsigned int *major,
225                          unsigned int *minor, unsigned int *micro)
226 {
227     char *suffix = NULL;
228 
229     if (virStrToLong_ui(str, &suffix, 10, major) < 0)
230         return -1;
231 
232     if (virStrToLong_ui(suffix + 1, &suffix, 10, minor) < 0)
233         return -1;
234 
235     if (virStrToLong_ui(suffix + 1, NULL, 10, micro) < 0)
236         return -1;
237 
238     return 0;
239 }
240 
241 
242 static int
hypervLookupHostSystemBiosUuid(hypervPrivate * priv,unsigned char * uuid)243 hypervLookupHostSystemBiosUuid(hypervPrivate *priv, unsigned char *uuid)
244 {
245     g_autoptr(Win32_ComputerSystemProduct) computerSystem = NULL;
246     g_auto(virBuffer) query = { g_string_new(WIN32_COMPUTERSYSTEMPRODUCT_WQL_SELECT), 0 };
247 
248     if (hypervGetWmiClass(Win32_ComputerSystemProduct, &computerSystem) < 0)
249         return -1;
250 
251     if (virUUIDParse(computerSystem->data->UUID, uuid) < 0) {
252         virReportError(VIR_ERR_INTERNAL_ERROR,
253                        _("Could not parse UUID from string '%s'"),
254                        computerSystem->data->UUID);
255         return -1;
256     }
257 
258     return 0;
259 }
260 
261 
262 static virCaps *
hypervCapsInit(hypervPrivate * priv)263 hypervCapsInit(hypervPrivate *priv)
264 {
265     virCaps *caps = NULL;
266     virCapsGuest *guest = NULL;
267 
268     caps = virCapabilitiesNew(VIR_ARCH_X86_64, 1, 1);
269 
270     if (!caps)
271         return NULL;
272 
273     if (hypervLookupHostSystemBiosUuid(priv, caps->host.host_uuid) < 0)
274         goto error;
275 
276     /* i686 caps */
277     guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM, VIR_ARCH_I686,
278                                     NULL, NULL, 0, NULL);
279 
280     virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_HYPERV,
281                                   NULL, NULL, 0, NULL);
282 
283     /* x86_64 caps */
284     guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM, VIR_ARCH_X86_64,
285                                     NULL, NULL, 0, NULL);
286 
287     virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_HYPERV,
288                                   NULL, NULL, 0, NULL);
289 
290     return caps;
291 
292  error:
293     virObjectUnref(caps);
294     return NULL;
295 }
296 
297 
298 static int
hypervGetVideoResolution(hypervPrivate * priv,char * vm_uuid,int * xRes,int * yRes,bool fallback)299 hypervGetVideoResolution(hypervPrivate *priv,
300                          char *vm_uuid,
301                          int *xRes,
302                          int *yRes,
303                          bool fallback)
304 {
305     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
306     g_autoptr(Msvm_S3DisplayController) s3Display = NULL;
307     g_autoptr(Msvm_SyntheticDisplayController) synthetic = NULL;
308     g_autoptr(Msvm_VideoHead) heads = NULL;
309     const char *wmiClass = NULL;
310     char *deviceId = NULL;
311     g_autofree char *enabledStateString = NULL;
312 
313     if (fallback) {
314         wmiClass = "Msvm_S3DisplayController";
315 
316         virBufferEscapeSQL(&query,
317                            MSVM_S3DISPLAYCONTROLLER_WQL_SELECT "WHERE SystemName = '%s'",
318                            vm_uuid);
319 
320         if (hypervGetWmiClass(Msvm_S3DisplayController, &s3Display) < 0 || !s3Display)
321             return -1;
322 
323         deviceId = s3Display->data->DeviceID;
324     } else {
325         wmiClass = "Msvm_SyntheticDisplayController";
326 
327         virBufferEscapeSQL(&query,
328                            MSVM_SYNTHETICDISPLAYCONTROLLER_WQL_SELECT "WHERE SystemName = '%s'",
329                            vm_uuid);
330 
331         if (hypervGetWmiClass(Msvm_SyntheticDisplayController, &synthetic) < 0 || !synthetic)
332             return -1;
333 
334         deviceId = synthetic->data->DeviceID;
335     }
336 
337     virBufferFreeAndReset(&query);
338 
339     virBufferAsprintf(&query,
340                       "ASSOCIATORS OF {%s."
341                       "CreationClassName='%s',"
342                       "DeviceID='%s',"
343                       "SystemCreationClassName='Msvm_ComputerSystem',"
344                       "SystemName='%s'"
345                       "} WHERE AssocClass = Msvm_VideoHeadOnController "
346                       "ResultClass = Msvm_VideoHead",
347                       wmiClass, wmiClass, deviceId, vm_uuid);
348 
349     if (hypervGetWmiClass(Msvm_VideoHead, &heads) < 0)
350         return -1;
351 
352     enabledStateString = g_strdup_printf("%d", CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_ENABLED);
353     if (heads && STREQ(heads->data->EnabledState, enabledStateString)) {
354         *xRes = heads->data->CurrentHorizontalResolution;
355         *yRes = heads->data->CurrentVerticalResolution;
356 
357         return 0;
358     }
359 
360     return -1;
361 }
362 
363 
364 /*
365  * Virtual device functions
366  */
367 static int
hypervGetDeviceParentRasdFromDeviceId(const char * parentDeviceId,Msvm_ResourceAllocationSettingData * list,Msvm_ResourceAllocationSettingData ** out)368 hypervGetDeviceParentRasdFromDeviceId(const char *parentDeviceId,
369                                       Msvm_ResourceAllocationSettingData *list,
370                                       Msvm_ResourceAllocationSettingData **out)
371 {
372     Msvm_ResourceAllocationSettingData *entry = list;
373     *out = NULL;
374 
375     while (entry) {
376         g_autofree char *escapedDeviceId = virStringReplace(entry->data->InstanceID, "\\", "\\\\");
377         g_autofree char *expectedSuffix = g_strdup_printf("%s\"", escapedDeviceId);
378 
379         if (g_str_has_suffix(parentDeviceId, expectedSuffix)) {
380             *out = entry;
381             break;
382         }
383 
384         entry = entry->next;
385     }
386 
387     if (*out)
388         return 0;
389 
390     virReportError(VIR_ERR_INTERNAL_ERROR,
391                    _("Failed to locate parent device with ID '%s'"),
392                    parentDeviceId);
393 
394     return -1;
395 }
396 
397 
398 static char *
hypervGetInstanceIDFromXMLResponse(WsXmlDocH response)399 hypervGetInstanceIDFromXMLResponse(WsXmlDocH response)
400 {
401     WsXmlNodeH envelope = NULL;
402     char *instanceId = NULL;
403 
404     envelope = ws_xml_get_soap_envelope(response);
405     if (!envelope) {
406         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid XML response"));
407         return NULL;
408     }
409 
410     instanceId = ws_xml_get_xpath_value(response, (char *)"//w:Selector[@Name='InstanceID']");
411 
412     if (!instanceId) {
413         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
414                        _("Could not find selectors in method response"));
415         return NULL;
416     }
417 
418     return instanceId;
419 }
420 
421 
422 static int
hypervDomainCreateSCSIController(virDomainPtr domain,virDomainControllerDef * def)423 hypervDomainCreateSCSIController(virDomainPtr domain, virDomainControllerDef *def)
424 {
425     g_autoptr(GHashTable) scsiResource = NULL;
426     g_autofree char *resourceType = NULL;
427 
428     if (def->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT &&
429         def->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO) {
430         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
431                        _("Unsupported SCSI controller model '%d'"), def->model);
432         return -1;
433     }
434 
435     if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
436         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
437                        _("Unsupported SCSI controller address type '%d'"), def->info.type);
438         return -1;
439     }
440 
441     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA);
442 
443     VIR_DEBUG("Attaching SCSI Controller");
444 
445     /* prepare embedded param */
446     scsiResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
447     if (!scsiResource)
448         return -1;
449 
450     if (hypervSetEmbeddedProperty(scsiResource, "ResourceType", resourceType) < 0)
451         return -1;
452 
453     if (hypervSetEmbeddedProperty(scsiResource, "ResourceSubType",
454                                   "Microsoft:Hyper-V:Synthetic SCSI Controller") < 0)
455         return -1;
456 
457     /* perform the settings change */
458     if (hypervMsvmVSMSAddResourceSettings(domain, &scsiResource,
459                                           Msvm_ResourceAllocationSettingData_WmiInfo, NULL) < 0)
460         return -1;
461 
462     return 0;
463 }
464 
465 
466 static int
hypervDomainAddVirtualDiskParent(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * controller,const char * hostname,WsXmlDocH * response)467 hypervDomainAddVirtualDiskParent(virDomainPtr domain,
468                                  virDomainDiskDef *disk,
469                                  Msvm_ResourceAllocationSettingData *controller,
470                                  const char *hostname,
471                                  WsXmlDocH *response)
472 {
473     g_autoptr(GHashTable) controllerResource = NULL;
474     g_autofree char *parentInstanceIDEscaped = NULL;
475     g_autofree char *parent__PATH = NULL;
476     g_autofree char *addressString = g_strdup_printf("%u", disk->info.addr.drive.unit);
477     g_autofree char *resourceType = NULL;
478 
479     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_DISK_DRIVE);
480 
481     controllerResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
482     if (!controllerResource)
483         return -1;
484 
485     parentInstanceIDEscaped = virStringReplace(controller->data->InstanceID, "\\", "\\\\");
486     parent__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
487                                    "Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
488                                    hostname, parentInstanceIDEscaped);
489     if (!parent__PATH)
490         return -1;
491 
492     if (hypervSetEmbeddedProperty(controllerResource, "Parent", parent__PATH) < 0)
493         return -1;
494 
495     if (hypervSetEmbeddedProperty(controllerResource, "AddressOnParent", addressString) < 0)
496         return -1;
497 
498     if (hypervSetEmbeddedProperty(controllerResource, "ResourceType", resourceType) < 0)
499         return -1;
500 
501     if (hypervSetEmbeddedProperty(controllerResource, "ResourceSubType",
502                                   "Microsoft:Hyper-V:Synthetic Disk Drive") < 0)
503         return -1;
504 
505     if (hypervMsvmVSMSAddResourceSettings(domain, &controllerResource,
506                                           Msvm_ResourceAllocationSettingData_WmiInfo,
507                                           response) < 0)
508         return -1;
509 
510     return 0;
511 }
512 
513 
514 static int
hypervDomainAddVirtualHardDisk(virDomainPtr domain,virDomainDiskDef * disk,const char * hostname,char * parentInstanceID)515 hypervDomainAddVirtualHardDisk(virDomainPtr domain,
516                                virDomainDiskDef *disk,
517                                const char *hostname,
518                                char *parentInstanceID)
519 {
520     g_autoptr(GHashTable) volumeResource = NULL;
521     g_autofree char *vhdInstanceIdEscaped = NULL;
522     g_autofree char *vhd__PATH = NULL;
523     g_autofree char *resourceType = NULL;
524 
525     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_LOGICAL_DISK);
526 
527     volumeResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
528     if (!volumeResource)
529         return -1;
530 
531     vhdInstanceIdEscaped = virStringReplace(parentInstanceID, "\\", "\\\\");
532     vhd__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
533                                 "Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
534                                 hostname, vhdInstanceIdEscaped);
535 
536     if (!vhd__PATH)
537         return -1;
538 
539     if (hypervSetEmbeddedProperty(volumeResource, "Parent", vhd__PATH) < 0)
540         return -1;
541 
542     if (hypervSetEmbeddedProperty(volumeResource, "HostResource", disk->src->path) < 0)
543         return -1;
544 
545     if (hypervSetEmbeddedProperty(volumeResource, "ResourceType", resourceType) < 0)
546         return -1;
547 
548     if (hypervSetEmbeddedProperty(volumeResource, "ResourceSubType",
549                                   "Microsoft:Hyper-V:Virtual Hard Disk") < 0)
550         return -1;
551 
552     if (hypervMsvmVSMSAddResourceSettings(domain, &volumeResource,
553                                           Msvm_ResourceAllocationSettingData_WmiInfo,
554                                           NULL) < 0)
555         return -1;
556 
557     return 0;
558 }
559 
560 
561 static int
hypervDomainAttachVirtualDisk(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * controller,const char * hostname)562 hypervDomainAttachVirtualDisk(virDomainPtr domain,
563                               virDomainDiskDef *disk,
564                               Msvm_ResourceAllocationSettingData *controller,
565                               const char *hostname)
566 {
567     g_autofree char *parentInstanceID = NULL;
568     g_auto(WsXmlDocH) response = NULL;
569 
570     VIR_DEBUG("Now attaching disk image '%s' with address %d to bus %d of type %d",
571               disk->src->path, disk->info.addr.drive.unit, disk->info.addr.drive.controller, disk->bus);
572 
573     if (hypervDomainAddVirtualDiskParent(domain, disk, controller, hostname, &response) < 0)
574         return -1;
575 
576     if (!response) {
577         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add virtual disk parent"));
578         return -1;
579     }
580 
581     parentInstanceID = hypervGetInstanceIDFromXMLResponse(response);
582     if (!parentInstanceID)
583         return -1;
584 
585     if (hypervDomainAddVirtualHardDisk(domain, disk, hostname, parentInstanceID) < 0)
586         return -1;
587 
588     return 0;
589 }
590 
591 
592 static int
hypervDomainAttachPhysicalDisk(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * controller,const char * hostname)593 hypervDomainAttachPhysicalDisk(virDomainPtr domain,
594                                virDomainDiskDef *disk,
595                                Msvm_ResourceAllocationSettingData *controller,
596                                const char *hostname)
597 {
598     hypervPrivate *priv = domain->conn->privateData;
599     g_autofree char *hostResource = NULL;
600     g_autofree char *controller__PATH = NULL;
601     g_auto(GStrv) matches = NULL;
602     ssize_t found = 0;
603     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
604     g_autoptr(Msvm_ResourceAllocationSettingData) diskdefault = NULL;
605     g_autofree char *controllerInstanceIdEscaped = NULL;
606     g_autoptr(GHashTable) diskResource = NULL;
607     g_autofree char *addressString = g_strdup_printf("%u", disk->info.addr.drive.unit);
608     g_autofree char *resourceType = NULL;
609 
610     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_DISK_DRIVE);
611 
612     if (strstr(disk->src->path, "NODRIVE")) {
613         /* Hyper-V doesn't let you define LUNs with no connection */
614         VIR_DEBUG("Skipping empty LUN '%s' with address %d on bus %d of type %d",
615                   disk->src->path, disk->info.addr.drive.unit,
616                   disk->info.addr.drive.controller, disk->bus);
617         return 0;
618     }
619 
620     VIR_DEBUG("Now attaching LUN '%s' with address %d to bus %d of type %d",
621               disk->src->path, disk->info.addr.drive.unit,
622               disk->info.addr.drive.controller, disk->bus);
623 
624     /* prepare HostResource */
625 
626     /* get Msvm_DiskDrive root device ID */
627     virBufferAddLit(&query,
628                     MSVM_RESOURCEALLOCATIONSETTINGDATA_WQL_SELECT
629                     "WHERE ResourceSubType = 'Microsoft:Hyper-V:Physical Disk Drive' "
630                     "AND InstanceID LIKE '%%Default%%'");
631 
632     if (hypervGetWmiClass(Msvm_ResourceAllocationSettingData, &diskdefault) < 0)
633         return -1;
634 
635     if (!diskdefault) {
636         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
637                        _("Could not retrieve default Msvm_DiskDrive object"));
638         return -1;
639     }
640 
641     found = virStringSearch(diskdefault->data->InstanceID,
642                             "([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})",
643                             1, &matches);
644 
645     if (found < 1) {
646         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
647                        _("Could not get Msvm_DiskDrive default InstanceID"));
648         return -1;
649     }
650 
651     hostResource = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
652                                    "Msvm_DiskDrive.CreationClassName=\"Msvm_DiskDrive\","
653                                    "DeviceID=\"Microsoft:%s\\\\%s\","
654                                    "SystemCreationClassName=\"Msvm_ComputerSystem\","
655                                    "SystemName=\"%s\"",
656                                    hostname, matches[0], disk->src->path, hostname);
657 
658     /* create embedded param */
659     diskResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
660     if (!diskResource)
661         return -1;
662 
663     controllerInstanceIdEscaped = virStringReplace(controller->data->InstanceID, "\\", "\\\\");
664     controller__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
665                                        "Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
666                                        hostname, controllerInstanceIdEscaped);
667     if (!controller__PATH)
668         return -1;
669 
670     if (hypervSetEmbeddedProperty(diskResource, "Parent", controller__PATH) < 0)
671         return -1;
672 
673     if (hypervSetEmbeddedProperty(diskResource, "AddressOnParent", addressString) < 0)
674         return -1;
675 
676     if (hypervSetEmbeddedProperty(diskResource, "ResourceType", resourceType) < 0)
677         return -1;
678 
679     if (hypervSetEmbeddedProperty(diskResource, "ResourceSubType",
680                                   "Microsoft:Hyper-V:Physical Disk Drive") < 0)
681         return -1;
682 
683     if (hypervSetEmbeddedProperty(diskResource, "HostResource", hostResource) < 0)
684         return -1;
685 
686     if (hypervMsvmVSMSAddResourceSettings(domain, &diskResource,
687                                           Msvm_ResourceAllocationSettingData_WmiInfo,
688                                           NULL) < 0)
689         return -1;
690 
691     return 0;
692 }
693 
694 
695 static int
hypervDomainAddOpticalDrive(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * controller,const char * hostname,WsXmlDocH * response)696 hypervDomainAddOpticalDrive(virDomainPtr domain,
697                             virDomainDiskDef *disk,
698                             Msvm_ResourceAllocationSettingData *controller,
699                             const char *hostname,
700                             WsXmlDocH *response)
701 {
702     g_autoptr(GHashTable) driveResource = NULL;
703     g_autofree char *parentInstanceIDEscaped = NULL;
704     g_autofree char *parent__PATH = NULL;
705     g_autofree char *addressString = g_strdup_printf("%u", disk->info.addr.drive.unit);
706     g_autofree char *resourceType = NULL;
707 
708     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_DVD_DRIVE);
709 
710     driveResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
711     if (!driveResource)
712         return -1;
713 
714     parentInstanceIDEscaped = virStringReplace(controller->data->InstanceID, "\\", "\\\\");
715     parent__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
716                                    "Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
717                                    hostname, parentInstanceIDEscaped);
718     if (!parent__PATH)
719         return -1;
720 
721     if (hypervSetEmbeddedProperty(driveResource, "Parent", parent__PATH) < 0)
722         return -1;
723 
724     if (hypervSetEmbeddedProperty(driveResource, "AddressOnParent", addressString) < 0)
725         return -1;
726 
727     if (hypervSetEmbeddedProperty(driveResource, "ResourceType", resourceType) < 0)
728         return -1;
729 
730     if (hypervSetEmbeddedProperty(driveResource, "ResourceSubType",
731                                   "Microsoft:Hyper-V:Synthetic DVD Drive") < 0)
732         return -1;
733 
734     if (hypervMsvmVSMSAddResourceSettings(domain, &driveResource,
735                                           Msvm_ResourceAllocationSettingData_WmiInfo, response) < 0)
736         return -1;
737 
738     return 0;
739 }
740 
741 
742 static int
hypervDomainAddOpticalDisk(virDomainPtr domain,virDomainDiskDef * disk,const char * hostname,char * driveInstanceID)743 hypervDomainAddOpticalDisk(virDomainPtr domain,
744                            virDomainDiskDef *disk,
745                            const char *hostname,
746                            char *driveInstanceID)
747 {
748     g_autoptr(GHashTable) volumeResource = NULL;
749     g_autofree char *vhdInstanceIdEscaped = NULL;
750     g_autofree char *vhd__PATH = NULL;
751     g_autofree char *resourceType = NULL;
752 
753     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_LOGICAL_DISK);
754 
755     volumeResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
756     if (!volumeResource)
757         return -1;
758 
759     vhdInstanceIdEscaped = virStringReplace(driveInstanceID, "\\", "\\\\");
760     vhd__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
761                                 "Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
762                                 hostname, vhdInstanceIdEscaped);
763     if (!vhd__PATH)
764         return -1;
765 
766     if (hypervSetEmbeddedProperty(volumeResource, "Parent", vhd__PATH) < 0)
767         return -1;
768 
769     if (hypervSetEmbeddedProperty(volumeResource, "HostResource", disk->src->path) < 0)
770         return -1;
771 
772     if (hypervSetEmbeddedProperty(volumeResource, "ResourceType", resourceType) < 0)
773         return -1;
774 
775     if (hypervSetEmbeddedProperty(volumeResource, "ResourceSubType",
776                                   "Microsoft:Hyper-V:Virtual CD/DVD Disk") < 0)
777         return -1;
778 
779     if (hypervMsvmVSMSAddResourceSettings(domain, &volumeResource,
780                                           Msvm_ResourceAllocationSettingData_WmiInfo, NULL) < 0)
781         return -1;
782 
783     return 0;
784 }
785 
786 
787 static int
hypervDomainAttachCDROM(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * controller,const char * hostname)788 hypervDomainAttachCDROM(virDomainPtr domain,
789                         virDomainDiskDef *disk,
790                         Msvm_ResourceAllocationSettingData *controller,
791                         const char *hostname)
792 {
793     g_auto(WsXmlDocH) response = NULL;
794     g_autofree char *driveInstanceID = NULL;
795 
796     VIR_DEBUG("Now attaching CD/DVD '%s' with address %d to bus %d of type %d",
797               disk->src->path, disk->info.addr.drive.unit,
798               disk->info.addr.drive.controller, disk->bus);
799 
800     if (hypervDomainAddOpticalDrive(domain, disk, controller, hostname, &response) < 0)
801         return -1;
802 
803     driveInstanceID = hypervGetInstanceIDFromXMLResponse(response);
804     if (!driveInstanceID)
805         return -1;
806 
807     if (hypervDomainAddOpticalDisk(domain, disk, hostname, driveInstanceID) < 0)
808         return -1;
809 
810     return 0;
811 }
812 
813 
814 static int
hypervDomainAttachFloppy(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * driveSettings,const char * hostname)815 hypervDomainAttachFloppy(virDomainPtr domain,
816                          virDomainDiskDef *disk,
817                          Msvm_ResourceAllocationSettingData *driveSettings,
818                          const char *hostname)
819 {
820     g_autoptr(GHashTable) volumeResource = NULL;
821     g_autofree char *vhdInstanceIdEscaped = NULL;
822     g_autofree char *vfd__PATH = NULL;
823     g_autofree char *resourceType = NULL;
824 
825     resourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_LOGICAL_DISK);
826 
827     volumeResource = hypervCreateEmbeddedParam(Msvm_ResourceAllocationSettingData_WmiInfo);
828     if (!volumeResource)
829         return -1;
830 
831     vhdInstanceIdEscaped = virStringReplace(driveSettings->data->InstanceID, "\\", "\\\\");
832     vfd__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
833                                 "Msvm_ResourceAllocationSettingData.InstanceID=\"%s\"",
834                                 hostname, vhdInstanceIdEscaped);
835 
836     if (!vfd__PATH)
837         return -1;
838 
839     if (hypervSetEmbeddedProperty(volumeResource, "Parent", vfd__PATH) < 0)
840         return -1;
841 
842     if (hypervSetEmbeddedProperty(volumeResource, "HostResource", disk->src->path) < 0)
843         return -1;
844 
845     if (hypervSetEmbeddedProperty(volumeResource, "ResourceType", resourceType) < 0)
846         return -1;
847 
848     if (hypervSetEmbeddedProperty(volumeResource, "ResourceSubType",
849                                   "Microsoft:Hyper-V:Virtual Floppy Disk") < 0)
850         return -1;
851 
852     if (hypervMsvmVSMSAddResourceSettings(domain, &volumeResource,
853                                           Msvm_ResourceAllocationSettingData_WmiInfo,
854                                           NULL) < 0)
855         return -1;
856 
857     return 0;
858 }
859 
860 
861 static int
hypervDomainAttachStorageVolume(virDomainPtr domain,virDomainDiskDef * disk,Msvm_ResourceAllocationSettingData * controller,const char * hostname)862 hypervDomainAttachStorageVolume(virDomainPtr domain,
863                                 virDomainDiskDef *disk,
864                                 Msvm_ResourceAllocationSettingData *controller,
865                                 const char *hostname)
866 {
867     if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
868         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported disk address type"));
869         return -1;
870     }
871 
872     switch (disk->device) {
873     case VIR_DOMAIN_DISK_DEVICE_DISK:
874         if (disk->src->type == VIR_STORAGE_TYPE_FILE)
875             return hypervDomainAttachVirtualDisk(domain, disk, controller, hostname);
876         else if (disk->src->type == VIR_STORAGE_TYPE_BLOCK)
877             return hypervDomainAttachPhysicalDisk(domain, disk, controller, hostname);
878         else
879             virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported disk type"));
880         break;
881     case VIR_DOMAIN_DISK_DEVICE_CDROM:
882         return hypervDomainAttachCDROM(domain, disk, controller, hostname);
883     case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
884         return hypervDomainAttachFloppy(domain, disk, controller, hostname);
885     case VIR_DOMAIN_DISK_DEVICE_LUN:
886     case VIR_DOMAIN_DISK_DEVICE_LAST:
887     default:
888         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported disk bus"));
889         break;
890     }
891 
892     return -1;
893 }
894 
895 
896 static int
hypervDomainAttachStorage(virDomainPtr domain,virDomainDef * def,const char * hostname)897 hypervDomainAttachStorage(virDomainPtr domain, virDomainDef *def, const char *hostname)
898 {
899     hypervPrivate *priv = domain->conn->privateData;
900     size_t i = 0;
901     char uuid_string[VIR_UUID_STRING_BUFLEN];
902     int num_scsi_controllers = 0;
903     int ctrlr_idx = -1;
904     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
905     g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
906     Msvm_ResourceAllocationSettingData *entry = NULL;
907     Msvm_ResourceAllocationSettingData *ideChannels[HYPERV_MAX_IDE_CHANNELS];
908     Msvm_ResourceAllocationSettingData *scsiControllers[HYPERV_MAX_SCSI_CONTROLLERS];
909     Msvm_ResourceAllocationSettingData *floppySettings = NULL;
910 
911     /* start with attaching scsi controllers */
912     for (i = 0; i < def->ncontrollers; i++) {
913         if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
914             continue;
915 
916         if (hypervDomainCreateSCSIController(domain, def->controllers[i]) < 0)
917             return -1;
918     }
919 
920     virUUIDFormat(domain->uuid, uuid_string);
921 
922     /* filter through all the rasd entries and isolate our controllers */
923     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
924         return -1;
925 
926     if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0)
927         return -1;
928 
929     entry = rasd;
930     while (entry) {
931         if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER)
932             ideChannels[entry->data->Address[0] - '0'] = entry;
933         else if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA)
934             scsiControllers[num_scsi_controllers++] = entry;
935         else if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE)
936             floppySettings = entry;
937 
938         entry = entry->next;
939     }
940 
941     /* now we loop through and attach all the disks */
942     for (i = 0; i < def->ndisks; i++) {
943         switch (def->disks[i]->bus) {
944         case VIR_DOMAIN_DISK_BUS_IDE:
945             ctrlr_idx = def->disks[i]->info.addr.drive.bus;
946             if (hypervDomainAttachStorageVolume(domain, def->disks[i],
947                                                 ideChannels[ctrlr_idx], hostname) < 0) {
948                 return -1;
949             }
950             break;
951         case VIR_DOMAIN_DISK_BUS_SCSI:
952             ctrlr_idx = def->disks[i]->info.addr.drive.controller;
953             if (hypervDomainAttachStorageVolume(domain, def->disks[i],
954                                                 scsiControllers[ctrlr_idx], hostname) < 0) {
955                 return -1;
956             }
957             break;
958         case VIR_DOMAIN_DISK_BUS_FDC:
959             if (hypervDomainAttachFloppy(domain, def->disks[i], floppySettings, hostname) < 0)
960                 return -1;
961             break;
962         case VIR_DOMAIN_DISK_BUS_NONE:
963         case VIR_DOMAIN_DISK_BUS_VIRTIO:
964         case VIR_DOMAIN_DISK_BUS_XEN:
965         case VIR_DOMAIN_DISK_BUS_USB:
966         case VIR_DOMAIN_DISK_BUS_UML:
967         case VIR_DOMAIN_DISK_BUS_SATA:
968         case VIR_DOMAIN_DISK_BUS_SD:
969         case VIR_DOMAIN_DISK_BUS_LAST:
970         default:
971             virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported controller type"));
972             return -1;
973         }
974     }
975 
976     return 0;
977 }
978 
979 
980 static int
hypervDomainAttachSerial(virDomainPtr domain,virDomainChrDef * serial)981 hypervDomainAttachSerial(virDomainPtr domain, virDomainChrDef *serial)
982 {
983     char uuid_string[VIR_UUID_STRING_BUFLEN];
984     hypervPrivate *priv = domain->conn->privateData;
985     g_autofree char *com_string = g_strdup_printf("COM %d", serial->target.port);
986     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
987     g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
988     g_autoptr(Msvm_SerialPortSettingData) spsd = NULL;
989     hypervWmiClassInfo *classInfo = NULL;
990     Msvm_ResourceAllocationSettingData *current = NULL;
991     Msvm_ResourceAllocationSettingData *entry = NULL;
992     g_autoptr(GHashTable) serialResource = NULL;
993     const char *connectionValue = NULL;
994     g_autofree const char *resourceType = NULL;
995 
996     virUUIDFormat(domain->uuid, uuid_string);
997 
998     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
999         return -1;
1000 
1001     if (g_str_has_prefix(priv->version, "6.")) {
1002         if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0)
1003             return -1;
1004 
1005         current = rasd;
1006         classInfo = Msvm_ResourceAllocationSettingData_WmiInfo;
1007     } else {
1008         if (hypervGetSerialPortSD(priv, vssd->data->InstanceID, &spsd) < 0)
1009             return -1;
1010 
1011         current = (Msvm_ResourceAllocationSettingData *)spsd;
1012         classInfo = Msvm_SerialPortSettingData_WmiInfo;
1013     }
1014 
1015     while (current) {
1016         if (current->data->ResourceType == MSVM_RASD_RESOURCETYPE_SERIAL_PORT &&
1017             STREQ(current->data->ElementName, com_string)) {
1018             /* found our com port */
1019             entry = current;
1020             break;
1021         }
1022         current = current->next;
1023     }
1024 
1025     if (!entry)
1026         return -1;
1027 
1028     if (STRNEQ(serial->source->data.file.path, "-1"))
1029         connectionValue = serial->source->data.file.path;
1030     else
1031         connectionValue = "";
1032 
1033     resourceType = g_strdup_printf("%d", entry->data->ResourceType);
1034 
1035     serialResource = hypervCreateEmbeddedParam(classInfo);
1036     if (!serialResource)
1037         return -1;
1038 
1039     if (hypervSetEmbeddedProperty(serialResource, "Connection", connectionValue) < 0)
1040         return -1;
1041 
1042     if (hypervSetEmbeddedProperty(serialResource, "InstanceID", entry->data->InstanceID) < 0)
1043         return -1;
1044 
1045     if (hypervSetEmbeddedProperty(serialResource, "ResourceType", resourceType) < 0)
1046         return -1;
1047 
1048     if (hypervSetEmbeddedProperty(serialResource, "ResourceSubType",
1049                                   entry->data->ResourceSubType) < 0)
1050         return -1;
1051 
1052     if (hypervMsvmVSMSModifyResourceSettings(priv, &serialResource, classInfo) < 0)
1053         return -1;
1054 
1055     return 0;
1056 }
1057 
1058 
1059 static int
hypervDomainAttachSyntheticEthernetAdapter(virDomainPtr domain,virDomainNetDef * net,char * hostname)1060 hypervDomainAttachSyntheticEthernetAdapter(virDomainPtr domain,
1061                                            virDomainNetDef *net,
1062                                            char *hostname)
1063 {
1064     hypervPrivate *priv = domain->conn->privateData;
1065     g_autofree char *portResourceType = NULL;
1066     unsigned char vsiGuid[VIR_UUID_BUFLEN];
1067     char guidString[VIR_UUID_STRING_BUFLEN];
1068     g_autofree char *virtualSystemIdentifiers = NULL;
1069     char macString[VIR_MAC_STRING_BUFLEN];
1070     g_autofree char *macAddressNoColons = NULL;
1071     g_autoptr(GHashTable) portResource = NULL;
1072     g_auto(WsXmlDocH) sepsdResponse = NULL;
1073     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
1074     g_autoptr(Msvm_VirtualEthernetSwitch) vSwitch = NULL;
1075     g_autofree char *enabledState = NULL;
1076     g_autofree char *switch__PATH = NULL;
1077     g_autofree char *sepsd__PATH = NULL;
1078     g_autofree char *sepsdInstanceID = NULL;
1079     g_autofree char *sepsdInstanceEscaped = NULL;
1080     g_autofree char *connectionResourceType = NULL;
1081     g_autoptr(GHashTable) connectionResource = NULL;
1082 
1083     /*
1084      * Step 1: Create the Msvm_SyntheticEthernetPortSettingData object
1085      * that holds half the settings for the new adapter we are creating
1086      */
1087     portResourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_ETHERNET_ADAPTER);
1088 
1089     if (virUUIDGenerate(vsiGuid) < 0)
1090         return -1;
1091 
1092     virUUIDFormat(vsiGuid, guidString);
1093     virtualSystemIdentifiers = g_strdup_printf("{%s}", guidString);
1094 
1095     virMacAddrFormat(&net->mac, macString);
1096     macAddressNoColons = virStringReplace(macString, ":", "");
1097 
1098     /* prepare embedded param */
1099     portResource = hypervCreateEmbeddedParam(Msvm_SyntheticEthernetPortSettingData_WmiInfo);
1100     if (!portResource)
1101         return -1;
1102 
1103     if (hypervSetEmbeddedProperty(portResource, "ResourceType", portResourceType) < 0)
1104         return -1;
1105 
1106     if (hypervSetEmbeddedProperty(portResource, "ResourceSubType",
1107                                   "Microsoft:Hyper-V:Synthetic Ethernet Port") < 0)
1108         return -1;
1109 
1110     if (hypervSetEmbeddedProperty(portResource,
1111                                   "VirtualSystemIdentifiers", virtualSystemIdentifiers) < 0)
1112         return -1;
1113 
1114     if (hypervSetEmbeddedProperty(portResource, "Address", macAddressNoColons) < 0)
1115         return -1;
1116 
1117     if (hypervSetEmbeddedProperty(portResource, "StaticMacAddress", "true") < 0)
1118         return -1;
1119 
1120     if (hypervMsvmVSMSAddResourceSettings(domain, &portResource,
1121                                           Msvm_SyntheticEthernetPortSettingData_WmiInfo,
1122                                           &sepsdResponse) < 0)
1123         return -1;
1124 
1125     /*
1126      * Step 2: Get the Msvm_VirtualEthernetSwitch object
1127      */
1128     virBufferAsprintf(&query,
1129                       MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE Name='%s'",
1130                       net->data.bridge.brname);
1131 
1132     if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &vSwitch) < 0)
1133         return -1;
1134 
1135     if (!vSwitch)
1136         return -1;
1137 
1138     /*
1139      * Step 3: Create the Msvm_EthernetPortAllocationSettingData object that
1140      * holds the other half of the network configuration
1141      */
1142     enabledState = g_strdup_printf("%d", MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_ENABLED);
1143 
1144     /* build the two __PATH variables */
1145     switch__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:"
1146                                    "Msvm_VirtualEthernetSwitch.CreationClassName=\"Msvm_VirtualEthernetSwitch\","
1147                                    "Name=\"%s\"",
1148                                    hostname, vSwitch->data->Name);
1149 
1150     /* Get the sepsd instance ID out of the XML response */
1151     sepsdInstanceID = hypervGetInstanceIDFromXMLResponse(sepsdResponse);
1152     sepsdInstanceEscaped = virStringReplace(sepsdInstanceID, "\\", "\\\\");
1153     sepsd__PATH = g_strdup_printf("\\\\%s\\root\\virtualization\\v2:"
1154                                   "Msvm_SyntheticEthernetPortSettingData.InstanceID=\"%s\"",
1155                                   hostname, sepsdInstanceEscaped);
1156 
1157     connectionResourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_ETHERNET_CONNECTION);
1158 
1159     connectionResource = hypervCreateEmbeddedParam(Msvm_EthernetPortAllocationSettingData_WmiInfo);
1160     if (!connectionResource)
1161         return -1;
1162 
1163     if (hypervSetEmbeddedProperty(connectionResource, "EnabledState", enabledState) < 0)
1164         return -1;
1165 
1166     if (hypervSetEmbeddedProperty(connectionResource, "HostResource", switch__PATH) < 0)
1167         return -1;
1168 
1169     if (hypervSetEmbeddedProperty(connectionResource, "Parent", sepsd__PATH) < 0)
1170         return -1;
1171 
1172     if (hypervSetEmbeddedProperty(connectionResource, "ResourceType", connectionResourceType) < 0)
1173         return -1;
1174 
1175     if (hypervSetEmbeddedProperty(connectionResource,
1176                                   "ResourceSubType", "Microsoft:Hyper-V:Ethernet Connection") < 0)
1177         return -1;
1178 
1179     if (hypervMsvmVSMSAddResourceSettings(domain, &connectionResource,
1180                                           Msvm_EthernetPortAllocationSettingData_WmiInfo, NULL) < 0)
1181         return -1;
1182 
1183     return 0;
1184 }
1185 
1186 
1187 /*
1188  * Functions for deserializing device entries
1189  */
1190 static int
hypervDomainDefAppendController(virDomainDef * def,int idx,virDomainControllerType controllerType)1191 hypervDomainDefAppendController(virDomainDef *def,
1192                                 int idx,
1193                                 virDomainControllerType controllerType)
1194 {
1195     virDomainControllerDef *controller = NULL;
1196 
1197     if (!(controller = virDomainControllerDefNew(controllerType)))
1198         return -1;
1199 
1200     controller->idx = idx;
1201 
1202     VIR_APPEND_ELEMENT(def->controllers, def->ncontrollers, controller);
1203 
1204     return 0;
1205 }
1206 
1207 
1208 static int
hypervDomainDefAppendIDEController(virDomainDef * def)1209 hypervDomainDefAppendIDEController(virDomainDef *def)
1210 {
1211     return hypervDomainDefAppendController(def, 0, VIR_DOMAIN_CONTROLLER_TYPE_IDE);
1212 }
1213 
1214 
1215 static int
hypervDomainDefAppendSCSIController(virDomainDef * def,int idx)1216 hypervDomainDefAppendSCSIController(virDomainDef *def, int idx)
1217 {
1218     return hypervDomainDefAppendController(def, idx, VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
1219 }
1220 
1221 
1222 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)1223 hypervDomainDefAppendDisk(virDomainDef *def,
1224                           virDomainDiskDef *disk,
1225                           virDomainDiskBus busType,
1226                           int diskNameOffset,
1227                           const char *diskNamePrefix,
1228                           int maxControllers,
1229                           Msvm_ResourceAllocationSettingData **controllers,
1230                           Msvm_ResourceAllocationSettingData *diskParent,
1231                           Msvm_ResourceAllocationSettingData *diskController)
1232 {
1233     size_t i = 0;
1234     int ctrlr_idx = -1;
1235     int addr = -1;
1236 
1237     if (virStrToLong_i(diskParent->data->AddressOnParent, NULL, 10, &addr) < 0)
1238         return -1;
1239 
1240     if (addr < 0)
1241         return -1;
1242 
1243     /* Find controller index */
1244     for (i = 0; i < maxControllers; i++) {
1245         if (diskController == controllers[i]) {
1246             ctrlr_idx = i;
1247             break;
1248         }
1249     }
1250 
1251     if (ctrlr_idx < 0) {
1252         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find controller for disk!"));
1253         return -1;
1254     }
1255 
1256     disk->bus = busType;
1257     disk->dst = virIndexToDiskName(ctrlr_idx * diskNameOffset + addr, diskNamePrefix);
1258     if (busType == VIR_DOMAIN_DISK_BUS_IDE) {
1259         disk->info.addr.drive.controller = 0;
1260         disk->info.addr.drive.bus = ctrlr_idx;
1261     } else {
1262         disk->info.addr.drive.controller = ctrlr_idx;
1263         disk->info.addr.drive.bus = 0;
1264     }
1265     disk->info.addr.drive.target = 0;
1266     disk->info.addr.drive.unit = addr;
1267 
1268     VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk);
1269 
1270     return 0;
1271 }
1272 
1273 
1274 static int
hypervDomainDefParseFloppyStorageExtent(virDomainDef * def,virDomainDiskDef * disk)1275 hypervDomainDefParseFloppyStorageExtent(virDomainDef *def, virDomainDiskDef *disk)
1276 {
1277     disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
1278     disk->dst = g_strdup("fda");
1279 
1280     VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk);
1281 
1282     return 0;
1283 }
1284 
1285 
1286 static int
hypervDomainDefParseVirtualExtent(hypervPrivate * priv,virDomainDef * def,Msvm_StorageAllocationSettingData * disk_entry,Msvm_ResourceAllocationSettingData * rasd,Msvm_ResourceAllocationSettingData ** ideChannels,Msvm_ResourceAllocationSettingData ** scsiControllers)1287 hypervDomainDefParseVirtualExtent(hypervPrivate *priv,
1288                                   virDomainDef *def,
1289                                   Msvm_StorageAllocationSettingData *disk_entry,
1290                                   Msvm_ResourceAllocationSettingData *rasd,
1291                                   Msvm_ResourceAllocationSettingData **ideChannels,
1292                                   Msvm_ResourceAllocationSettingData **scsiControllers)
1293 {
1294     Msvm_ResourceAllocationSettingData *diskParent = NULL;
1295     Msvm_ResourceAllocationSettingData *controller = NULL;
1296     virDomainDiskDef *disk = NULL;
1297     int result = -1;
1298 
1299     if (disk_entry->data->HostResource.count < 1)
1300         goto cleanup;
1301 
1302     if (!(disk = virDomainDiskDefNew(priv->xmlopt))) {
1303         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not allocate disk definition"));
1304         goto cleanup;
1305     }
1306 
1307     /* get disk associated with storage extent */
1308     if (hypervGetDeviceParentRasdFromDeviceId(disk_entry->data->Parent, rasd, &diskParent) < 0)
1309         goto cleanup;
1310 
1311     /* get associated controller */
1312     if (hypervGetDeviceParentRasdFromDeviceId(diskParent->data->Parent, rasd, &controller) < 0)
1313         goto cleanup;
1314 
1315     /* common fields first */
1316     disk->src->type = VIR_STORAGE_TYPE_FILE;
1317     disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
1318 
1319     /* note if it's a CDROM disk */
1320     if (STREQ(disk_entry->data->ResourceSubType, "Microsoft:Hyper-V:Virtual CD/DVD Disk"))
1321         disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
1322     else
1323         disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
1324 
1325     /* copy in the source path */
1326     virDomainDiskSetSource(disk, *(char **)disk_entry->data->HostResource.data);
1327 
1328     /* controller-specific fields */
1329     if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA) {
1330         if (hypervDomainDefAppendDisk(def, disk, VIR_DOMAIN_DISK_BUS_SCSI,
1331                                       64, "sd", HYPERV_MAX_SCSI_CONTROLLERS,
1332                                       scsiControllers, diskParent, controller) < 0) {
1333             goto cleanup;
1334         }
1335     } else if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER) {
1336         if (hypervDomainDefAppendDisk(def, disk, VIR_DOMAIN_DISK_BUS_IDE,
1337                                       2, "hd", HYPERV_MAX_IDE_CHANNELS,
1338                                       ideChannels, diskParent, controller) < 0) {
1339             goto cleanup;
1340         }
1341     } else if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_OTHER &&
1342                diskParent->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE) {
1343         if (hypervDomainDefParseFloppyStorageExtent(def, disk) < 0)
1344             goto cleanup;
1345         disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
1346     } else {
1347         virReportError(VIR_ERR_INTERNAL_ERROR,
1348                        _("Unrecognized controller type %d"),
1349                        controller->data->ResourceType);
1350         goto cleanup;
1351     }
1352 
1353     result = 0;
1354 
1355  cleanup:
1356     if (result != 0 && disk)
1357         virDomainDiskDefFree(disk);
1358 
1359     return result;
1360 }
1361 
1362 
1363 static int
hypervDomainDefParsePhysicalDisk(hypervPrivate * priv,virDomainDef * def,Msvm_ResourceAllocationSettingData * entry,Msvm_ResourceAllocationSettingData * rasd,Msvm_ResourceAllocationSettingData ** ideChannels,Msvm_ResourceAllocationSettingData ** scsiControllers)1364 hypervDomainDefParsePhysicalDisk(hypervPrivate *priv,
1365                                  virDomainDef *def,
1366                                  Msvm_ResourceAllocationSettingData *entry,
1367                                  Msvm_ResourceAllocationSettingData *rasd,
1368                                  Msvm_ResourceAllocationSettingData **ideChannels,
1369                                  Msvm_ResourceAllocationSettingData **scsiControllers)
1370 {
1371     int result = -1;
1372     Msvm_ResourceAllocationSettingData *controller = NULL;
1373     g_autoptr(Msvm_DiskDrive) diskdrive = NULL;
1374     virDomainDiskDef *disk = NULL;
1375     char **hostResource = entry->data->HostResource.data;
1376     g_autofree char *hostEscaped = NULL;
1377     g_autofree char *driveNumberStr = NULL;
1378     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
1379     int addr = -1, ctrlr_idx = -1;
1380     size_t i = 0;
1381 
1382     if (virStrToLong_i(entry->data->AddressOnParent, NULL, 10, &addr) < 0)
1383         return -1;
1384 
1385     if (addr < 0)
1386         return -1;
1387 
1388     if (hypervGetDeviceParentRasdFromDeviceId(entry->data->Parent, rasd, &controller) < 0)
1389         goto cleanup;
1390 
1391     /* create disk definition */
1392     if (!(disk = virDomainDiskDefNew(priv->xmlopt))) {
1393         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not allocate disk def"));
1394         goto cleanup;
1395     }
1396 
1397     /* Query Msvm_DiskDrive for the DriveNumber */
1398     hostEscaped = virStringReplace(*hostResource, "\\\"", "\"");
1399     hostEscaped = virStringReplace(hostEscaped, "\\", "\\\\");
1400 
1401     /* quotes must be preserved, so virBufferEscapeSQL can't be used */
1402     virBufferAsprintf(&query,
1403                       MSVM_DISKDRIVE_WQL_SELECT "WHERE __PATH='%s'",
1404                       hostEscaped);
1405 
1406     if (hypervGetWmiClass(Msvm_DiskDrive, &diskdrive) < 0)
1407         goto cleanup;
1408 
1409     if (!diskdrive) {
1410         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find Msvm_DiskDrive object"));
1411         goto cleanup;
1412     }
1413 
1414     driveNumberStr = g_strdup_printf("%u", diskdrive->data->DriveNumber);
1415     virDomainDiskSetSource(disk, driveNumberStr);
1416 
1417     if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA) {
1418         for (i = 0; i < HYPERV_MAX_SCSI_CONTROLLERS; i++) {
1419             if (controller == scsiControllers[i]) {
1420                 ctrlr_idx = i;
1421                 break;
1422             }
1423         }
1424         disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
1425         disk->dst = virIndexToDiskName(ctrlr_idx * 64 + addr, "sd");
1426         disk->info.addr.drive.unit = addr;
1427         disk->info.addr.drive.controller = ctrlr_idx;
1428         disk->info.addr.drive.bus = 0;
1429     } else if (controller->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER) {
1430         for (i = 0; i < HYPERV_MAX_IDE_CHANNELS; i++) {
1431             if (controller == ideChannels[i]) {
1432                 ctrlr_idx = i;
1433                 break;
1434             }
1435         }
1436         disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
1437         disk->dst = virIndexToDiskName(ctrlr_idx * 4 + addr, "hd");
1438         disk->info.addr.drive.unit = addr;
1439         disk->info.addr.drive.controller = 0;
1440         disk->info.addr.drive.bus = ctrlr_idx;
1441     } else {
1442         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid controller type for LUN"));
1443         goto cleanup;
1444     }
1445 
1446     disk->info.addr.drive.target = 0;
1447     virDomainDiskSetType(disk, VIR_STORAGE_TYPE_BLOCK);
1448     disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
1449 
1450     disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
1451 
1452     VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk);
1453 
1454     result = 0;
1455 
1456  cleanup:
1457     if (result != 0 && disk)
1458         virDomainDiskDefFree(disk);
1459 
1460     return result;
1461 }
1462 
1463 
1464 static int
hypervDomainDefParseStorage(hypervPrivate * priv,virDomainDef * def,Msvm_ResourceAllocationSettingData * rasd,Msvm_StorageAllocationSettingData * sasd)1465 hypervDomainDefParseStorage(hypervPrivate *priv,
1466                             virDomainDef *def,
1467                             Msvm_ResourceAllocationSettingData *rasd,
1468                             Msvm_StorageAllocationSettingData *sasd)
1469 {
1470     Msvm_ResourceAllocationSettingData *entry = rasd;
1471     Msvm_StorageAllocationSettingData *disk_entry = sasd;
1472     Msvm_ResourceAllocationSettingData *ideChannels[HYPERV_MAX_IDE_CHANNELS];
1473     Msvm_ResourceAllocationSettingData *scsiControllers[HYPERV_MAX_SCSI_CONTROLLERS];
1474     bool hasIdeController = false;
1475     int channel = -1;
1476     int scsi_idx = 0;
1477 
1478     /* first pass: populate storage controllers */
1479     while (entry) {
1480         if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER) {
1481             channel = entry->data->Address[0] - '0';
1482             ideChannels[channel] = entry;
1483             if (!hasIdeController) {
1484                 /* Hyper-V represents its PIIX4 controller's two channels as separate objects. */
1485                 if (hypervDomainDefAppendIDEController(def) < 0) {
1486                     virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add IDE controller"));
1487                     return -1;
1488                 }
1489                 hasIdeController = true;
1490             }
1491         } else if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA) {
1492             scsiControllers[scsi_idx++] = entry;
1493             if (hypervDomainDefAppendSCSIController(def, scsi_idx - 1) < 0) {
1494                 virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not parse SCSI controller"));
1495                 return -1;
1496             }
1497         }
1498 
1499         entry = entry->next;
1500     }
1501 
1502     /* second pass: populate physical disks */
1503     entry = rasd;
1504     while (entry) {
1505         if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISK_DRIVE &&
1506             entry->data->HostResource.count > 0) {
1507             char **hostResource = entry->data->HostResource.data;
1508 
1509             if (strstr(*hostResource, "NODRIVE")) {
1510                 /* Hyper-V doesn't let you define LUNs with no connection */
1511                 VIR_DEBUG("Skipping empty LUN '%s'", *hostResource);
1512                 entry = entry->next;
1513                 continue;
1514             }
1515 
1516             if (hypervDomainDefParsePhysicalDisk(priv, def, entry, rasd,
1517                                                  ideChannels, scsiControllers) < 0)
1518                 return -1;
1519         }
1520 
1521         entry = entry->next;
1522     }
1523 
1524     /* third pass: populate virtual disks */
1525     while (disk_entry) {
1526         if (hypervDomainDefParseVirtualExtent(priv, def, disk_entry, rasd,
1527                                               ideChannels, scsiControllers) < 0)
1528             return -1;
1529 
1530         disk_entry = disk_entry->next;
1531     }
1532 
1533     return 0;
1534 }
1535 
1536 
1537 static int
hypervDomainDefParseSerial(virDomainDef * def,Msvm_ResourceAllocationSettingData * rasd)1538 hypervDomainDefParseSerial(virDomainDef *def, Msvm_ResourceAllocationSettingData *rasd)
1539 {
1540     for (; rasd; rasd = rasd->next) {
1541         int port_num = 0;
1542         char **conn = NULL;
1543         const char *srcPath = NULL;
1544         virDomainChrDef *serial = NULL;
1545 
1546         if (rasd->data->ResourceType != MSVM_RASD_RESOURCETYPE_SERIAL_PORT)
1547             continue;
1548 
1549         /* get port number */
1550         port_num = rasd->data->ElementName[4] - '0';
1551         if (port_num < 1) {
1552             continue;
1553         }
1554 
1555         serial = virDomainChrDefNew(NULL);
1556 
1557         serial->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
1558         serial->source->type = VIR_DOMAIN_CHR_TYPE_PIPE;
1559         serial->target.port = port_num;
1560 
1561         /* set up source */
1562         if (rasd->data->Connection.count < 1) {
1563             srcPath = "-1";
1564         } else {
1565             conn = rasd->data->Connection.data;
1566             if (!*conn)
1567                 srcPath = "-1";
1568             else
1569                 srcPath = *conn;
1570         }
1571 
1572         serial->source->data.file.path = g_strdup(srcPath);
1573 
1574         VIR_APPEND_ELEMENT(def->serials, def->nserials, serial);
1575     }
1576 
1577     return 0;
1578 }
1579 
1580 
1581 static int
hypervDomainDefParseEthernetAdapter(virDomainDef * def,Msvm_EthernetPortAllocationSettingData * net,hypervPrivate * priv)1582 hypervDomainDefParseEthernetAdapter(virDomainDef *def,
1583                                     Msvm_EthernetPortAllocationSettingData *net,
1584                                     hypervPrivate *priv)
1585 {
1586     g_autoptr(virDomainNetDef) ndef = g_new0(virDomainNetDef, 1);
1587     g_autoptr(Msvm_SyntheticEthernetPortSettingData) sepsd = NULL;
1588     g_autoptr(Msvm_VirtualEthernetSwitch) vSwitch = NULL;
1589     char **switchConnection = NULL;
1590     g_autofree char *switchConnectionEscaped = NULL;
1591     char *sepsdPATH = NULL;
1592     g_autofree char *sepsdEscaped = NULL;
1593     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
1594 
1595     VIR_DEBUG("Parsing ethernet adapter '%s'", net->data->InstanceID);
1596 
1597     ndef->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
1598 
1599     /*
1600      * If there's no switch port connection or the EnabledState is disabled,
1601      * then the adapter isn't hooked up to anything and we don't have to
1602      * do anything more.
1603      */
1604     switchConnection = net->data->HostResource.data;
1605     if (net->data->HostResource.count < 1 || !*switchConnection ||
1606         net->data->EnabledState == MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_DISABLED) {
1607         VIR_DEBUG("Adapter not connected to switch");
1608         return 0;
1609     }
1610 
1611     /*
1612      * Now we retrieve the associated Msvm_SyntheticEthernetPortSettingData and
1613      * Msvm_VirtualEthernetSwitch objects and use them to build the XML definition.
1614      */
1615 
1616     /* begin by getting the Msvm_SyntheticEthernetPortSettingData object */
1617     sepsdPATH = net->data->Parent;
1618     sepsdEscaped = virStringReplace(sepsdPATH, "\\", "\\\\");
1619     virBufferAsprintf(&query,
1620                       MSVM_SYNTHETICETHERNETPORTSETTINGDATA_WQL_SELECT "WHERE __PATH = '%s'",
1621                       sepsdEscaped);
1622 
1623     if (hypervGetWmiClass(Msvm_SyntheticEthernetPortSettingData, &sepsd) < 0)
1624         return -1;
1625 
1626     if (!sepsd) {
1627         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve NIC settings"));
1628         return -1;
1629     }
1630 
1631     /* set mac address */
1632     if (virMacAddrParseHex(sepsd->data->Address, &ndef->mac) < 0)
1633         return -1;
1634 
1635     /* now we get the Msvm_VirtualEthernetSwitch */
1636     virBufferFreeAndReset(&query);
1637     switchConnectionEscaped = virStringReplace(*switchConnection, "\\", "\\\\");
1638     virBufferAsprintf(&query,
1639                       MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE __PATH = '%s'",
1640                       switchConnectionEscaped);
1641 
1642     if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &vSwitch) < 0)
1643         return -1;
1644 
1645     if (!vSwitch) {
1646         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve virtual switch"));
1647         return -1;
1648     }
1649 
1650     /* get bridge name */
1651     ndef->data.bridge.brname = g_strdup(vSwitch->data->Name);
1652 
1653     VIR_APPEND_ELEMENT(def->nets, def->nnets, ndef);
1654 
1655     return 0;
1656 }
1657 
1658 static int
hypervDomainDefParseEthernet(virDomainPtr domain,virDomainDef * def,Msvm_EthernetPortAllocationSettingData * nets)1659 hypervDomainDefParseEthernet(virDomainPtr domain,
1660                              virDomainDef *def,
1661                              Msvm_EthernetPortAllocationSettingData *nets)
1662 {
1663     Msvm_EthernetPortAllocationSettingData *entry = nets;
1664     hypervPrivate *priv = domain->conn->privateData;
1665 
1666     while (entry) {
1667         if (hypervDomainDefParseEthernetAdapter(def, entry, priv) < 0)
1668             return -1;
1669 
1670         entry = entry->next;
1671     }
1672 
1673     return 0;
1674 }
1675 
1676 
1677 /*
1678  * Driver functions
1679  */
1680 
1681 static void
hypervFreePrivate(hypervPrivate ** priv)1682 hypervFreePrivate(hypervPrivate **priv)
1683 {
1684     if (priv == NULL || *priv == NULL)
1685         return;
1686 
1687     if ((*priv)->client != NULL)
1688         wsmc_release((*priv)->client);
1689 
1690     if ((*priv)->caps)
1691         virObjectUnref((*priv)->caps);
1692 
1693     if ((*priv)->xmlopt)
1694         virObjectUnref((*priv)->xmlopt);
1695 
1696     if ((*priv)->version)
1697         g_free((*priv)->version);
1698 
1699     hypervFreeParsedUri(&(*priv)->parsedUri);
1700     VIR_FREE(*priv);
1701 }
1702 
1703 
1704 static int
hypervInitConnection(virConnectPtr conn,hypervPrivate * priv,char * username,char * password)1705 hypervInitConnection(virConnectPtr conn, hypervPrivate *priv,
1706                      char *username, char *password)
1707 {
1708     /* Initialize the openwsman connection */
1709     priv->client = wsmc_create(conn->uri->server, conn->uri->port, "/wsman",
1710                                priv->parsedUri->transport, username, password);
1711 
1712     if (priv->client == NULL) {
1713         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1714                        _("Could not create openwsman client"));
1715         return -1;
1716     }
1717 
1718     if (wsmc_transport_init(priv->client, NULL) != 0) {
1719         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1720                        _("Could not initialize openwsman transport"));
1721         return -1;
1722     }
1723 
1724     /* FIXME: Currently only basic authentication is supported  */
1725     wsman_transport_set_auth_method(priv->client, "basic");
1726 
1727     return 0;
1728 }
1729 
1730 
1731 virDomainDefParserConfig hypervDomainDefParserConfig;
1732 
1733 static virDrvOpenStatus
hypervConnectOpen(virConnectPtr conn,virConnectAuthPtr auth,virConf * conf G_GNUC_UNUSED,unsigned int flags)1734 hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
1735                   virConf *conf G_GNUC_UNUSED,
1736                   unsigned int flags)
1737 {
1738     virDrvOpenStatus result = VIR_DRV_OPEN_ERROR;
1739     hypervPrivate *priv = NULL;
1740     g_autofree char *username = NULL;
1741     g_autofree char *password = NULL;
1742     g_autoptr(Win32_OperatingSystem) os = NULL;
1743 
1744     virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
1745 
1746     /* Allocate per-connection private data */
1747     priv = g_new0(hypervPrivate, 1);
1748 
1749     if (hypervParseUri(&priv->parsedUri, conn->uri) < 0)
1750         goto cleanup;
1751 
1752     /* Set the port dependent on the transport protocol if no port is
1753      * specified. This allows us to rely on the port parameter being
1754      * correctly set when building URIs later on, without the need to
1755      * distinguish between the situations port == 0 and port != 0 */
1756     if (conn->uri->port == 0) {
1757         if (STRCASEEQ(priv->parsedUri->transport, "https")) {
1758             conn->uri->port = 5986;
1759         } else {
1760             conn->uri->port = 5985;
1761         }
1762     }
1763 
1764     /* Request credentials */
1765     if (conn->uri->user != NULL) {
1766         username = g_strdup(conn->uri->user);
1767     } else {
1768         if (!(username = virAuthGetUsername(conn, auth, "hyperv",
1769                                             "administrator",
1770                                             conn->uri->server)))
1771             goto cleanup;
1772     }
1773 
1774     if (!(password = virAuthGetPassword(conn, auth, "hyperv", username,
1775                                         conn->uri->server)))
1776         goto cleanup;
1777 
1778     if (hypervInitConnection(conn, priv, username, password) < 0)
1779         goto cleanup;
1780 
1781     /* set up capabilities */
1782     priv->caps = hypervCapsInit(priv);
1783     if (!priv->caps)
1784         goto cleanup;
1785 
1786     /* init xmlopt for domain XML */
1787     priv->xmlopt = virDomainXMLOptionNew(&hypervDomainDefParserConfig, NULL, NULL, NULL, NULL);
1788 
1789     if (hypervGetOperatingSystem(priv, &os) < 0)
1790         goto cleanup;
1791 
1792     if (!os) {
1793         virReportError(VIR_ERR_INTERNAL_ERROR,
1794                        _("Could not get version information for host %s"),
1795                        conn->uri->server);
1796         goto cleanup;
1797     }
1798 
1799     priv->version = g_strdup(os->data->Version);
1800 
1801     conn->privateData = priv;
1802     priv = NULL;
1803     result = VIR_DRV_OPEN_SUCCESS;
1804 
1805  cleanup:
1806     hypervFreePrivate(&priv);
1807 
1808     return result;
1809 }
1810 
1811 
1812 static int
hypervConnectClose(virConnectPtr conn)1813 hypervConnectClose(virConnectPtr conn)
1814 {
1815     hypervPrivate *priv = conn->privateData;
1816 
1817     hypervFreePrivate(&priv);
1818 
1819     conn->privateData = NULL;
1820 
1821     return 0;
1822 }
1823 
1824 
1825 static const char *
hypervConnectGetType(virConnectPtr conn G_GNUC_UNUSED)1826 hypervConnectGetType(virConnectPtr conn G_GNUC_UNUSED)
1827 {
1828     return "Hyper-V";
1829 }
1830 
1831 
1832 static int
hypervConnectGetVersion(virConnectPtr conn,unsigned long * version)1833 hypervConnectGetVersion(virConnectPtr conn, unsigned long *version)
1834 {
1835     hypervPrivate *priv = conn->privateData;
1836     unsigned int major, minor, micro;
1837 
1838     if (hypervParseVersionString(priv->version, &major, &minor, &micro) < 0) {
1839         virReportError(VIR_ERR_INTERNAL_ERROR,
1840                        _("Could not parse version from '%s'"),
1841                        priv->version);
1842         return -1;
1843     }
1844 
1845     /*
1846      * Pack the version into an unsigned long while retaining all the digits.
1847      *
1848      * Since Microsoft's build numbers are almost always over 1000, this driver
1849      * needs to pack the value differently compared to the format defined by
1850      * virConnectGetVersion().
1851      *
1852      * This results in `virsh version` producing unexpected output.
1853      *
1854      * For example...
1855      * 2008:      6.0.6001     =>   600.6.1
1856      * 2008 R2:   6.1.7600     =>   601.7.600
1857      * 2012:      6.2.9200     =>   602.9.200
1858      * 2012 R2:   6.3.9600     =>   603.9.600
1859      * 2016:      10.0.14393   =>   1000.14.393
1860      * 2019:      10.0.17763   =>   1000.17.763
1861      */
1862     if (major > 99 || minor > 99 || micro > 999999) {
1863         virReportError(VIR_ERR_INTERNAL_ERROR,
1864                        _("Could not produce packed version number from '%s'"),
1865                        priv->version);
1866         return -1;
1867     }
1868 
1869     *version = major * 100000000 + minor * 1000000 + micro;
1870 
1871     return 0;
1872 }
1873 
1874 
1875 static char *
hypervConnectGetHostname(virConnectPtr conn)1876 hypervConnectGetHostname(virConnectPtr conn)
1877 {
1878     g_autoptr(Win32_ComputerSystem) computerSystem = NULL;
1879 
1880     if (hypervGetPhysicalSystemList((hypervPrivate *)conn->privateData, &computerSystem) < 0)
1881         return NULL;
1882 
1883     return g_strdup(computerSystem->data->DNSHostName);
1884 }
1885 
1886 
1887 static char*
hypervConnectGetCapabilities(virConnectPtr conn)1888 hypervConnectGetCapabilities(virConnectPtr conn)
1889 {
1890     hypervPrivate *priv = conn->privateData;
1891 
1892     return virCapabilitiesFormatXML(priv->caps);
1893 }
1894 
1895 
1896 static int
hypervConnectGetMaxVcpus(virConnectPtr conn,const char * type G_GNUC_UNUSED)1897 hypervConnectGetMaxVcpus(virConnectPtr conn, const char *type G_GNUC_UNUSED)
1898 {
1899     hypervPrivate *priv = conn->privateData;
1900     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
1901     g_autoptr(Msvm_ProcessorSettingData) processorSettingData = NULL;
1902 
1903     /* Get max processors definition */
1904     virBufferAddLit(&query,
1905                     MSVM_PROCESSORSETTINGDATA_WQL_SELECT
1906                     "WHERE InstanceID LIKE 'Microsoft:Definition%Maximum'");
1907 
1908     if (hypervGetWmiClass(Msvm_ProcessorSettingData, &processorSettingData) < 0)
1909         return -1;
1910 
1911     if (!processorSettingData) {
1912         virReportError(VIR_ERR_INTERNAL_ERROR,
1913                        _("Could not get maximum definition of Msvm_ProcessorSettingData for host %s"),
1914                        conn->uri->server);
1915         return -1;
1916     }
1917 
1918     return processorSettingData->data->VirtualQuantity;
1919 }
1920 
1921 
1922 static int
hypervNodeGetInfo(virConnectPtr conn,virNodeInfoPtr info)1923 hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
1924 {
1925     hypervPrivate *priv = conn->privateData;
1926     g_autoptr(Win32_ComputerSystem) computerSystem = NULL;
1927     g_autoptr(Win32_Processor) processorList = NULL;
1928     Win32_Processor *processor = NULL;
1929     char *tmp;
1930 
1931     memset(info, 0, sizeof(*info));
1932 
1933     if (hypervGetPhysicalSystemList(priv, &computerSystem) < 0)
1934         return -1;
1935 
1936     if (hypervGetProcessorsByName(priv, computerSystem->data->Name, &processorList) < 0) {
1937         return -1;
1938     }
1939 
1940     /* Strip the string to fit more relevant information in 32 chars */
1941     tmp = processorList->data->Name;
1942 
1943     while (*tmp != '\0') {
1944         if (STRPREFIX(tmp, "  ")) {
1945             memmove(tmp, tmp + 1, strlen(tmp + 1) + 1);
1946             continue;
1947         } else if (STRPREFIX(tmp, "(R)") || STRPREFIX(tmp, "(C)")) {
1948             memmove(tmp, tmp + 3, strlen(tmp + 3) + 1);
1949             continue;
1950         } else if (STRPREFIX(tmp, "(TM)")) {
1951             memmove(tmp, tmp + 4, strlen(tmp + 4) + 1);
1952             continue;
1953         } else if (STRPREFIX(tmp, " @ ")) {
1954             /* Remove " @ X.YZGHz" from the end. */
1955             *tmp = '\0';
1956             break;
1957         }
1958 
1959         ++tmp;
1960     }
1961 
1962     /* Fill struct */
1963     if (virStrcpyStatic(info->model, processorList->data->Name) < 0) {
1964         virReportError(VIR_ERR_INTERNAL_ERROR,
1965                        _("CPU model %s too long for destination"),
1966                        processorList->data->Name);
1967         return -1;
1968     }
1969 
1970     info->memory = computerSystem->data->TotalPhysicalMemory / 1024; /* byte to kilobyte */
1971     info->mhz = processorList->data->MaxClockSpeed;
1972     info->nodes = 1;
1973     info->sockets = 0;
1974 
1975     for (processor = processorList; processor != NULL;
1976          processor = processor->next) {
1977         ++info->sockets;
1978     }
1979 
1980     info->cores = processorList->data->NumberOfCores;
1981     info->threads = processorList->data->NumberOfLogicalProcessors / info->cores;
1982     info->cpus = info->sockets * info->cores;
1983 
1984     return 0;
1985 }
1986 
1987 
1988 static int
hypervConnectListDomains(virConnectPtr conn,int * ids,int maxids)1989 hypervConnectListDomains(virConnectPtr conn, int *ids, int maxids)
1990 {
1991     hypervPrivate *priv = conn->privateData;
1992     g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
1993     Msvm_ComputerSystem *computerSystem = NULL;
1994     int count = 0;
1995 
1996     if (maxids == 0)
1997         return 0;
1998 
1999     if (hypervGetActiveVirtualSystemList(priv, &computerSystemList) < 0)
2000         return -1;
2001 
2002     for (computerSystem = computerSystemList; computerSystem != NULL;
2003          computerSystem = computerSystem->next) {
2004         ids[count++] = computerSystem->data->ProcessID;
2005 
2006         if (count >= maxids)
2007             break;
2008     }
2009 
2010     return count;
2011 }
2012 
2013 
2014 static int
hypervConnectNumOfDomains(virConnectPtr conn)2015 hypervConnectNumOfDomains(virConnectPtr conn)
2016 {
2017     hypervPrivate *priv = conn->privateData;
2018     g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
2019     Msvm_ComputerSystem *computerSystem = NULL;
2020     int count = 0;
2021 
2022     if (hypervGetActiveVirtualSystemList(priv, &computerSystemList) < 0)
2023         return -1;
2024 
2025     for (computerSystem = computerSystemList; computerSystem != NULL;
2026          computerSystem = computerSystem->next) {
2027         ++count;
2028     }
2029 
2030     return count;
2031 }
2032 
2033 
2034 static virDomainPtr
hypervDomainLookupByID(virConnectPtr conn,int id)2035 hypervDomainLookupByID(virConnectPtr conn, int id)
2036 {
2037     virDomainPtr domain = NULL;
2038     hypervPrivate *priv = conn->privateData;
2039     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2040 
2041     if (hypervGetVirtualSystemByID(priv, id, &computerSystem) < 0)
2042         return NULL;
2043 
2044     hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
2045 
2046     return domain;
2047 }
2048 
2049 
2050 static virDomainPtr
hypervDomainLookupByUUID(virConnectPtr conn,const unsigned char * uuid)2051 hypervDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
2052 {
2053     virDomainPtr domain = NULL;
2054     hypervPrivate *priv = conn->privateData;
2055     char uuid_string[VIR_UUID_STRING_BUFLEN];
2056     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2057 
2058     virUUIDFormat(uuid, uuid_string);
2059 
2060     if (hypervMsvmComputerSystemFromUUID(priv, uuid_string, &computerSystem) < 0)
2061         return NULL;
2062 
2063     hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
2064 
2065     return domain;
2066 }
2067 
2068 
2069 static virDomainPtr
hypervDomainLookupByName(virConnectPtr conn,const char * name)2070 hypervDomainLookupByName(virConnectPtr conn, const char *name)
2071 {
2072     virDomainPtr domain = NULL;
2073     hypervPrivate *priv = conn->privateData;
2074     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2075 
2076     if (hypervGetVirtualSystemByName(priv, name, &computerSystem) < 0)
2077         return NULL;
2078 
2079     if (computerSystem->next) {
2080         virReportError(VIR_ERR_MULTIPLE_DOMAINS,
2081                        _("Multiple domains exist with the name '%s': repeat the request using a UUID"),
2082                        name);
2083         return NULL;
2084     }
2085 
2086     hypervMsvmComputerSystemToDomain(conn, computerSystem, &domain);
2087 
2088     return domain;
2089 }
2090 
2091 
2092 static int
hypervDomainSuspend(virDomainPtr domain)2093 hypervDomainSuspend(virDomainPtr domain)
2094 {
2095     return hypervRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_QUIESCE);
2096 }
2097 
2098 
2099 static int
hypervDomainResume(virDomainPtr domain)2100 hypervDomainResume(virDomainPtr domain)
2101 {
2102     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2103 
2104     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2105         return -1;
2106 
2107     if (computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_QUIESCE) {
2108         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
2109                        _("Domain is not paused"));
2110         return -1;
2111     }
2112 
2113     return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
2114                                                             MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_ENABLED);
2115 }
2116 
2117 
2118 static int
hypervDomainShutdownFlags(virDomainPtr domain,unsigned int flags)2119 hypervDomainShutdownFlags(virDomainPtr domain, unsigned int flags)
2120 {
2121     hypervPrivate *priv = domain->conn->privateData;
2122     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2123     g_autoptr(Msvm_ShutdownComponent) shutdown = NULL;
2124     bool in_transition = false;
2125     char uuid[VIR_UUID_STRING_BUFLEN];
2126     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
2127     g_autoptr(hypervInvokeParamsList) params = NULL;
2128     g_autofree char *selector = NULL;
2129 
2130     virCheckFlags(0, -1);
2131 
2132     virUUIDFormat(domain->uuid, uuid);
2133 
2134     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2135         return -1;
2136 
2137     if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
2138         in_transition) {
2139         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
2140                        _("Domain is not active or in state transition"));
2141         return -1;
2142     }
2143 
2144     virBufferEscapeSQL(&query, MSVM_SHUTDOWNCOMPONENT_WQL_SELECT "WHERE SystemName = '%s'", uuid);
2145 
2146     if (hypervGetWmiClass(Msvm_ShutdownComponent, &shutdown) < 0 ||
2147         !shutdown) {
2148         virReportError(VIR_ERR_OPERATION_FAILED,
2149                        _("Could not get Msvm_ShutdownComponent for domain with UUID '%s'"),
2150                        uuid);
2151         return -1;
2152     }
2153 
2154     selector = g_strdup_printf("CreationClassName=\"Msvm_ShutdownComponent\"&DeviceID=\"%s\"&"
2155                                "SystemCreationClassName=\"Msvm_ComputerSystem\"&SystemName=\"%s\"",
2156                                shutdown->data->DeviceID, uuid);
2157 
2158     params = hypervCreateInvokeParamsList("InitiateShutdown", selector,
2159                                           Msvm_ShutdownComponent_WmiInfo);
2160     if (!params)
2161         return -1;
2162 
2163     hypervAddSimpleParam(params, "Force", "False");
2164 
2165     /* "Reason" is not translated because the Hyper-V administrator may not
2166      * know the libvirt user's language. They may not know English, either,
2167      * but this makes it consistent, at least. */
2168     hypervAddSimpleParam(params, "Reason", "Planned shutdown via libvirt");
2169 
2170     if (hypervInvokeMethod(priv, &params, NULL) < 0)
2171         return -1;
2172 
2173     return 0;
2174 }
2175 
2176 
2177 static int
hypervDomainShutdown(virDomainPtr domain)2178 hypervDomainShutdown(virDomainPtr domain)
2179 {
2180     return hypervDomainShutdownFlags(domain, 0);
2181 }
2182 
2183 
2184 static int
hypervDomainReboot(virDomainPtr domain,unsigned int flags)2185 hypervDomainReboot(virDomainPtr domain, unsigned int flags)
2186 {
2187     virCheckFlags(0, -1);
2188     return hypervRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_REBOOT);
2189 }
2190 
2191 
2192 static int
hypervDomainReset(virDomainPtr domain,unsigned int flags)2193 hypervDomainReset(virDomainPtr domain, unsigned int flags)
2194 {
2195     virCheckFlags(0, -1);
2196     return hypervRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_RESET);
2197 }
2198 
2199 
2200 static int
hypervDomainDestroyFlags(virDomainPtr domain,unsigned int flags)2201 hypervDomainDestroyFlags(virDomainPtr domain, unsigned int flags)
2202 {
2203     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2204     bool in_transition = false;
2205 
2206     virCheckFlags(0, -1);
2207 
2208     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2209         return -1;
2210 
2211     if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
2212         in_transition) {
2213         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
2214                        _("Domain is not active or is in state transition"));
2215         return -1;
2216     }
2217 
2218     return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
2219                                                             MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_DISABLED);
2220 }
2221 
2222 
2223 static int
hypervDomainDestroy(virDomainPtr domain)2224 hypervDomainDestroy(virDomainPtr domain)
2225 {
2226     return hypervDomainDestroyFlags(domain, 0);
2227 }
2228 
2229 
2230 static char *
hypervDomainGetOSType(virDomainPtr domain G_GNUC_UNUSED)2231 hypervDomainGetOSType(virDomainPtr domain G_GNUC_UNUSED)
2232 {
2233     return g_strdup("hvm");
2234 }
2235 
2236 
2237 static unsigned long long
hypervDomainGetMaxMemory(virDomainPtr domain)2238 hypervDomainGetMaxMemory(virDomainPtr domain)
2239 {
2240     char uuid_string[VIR_UUID_STRING_BUFLEN];
2241     hypervPrivate *priv = domain->conn->privateData;
2242     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
2243     g_autoptr(Msvm_MemorySettingData) mem_sd = NULL;
2244 
2245     virUUIDFormat(domain->uuid, uuid_string);
2246 
2247     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
2248         return 0;
2249 
2250     if (hypervGetMemorySD(priv, vssd->data->InstanceID, &mem_sd) < 0)
2251         return 0;
2252 
2253     return mem_sd->data->Limit * 1024;
2254 }
2255 
2256 
2257 static int
hypervDomainSetMemoryProperty(virDomainPtr domain,unsigned long memory,const char * propertyName)2258 hypervDomainSetMemoryProperty(virDomainPtr domain,
2259                               unsigned long memory,
2260                               const char* propertyName)
2261 {
2262     char uuid_string[VIR_UUID_STRING_BUFLEN];
2263     hypervPrivate *priv = domain->conn->privateData;
2264     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
2265     g_autoptr(Msvm_MemorySettingData) memsd = NULL;
2266     g_autoptr(GHashTable) memResource = NULL;
2267     g_autofree char *memory_str = g_strdup_printf("%lu", VIR_ROUND_UP(VIR_DIV_UP(memory, 1024), 2));
2268 
2269     virUUIDFormat(domain->uuid, uuid_string);
2270 
2271     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
2272         return -1;
2273 
2274     if (hypervGetMemorySD(priv, vssd->data->InstanceID, &memsd) < 0)
2275         return -1;
2276 
2277     memResource = hypervCreateEmbeddedParam(Msvm_MemorySettingData_WmiInfo);
2278     if (!memResource)
2279         return -1;
2280 
2281     if (hypervSetEmbeddedProperty(memResource, propertyName, memory_str) < 0)
2282         return -1;
2283 
2284     if (hypervSetEmbeddedProperty(memResource, "InstanceID", memsd->data->InstanceID) < 0)
2285         return -1;
2286 
2287     if (hypervMsvmVSMSModifyResourceSettings(priv, &memResource,
2288                                              Msvm_MemorySettingData_WmiInfo) < 0)
2289         return -1;
2290 
2291     return 0;
2292 }
2293 
2294 
2295 static int
hypervDomainSetMaxMemory(virDomainPtr domain,unsigned long memory)2296 hypervDomainSetMaxMemory(virDomainPtr domain, unsigned long memory)
2297 {
2298     return hypervDomainSetMemoryProperty(domain, memory, "Limit");
2299 }
2300 
2301 
2302 static int
hypervDomainSetMemoryFlags(virDomainPtr domain,unsigned long memory,unsigned int flags)2303 hypervDomainSetMemoryFlags(virDomainPtr domain, unsigned long memory, unsigned int flags)
2304 {
2305     virCheckFlags(0, -1);
2306     return hypervDomainSetMemoryProperty(domain, memory, "VirtualQuantity");
2307 }
2308 
2309 
2310 static int
hypervDomainSetMemory(virDomainPtr domain,unsigned long memory)2311 hypervDomainSetMemory(virDomainPtr domain, unsigned long memory)
2312 {
2313     return hypervDomainSetMemoryFlags(domain, memory, 0);
2314 }
2315 
2316 
2317 static int
hypervDomainGetInfo(virDomainPtr domain,virDomainInfoPtr info)2318 hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
2319 {
2320     hypervPrivate *priv = domain->conn->privateData;
2321     char uuid_string[VIR_UUID_STRING_BUFLEN];
2322     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2323     g_autoptr(Msvm_VirtualSystemSettingData) virtualSystemSettingData = NULL;
2324     g_autoptr(Msvm_ProcessorSettingData) processorSettingData = NULL;
2325     g_autoptr(Msvm_MemorySettingData) memorySettingData = NULL;
2326 
2327     memset(info, 0, sizeof(*info));
2328 
2329     virUUIDFormat(domain->uuid, uuid_string);
2330 
2331     /* Get Msvm_ComputerSystem */
2332     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2333         return -1;
2334 
2335     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string,
2336                                                       &virtualSystemSettingData) < 0)
2337         return -1;
2338 
2339     if (hypervGetProcessorSD(priv,
2340                              virtualSystemSettingData->data->InstanceID,
2341                              &processorSettingData) < 0)
2342         return -1;
2343 
2344     if (hypervGetMemorySD(priv,
2345                           virtualSystemSettingData->data->InstanceID,
2346                           &memorySettingData) < 0)
2347         return -1;
2348 
2349     /* Fill struct */
2350     info->state = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
2351     info->maxMem = memorySettingData->data->Limit * 1024; /* megabyte to kilobyte */
2352     info->memory = memorySettingData->data->VirtualQuantity * 1024; /* megabyte to kilobyte */
2353     info->nrVirtCpu = processorSettingData->data->VirtualQuantity;
2354     info->cpuTime = 0;
2355 
2356     return 0;
2357 }
2358 
2359 
2360 static int
hypervDomainGetState(virDomainPtr domain,int * state,int * reason,unsigned int flags)2361 hypervDomainGetState(virDomainPtr domain, int *state, int *reason,
2362                      unsigned int flags)
2363 {
2364     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2365 
2366     virCheckFlags(0, -1);
2367 
2368     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2369         return -1;
2370 
2371     *state = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
2372 
2373     if (reason != NULL)
2374         *reason = 0;
2375 
2376     return 0;
2377 }
2378 
2379 
2380 static char *
hypervDomainScreenshot(virDomainPtr domain,virStreamPtr stream,unsigned int screen G_GNUC_UNUSED,unsigned int flags)2381 hypervDomainScreenshot(virDomainPtr domain,
2382                        virStreamPtr stream,
2383                        unsigned int screen G_GNUC_UNUSED,
2384                        unsigned int flags)
2385 {
2386     char uuid_string[VIR_UUID_STRING_BUFLEN];
2387     hypervPrivate *priv = domain->conn->privateData;
2388     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
2389     g_autoptr(hypervInvokeParamsList) params = NULL;
2390     g_auto(WsXmlDocH) ret_doc = NULL;
2391     int xRes = 640;
2392     int yRes = 480;
2393     g_autofree char *width = NULL;
2394     g_autofree char *height = NULL;
2395     g_autofree char *imageDataText = NULL;
2396     g_autofree unsigned char *imageDataBuffer = NULL;
2397     size_t imageDataBufferSize;
2398     const char *temporaryDirectory = NULL;
2399     g_autofree char *temporaryFile = NULL;
2400     g_autofree uint8_t *ppmBuffer = NULL;
2401     char *result = NULL;
2402     const char *xpath = "/s:Envelope/s:Body/p:GetVirtualSystemThumbnailImage_OUTPUT/p:ImageData";
2403     int pixelCount;
2404     int pixelByteCount;
2405     size_t i = 0;
2406     int fd = -1;
2407     bool unlinkTemporaryFile = false;
2408 
2409     virCheckFlags(0, NULL);
2410 
2411     virUUIDFormat(domain->uuid, uuid_string);
2412 
2413     /* Hyper-V Generation 1 VMs use two video heads:
2414      *  - S3DisplayController is used for early boot screens.
2415      *  - SyntheticDisplayController takes over when the guest OS initializes its video driver.
2416      *
2417      * This attempts to get the resolution from the SyntheticDisplayController first.
2418      * If that fails, it falls back to S3DisplayController. */
2419     if (hypervGetVideoResolution(priv, uuid_string, &xRes, &yRes, false) < 0) {
2420         if (hypervGetVideoResolution(priv, uuid_string, &xRes, &yRes, true) < 0)
2421             goto cleanup;
2422     }
2423 
2424     /* prepare params */
2425     params = hypervCreateInvokeParamsList("GetVirtualSystemThumbnailImage",
2426                                           MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
2427                                           Msvm_VirtualSystemManagementService_WmiInfo);
2428     if (!params)
2429         goto cleanup;
2430 
2431     width = g_strdup_printf("%d", xRes);
2432     hypervAddSimpleParam(params, "WidthPixels", width);
2433 
2434     height = g_strdup_printf("%d", yRes);
2435     hypervAddSimpleParam(params, "HeightPixels", height);
2436 
2437     virBufferAsprintf(&query,
2438                       "ASSOCIATORS OF "
2439                       "{Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='%s'} "
2440                       "WHERE ResultClass = Msvm_VirtualSystemSettingData",
2441                       uuid_string);
2442     hypervAddEprParam(params, "TargetSystem", &query, Msvm_VirtualSystemSettingData_WmiInfo);
2443 
2444     /* capture and parse the screenshot */
2445     if (hypervInvokeMethod(priv, &params, &ret_doc) < 0)
2446         goto cleanup;
2447 
2448     if (!ws_xml_get_soap_envelope(ret_doc)) {
2449         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve screenshot"));
2450         goto cleanup;
2451     }
2452 
2453     imageDataText = ws_xml_get_xpath_value(ret_doc, (char *)xpath);
2454 
2455     if (!imageDataText) {
2456         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to retrieve image data"));
2457         goto cleanup;
2458     }
2459 
2460     imageDataBuffer = g_base64_decode(imageDataText, &imageDataBufferSize);
2461 
2462     pixelCount = imageDataBufferSize / 2;
2463     pixelByteCount = pixelCount * 3;
2464 
2465     ppmBuffer = g_new0(uint8_t, pixelByteCount);
2466 
2467     /* convert rgb565 to rgb888 */
2468     for (i = 0; i < pixelCount; i++) {
2469         const uint16_t pixel = imageDataBuffer[i * 2] + (imageDataBuffer[i * 2 + 1] << 8);
2470         const uint16_t redMask = 0xF800;
2471         const uint16_t greenMask = 0x7E0;
2472         const uint16_t blueMask = 0x1F;
2473         const uint8_t redFive = (pixel & redMask) >> 11;
2474         const uint8_t greenSix = (pixel & greenMask) >> 5;
2475         const uint8_t blueFive = pixel & blueMask;
2476         ppmBuffer[i * 3] = (redFive * 527 + 23) >> 6;
2477         ppmBuffer[i * 3 + 1] = (greenSix * 259 + 33) >> 6;
2478         ppmBuffer[i * 3 + 2] = (blueFive * 527 + 23) >> 6;
2479     }
2480 
2481     temporaryDirectory = getenv("TMPDIR");
2482     if (!temporaryDirectory)
2483         temporaryDirectory = "/tmp";
2484     temporaryFile = g_strdup_printf("%s/libvirt.hyperv.screendump.XXXXXX", temporaryDirectory);
2485     if ((fd = g_mkstemp_full(temporaryFile, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR)) == -1) {
2486         virReportSystemError(errno, _("g_mkstemp(\"%s\") failed"), temporaryFile);
2487         goto cleanup;
2488     }
2489     unlinkTemporaryFile = true;
2490 
2491     /* write image data */
2492     dprintf(fd, "P6\n%d %d\n255\n", xRes, yRes);
2493     if (safewrite(fd, ppmBuffer, pixelByteCount) != pixelByteCount) {
2494         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to write pixel data"));
2495         goto cleanup;
2496     }
2497 
2498     if (VIR_CLOSE(fd) < 0)
2499         virReportSystemError(errno, "%s", _("failed to close screenshot file"));
2500 
2501     if (virFDStreamOpenFile(stream, temporaryFile, 0, 0, O_RDONLY) < 0)
2502         goto cleanup;
2503 
2504     result = g_strdup("image/x-portable-pixmap");
2505 
2506  cleanup:
2507     VIR_FORCE_CLOSE(fd);
2508     if (unlinkTemporaryFile)
2509         unlink(temporaryFile);
2510 
2511     return result;
2512 }
2513 
2514 
2515 static int
hypervDomainSetVcpusFlags(virDomainPtr domain,unsigned int nvcpus,unsigned int flags)2516 hypervDomainSetVcpusFlags(virDomainPtr domain,
2517                           unsigned int nvcpus,
2518                           unsigned int flags)
2519 {
2520     char uuid_string[VIR_UUID_STRING_BUFLEN];
2521     hypervPrivate *priv = domain->conn->privateData;
2522     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
2523     g_autoptr(Msvm_ProcessorSettingData) proc_sd = NULL;
2524     g_autoptr(GHashTable) vcpuResource = NULL;
2525     g_autofree char *nvcpus_str = g_strdup_printf("%u", nvcpus);
2526 
2527     virCheckFlags(0, -1);
2528 
2529     virUUIDFormat(domain->uuid, uuid_string);
2530 
2531     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
2532         return -1;
2533 
2534     if (hypervGetProcessorSD(priv, vssd->data->InstanceID, &proc_sd) < 0)
2535         return -1;
2536 
2537     vcpuResource = hypervCreateEmbeddedParam(Msvm_ProcessorSettingData_WmiInfo);
2538     if (!vcpuResource)
2539         return -1;
2540 
2541     if (hypervSetEmbeddedProperty(vcpuResource, "VirtualQuantity", nvcpus_str) < 0)
2542         return -1;
2543 
2544     if (hypervSetEmbeddedProperty(vcpuResource, "InstanceID", proc_sd->data->InstanceID) < 0)
2545         return -1;
2546 
2547     if (hypervMsvmVSMSModifyResourceSettings(priv, &vcpuResource,
2548                                              Msvm_ProcessorSettingData_WmiInfo) < 0)
2549         return -1;
2550 
2551     return 0;
2552 }
2553 
2554 
2555 static int
hypervDomainSetVcpus(virDomainPtr domain,unsigned int nvcpus)2556 hypervDomainSetVcpus(virDomainPtr domain, unsigned int nvcpus)
2557 {
2558     return hypervDomainSetVcpusFlags(domain, nvcpus, 0);
2559 }
2560 
2561 
2562 static int
hypervDomainGetVcpusFlags(virDomainPtr domain,unsigned int flags)2563 hypervDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
2564 {
2565     char uuid_string[VIR_UUID_STRING_BUFLEN];
2566     hypervPrivate *priv = domain->conn->privateData;
2567     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2568     g_autoptr(Msvm_ProcessorSettingData) proc_sd = NULL;
2569     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
2570 
2571     virCheckFlags(VIR_DOMAIN_VCPU_LIVE |
2572                   VIR_DOMAIN_VCPU_CONFIG |
2573                   VIR_DOMAIN_VCPU_MAXIMUM, -1);
2574 
2575     virUUIDFormat(domain->uuid, uuid_string);
2576 
2577     /* Start by getting the Msvm_ComputerSystem */
2578     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2579         return -1;
2580 
2581     /* Check @flags to see if we are to query a running domain, and fail
2582      * if that domain is not running */
2583     if (flags & VIR_DOMAIN_VCPU_LIVE &&
2584         computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED) {
2585         virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain is not active"));
2586         return -1;
2587     }
2588 
2589     /* Check @flags to see if we are to return the maximum vCPU limit */
2590     if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
2591         return hypervConnectGetMaxVcpus(domain->conn, NULL);
2592 
2593     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
2594         return -1;
2595 
2596     if (hypervGetProcessorSD(priv, vssd->data->InstanceID, &proc_sd) < 0)
2597         return -1;
2598 
2599     return proc_sd->data->VirtualQuantity;
2600 }
2601 
2602 
2603 static int
hypervDomainGetVcpus(virDomainPtr domain,virVcpuInfoPtr info,int maxinfo,unsigned char * cpumaps,int maplen)2604 hypervDomainGetVcpus(virDomainPtr domain,
2605                      virVcpuInfoPtr info,
2606                      int maxinfo,
2607                      unsigned char *cpumaps,
2608                      int maplen)
2609 {
2610     int count = 0;
2611     int vcpu_number;
2612     hypervPrivate *priv = domain->conn->privateData;
2613     g_autoptr(Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor) vproc = NULL;
2614 
2615     /* Hyper-V does not allow setting CPU affinity: all cores will be used */
2616     if (cpumaps && maplen > 0)
2617         memset(cpumaps, 0xFF, maxinfo * maplen);
2618 
2619     for (vcpu_number = 0; vcpu_number < maxinfo; vcpu_number++) {
2620         g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
2621 
2622         /* Name format: <domain_name>:Hv VP <vCPU_number> */
2623         g_autofree char *vcpu_name = g_strdup_printf("%s:Hv VP %d", domain->name, vcpu_number);
2624 
2625         /* try to free objects from previous iteration */
2626         hypervFreeObject((hypervObject *)vproc);
2627         vproc = NULL;
2628 
2629         /* get the info */
2630         virBufferEscapeSQL(&query,
2631                            WIN32_PERFRAWDATA_HVSTATS_HYPERVHYPERVISORVIRTUALPROCESSOR_WQL_SELECT
2632                            "WHERE Name = '%s'",
2633                            vcpu_name);
2634 
2635         if (hypervGetWmiClass(Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor, &vproc) < 0)
2636             continue;
2637 
2638         /* fill structure info */
2639         info[vcpu_number].number = vcpu_number;
2640         if (vproc) {
2641             info[vcpu_number].state = VIR_VCPU_RUNNING;
2642             info[vcpu_number].cpuTime = vproc->data->PercentTotalRunTime * 100;
2643             info[vcpu_number].cpu = VIR_VCPU_INFO_CPU_UNAVAILABLE;
2644         } else {
2645             info[vcpu_number].state = VIR_VCPU_OFFLINE;
2646             info[vcpu_number].cpuTime = 0LLU;
2647             info[vcpu_number].cpu = VIR_VCPU_INFO_CPU_OFFLINE;
2648         }
2649         count++;
2650     }
2651 
2652     return count;
2653 }
2654 
2655 
2656 static char *
hypervDomainGetXMLDesc(virDomainPtr domain,unsigned int flags)2657 hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
2658 {
2659     hypervPrivate *priv = domain->conn->privateData;
2660     g_autoptr(virDomainDef) def = NULL;
2661     char uuid_string[VIR_UUID_STRING_BUFLEN];
2662     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2663     g_autoptr(Msvm_VirtualSystemSettingData) virtualSystemSettingData = NULL;
2664     g_autoptr(Msvm_ProcessorSettingData) processorSettingData = NULL;
2665     g_autoptr(Msvm_MemorySettingData) memorySettingData = NULL;
2666     g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
2667     g_autoptr(Msvm_StorageAllocationSettingData) sasd = NULL;
2668     g_autoptr(Msvm_SerialPortSettingData) spsd = NULL;
2669     Msvm_ResourceAllocationSettingData *serialDevices = NULL;
2670     g_autoptr(Msvm_EthernetPortAllocationSettingData) nets = NULL;
2671 
2672     virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
2673 
2674     if (!(def = virDomainDefNew(priv->xmlopt)))
2675         return NULL;
2676 
2677     virUUIDFormat(domain->uuid, uuid_string);
2678 
2679     /* Get Msvm_ComputerSystem */
2680     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2681         return NULL;
2682 
2683     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string,
2684                                                       &virtualSystemSettingData) < 0)
2685         return NULL;
2686 
2687     if (hypervGetProcessorSD(priv,
2688                              virtualSystemSettingData->data->InstanceID,
2689                              &processorSettingData) < 0)
2690         return NULL;
2691 
2692     if (hypervGetMemorySD(priv,
2693                           virtualSystemSettingData->data->InstanceID,
2694                           &memorySettingData) < 0)
2695         return NULL;
2696 
2697     if (hypervGetResourceAllocationSD(priv,
2698                                       virtualSystemSettingData->data->InstanceID,
2699                                       &rasd) < 0) {
2700         return NULL;
2701     }
2702 
2703     if (hypervGetStorageAllocationSD(priv,
2704                                      virtualSystemSettingData->data->InstanceID,
2705                                      &sasd) < 0) {
2706         return NULL;
2707     }
2708 
2709     if (hypervGetSerialPortSD(priv, virtualSystemSettingData->data->InstanceID, &spsd) < 0)
2710         return NULL;
2711 
2712     if (hypervGetEthernetPortAllocationSD(priv,
2713                                           virtualSystemSettingData->data->InstanceID, &nets) < 0)
2714         return NULL;
2715 
2716     /* Fill struct */
2717     def->virtType = VIR_DOMAIN_VIRT_HYPERV;
2718 
2719     if (hypervIsMsvmComputerSystemActive(computerSystem, NULL)) {
2720         def->id = computerSystem->data->ProcessID;
2721     } else {
2722         def->id = -1;
2723     }
2724 
2725     if (virUUIDParse(computerSystem->data->Name, def->uuid) < 0) {
2726         virReportError(VIR_ERR_INTERNAL_ERROR,
2727                        _("Could not parse UUID from string '%s'"),
2728                        computerSystem->data->Name);
2729         return NULL;
2730     }
2731 
2732     def->name = g_strdup(computerSystem->data->ElementName);
2733 
2734     if (virtualSystemSettingData->data->Notes.data) {
2735         char **notes = (char **)virtualSystemSettingData->data->Notes.data;
2736         g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
2737         size_t i = 0;
2738 
2739         /* in practice Notes has 1 element */
2740         for (i = 0; i < virtualSystemSettingData->data->Notes.count; i++) {
2741             /* but if there's more than 1, separate by double new line */
2742             if (virBufferUse(&buf) > 0)
2743                 virBufferAddLit(&buf, "\n\n");
2744 
2745             virBufferAdd(&buf, *notes, -1);
2746             notes++;
2747         }
2748 
2749         def->description = virBufferContentAndReset(&buf);
2750     }
2751 
2752     /* mebibytes to kibibytes */
2753     def->mem.max_memory = memorySettingData->data->Limit * 1024;
2754     def->mem.cur_balloon = memorySettingData->data->VirtualQuantity * 1024;
2755     virDomainDefSetMemoryTotal(def, memorySettingData->data->VirtualQuantity * 1024);
2756 
2757     if (virDomainDefSetVcpusMax(def, processorSettingData->data->VirtualQuantity, NULL) < 0)
2758         return NULL;
2759 
2760     if (virDomainDefSetVcpus(def, processorSettingData->data->VirtualQuantity) < 0)
2761         return NULL;
2762 
2763     def->os.type = VIR_DOMAIN_OSTYPE_HVM;
2764 
2765     /* Allocate space for all potential devices */
2766 
2767     /* 256 scsi drives + 4 ide drives */
2768     def->disks = g_new0(virDomainDiskDef *,
2769                         HYPERV_MAX_SCSI_CONTROLLERS * HYPERV_MAX_DRIVES_PER_SCSI_CONTROLLER +
2770                         HYPERV_MAX_IDE_CHANNELS * HYPERV_MAX_DRIVES_PER_IDE_CHANNEL);
2771     def->ndisks = 0;
2772 
2773     /* 1 ide & 4 scsi controllers */
2774     def->controllers = g_new0(virDomainControllerDef *, 5);
2775     def->ncontrollers = 0;
2776 
2777     /* 8 synthetic + 4 legacy NICs */
2778     def->nets = g_new0(virDomainNetDef *, 12);
2779     def->nnets = 0;
2780 
2781     if (hypervDomainDefParseStorage(priv, def, rasd, sasd) < 0)
2782         return NULL;
2783 
2784     if (g_str_has_prefix(priv->version, "6."))
2785         serialDevices = rasd;
2786     else
2787         serialDevices = (Msvm_ResourceAllocationSettingData *)spsd;
2788 
2789     if (hypervDomainDefParseSerial(def, serialDevices) < 0)
2790         return NULL;
2791 
2792     if (hypervDomainDefParseEthernet(domain, def, nets) < 0)
2793         return NULL;
2794 
2795     /* XXX xmlopts must be non-NULL */
2796     return virDomainDefFormat(def, NULL, virDomainDefFormatConvertXMLFlags(flags));
2797 }
2798 
2799 
2800 static int
hypervConnectListDefinedDomains(virConnectPtr conn,char ** const names,int maxnames)2801 hypervConnectListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
2802 {
2803     bool success = false;
2804     hypervPrivate *priv = conn->privateData;
2805     g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
2806     Msvm_ComputerSystem *computerSystem = NULL;
2807     int count = 0;
2808     size_t i;
2809 
2810     if (maxnames == 0)
2811         return 0;
2812 
2813     if (hypervGetInactiveVirtualSystemList(priv, &computerSystemList) < 0)
2814         goto cleanup;
2815 
2816     for (computerSystem = computerSystemList; computerSystem != NULL;
2817          computerSystem = computerSystem->next) {
2818         names[count] = g_strdup(computerSystem->data->ElementName);
2819 
2820         ++count;
2821 
2822         if (count >= maxnames)
2823             break;
2824     }
2825 
2826     success = true;
2827 
2828  cleanup:
2829     if (!success) {
2830         for (i = 0; i < count; ++i)
2831             VIR_FREE(names[i]);
2832 
2833         count = -1;
2834     }
2835 
2836     return count;
2837 }
2838 
2839 
2840 static int
hypervConnectNumOfDefinedDomains(virConnectPtr conn)2841 hypervConnectNumOfDefinedDomains(virConnectPtr conn)
2842 {
2843     hypervPrivate *priv = conn->privateData;
2844     g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
2845     Msvm_ComputerSystem *computerSystem = NULL;
2846     int count = 0;
2847 
2848     if (hypervGetInactiveVirtualSystemList(priv, &computerSystemList) < 0)
2849         return -1;
2850 
2851     for (computerSystem = computerSystemList; computerSystem != NULL;
2852          computerSystem = computerSystem->next) {
2853         ++count;
2854     }
2855 
2856     return count;
2857 }
2858 
2859 
2860 static int
hypervDomainCreateWithFlags(virDomainPtr domain,unsigned int flags)2861 hypervDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
2862 {
2863     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
2864 
2865     virCheckFlags(0, -1);
2866 
2867     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
2868         return -1;
2869 
2870     if (hypervIsMsvmComputerSystemActive(computerSystem, NULL)) {
2871         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
2872                        _("Domain is already active or is in state transition"));
2873         return -1;
2874     }
2875 
2876     return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
2877                                                             MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_ENABLED);
2878 }
2879 
2880 
2881 static int
hypervDomainCreate(virDomainPtr domain)2882 hypervDomainCreate(virDomainPtr domain)
2883 {
2884     return hypervDomainCreateWithFlags(domain, 0);
2885 }
2886 
2887 
2888 static int
hypervDomainUndefineFlags(virDomainPtr domain,unsigned int flags)2889 hypervDomainUndefineFlags(virDomainPtr domain, unsigned int flags)
2890 {
2891     char uuid_string[VIR_UUID_STRING_BUFLEN];
2892     hypervPrivate *priv = domain->conn->privateData;
2893     g_autoptr(hypervInvokeParamsList) params = NULL;
2894     g_auto(virBuffer) eprQuery = VIR_BUFFER_INITIALIZER;
2895 
2896     virCheckFlags(0, -1);
2897 
2898     virUUIDFormat(domain->uuid, uuid_string);
2899 
2900     /* prepare params */
2901     params = hypervCreateInvokeParamsList("DestroySystem",
2902                                           MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
2903                                           Msvm_VirtualSystemManagementService_WmiInfo);
2904 
2905     if (!params)
2906         return -1;
2907 
2908     virBufferEscapeSQL(&eprQuery, MSVM_COMPUTERSYSTEM_WQL_SELECT "WHERE Name = '%s'", uuid_string);
2909 
2910     if (hypervAddEprParam(params, "AffectedSystem", &eprQuery, Msvm_ComputerSystem_WmiInfo) < 0)
2911         return -1;
2912 
2913     /* actually destroy the VM */
2914     if (hypervInvokeMethod(priv, &params, NULL) < 0)
2915         return -1;
2916 
2917     return 0;
2918 }
2919 
2920 
2921 static int
hypervDomainUndefine(virDomainPtr domain)2922 hypervDomainUndefine(virDomainPtr domain)
2923 {
2924     return hypervDomainUndefineFlags(domain, 0);
2925 }
2926 
2927 
2928 static virDomainPtr
hypervDomainDefineXML(virConnectPtr conn,const char * xml)2929 hypervDomainDefineXML(virConnectPtr conn, const char *xml)
2930 {
2931     hypervPrivate *priv = conn->privateData;
2932     g_autofree char *hostname = hypervConnectGetHostname(conn);
2933     g_autoptr(virDomainDef) def = NULL;
2934     virDomainPtr domain = NULL;
2935     g_autoptr(hypervInvokeParamsList) params = NULL;
2936     g_autoptr(GHashTable) defineSystemParam = NULL;
2937     size_t i = 0;
2938 
2939     /* parse xml */
2940     def = virDomainDefParseString(xml, priv->xmlopt, NULL,
2941                                   1 << VIR_DOMAIN_VIRT_HYPERV | VIR_DOMAIN_XML_INACTIVE);
2942 
2943     if (!def)
2944         goto error;
2945 
2946     /* abort if a domain with this UUID already exists */
2947     if ((domain = hypervDomainLookupByUUID(conn, def->uuid))) {
2948         char uuid_string[VIR_UUID_STRING_BUFLEN];
2949         virUUIDFormat(domain->uuid, uuid_string);
2950         virReportError(VIR_ERR_DOM_EXIST, _("Domain already exists with UUID '%s'"), uuid_string);
2951 
2952         // Don't use the 'exit' label, since we don't want to delete the existing domain.
2953         virObjectUnref(domain);
2954         return NULL;
2955     }
2956 
2957     /* prepare params: only set the VM's name for now */
2958     params = hypervCreateInvokeParamsList("DefineSystem",
2959                                           MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
2960                                           Msvm_VirtualSystemManagementService_WmiInfo);
2961 
2962     if (!params)
2963         goto error;
2964 
2965     defineSystemParam = hypervCreateEmbeddedParam(Msvm_VirtualSystemSettingData_WmiInfo);
2966 
2967     if (hypervSetEmbeddedProperty(defineSystemParam, "ElementName", def->name) < 0)
2968         goto error;
2969 
2970     if (hypervAddEmbeddedParam(params, "SystemSettings",
2971                                &defineSystemParam, Msvm_VirtualSystemSettingData_WmiInfo) < 0)
2972         goto error;
2973 
2974     /* create the VM */
2975     if (hypervInvokeMethod(priv, &params, NULL) < 0)
2976         goto error;
2977 
2978     /* populate a domain ptr so that we can edit it */
2979     domain = hypervDomainLookupByName(conn, def->name);
2980 
2981     /* set domain vcpus */
2982     if (def->vcpus && hypervDomainSetVcpus(domain, def->maxvcpus) < 0)
2983         goto error;
2984 
2985     /* set VM maximum memory */
2986     if (def->mem.max_memory > 0 && hypervDomainSetMaxMemory(domain, def->mem.max_memory) < 0)
2987         goto error;
2988 
2989     /* set VM memory */
2990     if (def->mem.cur_balloon > 0 && hypervDomainSetMemory(domain, def->mem.cur_balloon) < 0)
2991         goto error;
2992 
2993     /* attach all storage */
2994     if (hypervDomainAttachStorage(domain, def, hostname) < 0)
2995         goto error;
2996 
2997     /* Attach serials */
2998     for (i = 0; i < def->nserials; i++) {
2999         if (hypervDomainAttachSerial(domain, def->serials[i]) < 0) {
3000             virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not attach serial port %zu"), i);
3001             goto error;
3002         }
3003     }
3004 
3005     /* Attach networks */
3006     for (i = 0; i < def->nnets; i++) {
3007         if (hypervDomainAttachSyntheticEthernetAdapter(domain, def->nets[i], hostname) < 0) {
3008             virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not attach network %zu"), i);
3009             goto error;
3010         }
3011     }
3012 
3013     return domain;
3014 
3015  error:
3016     VIR_DEBUG("Domain creation failed, rolling back");
3017     if (domain)
3018         hypervDomainUndefine(domain);
3019 
3020     return NULL;
3021 }
3022 
3023 
3024 static int
hypervDomainAttachDeviceFlags(virDomainPtr domain,const char * xml,unsigned int flags)3025 hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int flags)
3026 {
3027     hypervPrivate *priv = domain->conn->privateData;
3028     g_autoptr(virDomainDef) def = NULL;
3029     g_autoptr(virDomainDeviceDef) dev = NULL;
3030     g_autoptr(Win32_ComputerSystem) host = NULL;
3031     char *hostname = NULL;
3032     char uuid_string[VIR_UUID_STRING_BUFLEN];
3033     Msvm_ResourceAllocationSettingData *controller = NULL;
3034     g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL;
3035     Msvm_ResourceAllocationSettingData *entry = NULL;
3036     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
3037     int num_scsi = 0;
3038 
3039     virCheckFlags(0, -1);
3040 
3041     virUUIDFormat(domain->uuid, uuid_string);
3042 
3043     /* get domain definition */
3044     if (!(def = virDomainDefNew(priv->xmlopt)))
3045         return -1;
3046 
3047     /* get domain device definition */
3048     dev = virDomainDeviceDefParse(xml, def, priv->xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE);
3049     if (!dev)
3050         return -1;
3051 
3052     /* get the host computer system */
3053     if (hypervGetPhysicalSystemList(priv, &host) < 0)
3054         return -1;
3055 
3056     hostname = host->data->Name;
3057 
3058     switch (dev->type) {
3059     case VIR_DOMAIN_DEVICE_DISK:
3060         /* get our controller */
3061         if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
3062             return -1;
3063 
3064         if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0)
3065             return -1;
3066 
3067         entry = rasd;
3068         switch (dev->data.disk->bus) {
3069         case VIR_DOMAIN_DISK_BUS_IDE:
3070             while (entry) {
3071                 if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER &&
3072                     (entry->data->Address[0] - '0') == dev->data.disk->info.addr.drive.controller) {
3073                     controller = entry;
3074                     break;
3075                 }
3076                 entry = entry->next;
3077             }
3078             if (!entry)
3079                 return -1;
3080             break;
3081         case VIR_DOMAIN_DISK_BUS_SCSI:
3082             while (entry) {
3083                 if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA &&
3084                     num_scsi++ == dev->data.disk->info.addr.drive.controller) {
3085                     controller = entry;
3086                     break;
3087                 }
3088                 entry = entry->next;
3089             }
3090             if (!entry)
3091                 return -1;
3092             break;
3093         case VIR_DOMAIN_DISK_BUS_FDC:
3094             while (entry) {
3095                 if (entry->data->ResourceType == MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE) {
3096                     controller = entry;
3097                     break;
3098                 }
3099                 entry = entry->next;
3100             }
3101             if (!entry)
3102                 return -1;
3103             break;
3104         case VIR_DOMAIN_DISK_BUS_NONE:
3105         case VIR_DOMAIN_DISK_BUS_VIRTIO:
3106         case VIR_DOMAIN_DISK_BUS_XEN:
3107         case VIR_DOMAIN_DISK_BUS_USB:
3108         case VIR_DOMAIN_DISK_BUS_UML:
3109         case VIR_DOMAIN_DISK_BUS_SATA:
3110         case VIR_DOMAIN_DISK_BUS_SD:
3111         case VIR_DOMAIN_DISK_BUS_LAST:
3112         default:
3113             virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid disk bus in definition"));
3114             return -1;
3115         }
3116 
3117         if (hypervDomainAttachStorageVolume(domain, dev->data.disk, controller, hostname) < 0)
3118             return -1;
3119         break;
3120     case VIR_DOMAIN_DEVICE_CHR:
3121         if (hypervDomainAttachSerial(domain, dev->data.chr) < 0)
3122             return -1;
3123         break;
3124     case VIR_DOMAIN_DEVICE_NET:
3125         if (hypervDomainAttachSyntheticEthernetAdapter(domain, dev->data.net, hostname) < 0)
3126             return -1;
3127         break;
3128     default:
3129         /* unsupported device type */
3130         virReportError(VIR_ERR_INTERNAL_ERROR,
3131                        _("Attaching devices of type %d is not implemented"), dev->type);
3132         return -1;
3133     }
3134 
3135     return 0;
3136 }
3137 
3138 
3139 static int
hypervDomainAttachDevice(virDomainPtr domain,const char * xml)3140 hypervDomainAttachDevice(virDomainPtr domain, const char *xml)
3141 {
3142     return hypervDomainAttachDeviceFlags(domain, xml, 0);
3143 }
3144 
3145 
3146 static int
hypervDomainGetAutostart(virDomainPtr domain,int * autostart)3147 hypervDomainGetAutostart(virDomainPtr domain, int *autostart)
3148 {
3149     char uuid_string[VIR_UUID_STRING_BUFLEN];
3150     hypervPrivate *priv = domain->conn->privateData;
3151     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
3152 
3153     virUUIDFormat(domain->uuid, uuid_string);
3154 
3155     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
3156         return -1;
3157 
3158     *autostart = vssd->data->AutomaticStartupAction == 4;
3159 
3160     return 0;
3161 }
3162 
3163 
3164 static int
hypervDomainSetAutostart(virDomainPtr domain,int autostart)3165 hypervDomainSetAutostart(virDomainPtr domain, int autostart)
3166 {
3167     char uuid_string[VIR_UUID_STRING_BUFLEN];
3168     hypervPrivate *priv = domain->conn->privateData;
3169     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
3170     g_autoptr(hypervInvokeParamsList) params = NULL;
3171     g_autoptr(GHashTable) autostartParam = NULL;
3172 
3173     virUUIDFormat(domain->uuid, uuid_string);
3174 
3175     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
3176         return -1;
3177 
3178     params = hypervCreateInvokeParamsList("ModifySystemSettings",
3179                                           MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
3180                                           Msvm_VirtualSystemManagementService_WmiInfo);
3181 
3182     if (!params)
3183         return -1;
3184 
3185     autostartParam = hypervCreateEmbeddedParam(Msvm_VirtualSystemSettingData_WmiInfo);
3186 
3187     if (hypervSetEmbeddedProperty(autostartParam, "AutomaticStartupAction",
3188                                   autostart ? "4" : "2") < 0)
3189         return -1;
3190 
3191     if (hypervSetEmbeddedProperty(autostartParam, "InstanceID", vssd->data->InstanceID) < 0)
3192         return -1;
3193 
3194     if (hypervAddEmbeddedParam(params, "SystemSettings",
3195                                &autostartParam, Msvm_VirtualSystemSettingData_WmiInfo) < 0)
3196         return -1;
3197 
3198     if (hypervInvokeMethod(priv, &params, NULL) < 0)
3199         return -1;
3200 
3201     return 0;
3202 }
3203 
3204 
3205 static char *
hypervDomainGetSchedulerType(virDomainPtr domain G_GNUC_UNUSED,int * nparams)3206 hypervDomainGetSchedulerType(virDomainPtr domain G_GNUC_UNUSED, int *nparams)
3207 {
3208     if (nparams)
3209         *nparams = 3; /* reservation, limit, weight */
3210 
3211     return g_strdup("allocation");
3212 }
3213 
3214 
3215 static int
hypervDomainGetSchedulerParametersFlags(virDomainPtr domain,virTypedParameterPtr params,int * nparams,unsigned int flags)3216 hypervDomainGetSchedulerParametersFlags(virDomainPtr domain,
3217                                         virTypedParameterPtr params,
3218                                         int *nparams, unsigned int flags)
3219 {
3220     hypervPrivate *priv = domain->conn->privateData;
3221     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
3222     g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
3223     g_autoptr(Msvm_ProcessorSettingData) proc_sd = NULL;
3224     char uuid_string[VIR_UUID_STRING_BUFLEN];
3225     int saved_nparams = 0;
3226 
3227     virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1);
3228 
3229     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
3230         return -1;
3231 
3232     /* get info from host */
3233     virUUIDFormat(domain->uuid, uuid_string);
3234 
3235     if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
3236         return -1;
3237 
3238     if (hypervGetProcessorSD(priv, vssd->data->InstanceID, &proc_sd) < 0)
3239         return -1;
3240 
3241     /* parse it all out */
3242     if (virTypedParameterAssign(&params[0], VIR_DOMAIN_SCHEDULER_LIMIT,
3243                                 VIR_TYPED_PARAM_LLONG, proc_sd->data->Limit) < 0)
3244         return -1;
3245     saved_nparams++;
3246 
3247     if (*nparams > saved_nparams) {
3248         if (virTypedParameterAssign(&params[1], VIR_DOMAIN_SCHEDULER_RESERVATION,
3249                                     VIR_TYPED_PARAM_LLONG, proc_sd->data->Reservation) < 0)
3250             return -1;
3251         saved_nparams++;
3252     }
3253 
3254     if (*nparams > saved_nparams) {
3255         if (virTypedParameterAssign(&params[2], VIR_DOMAIN_SCHEDULER_WEIGHT,
3256                                     VIR_TYPED_PARAM_UINT, proc_sd->data->Weight) < 0)
3257             return -1;
3258         saved_nparams++;
3259     }
3260 
3261     *nparams = saved_nparams;
3262 
3263     return 0;
3264 }
3265 
3266 
3267 static int
hypervDomainGetSchedulerParameters(virDomainPtr domain,virTypedParameterPtr params,int * nparams)3268 hypervDomainGetSchedulerParameters(virDomainPtr domain,
3269                                    virTypedParameterPtr params,
3270                                    int *nparams)
3271 {
3272     return hypervDomainGetSchedulerParametersFlags(domain, params, nparams,
3273                                                    VIR_DOMAIN_AFFECT_CURRENT);
3274 }
3275 
3276 
3277 static unsigned long long
hypervNodeGetFreeMemory(virConnectPtr conn)3278 hypervNodeGetFreeMemory(virConnectPtr conn)
3279 {
3280     hypervPrivate *priv = conn->privateData;
3281     g_autoptr(Win32_OperatingSystem) operatingSystem = NULL;
3282 
3283     if (hypervGetOperatingSystem(priv, &operatingSystem) < 0)
3284         return 0;
3285 
3286     if (!operatingSystem) {
3287         virReportError(VIR_ERR_INTERNAL_ERROR,
3288                        _("Could not get free memory for host %s"),
3289                        conn->uri->server);
3290         return 0;
3291     }
3292 
3293     return operatingSystem->data->FreePhysicalMemory * 1024;
3294 }
3295 
3296 
3297 static int
hypervConnectIsEncrypted(virConnectPtr conn)3298 hypervConnectIsEncrypted(virConnectPtr conn)
3299 {
3300     hypervPrivate *priv = conn->privateData;
3301 
3302     if (STRCASEEQ(priv->parsedUri->transport, "https")) {
3303         return 1;
3304     } else {
3305         return 0;
3306     }
3307 }
3308 
3309 
3310 static int
hypervConnectIsSecure(virConnectPtr conn)3311 hypervConnectIsSecure(virConnectPtr conn)
3312 {
3313     hypervPrivate *priv = conn->privateData;
3314 
3315     if (STRCASEEQ(priv->parsedUri->transport, "https")) {
3316         return 1;
3317     } else {
3318         return 0;
3319     }
3320 }
3321 
3322 
3323 static int
hypervConnectIsAlive(virConnectPtr conn)3324 hypervConnectIsAlive(virConnectPtr conn)
3325 {
3326     hypervPrivate *priv = conn->privateData;
3327 
3328     /* XXX we should be able to do something better than this is simple, safe,
3329      * and good enough for now. In worst case, the function will return true
3330      * even though the connection is not alive.
3331      */
3332     if (priv->client)
3333         return 1;
3334     else
3335         return 0;
3336 }
3337 
3338 
3339 static int
hypervDomainIsActive(virDomainPtr domain)3340 hypervDomainIsActive(virDomainPtr domain)
3341 {
3342     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
3343 
3344     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
3345         return -1;
3346 
3347     return hypervIsMsvmComputerSystemActive(computerSystem, NULL) ? 1 : 0;
3348 }
3349 
3350 
3351 static int
hypervDomainGetMaxVcpus(virDomainPtr dom)3352 hypervDomainGetMaxVcpus(virDomainPtr dom)
3353 {
3354     if (hypervDomainIsActive(dom))
3355         return hypervDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_MAXIMUM));
3356     else
3357         return hypervConnectGetMaxVcpus(dom->conn, NULL);
3358 }
3359 
3360 
3361 static int
hypervDomainIsPersistent(virDomainPtr domain G_GNUC_UNUSED)3362 hypervDomainIsPersistent(virDomainPtr domain G_GNUC_UNUSED)
3363 {
3364     /* Hyper-V has no concept of transient domains, so all of them are persistent */
3365     return 1;
3366 }
3367 
3368 
3369 static int
hypervDomainIsUpdated(virDomainPtr domain G_GNUC_UNUSED)3370 hypervDomainIsUpdated(virDomainPtr domain G_GNUC_UNUSED)
3371 {
3372     return 0;
3373 }
3374 
3375 
3376 static int
hypervDomainManagedSave(virDomainPtr domain,unsigned int flags)3377 hypervDomainManagedSave(virDomainPtr domain, unsigned int flags)
3378 {
3379     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
3380     bool in_transition = false;
3381 
3382     virCheckFlags(0, -1);
3383 
3384     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
3385         return -1;
3386 
3387     if (!hypervIsMsvmComputerSystemActive(computerSystem, &in_transition) ||
3388         in_transition) {
3389         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
3390                        _("Domain is not active or is in state transition"));
3391         return -1;
3392     }
3393 
3394     return hypervInvokeMsvmComputerSystemRequestStateChange(domain, MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_OFFLINE);
3395 }
3396 
3397 
3398 static int
hypervDomainHasManagedSaveImage(virDomainPtr domain,unsigned int flags)3399 hypervDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags)
3400 {
3401     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
3402 
3403     virCheckFlags(0, -1);
3404 
3405     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
3406         return -1;
3407 
3408     return computerSystem->data->EnabledState == MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED ? 1 : 0;
3409 }
3410 
3411 
3412 static int
hypervDomainManagedSaveRemove(virDomainPtr domain,unsigned int flags)3413 hypervDomainManagedSaveRemove(virDomainPtr domain, unsigned int flags)
3414 {
3415     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
3416 
3417     virCheckFlags(0, -1);
3418 
3419     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
3420         return -1;
3421 
3422     if (computerSystem->data->EnabledState != MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED) {
3423         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
3424                        _("Domain has no managed save image"));
3425         return -1;
3426     }
3427 
3428     return hypervInvokeMsvmComputerSystemRequestStateChange(domain,
3429                                                             MSVM_COMPUTERSYSTEM_REQUESTEDSTATE_DISABLED);
3430 }
3431 
3432 
3433 #define MATCH(FLAG) (flags & (FLAG))
3434 static int
hypervConnectListAllDomains(virConnectPtr conn,virDomainPtr ** domains,unsigned int flags)3435 hypervConnectListAllDomains(virConnectPtr conn,
3436                             virDomainPtr **domains,
3437                             unsigned int flags)
3438 {
3439     hypervPrivate *priv = conn->privateData;
3440     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
3441     g_autoptr(Msvm_ComputerSystem) computerSystemList = NULL;
3442     Msvm_ComputerSystem *computerSystem = NULL;
3443     size_t ndoms;
3444     virDomainPtr domain;
3445     virDomainPtr *doms = NULL;
3446     int count = 0;
3447     int ret = -1;
3448     size_t i;
3449 
3450     virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
3451 
3452     /* check for filter combinations that return no results:
3453      * persistent: all hyperv guests are persistent
3454      * snapshot: the driver does not support snapshot management
3455      * autostart: the driver does not support autostarting guests
3456      */
3457     if ((MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) &&
3458          !MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT)) ||
3459         (MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) &&
3460          !MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART)) ||
3461         (MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) &&
3462          !MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT))) {
3463         if (domains)
3464             *domains = g_new0(virDomainPtr, 1);
3465 
3466         ret = 0;
3467         goto cleanup;
3468     }
3469 
3470     virBufferAddLit(&query,
3471                     MSVM_COMPUTERSYSTEM_WQL_SELECT
3472                     "WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
3473 
3474     /* construct query with filter depending on flags */
3475     if (!(MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) &&
3476           MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE))) {
3477         if (MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE)) {
3478             virBufferAddLit(&query, "AND " MSVM_COMPUTERSYSTEM_WQL_ACTIVE);
3479         }
3480 
3481         if (MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE)) {
3482             virBufferAddLit(&query, "AND " MSVM_COMPUTERSYSTEM_WQL_INACTIVE);
3483         }
3484     }
3485 
3486     if (hypervGetWmiClass(Msvm_ComputerSystem, &computerSystemList) < 0)
3487         goto cleanup;
3488 
3489     if (domains) {
3490         doms = g_new0(virDomainPtr, 1);
3491         ndoms = 1;
3492     }
3493 
3494     for (computerSystem = computerSystemList; computerSystem != NULL;
3495          computerSystem = computerSystem->next) {
3496 
3497         /* filter by domain state */
3498         if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE)) {
3499             int st = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
3500             if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
3501                    st == VIR_DOMAIN_RUNNING) ||
3502                   (MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
3503                    st == VIR_DOMAIN_PAUSED) ||
3504                   (MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
3505                    st == VIR_DOMAIN_SHUTOFF) ||
3506                   (MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
3507                    (st != VIR_DOMAIN_RUNNING &&
3508                     st != VIR_DOMAIN_PAUSED &&
3509                     st != VIR_DOMAIN_SHUTOFF))))
3510                 continue;
3511         }
3512 
3513         /* managed save filter */
3514         if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_MANAGEDSAVE)) {
3515             bool mansave = computerSystem->data->EnabledState ==
3516                            MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED;
3517 
3518             if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) && mansave) ||
3519                   (MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE) && !mansave)))
3520                 continue;
3521         }
3522 
3523         if (!doms) {
3524             count++;
3525             continue;
3526         }
3527 
3528         VIR_RESIZE_N(doms, ndoms, count, 2);
3529 
3530         domain = NULL;
3531 
3532         if (hypervMsvmComputerSystemToDomain(conn, computerSystem,
3533                                              &domain) < 0)
3534             goto cleanup;
3535 
3536         doms[count++] = domain;
3537     }
3538 
3539     if (doms)
3540         *domains = doms;
3541     doms = NULL;
3542     ret = count;
3543 
3544  cleanup:
3545     if (doms) {
3546         for (i = 0; i < count; ++i)
3547             virObjectUnref(doms[i]);
3548 
3549         VIR_FREE(doms);
3550     }
3551 
3552     return ret;
3553 }
3554 #undef MATCH
3555 
3556 
3557 static int
hypervDomainSendKey(virDomainPtr domain,unsigned int codeset,unsigned int holdtime,unsigned int * keycodes,int nkeycodes,unsigned int flags)3558 hypervDomainSendKey(virDomainPtr domain, unsigned int codeset,
3559                     unsigned int holdtime, unsigned int *keycodes, int nkeycodes,
3560                     unsigned int flags)
3561 {
3562     size_t i = 0;
3563     int keycode = 0;
3564     g_autofree int *translatedKeycodes = NULL;
3565     hypervPrivate *priv = domain->conn->privateData;
3566     char uuid_string[VIR_UUID_STRING_BUFLEN];
3567     g_autofree char *selector = NULL;
3568     g_autoptr(Msvm_ComputerSystem) computerSystem = NULL;
3569     g_autoptr(Msvm_Keyboard) keyboard = NULL;
3570     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
3571     g_autoptr(hypervInvokeParamsList) params = NULL;
3572     char keycodeStr[VIR_INT64_STR_BUFLEN];
3573 
3574     virCheckFlags(0, -1);
3575 
3576     virUUIDFormat(domain->uuid, uuid_string);
3577 
3578     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
3579         return -1;
3580 
3581     virBufferEscapeSQL(&query,
3582                        "ASSOCIATORS OF {Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='%s'} "
3583                        "WHERE ResultClass = Msvm_Keyboard",
3584                        uuid_string);
3585 
3586     if (hypervGetWmiClass(Msvm_Keyboard, &keyboard) < 0)
3587         return -1;
3588 
3589     translatedKeycodes = g_new0(int, nkeycodes);
3590 
3591     /* translate keycodes to win32 and generate keyup scancodes. */
3592     for (i = 0; i < nkeycodes; i++) {
3593         if (codeset != VIR_KEYCODE_SET_WIN32) {
3594             keycode = virKeycodeValueTranslate(codeset,
3595                                                VIR_KEYCODE_SET_WIN32,
3596                                                keycodes[i]);
3597 
3598             if (keycode < 0) {
3599                 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
3600                                _("Could not translate keycode"));
3601                 return -1;
3602             }
3603             translatedKeycodes[i] = keycode;
3604         }
3605     }
3606 
3607     selector = g_strdup_printf("CreationClassName=Msvm_Keyboard&DeviceID=%s&"
3608                                "SystemCreationClassName=Msvm_ComputerSystem&"
3609                                "SystemName=%s", keyboard->data->DeviceID, uuid_string);
3610 
3611     /* press the keys */
3612     for (i = 0; i < nkeycodes; i++) {
3613         g_snprintf(keycodeStr, sizeof(keycodeStr), "%d", translatedKeycodes[i]);
3614 
3615         params = hypervCreateInvokeParamsList("PressKey", selector,
3616                                               Msvm_Keyboard_WmiInfo);
3617 
3618         if (!params)
3619             return -1;
3620 
3621         if (hypervAddSimpleParam(params, "keyCode", keycodeStr) < 0)
3622             return -1;
3623 
3624         if (hypervInvokeMethod(priv, &params, NULL) < 0)
3625             return -1;
3626     }
3627 
3628     /* simulate holdtime by sleeping */
3629     if (holdtime > 0)
3630         g_usleep(holdtime * 1000);
3631 
3632     /* release the keys */
3633     for (i = 0; i < nkeycodes; i++) {
3634         g_snprintf(keycodeStr, sizeof(keycodeStr), "%d", translatedKeycodes[i]);
3635         params = hypervCreateInvokeParamsList("ReleaseKey", selector,
3636                                               Msvm_Keyboard_WmiInfo);
3637 
3638         if (!params)
3639             return -1;
3640 
3641         if (hypervAddSimpleParam(params, "keyCode", keycodeStr) < 0)
3642             return -1;
3643 
3644         if (hypervInvokeMethod(priv, &params, NULL) < 0)
3645             return -1;
3646     }
3647 
3648     return 0;
3649 }
3650 
3651 
3652 static virHypervisorDriver hypervHypervisorDriver = {
3653     .name = "Hyper-V",
3654     .connectOpen = hypervConnectOpen, /* 0.9.5 */
3655     .connectClose = hypervConnectClose, /* 0.9.5 */
3656     .connectGetType = hypervConnectGetType, /* 0.9.5 */
3657     .connectGetVersion = hypervConnectGetVersion, /* 6.9.0 */
3658     .connectGetHostname = hypervConnectGetHostname, /* 0.9.5 */
3659     .connectGetMaxVcpus = hypervConnectGetMaxVcpus, /* 6.9.0 */
3660     .nodeGetInfo = hypervNodeGetInfo, /* 0.9.5 */
3661     .connectGetCapabilities = hypervConnectGetCapabilities, /* 6.9.0 */
3662     .connectListDomains = hypervConnectListDomains, /* 0.9.5 */
3663     .connectNumOfDomains = hypervConnectNumOfDomains, /* 0.9.5 */
3664     .connectListAllDomains = hypervConnectListAllDomains, /* 0.10.2 */
3665     .domainLookupByID = hypervDomainLookupByID, /* 0.9.5 */
3666     .domainLookupByUUID = hypervDomainLookupByUUID, /* 0.9.5 */
3667     .domainLookupByName = hypervDomainLookupByName, /* 0.9.5 */
3668     .domainSuspend = hypervDomainSuspend, /* 0.9.5 */
3669     .domainResume = hypervDomainResume, /* 0.9.5 */
3670     .domainShutdown = hypervDomainShutdown, /* 6.9.0 */
3671     .domainShutdownFlags = hypervDomainShutdownFlags, /* 6.9.0 */
3672     .domainReboot = hypervDomainReboot, /* 6.9.0 */
3673     .domainReset = hypervDomainReset, /* 6.9.0 */
3674     .domainDestroy = hypervDomainDestroy, /* 0.9.5 */
3675     .domainDestroyFlags = hypervDomainDestroyFlags, /* 0.9.5 */
3676     .domainGetOSType = hypervDomainGetOSType, /* 0.9.5 */
3677     .domainGetMaxMemory = hypervDomainGetMaxMemory, /* 6.10.0 */
3678     .domainSetMaxMemory = hypervDomainSetMaxMemory, /* 6.10.0 */
3679     .domainSetMemory = hypervDomainSetMemory, /* 3.6.0 */
3680     .domainSetMemoryFlags = hypervDomainSetMemoryFlags, /* 3.6.0 */
3681     .domainGetInfo = hypervDomainGetInfo, /* 0.9.5 */
3682     .domainGetState = hypervDomainGetState, /* 0.9.5 */
3683     .domainScreenshot = hypervDomainScreenshot, /* 7.1.0 */
3684     .domainSetVcpus = hypervDomainSetVcpus, /* 6.10.0 */
3685     .domainSetVcpusFlags = hypervDomainSetVcpusFlags, /* 6.10.0 */
3686     .domainGetVcpusFlags = hypervDomainGetVcpusFlags, /* 6.10.0 */
3687     .domainGetVcpus = hypervDomainGetVcpus, /* 6.10.0 */
3688     .domainGetMaxVcpus = hypervDomainGetMaxVcpus, /* 6.10.0 */
3689     .domainGetXMLDesc = hypervDomainGetXMLDesc, /* 0.9.5 */
3690     .connectListDefinedDomains = hypervConnectListDefinedDomains, /* 0.9.5 */
3691     .connectNumOfDefinedDomains = hypervConnectNumOfDefinedDomains, /* 0.9.5 */
3692     .domainCreate = hypervDomainCreate, /* 0.9.5 */
3693     .domainCreateWithFlags = hypervDomainCreateWithFlags, /* 0.9.5 */
3694     .domainDefineXML = hypervDomainDefineXML, /* 7.1.0 */
3695     .domainUndefine = hypervDomainUndefine, /* 7.1.0 */
3696     .domainUndefineFlags = hypervDomainUndefineFlags, /* 7.1.0 */
3697     .domainAttachDevice = hypervDomainAttachDevice, /* 7.1.0 */
3698     .domainAttachDeviceFlags = hypervDomainAttachDeviceFlags, /* 7.1.0 */
3699     .domainGetAutostart = hypervDomainGetAutostart, /* 6.9.0 */
3700     .domainSetAutostart = hypervDomainSetAutostart, /* 6.9.0 */
3701     .domainGetSchedulerType = hypervDomainGetSchedulerType, /* 6.10.0 */
3702     .domainGetSchedulerParameters = hypervDomainGetSchedulerParameters, /* 6.10.0 */
3703     .domainGetSchedulerParametersFlags = hypervDomainGetSchedulerParametersFlags, /* 6.10.0 */
3704     .nodeGetFreeMemory = hypervNodeGetFreeMemory, /* 6.9.0 */
3705     .connectIsEncrypted = hypervConnectIsEncrypted, /* 0.9.5 */
3706     .connectIsSecure = hypervConnectIsSecure, /* 0.9.5 */
3707     .domainIsActive = hypervDomainIsActive, /* 0.9.5 */
3708     .domainIsPersistent = hypervDomainIsPersistent, /* 0.9.5 */
3709     .domainIsUpdated = hypervDomainIsUpdated, /* 0.9.5 */
3710     .domainManagedSave = hypervDomainManagedSave, /* 0.9.5 */
3711     .domainHasManagedSaveImage = hypervDomainHasManagedSaveImage, /* 0.9.5 */
3712     .domainManagedSaveRemove = hypervDomainManagedSaveRemove, /* 0.9.5 */
3713     .domainSendKey = hypervDomainSendKey, /* 3.6.0 */
3714     .connectIsAlive = hypervConnectIsAlive, /* 0.9.8 */
3715 };
3716 
3717 
3718 virDomainDefParserConfig hypervDomainDefParserConfig = {
3719     .features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG,
3720 };
3721 
3722 
3723 static void
hypervDebugHandler(const char * message,debug_level_e level,void * user_data G_GNUC_UNUSED)3724 hypervDebugHandler(const char *message, debug_level_e level,
3725                    void *user_data G_GNUC_UNUSED)
3726 {
3727     switch (level) {
3728     case DEBUG_LEVEL_ERROR:
3729     case DEBUG_LEVEL_CRITICAL:
3730     case DEBUG_LEVEL_ALWAYS:
3731         VIR_ERROR(_("openwsman: %s"), message);
3732         break;
3733 
3734     case DEBUG_LEVEL_WARNING:
3735         VIR_WARN("openwsman: %s", message);
3736         break;
3737 
3738     case DEBUG_LEVEL_MESSAGE:
3739         VIR_INFO("openwsman: %s", message);
3740         break;
3741 
3742     case DEBUG_LEVEL_INFO:
3743         VIR_INFO("openwsman: %s", message);
3744         break;
3745 
3746     case DEBUG_LEVEL_DEBUG:
3747         VIR_DEBUG("openwsman: %s", message);
3748         break;
3749 
3750     case DEBUG_LEVEL_NONE:
3751     default:
3752         /* Ignore the rest */
3753         break;
3754     }
3755 }
3756 
3757 
3758 static virConnectDriver hypervConnectDriver = {
3759     .remoteOnly = true,
3760     .uriSchemes = (const char *[]){ "hyperv", NULL },
3761     .hypervisorDriver = &hypervHypervisorDriver,
3762     .networkDriver = &hypervNetworkDriver,
3763 };
3764 
3765 int
hypervRegister(void)3766 hypervRegister(void)
3767 {
3768     /* Forward openwsman errors and warnings to libvirt's logging */
3769     debug_add_handler(hypervDebugHandler, DEBUG_LEVEL_WARNING, NULL);
3770 
3771     return virRegisterConnectDriver(&hypervConnectDriver,
3772                                     false);
3773 }
3774