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, µ) < 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, ¶ms, 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, ¶ms, &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, ¶ms, 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, ¶ms, 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, ¶ms, 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(¶ms[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(¶ms[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(¶ms[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, ¶ms, 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, ¶ms, 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