[PATCH v2 00/13] Hyper-V serial ports, NICs, and screeshots

This patchset makes the following changes to the Hyper-V driver: * enable XML parsing and creation of serial ports and NICs * implement several networking APIs * implement screenshots Changes since v1: * simplified hypervDomainDefParseSerial based on Michal's feedback * enabled the use of g_autoptr for virDomainNetDef * use g_autoptr for ndef in hypervDomainDefParseEthernetAdapter to fix the memory leaks that Michal spotted Matt Coleman (13): hyperv: XML parsing of serial ports hyperv: add support for creating serial devices domain_conf: enable use of g_autofree for virDomainNetDef hyperv: XML parsing of Ethernet adapters hyperv: add support for creating network adapters hyperv: implement connectListAllNetworks and connectNumOfNetworks hyperv: implement networkLookupByName and networkLookupByUUID hyperv: implement connectNumOfDefinedNetworks and connectListDefinedNetworks hyperv: implement networkGetAutostart, networkIsActive, and networkIsPersistent hyperv: implement networkGetXMLDesc hyperv: implement domainScreenshot hyperv: provide a more detailed error message for WSMan faults news: implement new Hyper-V APIs NEWS.rst | 12 + po/POTFILES.in | 1 + src/conf/domain_conf.h | 1 + src/hyperv/hyperv_driver.c | 611 ++++++++++++++++++++++++++ src/hyperv/hyperv_network_driver.c | 242 ++++++++++ src/hyperv/hyperv_network_driver.h | 26 ++ src/hyperv/hyperv_wmi.c | 45 +- src/hyperv/hyperv_wmi.h | 12 + src/hyperv/hyperv_wmi_classes.h | 36 ++ src/hyperv/hyperv_wmi_generator.input | 309 +++++++++++++ src/hyperv/meson.build | 1 + 11 files changed, 1293 insertions(+), 3 deletions(-) create mode 100644 src/hyperv/hyperv_network_driver.c create mode 100644 src/hyperv/hyperv_network_driver.h -- 2.30.0

Co-authored-by: Sri Ramanujam <sramanujam@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_driver.c | 61 +++++++++++++++++++++++++++ src/hyperv/hyperv_wmi.c | 10 +++++ src/hyperv/hyperv_wmi.h | 4 ++ src/hyperv/hyperv_wmi_classes.h | 1 + src/hyperv/hyperv_wmi_generator.input | 5 +++ 5 files changed, 81 insertions(+) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index bdc084790a..9902fa75b8 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -1253,6 +1253,54 @@ hypervDomainDefParseStorage(hypervPrivate *priv, } +static int +hypervDomainDefParseSerial(virDomainDefPtr def, Msvm_ResourceAllocationSettingData *rasd) +{ + while (rasd) { + if (rasd->data->ResourceType == MSVM_RASD_RESOURCETYPE_SERIAL_PORT) { + int port_num = 0; + char **conn = NULL; + const char *srcPath = NULL; + virDomainChrDefPtr serial = NULL; + + /* get port number */ + port_num = rasd->data->ElementName[4] - '0'; + if (port_num < 1) { + rasd = rasd->next; + continue; + } + + serial = virDomainChrDefNew(NULL); + + serial->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; + serial->source->type = VIR_DOMAIN_CHR_TYPE_PIPE; + serial->target.port = port_num; + + /* set up source */ + if (rasd->data->Connection.count < 1) { + srcPath = "-1"; + } else { + conn = rasd->data->Connection.data; + if (!*conn) + srcPath = "-1"; + else + srcPath = *conn; + } + + serial->source->data.file.path = g_strdup(srcPath); + + if (VIR_APPEND_ELEMENT(def->serials, def->nserials, serial) < 0) { + virDomainChrDefFree(serial); + return -1; + } + } + + rasd = rasd->next; + } + + return 0; +} + /* * Driver functions @@ -2113,6 +2161,8 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) g_autoptr(Msvm_MemorySettingData) memorySettingData = NULL; g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL; g_autoptr(Msvm_StorageAllocationSettingData) sasd = NULL; + g_autoptr(Msvm_SerialPortSettingData) spsd = NULL; + Msvm_ResourceAllocationSettingData *serialDevices = NULL; virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL); @@ -2151,6 +2201,9 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) return NULL; } + if (hypervGetSerialPortSD(priv, virtualSystemSettingData->data->InstanceID, &spsd) < 0) + return NULL; + /* Fill struct */ def->virtType = VIR_DOMAIN_VIRT_HYPERV; @@ -2215,6 +2268,14 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) if (hypervDomainDefParseStorage(priv, def, rasd, sasd) < 0) return NULL; + if (g_str_has_prefix(priv->version, "6.")) + serialDevices = rasd; + else + serialDevices = (Msvm_ResourceAllocationSettingData *)spsd; + + if (hypervDomainDefParseSerial(def, serialDevices) < 0) + return NULL; + /* XXX xmlopts must be non-NULL */ return virDomainDefFormat(def, NULL, virDomainDefFormatConvertXMLFlags(flags)); } diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index a28bb0e815..f5f0797605 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -1488,6 +1488,16 @@ hypervGetStorageAllocationSD(hypervPrivate *priv, } +int +hypervGetSerialPortSD(hypervPrivate *priv, + const char *id, + Msvm_SerialPortSettingData **data) +{ + hypervGetSettingData(Msvm_SerialPortSettingData, id, data); + return 0; +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Msvm_VirtualSystemManagementService */ diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h index f09948895e..2b0d0e4a3f 100644 --- a/src/hyperv/hyperv_wmi.h +++ b/src/hyperv/hyperv_wmi.h @@ -254,6 +254,10 @@ int hypervGetStorageAllocationSD(hypervPrivate *priv, const char *id, Msvm_StorageAllocationSettingData **data); +int hypervGetSerialPortSD(hypervPrivate *priv, + const char *id, + Msvm_SerialPortSettingData **data); + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Msvm_VirtualSystemManagementService */ diff --git a/src/hyperv/hyperv_wmi_classes.h b/src/hyperv/hyperv_wmi_classes.h index 2c03ca3745..2f813bedb3 100644 --- a/src/hyperv/hyperv_wmi_classes.h +++ b/src/hyperv/hyperv_wmi_classes.h @@ -113,6 +113,7 @@ enum _Msvm_ResourceAllocationSettingData_ResourceType { MSVM_RASD_RESOURCETYPE_DVD_DRIVE = 16, MSVM_RASD_RESOURCETYPE_DISK_DRIVE = 17, MSVM_RASD_RESOURCETYPE_STORAGE_EXTENT = 19, + MSVM_RASD_RESOURCETYPE_SERIAL_PORT = 21, MSVM_RASD_RESOURCETYPE_LOGICAL_DISK = 31, }; diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input index e1b4fd3f9f..765ffba169 100644 --- a/src/hyperv/hyperv_wmi_generator.input +++ b/src/hyperv/hyperv_wmi_generator.input @@ -629,6 +629,11 @@ class Msvm_ResourceAllocationSettingData end +class Msvm_SerialPortSettingData : Msvm_ResourceAllocationSettingData + boolean DebuggerMode +end + + class Msvm_Keyboard string InstanceID string Caption -- 2.30.0

On 2/2/21 1:48 AM, Matt Coleman wrote:
Co-authored-by: Sri Ramanujam <sramanujam@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_driver.c | 61 +++++++++++++++++++++++++++ src/hyperv/hyperv_wmi.c | 10 +++++ src/hyperv/hyperv_wmi.h | 4 ++ src/hyperv/hyperv_wmi_classes.h | 1 + src/hyperv/hyperv_wmi_generator.input | 5 +++ 5 files changed, 81 insertions(+)
diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index bdc084790a..9902fa75b8 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -1253,6 +1253,54 @@ hypervDomainDefParseStorage(hypervPrivate *priv, }
+static int +hypervDomainDefParseSerial(virDomainDefPtr def, Msvm_ResourceAllocationSettingData *rasd) +{ + while (rasd) { + if (rasd->data->ResourceType == MSVM_RASD_RESOURCETYPE_SERIAL_PORT) {
I think we can save one level of indendation if this was: if (rasd->data->ResourceType != MSVM_RASD_RESOURCETYPE_SERIAL_PORT) { rasd = rasd->next; continue; } and to avoid having to advance pointer in every path, let's just use for() loop. Sorry for not spotting this earlier. I'll fix this locally before pushing.
+ int port_num = 0; + char **conn = NULL; + const char *srcPath = NULL; + virDomainChrDefPtr serial = NULL; + + /* get port number */ + port_num = rasd->data->ElementName[4] - '0'; + if (port_num < 1) { + rasd = rasd->next; + continue; + } + + serial = virDomainChrDefNew(NULL); + + serial->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; + serial->source->type = VIR_DOMAIN_CHR_TYPE_PIPE; + serial->target.port = port_num; + + /* set up source */ + if (rasd->data->Connection.count < 1) { + srcPath = "-1"; + } else { + conn = rasd->data->Connection.data; + if (!*conn) + srcPath = "-1"; + else + srcPath = *conn; + } + + serial->source->data.file.path = g_strdup(srcPath); + + if (VIR_APPEND_ELEMENT(def->serials, def->nserials, serial) < 0) { + virDomainChrDefFree(serial); + return -1; + } + } + + rasd = rasd->next; + } + + return 0; +} +
Michal

Co-authored-by: Sri Ramanujam <sramanujam@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_driver.c | 92 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 9902fa75b8..a7947be267 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -899,6 +899,85 @@ hypervDomainAttachStorage(virDomainPtr domain, virDomainDefPtr def, const char * } +static int +hypervDomainAttachSerial(virDomainPtr domain, virDomainChrDefPtr serial) +{ + char uuid_string[VIR_UUID_STRING_BUFLEN]; + hypervPrivate *priv = domain->conn->privateData; + g_autofree char *com_string = g_strdup_printf("COM %d", serial->target.port); + g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL; + g_autoptr(Msvm_ResourceAllocationSettingData) rasd = NULL; + g_autoptr(Msvm_SerialPortSettingData) spsd = NULL; + hypervWmiClassInfoPtr classInfo = NULL; + Msvm_ResourceAllocationSettingData *current = NULL; + Msvm_ResourceAllocationSettingData *entry = NULL; + g_autoptr(GHashTable) serialResource = NULL; + const char *connectionValue = NULL; + g_autofree const char *resourceType = NULL; + + virUUIDFormat(domain->uuid, uuid_string); + + if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0) + return -1; + + if (g_str_has_prefix(priv->version, "6.")) { + if (hypervGetResourceAllocationSD(priv, vssd->data->InstanceID, &rasd) < 0) + return -1; + + current = rasd; + classInfo = Msvm_ResourceAllocationSettingData_WmiInfo; + } else { + if (hypervGetSerialPortSD(priv, vssd->data->InstanceID, &spsd) < 0) + return -1; + + current = (Msvm_ResourceAllocationSettingData *)spsd; + classInfo = Msvm_SerialPortSettingData_WmiInfo; + } + + while (current) { + if (current->data->ResourceType == MSVM_RASD_RESOURCETYPE_SERIAL_PORT && + STREQ(current->data->ElementName, com_string)) { + /* found our com port */ + entry = current; + break; + } + current = current->next; + } + + if (!entry) + return -1; + + if (STRNEQ(serial->source->data.file.path, "-1")) + connectionValue = serial->source->data.file.path; + else + connectionValue = ""; + + resourceType = g_strdup_printf("%d", entry->data->ResourceType); + + serialResource = hypervCreateEmbeddedParam(classInfo); + if (!serialResource) + return -1; + + if (hypervSetEmbeddedProperty(serialResource, "Connection", connectionValue) < 0) + return -1; + + if (hypervSetEmbeddedProperty(serialResource, "InstanceID", entry->data->InstanceID) < 0) + return -1; + + if (hypervSetEmbeddedProperty(serialResource, "ResourceType", resourceType) < 0) + return -1; + + if (hypervSetEmbeddedProperty(serialResource, "ResourceSubType", + entry->data->ResourceSubType) < 0) + return -1; + + if (hypervMsvmVSMSModifyResourceSettings(priv, &serialResource, classInfo) < 0) + return -1; + + return 0; +} + + /* * Functions for deserializing device entries */ @@ -2418,6 +2497,7 @@ hypervDomainDefineXML(virConnectPtr conn, const char *xml) virDomainPtr domain = NULL; g_autoptr(hypervInvokeParamsList) params = NULL; g_autoptr(GHashTable) defineSystemParam = NULL; + size_t i = 0; /* parse xml */ def = virDomainDefParseString(xml, priv->xmlopt, NULL, @@ -2477,6 +2557,14 @@ hypervDomainDefineXML(virConnectPtr conn, const char *xml) if (hypervDomainAttachStorage(domain, def, hostname) < 0) goto error; + /* Attach serials */ + for (i = 0; i < def->nserials; i++) { + if (hypervDomainAttachSerial(domain, def->serials[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not attach serial port %lu"), i); + goto error; + } + } + return domain; error: @@ -2576,6 +2664,10 @@ hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int if (hypervDomainAttachStorageVolume(domain, dev->data.disk, controller, hostname) < 0) return -1; break; + case VIR_DOMAIN_DEVICE_CHR: + if (hypervDomainAttachSerial(domain, dev->data.chr) < 0) + return -1; + break; default: /* unsupported device type */ virReportError(VIR_ERR_INTERNAL_ERROR, -- 2.30.0

Signed-off-by: Matt Coleman <matt@datto.com> --- src/conf/domain_conf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 0a5d151150..f9ea31d48c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3099,6 +3099,7 @@ virDomainVsockDefPtr virDomainVsockDefNew(virDomainXMLOptionPtr xmlopt); void virDomainVsockDefFree(virDomainVsockDefPtr vsock); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainVsockDef, virDomainVsockDefFree); void virDomainNetDefFree(virDomainNetDefPtr def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainNetDef, virDomainNetDefFree); void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainSmartcardDef, virDomainSmartcardDefFree); void virDomainChrDefFree(virDomainChrDefPtr def); -- 2.30.0

Co-authored-by: Sri Ramanujam <sramanujam@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_driver.c | 111 ++++++++++++++++++++++++++ src/hyperv/hyperv_wmi.c | 20 +++++ src/hyperv/hyperv_wmi.h | 8 ++ src/hyperv/hyperv_wmi_classes.h | 12 +++ src/hyperv/hyperv_wmi_generator.input | 109 +++++++++++++++++++++++++ 5 files changed, 260 insertions(+) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index a7947be267..e94fbff87b 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -1381,6 +1381,105 @@ hypervDomainDefParseSerial(virDomainDefPtr def, Msvm_ResourceAllocationSettingDa } +static int +hypervDomainDefParseEthernetAdapter(virDomainDefPtr def, + Msvm_EthernetPortAllocationSettingData *net, + hypervPrivate *priv) +{ + g_autoptr(virDomainNetDef) ndef = g_new0(virDomainNetDef, 1); + g_autoptr(Msvm_SyntheticEthernetPortSettingData) sepsd = NULL; + g_autoptr(Msvm_VirtualEthernetSwitch) vSwitch = NULL; + char **switchConnection = NULL; + g_autofree char *switchConnectionEscaped = NULL; + char *sepsdPATH = NULL; + g_autofree char *sepsdEscaped = NULL; + g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER; + + VIR_DEBUG("Parsing ethernet adapter '%s'", net->data->InstanceID); + + ndef->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + + /* + * If there's no switch port connection or the EnabledState is disabled, + * then the adapter isn't hooked up to anything and we don't have to + * do anything more. + */ + switchConnection = net->data->HostResource.data; + if (net->data->HostResource.count < 1 || !*switchConnection || + net->data->EnabledState == MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_DISABLED) { + VIR_DEBUG("Adapter not connected to switch"); + return 0; + } + + /* + * Now we retrieve the associated Msvm_SyntheticEthernetPortSettingData and + * Msvm_VirtualEthernetSwitch objects and use them to build the XML definition. + */ + + /* begin by getting the Msvm_SyntheticEthernetPortSettingData object */ + sepsdPATH = net->data->Parent; + sepsdEscaped = virStringReplace(sepsdPATH, "\\", "\\\\"); + virBufferAsprintf(&query, + MSVM_SYNTHETICETHERNETPORTSETTINGDATA_WQL_SELECT "WHERE __PATH = '%s'", + sepsdEscaped); + + if (hypervGetWmiClass(Msvm_SyntheticEthernetPortSettingData, &sepsd) < 0) + return -1; + + if (!sepsd) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve NIC settings")); + return -1; + } + + /* set mac address */ + if (virMacAddrParseHex(sepsd->data->Address, &ndef->mac) < 0) + return -1; + + /* now we get the Msvm_VirtualEthernetSwitch */ + virBufferFreeAndReset(&query); + switchConnectionEscaped = virStringReplace(*switchConnection, "\\", "\\\\"); + virBufferAsprintf(&query, + MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE __PATH = '%s'", + switchConnectionEscaped); + + if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &vSwitch) < 0) + return -1; + + if (!vSwitch) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve virtual switch")); + return -1; + } + + /* get bridge name */ + ndef->data.bridge.brname = g_strdup(vSwitch->data->Name); + + if (VIR_APPEND_ELEMENT(def->nets, def->nnets, ndef) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not append definition to domain")); + return -1; + } + + return 0; +} + +static int +hypervDomainDefParseEthernet(virDomainPtr domain, + virDomainDefPtr def, + Msvm_EthernetPortAllocationSettingData *nets) +{ + Msvm_EthernetPortAllocationSettingData *entry = nets; + hypervPrivate *priv = domain->conn->privateData; + + while (entry) { + if (hypervDomainDefParseEthernetAdapter(def, entry, priv) < 0) + return -1; + + entry = entry->next; + } + + return 0; +} + + /* * Driver functions */ @@ -2242,6 +2341,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) g_autoptr(Msvm_StorageAllocationSettingData) sasd = NULL; g_autoptr(Msvm_SerialPortSettingData) spsd = NULL; Msvm_ResourceAllocationSettingData *serialDevices = NULL; + g_autoptr(Msvm_EthernetPortAllocationSettingData) nets = NULL; virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL); @@ -2283,6 +2383,10 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) if (hypervGetSerialPortSD(priv, virtualSystemSettingData->data->InstanceID, &spsd) < 0) return NULL; + if (hypervGetEthernetPortAllocationSD(priv, + virtualSystemSettingData->data->InstanceID, &nets) < 0) + return NULL; + /* Fill struct */ def->virtType = VIR_DOMAIN_VIRT_HYPERV; @@ -2344,6 +2448,10 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) def->controllers = g_new0(virDomainControllerDefPtr, 5); def->ncontrollers = 0; + /* 8 synthetic + 4 legacy NICs */ + def->nets = g_new0(virDomainNetDefPtr, 12); + def->nnets = 0; + if (hypervDomainDefParseStorage(priv, def, rasd, sasd) < 0) return NULL; @@ -2355,6 +2463,9 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) if (hypervDomainDefParseSerial(def, serialDevices) < 0) return NULL; + if (hypervDomainDefParseEthernet(domain, def, nets) < 0) + return NULL; + /* XXX xmlopts must be non-NULL */ return virDomainDefFormat(def, NULL, virDomainDefFormatConvertXMLFlags(flags)); } diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index f5f0797605..4c1bd5e0d2 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -1498,6 +1498,26 @@ hypervGetSerialPortSD(hypervPrivate *priv, } +int +hypervGetSyntheticEthernetPortSD(hypervPrivate *priv, + const char *id, + Msvm_SyntheticEthernetPortSettingData **data) +{ + hypervGetSettingData(Msvm_SyntheticEthernetPortSettingData, id, data); + return 0; +} + + +int +hypervGetEthernetPortAllocationSD(hypervPrivate *priv, + const char *id, + Msvm_EthernetPortAllocationSettingData **data) +{ + hypervGetSettingData(Msvm_EthernetPortAllocationSettingData, id, data); + return 0; +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Msvm_VirtualSystemManagementService */ diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h index 2b0d0e4a3f..6021a46f3e 100644 --- a/src/hyperv/hyperv_wmi.h +++ b/src/hyperv/hyperv_wmi.h @@ -258,6 +258,14 @@ int hypervGetSerialPortSD(hypervPrivate *priv, const char *id, Msvm_SerialPortSettingData **data); +int hypervGetSyntheticEthernetPortSD(hypervPrivate *priv, + const char *id, + Msvm_SyntheticEthernetPortSettingData **data); + +int hypervGetEthernetPortAllocationSD(hypervPrivate *priv, + const char *id, + Msvm_EthernetPortAllocationSettingData **data); + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Msvm_VirtualSystemManagementService */ diff --git a/src/hyperv/hyperv_wmi_classes.h b/src/hyperv/hyperv_wmi_classes.h index 2f813bedb3..eea24235a5 100644 --- a/src/hyperv/hyperv_wmi_classes.h +++ b/src/hyperv/hyperv_wmi_classes.h @@ -119,6 +119,18 @@ enum _Msvm_ResourceAllocationSettingData_ResourceType { +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Msvm_EthernetPortAllocationSettingData + */ + +/* https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-ethernetportal... */ +enum _Msvm_EthernetPortAllocationSettingData_EnabledState { + MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_ENABLED = 2, + MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_DISABLED = 3, +}; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * WMI */ diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input index 765ffba169..df240361e6 100644 --- a/src/hyperv/hyperv_wmi_generator.input +++ b/src/hyperv/hyperv_wmi_generator.input @@ -827,3 +827,112 @@ class Msvm_StorageAllocationSettingData string IOPSAllocationUnits boolean PersistentReservationsSupported end + + +class Msvm_SyntheticEthernetPortSettingData + string InstanceID + string Caption + string Description + string ElementName + uint16 ResourceType + string OtherResourceType + string ResourceSubType + string PoolID + uint16 ConsumerVisibility + string HostResource[] + string AllocationUnits + uint64 VirtualQuantity + uint64 Reservation + uint64 Limit + uint32 Weight + boolean AutomaticAllocation + boolean AutomaticDeallocation + string Parent + string Connection[] + string Address + uint16 MappingBehavior + string AddressOnParent + string VirtualQuantityUnits + uint16 DesiredVLANEndpointMode + string OtherEndpointMode + string VirtualSystemIdentifiers[] +# DeviceNamingEnabled and AllowPacketDirect are not present in Windows Server 2012R2. +# They were added in Windows 10 and Windows Server 2016. +# They have been omitted to retain compatibility with Windows Server 2012R2. +# boolean DeviceNamingEnabled +# boolean AllowPacketDirect + boolean StaticMacAddress + boolean ClusterMonitored +end + + +class Msvm_EthernetPortAllocationSettingData + string InstanceID + string Caption + string Description + string ElementName + uint16 ResourceType + string OtherResourceType + string ResourceSubType + string PoolID + uint16 ConsumerVisibility + string HostResource[] + string AllocationUnits + uint64 VirtualQuantity + uint64 Reservation + uint64 Limit + uint32 Weight + boolean AutomaticAllocation + boolean AutomaticDeallocation + string Parent + string Connection[] + string Address + uint16 MappingBehavior + string AddressOnParent + string VirtualQuantityUnits + uint16 DesiredVLANEndpointMode + string OtherEndpointMode + uint16 EnabledState + string RequiredFeatures[] + string RequiredFeatureHints[] + string TestReplicaPoolID + string TestReplicaSwitchName +end + + +class Msvm_VirtualEthernetSwitch + string InstanceID + string Caption + string Description + string ElementName + datetime InstallDate + uint16 OperationalStatus[] + string StatusDescriptions[] + string Status + uint16 HealthState + uint16 CommunicationStatus + uint16 DetailedStatus + uint16 OperatingStatus + uint16 PrimaryStatus + uint16 EnabledState + string OtherEnabledState + uint16 RequestedState + uint16 EnabledDefault + datetime TimeOfLastStateChange + uint16 AvailableRequestedStates[] + uint16 TransitioningToState + string CreationClassName + string Name + string PrimaryOwnerName + string PrimaryOwnerContact + string Roles[] + string NameFormat + string OtherIdentifyingInfo[] + string IdentifyingDescriptions[] + uint16 Dedicated[] + string OtherDedicatedDescriptions[] + uint16 ResetCapability + uint16 PowerManagementCapabilities[] + uint32 MaxVMQOffloads + uint32 MaxIOVOffloads +end -- 2.30.0

Co-authored-by: Sri Ramanujam <sramanujam@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_driver.c | 138 ++++++++++++++++++++++++++ src/hyperv/hyperv_wmi_classes.h | 2 + src/hyperv/hyperv_wmi_generator.input | 32 ++++++ 3 files changed, 172 insertions(+) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index e94fbff87b..29908f2e75 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -978,6 +978,132 @@ hypervDomainAttachSerial(virDomainPtr domain, virDomainChrDefPtr serial) } +static int +hypervDomainAttachSyntheticEthernetAdapter(virDomainPtr domain, + virDomainNetDefPtr net, + char *hostname) +{ + hypervPrivate *priv = domain->conn->privateData; + g_autofree char *portResourceType = NULL; + unsigned char vsiGuid[VIR_UUID_BUFLEN]; + char guidString[VIR_UUID_STRING_BUFLEN]; + g_autofree char *virtualSystemIdentifiers = NULL; + char macString[VIR_MAC_STRING_BUFLEN]; + g_autofree char *macAddressNoColons = NULL; + g_autoptr(GHashTable) portResource = NULL; + g_auto(WsXmlDocH) sepsdResponse = NULL; + g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER; + g_autoptr(Msvm_VirtualEthernetSwitch) vSwitch = NULL; + g_autofree char *enabledState = NULL; + g_autofree char *switch__PATH = NULL; + g_autofree char *sepsd__PATH = NULL; + g_autofree char *sepsdInstanceID = NULL; + g_autofree char *sepsdInstanceEscaped = NULL; + g_autofree char *connectionResourceType = NULL; + g_autoptr(GHashTable) connectionResource = NULL; + + /* + * Step 1: Create the Msvm_SyntheticEthernetPortSettingData object + * that holds half the settings for the new adapter we are creating + */ + portResourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_ETHERNET_ADAPTER); + + virUUIDGenerate(vsiGuid); + virUUIDFormat(vsiGuid, guidString); + virtualSystemIdentifiers = g_strdup_printf("{%s}", guidString); + + virMacAddrFormat(&net->mac, macString); + macAddressNoColons = virStringReplace(macString, ":", ""); + + /* prepare embedded param */ + portResource = hypervCreateEmbeddedParam(Msvm_SyntheticEthernetPortSettingData_WmiInfo); + if (!portResource) + return -1; + + if (hypervSetEmbeddedProperty(portResource, "ResourceType", portResourceType) < 0) + return -1; + + if (hypervSetEmbeddedProperty(portResource, "ResourceSubType", + "Microsoft:Hyper-V:Synthetic Ethernet Port") < 0) + return -1; + + if (hypervSetEmbeddedProperty(portResource, + "VirtualSystemIdentifiers", virtualSystemIdentifiers) < 0) + return -1; + + if (hypervSetEmbeddedProperty(portResource, "Address", macAddressNoColons) < 0) + return -1; + + if (hypervSetEmbeddedProperty(portResource, "StaticMacAddress", "true") < 0) + return -1; + + if (hypervMsvmVSMSAddResourceSettings(domain, &portResource, + Msvm_SyntheticEthernetPortSettingData_WmiInfo, + &sepsdResponse) < 0) + return -1; + + /* + * Step 2: Get the Msvm_VirtualEthernetSwitch object + */ + virBufferAsprintf(&query, + MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE Name='%s'", + net->data.bridge.brname); + + if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &vSwitch) < 0) + return -1; + + if (!vSwitch) + return -1; + + /* + * Step 3: Create the Msvm_EthernetPortAllocationSettingData object that + * holds the other half of the network configuration + */ + enabledState = g_strdup_printf("%d", MSVM_ETHERNETPORTALLOCATIONSETTINGDATA_ENABLEDSTATE_ENABLED); + + /* build the two __PATH variables */ + switch__PATH = g_strdup_printf("\\\\%s\\Root\\Virtualization\\V2:" + "Msvm_VirtualEthernetSwitch.CreationClassName=\"Msvm_VirtualEthernetSwitch\"," + "Name=\"%s\"", + hostname, vSwitch->data->Name); + + /* Get the sepsd instance ID out of the XML response */ + sepsdInstanceID = hypervGetInstanceIDFromXMLResponse(sepsdResponse); + sepsdInstanceEscaped = virStringReplace(sepsdInstanceID, "\\", "\\\\"); + sepsd__PATH = g_strdup_printf("\\\\%s\\root\\virtualization\\v2:" + "Msvm_SyntheticEthernetPortSettingData.InstanceID=\"%s\"", + hostname, sepsdInstanceEscaped); + + connectionResourceType = g_strdup_printf("%d", MSVM_RASD_RESOURCETYPE_ETHERNET_CONNECTION); + + connectionResource = hypervCreateEmbeddedParam(Msvm_EthernetPortAllocationSettingData_WmiInfo); + if (!connectionResource) + return -1; + + if (hypervSetEmbeddedProperty(connectionResource, "EnabledState", enabledState) < 0) + return -1; + + if (hypervSetEmbeddedProperty(connectionResource, "HostResource", switch__PATH) < 0) + return -1; + + if (hypervSetEmbeddedProperty(connectionResource, "Parent", sepsd__PATH) < 0) + return -1; + + if (hypervSetEmbeddedProperty(connectionResource, "ResourceType", connectionResourceType) < 0) + return -1; + + if (hypervSetEmbeddedProperty(connectionResource, + "ResourceSubType", "Microsoft:Hyper-V:Ethernet Connection") < 0) + return -1; + + if (hypervMsvmVSMSAddResourceSettings(domain, &connectionResource, + Msvm_EthernetPortAllocationSettingData_WmiInfo, NULL) < 0) + return -1; + + return 0; +} + + /* * Functions for deserializing device entries */ @@ -2676,6 +2802,14 @@ hypervDomainDefineXML(virConnectPtr conn, const char *xml) } } + /* Attach networks */ + for (i = 0; i < def->nnets; i++) { + if (hypervDomainAttachSyntheticEthernetAdapter(domain, def->nets[i], hostname) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not attach network %lu"), i); + goto error; + } + } + return domain; error: @@ -2779,6 +2913,10 @@ hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int if (hypervDomainAttachSerial(domain, dev->data.chr) < 0) return -1; break; + case VIR_DOMAIN_DEVICE_NET: + if (hypervDomainAttachSyntheticEthernetAdapter(domain, dev->data.net, hostname) < 0) + return -1; + break; default: /* unsupported device type */ virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/hyperv/hyperv_wmi_classes.h b/src/hyperv/hyperv_wmi_classes.h index eea24235a5..86a7124799 100644 --- a/src/hyperv/hyperv_wmi_classes.h +++ b/src/hyperv/hyperv_wmi_classes.h @@ -108,6 +108,7 @@ enum _Msvm_ResourceAllocationSettingData_ResourceType { MSVM_RASD_RESOURCETYPE_OTHER = 1, MSVM_RASD_RESOURCETYPE_IDE_CONTROLLER = 5, MSVM_RASD_RESOURCETYPE_PARALLEL_SCSI_HBA = 6, + MSVM_RASD_RESOURCETYPE_ETHERNET_ADAPTER = 10, MSVM_RASD_RESOURCETYPE_DISKETTE_DRIVE = 14, MSVM_RASD_RESOURCETYPE_CD_DRIVE = 15, MSVM_RASD_RESOURCETYPE_DVD_DRIVE = 16, @@ -115,6 +116,7 @@ enum _Msvm_ResourceAllocationSettingData_ResourceType { MSVM_RASD_RESOURCETYPE_STORAGE_EXTENT = 19, MSVM_RASD_RESOURCETYPE_SERIAL_PORT = 21, MSVM_RASD_RESOURCETYPE_LOGICAL_DISK = 31, + MSVM_RASD_RESOURCETYPE_ETHERNET_CONNECTION = 33, }; diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input index df240361e6..f9d486bd4c 100644 --- a/src/hyperv/hyperv_wmi_generator.input +++ b/src/hyperv/hyperv_wmi_generator.input @@ -936,3 +936,35 @@ class Msvm_VirtualEthernetSwitch uint32 MaxVMQOffloads uint32 MaxIOVOffloads end + + +class Msvm_VirtualEthernetSwitchManagementService + string InstanceID + string Caption + string Description + string ElementName + datetime InstallDate + string Name + uint16 OperationalStatus[] + string StatusDescriptions[] + string Status + uint16 HealthState + uint16 CommunicationStatus + uint16 DetailedStatus + uint16 OperatingStatus + uint16 PrimaryStatus + uint16 EnabledState + string OtherEnabledState + uint16 RequestedState + uint16 EnabledDefault + datetime TimeOfLastStateChange + uint16 AvailableRequestedStates[] + uint16 TransitioningToState + string SystemCreationClassName + string SystemName + string CreationClassName + string PrimaryOwnerName + string PrimaryOwnerContact + string StartMode + boolean Started +end -- 2.30.0

Co-authored-by: Dawid Zamirski <dzamirski@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- po/POTFILES.in | 1 + src/hyperv/hyperv_driver.c | 2 + src/hyperv/hyperv_network_driver.c | 127 +++++++++++++++++++++++++++++ src/hyperv/hyperv_network_driver.h | 26 ++++++ src/hyperv/meson.build | 1 + 5 files changed, 157 insertions(+) create mode 100644 src/hyperv/hyperv_network_driver.c create mode 100644 src/hyperv/hyperv_network_driver.h diff --git a/po/POTFILES.in b/po/POTFILES.in index b09e58f14a..80c5f145be 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -78,6 +78,7 @@ @SRCDIR@src/esx/esx_vi_methods.c @SRCDIR@src/esx/esx_vi_types.c @SRCDIR@src/hyperv/hyperv_driver.c +@SRCDIR@src/hyperv/hyperv_network_driver.c @SRCDIR@src/hyperv/hyperv_util.c @SRCDIR@src/hyperv/hyperv_wmi.c @SRCDIR@src/hypervisor/domain_cgroup.c diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 29908f2e75..c99d2a4e79 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -31,6 +31,7 @@ #include "viruuid.h" #include "virutil.h" #include "hyperv_driver.h" +#include "hyperv_network_driver.h" #include "hyperv_private.h" #include "hyperv_util.h" #include "hyperv_wmi.h" @@ -3551,6 +3552,7 @@ static virConnectDriver hypervConnectDriver = { .remoteOnly = true, .uriSchemes = (const char *[]){ "hyperv", NULL }, .hypervisorDriver = &hypervHypervisorDriver, + .networkDriver = &hypervNetworkDriver, }; int diff --git a/src/hyperv/hyperv_network_driver.c b/src/hyperv/hyperv_network_driver.c new file mode 100644 index 0000000000..3931e548f5 --- /dev/null +++ b/src/hyperv/hyperv_network_driver.c @@ -0,0 +1,127 @@ +/* + * hyperv_network_driver.c: network driver functions for Microsoft Hyper-V hosts + * + * Copyright (C) 2020 Datto Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "datatypes.h" +#include "viralloc.h" +#include "network_conf.h" +#include "hyperv_network_driver.h" +#include "hyperv_wmi.h" + +#define VIR_FROM_THIS VIR_FROM_HYPERV + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Utility functions + */ + +static virNetworkPtr +hypervMsvmVirtualSwitchToNetwork(virConnectPtr conn, Msvm_VirtualEthernetSwitch *virtualSwitch) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + + if (virUUIDParse(virtualSwitch->data->Name, uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not parse UUID from string '%s'"), + virtualSwitch->data->Name); + return NULL; + } + + return virGetNetwork(conn, virtualSwitch->data->ElementName, uuid); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Exported API functions + */ + +#define MATCH(FLAG) (flags & (FLAG)) +static int +hypervConnectListAllNetworks(virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags) +{ + int ret = -1; + hypervPrivate *priv = conn->privateData; + size_t count = 0; + size_t i; + g_auto(virBuffer) query = { g_string_new(MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT + "WHERE HealthState = 5"), 0 }; + g_autoptr(Msvm_VirtualEthernetSwitch) switches = NULL; + Msvm_VirtualEthernetSwitch *entry = NULL; + + virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1); + + /* + * Hyper-V networks are always active, persistent, and + * autostarted, so return zero elements in case we are asked + * for networks different than that. + */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) && + !(MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE))) + return 0; + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT) && + !(MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT))) + return 0; + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART) && + !(MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART))) + return 0; + + if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &switches) < 0) + goto cleanup; + + for (entry = switches; entry; entry = entry->next) { + if (nets) { + virNetworkPtr net = hypervMsvmVirtualSwitchToNetwork(conn, entry); + if (!net) + goto cleanup; + if (VIR_APPEND_ELEMENT(*nets, count, net) < 0) + goto cleanup; + } else { + ++count; + } + } + + ret = count; + + cleanup: + if (ret < 0 && nets && *nets) { + for (i = 0; i < count; ++i) + VIR_FREE((*nets)[i]); + VIR_FREE(*nets); + } + + return ret; +} +#undef MATCH + + +static int +hypervConnectNumOfNetworks(virConnectPtr conn) +{ + return hypervConnectListAllNetworks(conn, NULL, 0); +} + + +virNetworkDriver hypervNetworkDriver = { + .connectNumOfNetworks = hypervConnectNumOfNetworks, /* 7.1.0 */ + .connectListAllNetworks = hypervConnectListAllNetworks, /* 7.1.0 */ +}; diff --git a/src/hyperv/hyperv_network_driver.h b/src/hyperv/hyperv_network_driver.h new file mode 100644 index 0000000000..16b0621f05 --- /dev/null +++ b/src/hyperv/hyperv_network_driver.h @@ -0,0 +1,26 @@ +/* + * hyperv_network_driver.h: network driver functions for Microsoft Hyper-V hosts + * + * Copyright (C) 2020 Datto Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "driver.h" + +extern virNetworkDriver hypervNetworkDriver; diff --git a/src/hyperv/meson.build b/src/hyperv/meson.build index 1020e3d6b0..0f0cdb9e54 100644 --- a/src/hyperv/meson.build +++ b/src/hyperv/meson.build @@ -1,5 +1,6 @@ hyperv_sources = [ 'hyperv_driver.c', + 'hyperv_network_driver.c', 'hyperv_util.c', 'hyperv_wmi.c', 'hyperv_wmi_classes.c', -- 2.30.0

Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_network_driver.c | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/hyperv/hyperv_network_driver.c b/src/hyperv/hyperv_network_driver.c index 3931e548f5..25755f2525 100644 --- a/src/hyperv/hyperv_network_driver.c +++ b/src/hyperv/hyperv_network_driver.c @@ -49,6 +49,29 @@ hypervMsvmVirtualSwitchToNetwork(virConnectPtr conn, Msvm_VirtualEthernetSwitch } +static virNetworkPtr +hypervNetworkLookup(virConnectPtr conn, const char *property, const char *value) +{ + hypervPrivate *priv = conn->privateData; + g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER; + g_autoptr(Msvm_VirtualEthernetSwitch) virtualSwitch = NULL; + + virBufferAsprintf(&query, MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE %s", property); + virBufferEscapeSQL(&query, " = '%s'", value); + + if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &virtualSwitch) < 0) + return NULL; + + if (!virtualSwitch) { + virReportError(VIR_ERR_NO_NETWORK, + _("No network found with property '%s' = '%s'"), property, value); + return NULL; + } + + return hypervMsvmVirtualSwitchToNetwork(conn, virtualSwitch); +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Exported API functions */ @@ -121,7 +144,27 @@ hypervConnectNumOfNetworks(virConnectPtr conn) } +static virNetworkPtr +hypervNetworkLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + char uuid_string[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(uuid, uuid_string); + + return hypervNetworkLookup(conn, "Name", uuid_string); +} + + +static virNetworkPtr +hypervNetworkLookupByName(virConnectPtr conn, const char *name) +{ + return hypervNetworkLookup(conn, "ElementName", name); +} + + virNetworkDriver hypervNetworkDriver = { .connectNumOfNetworks = hypervConnectNumOfNetworks, /* 7.1.0 */ .connectListAllNetworks = hypervConnectListAllNetworks, /* 7.1.0 */ + .networkLookupByUUID = hypervNetworkLookupByUUID, /* 7.1.0 */ + .networkLookupByName = hypervNetworkLookupByName, /* 7.1.0 */ }; -- 2.30.0

Co-authored-by: Dawid Zamirski <dzamirski@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_network_driver.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/hyperv/hyperv_network_driver.c b/src/hyperv/hyperv_network_driver.c index 25755f2525..f85dea31fe 100644 --- a/src/hyperv/hyperv_network_driver.c +++ b/src/hyperv/hyperv_network_driver.c @@ -76,6 +76,24 @@ hypervNetworkLookup(virConnectPtr conn, const char *property, const char *value) * Exported API functions */ +static int +hypervConnectNumOfDefinedNetworks(virConnectPtr conn G_GNUC_UNUSED) +{ + /* Hyper-V networks are always active */ + return 0; +} + + +static int +hypervConnectListDefinedNetworks(virConnectPtr conn G_GNUC_UNUSED, + char **const names G_GNUC_UNUSED, + int maxnames G_GNUC_UNUSED) +{ + /* Hyper-V networks are always active */ + return 0; +} + + #define MATCH(FLAG) (flags & (FLAG)) static int hypervConnectListAllNetworks(virConnectPtr conn, @@ -164,6 +182,8 @@ hypervNetworkLookupByName(virConnectPtr conn, const char *name) virNetworkDriver hypervNetworkDriver = { .connectNumOfNetworks = hypervConnectNumOfNetworks, /* 7.1.0 */ + .connectNumOfDefinedNetworks = hypervConnectNumOfDefinedNetworks, /* 7.1.0 */ + .connectListDefinedNetworks = hypervConnectListDefinedNetworks, /* 7.1.0 */ .connectListAllNetworks = hypervConnectListAllNetworks, /* 7.1.0 */ .networkLookupByUUID = hypervNetworkLookupByUUID, /* 7.1.0 */ .networkLookupByName = hypervNetworkLookupByName, /* 7.1.0 */ -- 2.30.0

Co-authored-by: Dawid Zamirski <dzamirski@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_network_driver.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/hyperv/hyperv_network_driver.c b/src/hyperv/hyperv_network_driver.c index f85dea31fe..d6407ac591 100644 --- a/src/hyperv/hyperv_network_driver.c +++ b/src/hyperv/hyperv_network_driver.c @@ -180,6 +180,31 @@ hypervNetworkLookupByName(virConnectPtr conn, const char *name) } +static int +hypervNetworkGetAutostart(virNetworkPtr network G_GNUC_UNUSED, int *autostart) +{ + /* Hyper-V networks are always active */ + *autostart = 1; + return 0; +} + + +static int +hypervNetworkIsActive(virNetworkPtr network G_GNUC_UNUSED) +{ + /* Hyper-V networks are always active */ + return 1; +} + + +static int +hypervNetworkIsPersistent(virNetworkPtr network G_GNUC_UNUSED) +{ + /* Hyper-V networks are always persistent */ + return 1; +} + + virNetworkDriver hypervNetworkDriver = { .connectNumOfNetworks = hypervConnectNumOfNetworks, /* 7.1.0 */ .connectNumOfDefinedNetworks = hypervConnectNumOfDefinedNetworks, /* 7.1.0 */ @@ -187,4 +212,7 @@ virNetworkDriver hypervNetworkDriver = { .connectListAllNetworks = hypervConnectListAllNetworks, /* 7.1.0 */ .networkLookupByUUID = hypervNetworkLookupByUUID, /* 7.1.0 */ .networkLookupByName = hypervNetworkLookupByName, /* 7.1.0 */ + .networkGetAutostart = hypervNetworkGetAutostart, /* 7.1.0 */ + .networkIsActive = hypervNetworkIsActive, /* 7.1.0 */ + .networkIsPersistent = hypervNetworkIsPersistent, /* 7.1.0 */ }; -- 2.30.0

Co-authored-by: Dawid Zamirski <dzamirski@datto.com> Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_network_driver.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/hyperv/hyperv_network_driver.c b/src/hyperv/hyperv_network_driver.c index d6407ac591..93ef01c9aa 100644 --- a/src/hyperv/hyperv_network_driver.c +++ b/src/hyperv/hyperv_network_driver.c @@ -180,6 +180,29 @@ hypervNetworkLookupByName(virConnectPtr conn, const char *name) } +static char * +hypervNetworkGetXMLDesc(virNetworkPtr network, unsigned int flags) +{ + g_autoptr(virNetwork) hypervNetwork = NULL; + g_autoptr(virNetworkDef) def = NULL; + + def = g_new0(virNetworkDef, 1); + + hypervNetwork = hypervNetworkLookupByUUID(network->conn, network->uuid); + if (!hypervNetwork) + return NULL; + + memcpy(def->uuid, network->uuid, VIR_UUID_BUFLEN); + def->uuid_specified = true; + + def->name = g_strdup(hypervNetwork->name); + + def->forward.type = VIR_NETWORK_FORWARD_NONE; + + return virNetworkDefFormat(def, NULL, flags); +} + + static int hypervNetworkGetAutostart(virNetworkPtr network G_GNUC_UNUSED, int *autostart) { @@ -212,6 +235,7 @@ virNetworkDriver hypervNetworkDriver = { .connectListAllNetworks = hypervConnectListAllNetworks, /* 7.1.0 */ .networkLookupByUUID = hypervNetworkLookupByUUID, /* 7.1.0 */ .networkLookupByName = hypervNetworkLookupByName, /* 7.1.0 */ + .networkGetXMLDesc = hypervNetworkGetXMLDesc, /* 7.1.0 */ .networkGetAutostart = hypervNetworkGetAutostart, /* 7.1.0 */ .networkIsActive = hypervNetworkIsActive, /* 7.1.0 */ .networkIsPersistent = hypervNetworkIsPersistent, /* 7.1.0 */ -- 2.30.0

Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_driver.c | 207 ++++++++++++++++++++++++++ src/hyperv/hyperv_wmi_classes.h | 21 +++ src/hyperv/hyperv_wmi_generator.input | 163 ++++++++++++++++++++ 3 files changed, 391 insertions(+) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index c99d2a4e79..419ca96828 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -22,6 +22,8 @@ #include <config.h> +#include <fcntl.h> + #include "internal.h" #include "datatypes.h" #include "virdomainobjlist.h" @@ -38,6 +40,8 @@ #include "virstring.h" #include "virkeycode.h" #include "domain_conf.h" +#include "virfdstream.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_HYPERV @@ -294,6 +298,73 @@ hypervCapsInit(hypervPrivate *priv) return NULL; } + +static int +hypervGetVideoResolution(hypervPrivate *priv, + char *vm_uuid, + int *xRes, + int *yRes, + bool fallback) +{ + g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER; + g_autoptr(Msvm_S3DisplayController) s3Display = NULL; + g_autoptr(Msvm_SyntheticDisplayController) synthetic = NULL; + g_autoptr(Msvm_VideoHead) heads = NULL; + const char *wmiClass = NULL; + char *deviceId = NULL; + g_autofree char *enabledStateString = NULL; + + if (fallback) { + wmiClass = "Msvm_S3DisplayController"; + + virBufferEscapeSQL(&query, + MSVM_S3DISPLAYCONTROLLER_WQL_SELECT "WHERE SystemName = '%s'", + vm_uuid); + + if (hypervGetWmiClass(Msvm_S3DisplayController, &s3Display) < 0 || !s3Display) + return -1; + + deviceId = s3Display->data->DeviceID; + } else { + wmiClass = "Msvm_SyntheticDisplayController"; + + virBufferEscapeSQL(&query, + MSVM_SYNTHETICDISPLAYCONTROLLER_WQL_SELECT "WHERE SystemName = '%s'", + vm_uuid); + + if (hypervGetWmiClass(Msvm_SyntheticDisplayController, &synthetic) < 0 || !synthetic) + return -1; + + deviceId = synthetic->data->DeviceID; + } + + virBufferFreeAndReset(&query); + + virBufferAsprintf(&query, + "ASSOCIATORS OF {%s." + "CreationClassName='%s'," + "DeviceID='%s'," + "SystemCreationClassName='Msvm_ComputerSystem'," + "SystemName='%s'" + "} WHERE AssocClass = Msvm_VideoHeadOnController " + "ResultClass = Msvm_VideoHead", + wmiClass, wmiClass, deviceId, vm_uuid); + + if (hypervGetWmiClass(Msvm_VideoHead, &heads) < 0) + return -1; + + enabledStateString = g_strdup_printf("%d", CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_ENABLED); + if (heads && STREQ(heads->data->EnabledState, enabledStateString)) { + *xRes = heads->data->CurrentHorizontalResolution; + *yRes = heads->data->CurrentVerticalResolution; + + return 0; + } + + return -1; +} + + /* * Virtual device functions */ @@ -2313,6 +2384,141 @@ hypervDomainGetState(virDomainPtr domain, int *state, int *reason, } +static char * +hypervDomainScreenshot(virDomainPtr domain, + virStreamPtr stream, + unsigned int screen G_GNUC_UNUSED, + unsigned int flags) +{ + char uuid_string[VIR_UUID_STRING_BUFLEN]; + hypervPrivate *priv = domain->conn->privateData; + g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER; + g_autoptr(hypervInvokeParamsList) params = NULL; + g_auto(WsXmlDocH) ret_doc = NULL; + int xRes = 640; + int yRes = 480; + g_autofree char *width = NULL; + g_autofree char *height = NULL; + g_autofree char *imageDataText = NULL; + g_autofree unsigned char *imageDataBuffer = NULL; + size_t imageDataBufferSize; + const char *temporaryDirectory = NULL; + g_autofree char *temporaryFile = NULL; + g_autofree uint8_t *ppmBuffer = NULL; + char *result = NULL; + const char *xpath = "/s:Envelope/s:Body/p:GetVirtualSystemThumbnailImage_OUTPUT/p:ImageData"; + int pixelCount; + int pixelByteCount; + size_t i = 0; + int fd = -1; + bool unlinkTemporaryFile = false; + + virCheckFlags(0, NULL); + + virUUIDFormat(domain->uuid, uuid_string); + + /* Hyper-V Generation 1 VMs use two video heads: + * - S3DisplayController is used for early boot screens. + * - SyntheticDisplayController takes over when the guest OS initializes its video driver. + * + * This attempts to get the resolution from the SyntheticDisplayController first. + * If that fails, it falls back to S3DisplayController. */ + if (hypervGetVideoResolution(priv, uuid_string, &xRes, &yRes, false) < 0) { + if (hypervGetVideoResolution(priv, uuid_string, &xRes, &yRes, true) < 0) + goto cleanup; + } + + /* prepare params */ + params = hypervCreateInvokeParamsList("GetVirtualSystemThumbnailImage", + MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR, + Msvm_VirtualSystemManagementService_WmiInfo); + if (!params) + goto cleanup; + + width = g_strdup_printf("%d", xRes); + hypervAddSimpleParam(params, "WidthPixels", width); + + height = g_strdup_printf("%d", yRes); + hypervAddSimpleParam(params, "HeightPixels", height); + + virBufferAsprintf(&query, + "ASSOCIATORS OF " + "{Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='%s'} " + "WHERE ResultClass = Msvm_VirtualSystemSettingData", + uuid_string); + hypervAddEprParam(params, "TargetSystem", &query, Msvm_VirtualSystemSettingData_WmiInfo); + + /* capture and parse the screenshot */ + if (hypervInvokeMethod(priv, ¶ms, &ret_doc) < 0) + goto cleanup; + + if (!ws_xml_get_soap_envelope(ret_doc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not retrieve screenshot")); + goto cleanup; + } + + imageDataText = ws_xml_get_xpath_value(ret_doc, (char *)xpath); + + if (!imageDataText) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to retrieve image data")); + goto cleanup; + } + + imageDataBuffer = g_base64_decode(imageDataText, &imageDataBufferSize); + + pixelCount = imageDataBufferSize / 2; + pixelByteCount = pixelCount * 3; + + ppmBuffer = g_new0(uint8_t, pixelByteCount); + + /* convert rgb565 to rgb888 */ + for (i = 0; i < pixelCount; i++) { + const uint16_t pixel = imageDataBuffer[i * 2] + (imageDataBuffer[i * 2 + 1] << 8); + const uint16_t redMask = 0xF800; + const uint16_t greenMask = 0x7E0; + const uint16_t blueMask = 0x1F; + const uint8_t redFive = (pixel & redMask) >> 11; + const uint8_t greenSix = (pixel & greenMask) >> 5; + const uint8_t blueFive = pixel & blueMask; + ppmBuffer[i * 3] = (redFive * 527 + 23) >> 6; + ppmBuffer[i * 3 + 1] = (greenSix * 259 + 33) >> 6; + ppmBuffer[i * 3 + 2] = (blueFive * 527 + 23) >> 6; + } + + temporaryDirectory = getenv("TMPDIR"); + if (!temporaryDirectory) + temporaryDirectory = "/tmp"; + temporaryFile = g_strdup_printf("%s/libvirt.hyperv.screendump.XXXXXX", temporaryDirectory); + if ((fd = g_mkstemp_full(temporaryFile, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR)) == -1) { + virReportSystemError(errno, _("g_mkstemp(\"%s\") failed"), temporaryFile); + goto cleanup; + } + unlinkTemporaryFile = true; + + /* write image data */ + dprintf(fd, "P6\n%d %d\n255\n", xRes, yRes); + if (safewrite(fd, ppmBuffer, pixelByteCount) != pixelByteCount) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to write pixel data")); + goto cleanup; + } + + if (VIR_CLOSE(fd) < 0) + virReportSystemError(errno, "%s", _("failed to close screenshot file")); + + if (virFDStreamOpenFile(stream, temporaryFile, 0, 0, O_RDONLY) < 0) + goto cleanup; + + result = g_strdup("image/x-portable-pixmap"); + + cleanup: + VIR_FORCE_CLOSE(fd); + if (unlinkTemporaryFile) + unlink(temporaryFile); + + return result; +} + + static int hypervDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus, @@ -3474,6 +3680,7 @@ static virHypervisorDriver hypervHypervisorDriver = { .domainSetMemoryFlags = hypervDomainSetMemoryFlags, /* 3.6.0 */ .domainGetInfo = hypervDomainGetInfo, /* 0.9.5 */ .domainGetState = hypervDomainGetState, /* 0.9.5 */ + .domainScreenshot = hypervDomainScreenshot, /* 7.1.0 */ .domainSetVcpus = hypervDomainSetVcpus, /* 6.10.0 */ .domainSetVcpusFlags = hypervDomainSetVcpusFlags, /* 6.10.0 */ .domainGetVcpusFlags = hypervDomainGetVcpusFlags, /* 6.10.0 */ diff --git a/src/hyperv/hyperv_wmi_classes.h b/src/hyperv/hyperv_wmi_classes.h index 86a7124799..faf98077eb 100644 --- a/src/hyperv/hyperv_wmi_classes.h +++ b/src/hyperv/hyperv_wmi_classes.h @@ -133,6 +133,27 @@ enum _Msvm_EthernetPortAllocationSettingData_EnabledState { +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * CIM_EnabledLogicalElement + */ + +/* https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/cim-enabledlogicale... */ +enum _CIM_EnabledLogicalElement_EnabledState { + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_UNKNOWN = 0, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_OTHER = 1, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_ENABLED = 2, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_DISABLED = 3, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_SHUTTING_DOWN = 4, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_NOT_APPLICABLE = 5, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_ENABLED_BUT_OFFLINE = 6, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_IN_TEST = 7, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_DEFERRED = 8, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_QUIESCE = 9, + CIM_ENABLEDLOGICALELEMENT_ENABLEDSTATE_STARTING = 10, +}; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * WMI */ diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input index f9d486bd4c..0b342cbfa6 100644 --- a/src/hyperv/hyperv_wmi_generator.input +++ b/src/hyperv/hyperv_wmi_generator.input @@ -968,3 +968,166 @@ class Msvm_VirtualEthernetSwitchManagementService string StartMode boolean Started end + + +class Msvm_SyntheticDisplayController + string InstanceID + string Caption + string Description + string ElementName + datetime InstallDate + string Name + uint16 OperationalStatus[] + string StatusDescriptions[] + string Status + uint16 HealthState + uint16 CommunicationStatus + uint16 DetailedStatus + uint16 OperatingStatus + uint16 PrimaryStatus + string EnabledState + string OtherEnabledState + uint16 RequestedState + uint16 EnabledDefault + datetime TimeOfLastStateChange + uint16 AvailableRequestedStates[] + uint16 TransitioningToState + string SystemCreationClassName + string SystemName + string CreationClassName + string DeviceID + boolean PowerManagementSupported + uint16 PowerManagementCapabilities[] + uint16 Availability + uint16 StatusInfo + uint32 LastErrorCode + string ErrorDescription + boolean ErrorCleared + uint64 PowerOnHours + uint64 TotalPowerOnHours + string OtherIdentifyingInfo[] + string IdentifyingDescriptions[] + uint16 AdditionalAvailability[] + uint64 MaxQuiesceTime + datetime TimeOfLastReset + uint16 ProtocolSupported + uint32 MaxNumberControlled + string ProtocolDescription + string VideoProcessor + uint16 VideoMemoryType + string OtherVideoMemoryType + uint32 NumberOfVideoPages + uint32 MaxMemorySupported + uint16 AcceleratorCapabilities[] + string CapabilityDescriptions[] + string OtherVideoArchitecture + uint16 VideoArchitecture +end + + +class Msvm_S3DisplayController + string InstanceID + string Caption + string Description + string ElementName + datetime InstallDate + string Name + uint16 OperationalStatus[] + string StatusDescriptions[] + string Status + uint16 HealthState + uint16 CommunicationStatus + uint16 DetailedStatus + uint16 OperatingStatus + uint16 PrimaryStatus + string EnabledState + string OtherEnabledState + uint16 RequestedState + uint16 EnabledDefault + datetime TimeOfLastStateChange + uint16 AvailableRequestedStates[] + uint16 TransitioningToState + string SystemCreationClassName + string SystemName + string CreationClassName + string DeviceID + boolean PowerManagementSupported + uint16 PowerManagementCapabilities[] + uint16 Availability + uint16 StatusInfo + uint32 LastErrorCode + string ErrorDescription + boolean ErrorCleared + string OtherIdentifyingInfo[] + uint64 PowerOnHours + uint64 TotalPowerOnHours + string IdentifyingDescriptions[] + uint16 AdditionalAvailability[] + uint64 MaxQuiesceTime + datetime TimeOfLastReset + uint16 ProtocolSupported + uint32 MaxNumberControlled + string ProtocolDescription + string VideoProcessor + uint16 VideoMemoryType + string OtherVideoMemoryType + uint32 NumberOfVideoPages + uint32 MaxMemorySupported + uint16 AcceleratorCapabilities[] + string CapabilityDescriptions[] + string OtherVideoArchitecture + uint16 VideoArchitecture +end + + +class Msvm_VideoHead + string InstanceID + string Caption + string Description + string ElementName + datetime InstallDate + string Name + uint16 OperationalStatus[] + string StatusDescriptions[] + string Status + uint16 HealthState + uint16 CommunicationStatus + uint16 DetailedStatus + uint16 OperatingStatus + uint16 PrimaryStatus + string EnabledState + string OtherEnabledState + uint16 RequestedState + uint16 EnabledDefault + datetime TimeOfLastStateChange + uint16 AvailableRequestedStates[] + uint16 TransitioningToState + string SystemCreationClassName + string SystemName + string CreationClassName + string DeviceID + boolean PowerManagementSupported + uint16 PowerManagementCapabilities[] + uint16 Availability + uint16 StatusInfo + uint32 LastErrorCode + string ErrorDescription + boolean ErrorCleared + string OtherIdentifyingInfo[] + uint64 PowerOnHours + uint64 TotalPowerOnHours + string IdentifyingDescriptions[] + uint16 AdditionalAvailability[] + uint64 MaxQuiesceTime + uint32 CurrentBitsPerPixel + uint32 CurrentHorizontalResolution + uint32 CurrentVerticalResolution + uint32 MaxRefreshRate + uint32 MinRefreshRate + uint32 CurrentRefreshRate + uint16 CurrentScanMode + string OtherCurrentScanMode + uint32 CurrentNumberOfRows + uint32 CurrentNumberOfColumns + uint64 CurrentNumberOfColors +end -- 2.30.0

Signed-off-by: Matt Coleman <matt@datto.com> --- src/hyperv/hyperv_wmi.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index 4c1bd5e0d2..c14ff0e64a 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -789,9 +789,18 @@ hypervInvokeMethod(hypervPrivate *priv, returnValue = ws_xml_get_xpath_value(response, returnValue_xpath); if (!returnValue) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Could not get return value for %s invocation"), - params->method); + g_autofree char *faultReason_xpath = g_strdup("/s:Envelope/s:Body/s:Fault/s:Reason/s:Text"); + g_autofree char *faultReason = ws_xml_get_xpath_value(response, faultReason_xpath); + + if (faultReason) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("WS-Management fault during %s invocation: %s"), + params->method, faultReason); + else + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get return value for %s invocation"), + params->method); + return -1; } -- 2.30.0

Signed-off-by: Matt Coleman <matt@datto.com> --- NEWS.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 8b8a132e41..c6ae6a6c60 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -23,6 +23,18 @@ v7.1.0 (unreleased) Booting is possible from virtiofs filesystems. Introduce an option to control the boot order, like we do for other bootable devices. + * hyperv: implement new APIs + + The ``virDomainUndefine()``, ``virDomainUndefineFlags()``, + ``virDomainDefineXML()``, ``virDomainAttachDevice()``, and + ``virDomainAttachDeviceFlags()``, ``virConnectListAllNetworks()``, + ``virConnectNumOfNetworks()``, ``virNetworkLookupByName()``, + ``virNetworkLookupByUUID()``, ``virConnectNumOfDefinedNetworks()``, + ``virConnectListDefinedNetworks()``, ``virNetworkGetAutostart()``, + ``virNetworkIsActive()``, ``virNetworkIsPersistent()``, + ``virNetworkGetXMLDesc()``, and ``virDomainScreenshot()``, APIs have been + implemented in the Hyper-V driver. + * **Improvements** * **Bug fixes** -- 2.30.0

On 2/2/21 1:48 AM, Matt Coleman wrote:
This patchset makes the following changes to the Hyper-V driver: * enable XML parsing and creation of serial ports and NICs * implement several networking APIs * implement screenshots
Changes since v1: * simplified hypervDomainDefParseSerial based on Michal's feedback * enabled the use of g_autoptr for virDomainNetDef * use g_autoptr for ndef in hypervDomainDefParseEthernetAdapter to fix the memory leaks that Michal spotted
Matt Coleman (13): hyperv: XML parsing of serial ports hyperv: add support for creating serial devices domain_conf: enable use of g_autofree for virDomainNetDef hyperv: XML parsing of Ethernet adapters hyperv: add support for creating network adapters hyperv: implement connectListAllNetworks and connectNumOfNetworks hyperv: implement networkLookupByName and networkLookupByUUID hyperv: implement connectNumOfDefinedNetworks and connectListDefinedNetworks hyperv: implement networkGetAutostart, networkIsActive, and networkIsPersistent hyperv: implement networkGetXMLDesc hyperv: implement domainScreenshot hyperv: provide a more detailed error message for WSMan faults news: implement new Hyper-V APIs
NEWS.rst | 12 + po/POTFILES.in | 1 + src/conf/domain_conf.h | 1 + src/hyperv/hyperv_driver.c | 611 ++++++++++++++++++++++++++ src/hyperv/hyperv_network_driver.c | 242 ++++++++++ src/hyperv/hyperv_network_driver.h | 26 ++ src/hyperv/hyperv_wmi.c | 45 +- src/hyperv/hyperv_wmi.h | 12 + src/hyperv/hyperv_wmi_classes.h | 36 ++ src/hyperv/hyperv_wmi_generator.input | 309 +++++++++++++ src/hyperv/meson.build | 1 + 11 files changed, 1293 insertions(+), 3 deletions(-) create mode 100644 src/hyperv/hyperv_network_driver.c create mode 100644 src/hyperv/hyperv_network_driver.h
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> and pushed. Michal
participants (2)
-
Matt Coleman
-
Michal Privoznik