[PATCH 7/8] hyperv: implement connectGetVersion

mcoleman at datto.com mcoleman at datto.com
Thu Oct 1 21:47:16 UTC 2020


From: Matt Coleman <matt at datto.com>

Hyper-V version numbers are not compatible with the encoding in
virParseVersionString():
https://gitlab.com/libvirt/libvirt/-/blob/master/src/util/virutil.c#L246

For example, the Windows Server 2016 Hyper-V version is 10.0.14393: its
micro is over 14 times larger than the encoding allows.

This commit repacks the Hyper-V version number in order to preserve all
of the digits. The major and minor are concatenated (with minor zero-
padded to two digits) to form the repacked major value. This works
because Microsoft's major and minor versions numbers are unlikely to
exceed 99. The repacked minor value is derived from the digits in the
thousands, ten-thousands, and hundred-thousands places of Hyper-V's
micro. The repacked micro is derived from the digits in the ones, tens,
and hundreds places of Hyper-V's micro.

Co-authored-by: Sri Ramanujam <sramanujam at datto.com>
Signed-off-by: Matt Coleman <matt at datto.com>
---
 NEWS.rst                              |  4 +-
 src/hyperv/hyperv_driver.c            | 86 +++++++++++++++++++++++++++
 src/hyperv/hyperv_wmi_generator.input |  2 +-
 3 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/NEWS.rst b/NEWS.rst
index c52dddde74..05e129d9cf 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -15,8 +15,8 @@ v6.9.0 (unreleased)
 
   * hyperv: implement new APIs
 
-    The ``virConnectGetCapabilities()`` and ``virConnectGetMaxVcpus()`` APIs
-    have been implemented in the Hyper-V driver.
+    The ``virConnectGetCapabilities()``, ``virConnectGetMaxVcpus()``, and
+    ``virConnectGetVersion()`` APIs have been implemented in the Hyper-V driver.
 
 * **Improvements**
 
diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c
index faeb826601..14ebb4b62d 100644
--- a/src/hyperv/hyperv_driver.c
+++ b/src/hyperv/hyperv_driver.c
@@ -29,6 +29,7 @@
 #include "viralloc.h"
 #include "virlog.h"
 #include "viruuid.h"
+#include "virutil.h"
 #include "hyperv_driver.h"
 #include "hyperv_private.h"
 #include "hyperv_util.h"
@@ -492,6 +493,90 @@ hypervConnectGetType(virConnectPtr conn G_GNUC_UNUSED)
 
 
 
+static int
+hypervConnectGetVersion(virConnectPtr conn, unsigned long *version)
+{
+    int result = -1;
+    hypervPrivate *priv = conn->privateData;
+    Win32_OperatingSystem *os = NULL;
+    g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
+    char *suffix;
+    unsigned int major, minor, micro;
+    g_auto(virBuffer) mangled_version = VIR_BUFFER_INITIALIZER;
+
+    virBufferAddLit(&query, WIN32_OPERATINGSYSTEM_WQL_SELECT);
+
+    if (hypervGetWmiClass(Win32_OperatingSystem, &os) < 0)
+        goto cleanup;
+
+    if (!os) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not get version information for host %s"),
+                       conn->uri->server);
+        goto cleanup;
+    }
+
+    /*
+     * Mangle the version to work with virParseVersionString() while retaining all the digits.
+     *
+     * For example...
+     * 2008:      6.0.6001     =>   600.6.1
+     * 2008 R2:   6.1.7600     =>   601.7.600
+     * 2012:      6.2.9200     =>   602.9.200
+     * 2012 R2:   6.3.9600     =>   603.9.600
+     * 2016:      10.0.14393   =>   1000.14.393
+     * 2019:      10.0.17763   =>   1000.17.763
+     */
+    if (virStrToLong_ui(os->data.common->Version, &suffix, 10, &major) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                      _("Could not parse major from '%s'"),
+                      os->data.common->Version);
+        goto cleanup;
+    }
+
+    if (virStrToLong_ui(suffix + 1, &suffix, 10, &minor) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                      _("Could not parse minor from '%s'"),
+                      os->data.common->Version);
+        goto cleanup;
+    }
+
+    if (virStrToLong_ui(suffix + 1, NULL, 10, &micro) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                      _("Could not parse micro from '%s'"),
+                      os->data.common->Version);
+        goto cleanup;
+    }
+
+    if (major > 99 || minor > 99 || micro > 999999) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not produce mangled version number from '%s'"),
+                       os->data.common->Version);
+        goto cleanup;
+    }
+
+    virBufferAsprintf(&mangled_version, "%d%02d.%d.%d", major, minor,
+                      micro > 999 ? micro / 1000 : 0,
+                      micro > 999 ? micro % 1000 : micro);
+
+    /* Parse version string to long */
+    if (virParseVersionString(virBufferCurrentContent(&mangled_version), version, true) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not parse version number from '%s'"),
+                       os->data.common->Version);
+        goto cleanup;
+    }
+
+    result = 0;
+
+ cleanup:
+    hypervFreeObject(priv, (hypervObject *) os);
+
+    return result;
+}
+
+
+
 static char *
 hypervConnectGetHostname(virConnectPtr conn)
 {
@@ -1722,6 +1807,7 @@ static virHypervisorDriver hypervHypervisorDriver = {
     .connectOpen = hypervConnectOpen, /* 0.9.5 */
     .connectClose = hypervConnectClose, /* 0.9.5 */
     .connectGetType = hypervConnectGetType, /* 0.9.5 */
+    .connectGetVersion = hypervConnectGetVersion, /* 6.9.0 */
     .connectGetHostname = hypervConnectGetHostname, /* 0.9.5 */
     .connectGetMaxVcpus = hypervConnectGetMaxVcpus, /* 6.9.0 */
     .nodeGetInfo = hypervNodeGetInfo, /* 0.9.5 */
diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input
index 77543cf6ef..bbca550790 100644
--- a/src/hyperv/hyperv_wmi_generator.input
+++ b/src/hyperv/hyperv_wmi_generator.input
@@ -673,7 +673,7 @@ class Win32_OperatingSystem
     string   CSCreationClassName
     string   CSDVersion
     string   CSName
-    uint16   CurrentTimeZone
+    int16    CurrentTimeZone
     boolean  DataExecutionPrevention_Available
     boolean  DataExecutionPrevention_32BitApplications
     boolean  DataExecutionPrevention_Drivers
-- 
2.27.0





More information about the libvir-list mailing list