]> xenbits.xensource.com Git - libvirt.git/commitdiff
hyperv: add support for Hyper-V 2012 and newer
authorDawid Zamirski <dzamirski@datto.com>
Tue, 4 Apr 2017 22:26:08 +0000 (18:26 -0400)
committerMatthias Bolte <matthias.bolte@googlemail.com>
Sat, 8 Apr 2017 13:26:18 +0000 (15:26 +0200)
This patch reworks the Hyper-V driver structs and the code generator
to provide seamless support for both Hyper-V 2008 and 2012 or newer.
This does not implement any new libvirt APIs, it just adapts existing
2008-only driver to also handle 2012 and newer by sharing as much
driver code as possible (currently it's all of it :-)). This is needed
to set the foundation before we can move forward with implementing the
rest of the driver APIs.

With the 2012 release, Microsoft introduced "v2" version of Msvm_* WMI
classes. Those are largely the same as "v1" (used in 2008) but have some
new properties as well as need different wsman request URIs. To
accomodate those differences, most of work went into the code generator
so that it's "aware" of possibility of multiple versions of the same WMI
class and produce C code accordingly.

To accomplish this the following changes were made:

 * the abstract hypervObject struct's data member was changed to a union
   that has "common", "v1" and "v2" members. Those are structs that
   represent WMI classes that we get back from wsman response. The
   "common" struct has members that are present in both "v1" and "v2"
   which the driver API callbacks can use to read the data from in
   version-independent manner (if version-specific member needs to be
   accessed the driver can check priv->wmiVersion and read from "v1" or
   "v2" as needed). Those structs are guaranteed to be  memory aligned
   by the code generator (see the align_property_members implementation
   that takes care of that)
 * the generator produces *_WmiInfo for each WMI class "family" that
   holds an array of hypervWmiClassInfoPtr each providing information
   as to which request URI to use for each "version" of given WMI class
   as well as XmlSerializerInfo struct needed to unserilize WS-MAN
   responsed into the data structs. The driver uses those to make proper
   WS-MAN request depending on which version it's connected to.
 * the generator no longer produces "helper" functions such as
   hypervGetMsvmComputerSystemList as those were originally just simple
   wrappers around hypervEnumAndPull, instead those were hand-written
   now (to keep driver changes minimal). The reason is that we'll have
   more code coming implementing missing libvirt APIs and surely code
   patterns will emerge that would warrant more useful "utility" functions
   like that.
 * a hypervInitConnection was added to the driver which "detects"
   Hyper-V version by testing simple wsman request using v2 then falling
   back to v1, obviously if both fail, the we're erroring out.

To express how the above translates in code:

void
hypervImplementSomeLibvirtApi(virConnectPtr conn, ...)
{
    hypervPrivate *priv = conn->privateData;
    virBuffer query = VIR_BUFFER_INITIALIZER;
    hypervWqlQuery wqlQuery = HYPERV_WQL_QUERY_INITIALIZER;
    Msvm_ComputerSystem *list = NULL; /* typed hypervObject instance */

    /* the WmiInfo struct has the data needed for wsman request and
     * response handling for both v1 and v2 */
    wqlQuery.info = Msvm_ComputerSystem_WmiInfo;
    wqlQuery.query = &query;

    virBufferAddLit(&query, "select * from Msvm_ComputerSystem");

    if (hypervEnumAndPull(priv, &wqlQuery, (hypervObject **) &list) < 0) {
        goto cleanup;
    }

    if (list == NULL) {
        /* none found */
        goto cleanup;
    }

    /* works with v1 and v2 */
    char *vmName = list->data.common->Name;

    /* access property that is in v2 only */
    if (priv->wmiVersion == HYPERV_WMI_VERSION_V2)
        char *foo = list->data.v2->V2Property;
    else
        char *foo = list->data.v1->V1Property;

 cleanup:
    hypervFreeObject(priv, (hypervObject *)list);
}

src/Makefile.am
src/hyperv/hyperv_driver.c
src/hyperv/hyperv_wmi.c
src/hyperv/hyperv_wmi.h
src/hyperv/hyperv_wmi_classes.h
src/hyperv/hyperv_wmi_generator.input
src/hyperv/hyperv_wmi_generator.py

index 3c7adae492bf6205bbe64e44ebc2033887305bbe..e2dc77e3d4e1899e21ee925b0a388a8be66bcacc 100644 (file)
@@ -912,8 +912,6 @@ HYPERV_DRIVER_SOURCES =                                                                     \
                hyperv/openwsman.h
 
 HYPERV_DRIVER_GENERATED =                                                      \
-               hyperv/hyperv_wmi.generated.c                                   \
-               hyperv/hyperv_wmi.generated.h                                   \
                hyperv/hyperv_wmi_classes.generated.c                           \
                hyperv/hyperv_wmi_classes.generated.h                           \
                hyperv/hyperv_wmi_classes.generated.typedef
index b642a025d229a333c7cfc7c2186f8ffd08319b13..090ea243ba5d30d7ee7d2955d5fcd89980b95802 100644 (file)
@@ -55,7 +55,67 @@ hypervFreePrivate(hypervPrivate **priv)
     VIR_FREE(*priv);
 }
 
+static int
+hypervInitConnection(virConnectPtr conn, hypervPrivate *priv,
+                     char *username, char *password)
+{
+    virBuffer query = VIR_BUFFER_INITIALIZER;
+    hypervWqlQuery wqlQuery = HYPERV_WQL_QUERY_INITIALIZER;
+    hypervObject *computerSystem = NULL;
+    int ret = -1;
+
+    /* Initialize the openwsman connection */
+    priv->client = wsmc_create(conn->uri->server, conn->uri->port, "/wsman",
+                               priv->parsedUri->transport, username, password);
+
+    if (priv->client == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not create openwsman client"));
+        goto cleanup;
+    }
+
+    if (wsmc_transport_init(priv->client, NULL) != 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not initialize openwsman transport"));
+        goto cleanup;
+    }
+
+    /* FIXME: Currently only basic authentication is supported  */
+    wsman_transport_set_auth_method(priv->client, "basic");
+
+    wqlQuery.info = Msvm_ComputerSystem_WmiInfo;
+    wqlQuery.query = &query;
+
+    virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
+    virBufferAddLit(&query, "WHERE ");
+    virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_PHYSICAL);
+
+    /* try query using V2 namespace (for Hyper-V 2012+) */
+    priv->wmiVersion = HYPERV_WMI_VERSION_V2;
+
+    if (hypervEnumAndPull(priv, &wqlQuery, &computerSystem) < 0) {
+        /* rebuild query because hypervEnumAndPull consumes it */
+        virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
+        virBufferAddLit(&query, "WHERE ");
+        virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_PHYSICAL);
+
+        /* fall back to V1 namespace (for Hyper-V 2008) */
+        priv->wmiVersion = HYPERV_WMI_VERSION_V1;
+
+        if (hypervEnumAndPull(priv, &wqlQuery, &computerSystem) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("%s is not a Hyper-V server"), conn->uri->server);
+            goto cleanup;
+        }
+    }
+
+    ret = 0;
 
+ cleanup:
+    hypervFreeObject(priv, computerSystem);
+
+    return ret;
+}
 
 static virDrvOpenStatus
 hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
@@ -67,8 +127,6 @@ hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
     hypervPrivate *priv = NULL;
     char *username = NULL;
     char *password = NULL;
-    virBuffer query = VIR_BUFFER_INITIALIZER;
-    Msvm_ComputerSystem *computerSystem = NULL;
 
     virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
 
@@ -147,41 +205,9 @@ hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
         goto cleanup;
     }
 
-    /* Initialize the openwsman connection */
-    priv->client = wsmc_create(conn->uri->server, conn->uri->port, "/wsman",
-                               priv->parsedUri->transport, username, password);
 
-    if (priv->client == NULL) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Could not create openwsman client"));
+    if (hypervInitConnection(conn, priv, username, password) < 0)
         goto cleanup;
-    }
-
-    if (wsmc_transport_init(priv->client, NULL) != 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Could not initialize openwsman transport"));
-        goto cleanup;
-    }
-
-    /* FIXME: Currently only basic authentication is supported  */
-    wsman_transport_set_auth_method(priv->client, "basic");
-
-    /* Check if the connection can be established and if the server has the
-     * Hyper-V role installed. If the call to hypervGetMsvmComputerSystemList
-     * succeeds than the connection has been established. If the returned list
-     * is empty than the server isn't a Hyper-V server. */
-    virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
-    virBufferAddLit(&query, "where ");
-    virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_PHYSICAL);
-
-    if (hypervGetMsvmComputerSystemList(priv, &query, &computerSystem) < 0)
-        goto cleanup;
-
-    if (computerSystem == NULL) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("%s is not a Hyper-V server"), conn->uri->server);
-        goto cleanup;
-    }
 
     conn->privateData = priv;
     priv = NULL;
@@ -191,7 +217,6 @@ hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth,
     hypervFreePrivate(&priv);
     VIR_FREE(username);
     VIR_FREE(password);
-    hypervFreeObject(priv, (hypervObject *)computerSystem);
 
     return result;
 }
@@ -240,7 +265,7 @@ hypervConnectGetHostname(virConnectPtr conn)
         goto cleanup;
     }
 
-    ignore_value(VIR_STRDUP(hostname, computerSystem->data->DNSHostName));
+    ignore_value(VIR_STRDUP(hostname, computerSystem->data.common->DNSHostName));
 
  cleanup:
     hypervFreeObject(priv, (hypervObject *)computerSystem);
@@ -282,7 +307,7 @@ hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
                       "{Win32_ComputerSystem.Name=\"%s\"} "
                       "where AssocClass = Win32_ComputerSystemProcessor "
                       "ResultClass = Win32_Processor",
-                      computerSystem->data->Name);
+                      computerSystem->data.common->Name);
 
     if (hypervGetWin32ProcessorList(priv, &query, &processorList) < 0)
         goto cleanup;
@@ -295,7 +320,7 @@ hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
     }
 
     /* Strip the string to fit more relevant information in 32 chars */
-    tmp = processorList->data->Name;
+    tmp = processorList->data.common->Name;
 
     while (*tmp != '\0') {
         if (STRPREFIX(tmp, "  ")) {
@@ -313,16 +338,16 @@ hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
     }
 
     /* Fill struct */
-    if (virStrncpy(info->model, processorList->data->Name,
+    if (virStrncpy(info->model, processorList->data.common->Name,
                    sizeof(info->model) - 1, sizeof(info->model)) == NULL) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("CPU model %s too long for destination"),
-                       processorList->data->Name);
+                       processorList->data.common->Name);
         goto cleanup;
     }
 
-    info->memory = computerSystem->data->TotalPhysicalMemory / 1024; /* byte to kilobyte */
-    info->mhz = processorList->data->MaxClockSpeed;
+    info->memory = computerSystem->data.common->TotalPhysicalMemory / 1024; /* byte to kilobyte */
+    info->mhz = processorList->data.common->MaxClockSpeed;
     info->nodes = 1;
     info->sockets = 0;
 
@@ -331,8 +356,8 @@ hypervNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
         ++info->sockets;
     }
 
-    info->cores = processorList->data->NumberOfCores;
-    info->threads = info->cores / processorList->data->NumberOfLogicalProcessors;
+    info->cores = processorList->data.common->NumberOfCores;
+    info->threads = info->cores / processorList->data.common->NumberOfLogicalProcessors;
     info->cpus = info->sockets * info->cores;
 
     result = 0;
@@ -372,7 +397,7 @@ hypervConnectListDomains(virConnectPtr conn, int *ids, int maxids)
 
     for (computerSystem = computerSystemList; computerSystem != NULL;
          computerSystem = computerSystem->next) {
-        ids[count++] = computerSystem->data->ProcessID;
+        ids[count++] = computerSystem->data.common->ProcessID;
 
         if (count >= maxids)
             break;
@@ -532,7 +557,7 @@ hypervDomainSuspend(virDomainPtr domain)
     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
         goto cleanup;
 
-    if (computerSystem->data->EnabledState !=
+    if (computerSystem->data.common->EnabledState !=
         MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED) {
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                        _("Domain is not active"));
@@ -560,7 +585,7 @@ hypervDomainResume(virDomainPtr domain)
     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
         goto cleanup;
 
-    if (computerSystem->data->EnabledState !=
+    if (computerSystem->data.common->EnabledState !=
         MSVM_COMPUTERSYSTEM_ENABLEDSTATE_PAUSED) {
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                        _("Domain is not paused"));
@@ -666,7 +691,7 @@ hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not lookup %s for domain %s"),
                        "Msvm_VirtualSystemSettingData",
-                       computerSystem->data->ElementName);
+                       computerSystem->data.common->ElementName);
         goto cleanup;
     }
 
@@ -676,7 +701,7 @@ hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
                       "{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
                       "where AssocClass = Msvm_VirtualSystemSettingDataComponent "
                       "ResultClass = Msvm_ProcessorSettingData",
-                      virtualSystemSettingData->data->InstanceID);
+                      virtualSystemSettingData->data.common->InstanceID);
 
     if (hypervGetMsvmProcessorSettingDataList(priv, &query,
                                               &processorSettingData) < 0) {
@@ -687,7 +712,7 @@ hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not lookup %s for domain %s"),
                        "Msvm_ProcessorSettingData",
-                       computerSystem->data->ElementName);
+                       computerSystem->data.common->ElementName);
         goto cleanup;
     }
 
@@ -697,7 +722,7 @@ hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
                       "{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
                       "where AssocClass = Msvm_VirtualSystemSettingDataComponent "
                       "ResultClass = Msvm_MemorySettingData",
-                      virtualSystemSettingData->data->InstanceID);
+                      virtualSystemSettingData->data.common->InstanceID);
 
     if (hypervGetMsvmMemorySettingDataList(priv, &query,
                                            &memorySettingData) < 0) {
@@ -709,15 +734,15 @@ hypervDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not lookup %s for domain %s"),
                        "Msvm_MemorySettingData",
-                       computerSystem->data->ElementName);
+                       computerSystem->data.common->ElementName);
         goto cleanup;
     }
 
     /* Fill struct */
     info->state = hypervMsvmComputerSystemEnabledStateToDomainState(computerSystem);
-    info->maxMem = memorySettingData->data->Limit * 1024; /* megabyte to kilobyte */
-    info->memory = memorySettingData->data->VirtualQuantity * 1024; /* megabyte to kilobyte */
-    info->nrVirtCpu = processorSettingData->data->VirtualQuantity;
+    info->maxMem = memorySettingData->data.common->Limit * 1024; /* megabyte to kilobyte */
+    info->memory = memorySettingData->data.common->VirtualQuantity * 1024; /* megabyte to kilobyte */
+    info->nrVirtCpu = processorSettingData->data.common->VirtualQuantity;
     info->cpuTime = 0;
 
     result = 0;
@@ -803,7 +828,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not lookup %s for domain %s"),
                        "Msvm_VirtualSystemSettingData",
-                       computerSystem->data->ElementName);
+                       computerSystem->data.common->ElementName);
         goto cleanup;
     }
 
@@ -813,7 +838,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
                       "{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
                       "where AssocClass = Msvm_VirtualSystemSettingDataComponent "
                       "ResultClass = Msvm_ProcessorSettingData",
-                      virtualSystemSettingData->data->InstanceID);
+                      virtualSystemSettingData->data.common->InstanceID);
 
     if (hypervGetMsvmProcessorSettingDataList(priv, &query,
                                               &processorSettingData) < 0) {
@@ -824,7 +849,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not lookup %s for domain %s"),
                        "Msvm_ProcessorSettingData",
-                       computerSystem->data->ElementName);
+                       computerSystem->data.common->ElementName);
         goto cleanup;
     }
 
@@ -834,7 +859,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
                       "{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
                       "where AssocClass = Msvm_VirtualSystemSettingDataComponent "
                       "ResultClass = Msvm_MemorySettingData",
-                      virtualSystemSettingData->data->InstanceID);
+                      virtualSystemSettingData->data.common->InstanceID);
 
     if (hypervGetMsvmMemorySettingDataList(priv, &query,
                                            &memorySettingData) < 0) {
@@ -846,7 +871,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not lookup %s for domain %s"),
                        "Msvm_MemorySettingData",
-                       computerSystem->data->ElementName);
+                       computerSystem->data.common->ElementName);
         goto cleanup;
     }
 
@@ -854,34 +879,34 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
     def->virtType = VIR_DOMAIN_VIRT_HYPERV;
 
     if (hypervIsMsvmComputerSystemActive(computerSystem, NULL)) {
-        def->id = computerSystem->data->ProcessID;
+        def->id = computerSystem->data.common->ProcessID;
     } else {
         def->id = -1;
     }
 
-    if (virUUIDParse(computerSystem->data->Name, def->uuid) < 0) {
+    if (virUUIDParse(computerSystem->data.common->Name, def->uuid) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not parse UUID from string '%s'"),
-                       computerSystem->data->Name);
+                       computerSystem->data.common->Name);
         return NULL;
     }
 
-    if (VIR_STRDUP(def->name, computerSystem->data->ElementName) < 0)
+    if (VIR_STRDUP(def->name, computerSystem->data.common->ElementName) < 0)
         goto cleanup;
 
-    if (VIR_STRDUP(def->description, virtualSystemSettingData->data->Notes) < 0)
+    if (VIR_STRDUP(def->description, virtualSystemSettingData->data.common->Notes) < 0)
         goto cleanup;
 
-    virDomainDefSetMemoryTotal(def, memorySettingData->data->Limit * 1024); /* megabyte to kilobyte */
-    def->mem.cur_balloon = memorySettingData->data->VirtualQuantity * 1024; /* megabyte to kilobyte */
+    virDomainDefSetMemoryTotal(def, memorySettingData->data.common->Limit * 1024); /* megabyte to kilobyte */
+    def->mem.cur_balloon = memorySettingData->data.common->VirtualQuantity * 1024; /* megabyte to kilobyte */
 
     if (virDomainDefSetVcpusMax(def,
-                                processorSettingData->data->VirtualQuantity,
+                                processorSettingData->data.common->VirtualQuantity,
                                 NULL) < 0)
         goto cleanup;
 
     if (virDomainDefSetVcpus(def,
-                             processorSettingData->data->VirtualQuantity) < 0)
+                             processorSettingData->data.common->VirtualQuantity) < 0)
         goto cleanup;
 
     def->os.type = VIR_DOMAIN_OSTYPE_HVM;
@@ -930,7 +955,7 @@ hypervConnectListDefinedDomains(virConnectPtr conn, char **const names, int maxn
 
     for (computerSystem = computerSystemList; computerSystem != NULL;
          computerSystem = computerSystem->next) {
-        if (VIR_STRDUP(names[count], computerSystem->data->ElementName) < 0)
+        if (VIR_STRDUP(names[count], computerSystem->data.common->ElementName) < 0)
             goto cleanup;
 
         ++count;
@@ -1154,7 +1179,7 @@ hypervDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags)
     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
         goto cleanup;
 
-    result = computerSystem->data->EnabledState ==
+    result = computerSystem->data.common->EnabledState ==
              MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED ? 1 : 0;
 
  cleanup:
@@ -1177,7 +1202,7 @@ hypervDomainManagedSaveRemove(virDomainPtr domain, unsigned int flags)
     if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
         goto cleanup;
 
-    if (computerSystem->data->EnabledState !=
+    if (computerSystem->data.common->EnabledState !=
         MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED) {
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                        _("Domain has no managed save image"));
@@ -1280,7 +1305,7 @@ hypervConnectListAllDomains(virConnectPtr conn,
 
         /* managed save filter */
         if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_MANAGEDSAVE)) {
-            bool mansave = computerSystem->data->EnabledState ==
+            bool mansave = computerSystem->data.common->EnabledState ==
                            MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED;
 
             if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) && mansave) ||
index b2b52435c70d58e7b3d3a0623139ab845fb74623..a3c7dc0750423d0d546e30cb1d793fd71476b157 100644 (file)
 
 #define WS_SERIALIZER_FREE_MEM_WORKS 0
 
-#define ROOT_CIMV2 \
-    "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/*"
+#define VIR_FROM_THIS VIR_FROM_HYPERV
 
-#define ROOT_VIRTUALIZATION \
-    "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/virtualization/*"
 
-#define VIR_FROM_THIS VIR_FROM_HYPERV
+static int
+hypervGetWmiClassInfo(hypervPrivate *priv, hypervWmiClassInfoListPtr list,
+                      hypervWmiClassInfoPtr *info)
+{
+    const char *version = "v2";
+    size_t i;
+
+    if (list->count == 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("The WMI class info list is empty"));
+        return -1;
+    }
 
+    /* if there's just one WMI class and isn't versioned, assume "shared" */
+    if (list->count == 1 && list->objs[0]->version == NULL) {
+        *info = list->objs[0];
+        return 0;
+    }
 
+    if (priv->wmiVersion == HYPERV_WMI_VERSION_V1)
+        version = "v1";
+
+    for (i = 0; i < list->count; i++) {
+       if (STRCASEEQ(list->objs[i]->version, version)) {
+           *info = list->objs[i];
+           return 0;
+       }
+    }
+
+    virReportError(VIR_ERR_INTERNAL_ERROR,
+                   _("Could not match WMI class info for version %s"),
+                   version);
+
+    return -1;
+}
+
+static int
+hypervGetWmiClassList(hypervPrivate *priv, hypervWmiClassInfoListPtr wmiInfo,
+                      virBufferPtr query, hypervObject **wmiClass)
+{
+    hypervWqlQuery wqlQuery = HYPERV_WQL_QUERY_INITIALIZER;
+
+    wqlQuery.info = wmiInfo;
+    wqlQuery.query = query;
+
+    return hypervEnumAndPull(priv, &wqlQuery, wmiClass);
+}
 
 int
 hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
@@ -106,16 +147,16 @@ hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
  * Object
  */
 
-/* This function guarantees that query is freed, even on failure */
+/* This function guarantees that wqlQuery->query is reset, even on failure */
 int
-hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
-                  XmlSerializerInfo *serializerInfo, const char *resourceUri,
-                  const char *className, hypervObject **list)
+hypervEnumAndPull(hypervPrivate *priv, hypervWqlQueryPtr wqlQuery,
+                  hypervObject **list)
 {
     int result = -1;
     WsSerializerContextH serializerContext;
     client_opt_t *options = NULL;
     char *query_string = NULL;
+    hypervWmiClassInfoPtr wmiInfo = NULL;
     filter_t *filter = NULL;
     WsXmlDocH response = NULL;
     char *enumContext = NULL;
@@ -125,11 +166,12 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
     XML_TYPE_PTR data = NULL;
     hypervObject *object;
 
-    if (virBufferCheckError(query) < 0) {
-        virBufferFreeAndReset(query);
+    if (virBufferCheckError(wqlQuery->query) < 0) {
+        virBufferFreeAndReset(wqlQuery->query);
         return -1;
     }
-    query_string = virBufferContentAndReset(query);
+
+    query_string = virBufferContentAndReset(wqlQuery->query);
 
     if (list == NULL || *list != NULL) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
@@ -137,6 +179,9 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
         return -1;
     }
 
+    if (hypervGetWmiClassInfo(priv, wqlQuery->info, &wmiInfo) < 0)
+        goto cleanup;
+
     serializerContext = wsmc_get_serialization_context(priv->client);
 
     options = wsmc_options_init();
@@ -155,7 +200,8 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
         goto cleanup;
     }
 
-    response = wsmc_action_enumerate(priv->client, root, options, filter);
+    response = wsmc_action_enumerate(priv->client, wmiInfo->rootUri, options,
+                                     filter);
 
     if (hypervVerifyResponse(priv->client, response, "enumeration") < 0)
         goto cleanup;
@@ -166,7 +212,7 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
     response = NULL;
 
     while (enumContext != NULL && *enumContext != '\0') {
-        response = wsmc_action_pull(priv->client, resourceUri, options,
+        response = wsmc_action_pull(priv->client, wmiInfo->resourceUri, options,
                                     filter, enumContext);
 
         if (hypervVerifyResponse(priv->client, response, "pull") < 0)
@@ -196,11 +242,12 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
             goto cleanup;
         }
 
-        if (ws_xml_get_child(node, 0, resourceUri, className) == NULL)
+        if (ws_xml_get_child(node, 0, wmiInfo->resourceUri,
+                             wmiInfo->name) == NULL)
             break;
 
-        data = ws_deserialize(serializerContext, node, serializerInfo,
-                              className, resourceUri, NULL, 0, 0);
+        data = ws_deserialize(serializerContext, node, wmiInfo->serializerInfo,
+                              wmiInfo->name, wmiInfo->resourceUri, NULL, 0, 0);
 
         if (data == NULL) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -211,8 +258,8 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
         if (VIR_ALLOC(object) < 0)
             goto cleanup;
 
-        object->serializerInfo = serializerInfo;
-        object->data = data;
+        object->info = wmiInfo;
+        object->data.common = data;
 
         data = NULL;
 
@@ -248,7 +295,7 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root,
         /* FIXME: ws_serializer_free_mem is broken in openwsman <= 2.2.6,
          *        see hypervFreeObject for a detailed explanation. */
         if (ws_serializer_free_mem(serializerContext, data,
-                                   serializerInfo) < 0) {
+                                   wmiInfo->serializerInfo) < 0) {
             VIR_ERROR(_("Could not free deserialized data"));
         }
 #endif
@@ -287,8 +334,8 @@ hypervFreeObject(hypervPrivate *priv ATTRIBUTE_UNUSED, hypervObject *object)
          *        them in wsmc_release. So this doesn't result in a real
          *        memory leak, but just in piling up unused memory until
          *        the connection is closed. */
-        if (ws_serializer_free_mem(serializerContext, object->data,
-                                   object->serializerInfo) < 0) {
+        if (ws_serializer_free_mem(serializerContext, object->data.common,
+                                   object->info->serializerInfo) < 0) {
             VIR_ERROR(_("Could not free deserialized data"));
         }
 #endif
@@ -381,6 +428,70 @@ hypervReturnCodeToString(int returnCode)
 
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Generic "Get WMI class list" helpers
+ */
+
+int
+hypervGetMsvmComputerSystemList(hypervPrivate *priv, virBufferPtr query,
+                                Msvm_ComputerSystem **list)
+{
+    return hypervGetWmiClassList(priv, Msvm_ComputerSystem_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+int
+hypervGetMsvmConcreteJobList(hypervPrivate *priv, virBufferPtr query,
+                             Msvm_ConcreteJob **list)
+{
+    return hypervGetWmiClassList(priv, Msvm_ConcreteJob_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+int
+hypervGetWin32ComputerSystemList(hypervPrivate *priv, virBufferPtr query,
+                                 Win32_ComputerSystem **list)
+{
+    return hypervGetWmiClassList(priv, Win32_ComputerSystem_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+int
+hypervGetWin32ProcessorList(hypervPrivate *priv, virBufferPtr query,
+                            Win32_Processor **list)
+{
+    return hypervGetWmiClassList(priv, Win32_Processor_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+int
+hypervGetMsvmVirtualSystemSettingDataList(hypervPrivate *priv,
+                                          virBufferPtr query,
+                                          Msvm_VirtualSystemSettingData **list)
+{
+    return hypervGetWmiClassList(priv, Msvm_VirtualSystemSettingData_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+int
+hypervGetMsvmProcessorSettingDataList(hypervPrivate *priv,
+                                      virBufferPtr query,
+                                      Msvm_ProcessorSettingData **list)
+{
+    return hypervGetWmiClassList(priv, Msvm_ProcessorSettingData_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+int
+hypervGetMsvmMemorySettingDataList(hypervPrivate *priv, virBufferPtr query,
+                                   Msvm_MemorySettingData **list)
+{
+    return hypervGetWmiClassList(priv, Msvm_MemorySettingData_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
+
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Msvm_ComputerSystem
  */
@@ -402,6 +513,7 @@ hypervInvokeMsvmComputerSystemRequestStateChange(virDomainPtr domain,
     virBuffer query = VIR_BUFFER_INITIALIZER;
     Msvm_ConcreteJob *concreteJob = NULL;
     bool completed = false;
+    const char *resourceUri = MSVM_COMPUTERSYSTEM_V2_RESOURCE_URI;
 
     virUUIDFormat(domain->uuid, uuid_string);
 
@@ -410,6 +522,9 @@ hypervInvokeMsvmComputerSystemRequestStateChange(virDomainPtr domain,
         virAsprintf(&properties, "RequestedState=%d", requestedState) < 0)
         goto cleanup;
 
+    if (priv->wmiVersion == HYPERV_WMI_VERSION_V1)
+        resourceUri = MSVM_COMPUTERSYSTEM_V1_RESOURCE_URI;
+
     options = wsmc_options_init();
 
     if (options == NULL) {
@@ -422,7 +537,7 @@ hypervInvokeMsvmComputerSystemRequestStateChange(virDomainPtr domain,
     wsmc_add_prop_from_str(options, properties);
 
     /* Invoke method */
-    response = wsmc_action_invoke(priv->client, MSVM_COMPUTERSYSTEM_RESOURCE_URI,
+    response = wsmc_action_invoke(priv->client, resourceUri,
                                   options, "RequestStateChange", NULL);
 
     if (hypervVerifyResponse(priv->client, response, "invocation") < 0)
@@ -471,7 +586,7 @@ hypervInvokeMsvmComputerSystemRequestStateChange(virDomainPtr domain,
                 goto cleanup;
             }
 
-            switch (concreteJob->data->JobState) {
+            switch (concreteJob->data.common->JobState) {
               case MSVM_CONCRETEJOB_JOBSTATE_NEW:
               case MSVM_CONCRETEJOB_JOBSTATE_STARTING:
               case MSVM_CONCRETEJOB_JOBSTATE_RUNNING:
@@ -530,7 +645,7 @@ int
 hypervMsvmComputerSystemEnabledStateToDomainState
   (Msvm_ComputerSystem *computerSystem)
 {
-    switch (computerSystem->data->EnabledState) {
+    switch (computerSystem->data.common->EnabledState) {
       case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_UNKNOWN:
         return VIR_DOMAIN_NOSTATE;
 
@@ -570,7 +685,7 @@ hypervIsMsvmComputerSystemActive(Msvm_ComputerSystem *computerSystem,
     if (in_transition != NULL)
         *in_transition = false;
 
-    switch (computerSystem->data->EnabledState) {
+    switch (computerSystem->data.common->EnabledState) {
       case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_UNKNOWN:
         return false;
 
@@ -615,17 +730,17 @@ hypervMsvmComputerSystemToDomain(virConnectPtr conn,
         return -1;
     }
 
-    if (virUUIDParse(computerSystem->data->Name, uuid) < 0) {
+    if (virUUIDParse(computerSystem->data.common->Name, uuid) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not parse UUID from string '%s'"),
-                       computerSystem->data->Name);
+                       computerSystem->data.common->Name);
         return -1;
     }
 
     if (hypervIsMsvmComputerSystemActive(computerSystem, NULL))
-        id = computerSystem->data->ProcessID;
+        id = computerSystem->data.common->ProcessID;
 
-    *domain = virGetDomain(conn, computerSystem->data->ElementName, uuid, id);
+    *domain = virGetDomain(conn, computerSystem->data.common->ElementName, uuid, id);
 
     return *domain ? 0 : -1;
 }
@@ -661,7 +776,3 @@ hypervMsvmComputerSystemFromDomain(virDomainPtr domain,
 
     return 0;
 }
-
-
-
-#include "hyperv_wmi.generated.c"
index 563475c65c75f18ae7b37702b1af8346a3301219..edb8efa3d24a92ea6af0effb6269413ed5c1ca61 100644 (file)
@@ -31,7 +31,7 @@
 
 
 
-typedef struct _hypervObject hypervObject;
+# define HYPERV_WQL_QUERY_INITIALIZER { NULL, NULL }
 
 int hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
                          const char *detail);
@@ -41,8 +41,9 @@ int hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Object
  */
-typedef struct _hypervObjectUnified hypervObjectUnified;
-struct _hypervObjectUnified {
+
+typedef struct _hypervObject hypervObject;
+struct _hypervObject {
     /* Unserialized data from wsman response. The member called "common" has
      * properties that are the same type and name for all "versions" of given
      * WMI class. This means that calling code does not have to make any
@@ -57,18 +58,17 @@ struct _hypervObjectUnified {
     } data;
     /* The info used to make wsman request */
     hypervWmiClassInfoPtr info;
-    hypervObjectUnified *next;
+    hypervObject *next;
 };
 
-struct _hypervObject {
-    XmlSerializerInfo *serializerInfo;
-    XML_TYPE_PTR data;
-    hypervObject *next;
+typedef struct _hypervWqlQuery hypervWqlQuery;
+typedef hypervWqlQuery *hypervWqlQueryPtr;
+struct _hypervWqlQuery {
+    virBufferPtr query;
+    hypervWmiClassInfoListPtr info;
 };
 
-int hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query,
-                      const char *root, XmlSerializerInfo *serializerInfo,
-                      const char *resourceUri, const char *className,
+int hypervEnumAndPull(hypervPrivate *priv, hypervWqlQueryPtr wqlQuery,
                       hypervObject **list);
 
 void hypervFreeObject(hypervPrivate *priv, hypervObject *object);
@@ -111,6 +111,35 @@ const char *hypervReturnCodeToString(int returnCode);
 
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Generic "Get WMI class list" helpers
+ */
+
+int hypervGetMsvmComputerSystemList(hypervPrivate *priv, virBufferPtr query,
+                                    Msvm_ComputerSystem **list);
+
+int hypervGetMsvmConcreteJobList(hypervPrivate *priv, virBufferPtr query,
+                                 Msvm_ConcreteJob **list);
+
+int hypervGetWin32ComputerSystemList(hypervPrivate *priv, virBufferPtr query,
+                                     Win32_ComputerSystem **list);
+
+int hypervGetWin32ProcessorList(hypervPrivate *priv, virBufferPtr query,
+                                    Win32_Processor **list);
+
+int hypervGetMsvmVirtualSystemSettingDataList(hypervPrivate *priv,
+                                              virBufferPtr query,
+                                              Msvm_VirtualSystemSettingData **list);
+
+int hypervGetMsvmProcessorSettingDataList(hypervPrivate *priv,
+                                          virBufferPtr query,
+                                          Msvm_ProcessorSettingData **list);
+
+int hypervGetMsvmMemorySettingDataList(hypervPrivate *priv, virBufferPtr query,
+                                       Msvm_MemorySettingData **list);
+
+
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Msvm_ComputerSystem
  */
@@ -131,8 +160,4 @@ int hypervMsvmComputerSystemToDomain(virConnectPtr conn,
 int hypervMsvmComputerSystemFromDomain(virDomainPtr domain,
                                        Msvm_ComputerSystem **computerSystem);
 
-
-
-# include "hyperv_wmi.generated.h"
-
 #endif /* __HYPERV_WMI_H__ */
index b0f3e3c43a205b6a3a6297f8f3f9fe6826c9fd75..f7d596febf73018418b4060e4054a359a449e2ac 100644 (file)
 
 # include "hyperv_wmi_classes.generated.typedef"
 
+# define ROOT_CIMV2 \
+    "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/*"
+
+# define ROOT_VIRTUALIZATION \
+    "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/virtualization/*"
+
+# define ROOT_VIRTUALIZATION_V2 \
+    "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/virtualization/v2/*"
+
 
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
index 209a9ff140539329540f27dc497ffc4b7d62b18a..d7f819edee3c47e8b85b84230496c7a64f244763 100644 (file)
@@ -56,6 +56,55 @@ class Msvm_ComputerSystem
 end
 
 
+class v2/Msvm_ComputerSystem
+    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[]
+    uint64   OnTimeInMilliseconds
+    uint32   ProcessID
+    datetime TimeOfLastConfigurationChange
+    uint16   NumberOfNumaNodes
+    uint16   ReplicationState
+    uint16   ReplicationHealth
+    uint16   ReplicationMode
+    uint16   FailedOverReplicationType
+    uint16   LastReplicationType
+    datetime LastApplicationConsistentReplicationTime
+    datetime LastReplicationTime
+    datetime LastSuccessfulBackupTime
+    uint16   EnhancedSessionModeState
+end
+
+
 class Msvm_ConcreteJob
     string   Caption
     string   Description
@@ -96,6 +145,51 @@ class Msvm_ConcreteJob
 end
 
 
+class v2/Msvm_ConcreteJob
+    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   JobStatus
+    datetime TimeSubmitted
+    datetime ScheduledStartTime
+    datetime StartTime
+    datetime ElapsedTime
+    uint32   JobRunTimes
+    uint8    RunMonth
+    sint8    RunDay
+    sint8    RunDayOfWeek
+    datetime RunStartInterval
+    uint16   LocalOrUtcTime
+    datetime UntilTime
+    string   Notify
+    string   Owner
+    uint32   Priority
+    uint16   PercentComplete
+    boolean  DeleteOnCompletion
+    uint16   ErrorCode
+    string   ErrorDescription
+    string   ErrorSummaryDescription
+    uint16   RecoveryAction
+    string   OtherRecoveryAction
+    uint16   JobState
+    datetime TimeOfLastStateChange
+    datetime TimeBeforeRemoval
+    boolean  Cancellable
+    uint16   JobType
+end
+
+
 class Msvm_MemorySettingData
     string   Caption
     string   Description
@@ -126,6 +220,38 @@ class Msvm_MemorySettingData
 end
 
 
+class v2/Msvm_MemorySettingData
+    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
+    boolean DynamicMemoryEnabled
+    uint32  TargetMemoryBuffer
+    boolean IsVirtualized
+    boolean SwapFilesInUse
+    uint64  MaxMemoryBlocksPerNumaNode
+end
+
+
 class Msvm_ProcessorSettingData
     string   Caption
     string   Description
@@ -159,6 +285,37 @@ class Msvm_ProcessorSettingData
 end
 
 
+class v2/Msvm_ProcessorSettingData
+    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
+    boolean LimitCPUID
+    boolean LimitProcessorFeatures
+    uint64  MaxProcessorsPerNumaNode
+    uint64  MaxNumaNodesPerSocket
+end
+
+
 class Msvm_VirtualSystemSettingData
     string   Caption
     string   Description
@@ -184,6 +341,55 @@ class Msvm_VirtualSystemSettingData
 end
 
 
+class v2/Msvm_VirtualSystemSettingData
+    string   InstanceID
+    string   Caption
+    string   Description
+    string   ElementName
+    string   VirtualSystemIdentifier
+    string   VirtualSystemType
+    string   Notes[]
+    datetime CreationTime
+    string   ConfigurationID
+    string   ConfigurationDataRoot
+    string   ConfigurationFile
+    string   SnapshotDataRoot
+    string   SuspendDataRoot
+    string   SwapFileDataRoot
+    string   LogDataRoot
+    uint16   AutomaticStartupAction
+    datetime AutomaticStartupActionDelay
+    uint16   AutomaticStartupActionSequenceNumber
+    uint16   AutomaticShutdownAction
+    uint16   AutomaticRecoveryAction
+    string   RecoveryFile
+    string   BIOSGUID
+    string   BIOSSerialNumber
+    string   BaseBoardSerialNumber
+    string   ChassisSerialNumber
+    string   ChassisAssetTag
+    boolean  BIOSNumLock
+    uint16   BootOrder[]
+    string   Parent
+    boolean  IsSaved
+    string   AdditionalRecoveryInformation
+    boolean  AllowFullSCSICommandSet
+    uint32   DebugChannelId
+    uint16   DebugPortEnabled
+    uint32   DebugPort
+    string   Version
+    boolean  IncrementalBackupEnabled
+    boolean  VirtualNumaEnabled
+    boolean  AllowReducedFcRedundancy
+    string   VirtualSystemSubType
+    string   BootSourceOrder[]
+    boolean  PauseAfterBootFailure
+    uint16   NetworkBootPreferredProtocol
+    boolean  SecureBootEnabled
+    uint64   LowMmioGapSize
+end
+
+
 class Win32_ComputerSystem
     uint16   AdminPasswordStatus
     boolean  AutomaticManagedPagefile
@@ -607,39 +813,6 @@ class Msvm_VirtualSystemGlobalSettingData
 end
 
 
-class Msvm_VirtualSwitch
-    string   Caption
-    string   Description
-    string   ElementName
-    datetime InstallDate
-    uint16   OperationalStatus[]
-    string   StatusDescriptions[]
-    string   Status
-    uint16   HealthState
-    uint16   EnabledState
-    string   OtherEnabledState
-    uint16   RequestedState
-    uint16   EnabledDefault
-    datetime TimeOfLastStateChange
-    string   CreationClassName
-    string   Name
-    string   PrimaryOwnerContact
-    string   PrimaryOwnerName
-    string   Roles[]
-    string   NameFormat
-    string   OtherIdentifyingInfo[]
-    string   IdentifyingDescriptions[]
-    uint16   Dedicated[]
-    string   OtherDedicatedDescriptions[]
-    uint16   ResetCapability
-    uint16   PowerManagementCapabilities[]
-    string   ScopeOfResidence
-    uint32   NumLearnableAddresses
-    uint32   MaxVMQOffloads
-    uint32   MaxChimneyOffloads
-end
-
-
 class Msvm_ResourceAllocationSettingData
     string   Caption
     string   Description
index 8c62882d61e7ce454c04ba6b262c393226063d2f..c15d97adc5b70be12f8db2240ac05268b5e869d5 100755 (executable)
@@ -24,130 +24,310 @@ import sys
 import os
 import os.path
 
+separator = "/*" + ("*" * 50) + "*\n"
+wmi_version_separator = "/"
+wmi_classes_by_name = {}
+
+class WmiClass:
+    """Represents WMI class and provides methods to generate C code.
+
+    This class holds one or more instances of WmiClassVersion because with the
+    Windows 2012 release, Microsoft introduced "v2" version of Msvm_* family of
+    classes that need different URI for making wsman requests and also have
+    some additional/changed properties (though many of the properies are the
+    same as in "v1". Therefore, this class makes sure that C code is generated
+    for each of them while avoiding name conflics, identifies common members,
+    and defined *_WmiInfo structs holding info about each version so the driver
+    code can make the right choices based on which Hyper-V host it's connected
+    to.
+    """
+
+    def __init__(self, name, versions = []):
+        self.name = name
+        self.versions = versions
+        self.common = None
 
 
-separator = "/* " + ("* " * 37) + "*\n"
+    def prepare(self):
+        """Prepares the class for code generation
 
+        Makes sure that "versioned" classes are sorted by version, identifies
+        common properies and ensures that they are aligned by name and
+        type in each version
+        """
+        # sort vesioned classes by version in case input file did not have them
+        # in order
+        self.versions = sorted(self.versions, key=lambda cls: cls.version)
 
+        # if there's more than one verion make sure first one has name suffixed
+        # because we'll generate "common" memeber and will be the "base" name
+        if len(self.versions) > 1:
+            first = self.versions[0]
+            if first.version == None:
+                first.version = "v1"
+            first.name = "%s_%s" % (first.name, first.version)
 
-class Class:
-    def __init__(self, name, properties):
-        self.name = name
-        self.properties = properties
+        # finally, identify common members in all versions and make sure they
+        # are in the same order - to ensure C struct member alignment
+        self._align_property_members()
 
 
-    def generate_header(self):
+    def generate_classes_header(self):
+        """Generate C header code and return it as string
+
+        Declares:
+          <class_name>_Data - used as one of hypervObject->data members
+          <class_name>_TypeInfo - used as wsman XmlSerializerInfo
+          <class_name> - "inherits" hypervObject struct
+        """
+
         name_upper = self.name.upper()
 
         header = separator
         header += " * %s\n" % self.name
         header += " */\n"
         header += "\n"
-        header += "int hypervGet%sList(hypervPrivate *priv, virBufferPtr query, %s **list);\n" \
-                  % (self.name.replace("_", ""), self.name)
-        header += "\n"
+        header += "#define %s_CLASSNAME \\\n" % name_upper
+        header += "    \"%s\"\n" % self.name
         header += "\n"
+        header += "#define %s_WQL_SELECT \\\n" % name_upper
+        header += "    \"SELECT * FROM %s \"\n" % self.name
         header += "\n"
+        header += "extern hypervWmiClassInfoListPtr %s_WmiInfo;\n\n" % self.name
+
+        header += self._declare_data_structs()
+        header += self._declare_hypervObject_struct()
 
         return header
 
 
+    def generate_classes_source(self):
+        """Returns a C code string defining wsman data structs
+
+        Defines:
+          <class_name>_Data structs
+          <class_name>_WmiInfo - list holding metadata (e.g. request URIs) for
+                                 each known version of WMI class.
+        """
+
+        source = separator
+        source += " * %s\n" % self.name
+        source += " */\n"
+
+        for cls in self.versions:
+            source += "SER_START_ITEMS(%s_Data)\n" % cls.name
+
+            for property in cls.properties:
+                source += property.generate_classes_source(cls.name)
+
+            source += "SER_END_ITEMS(%s_Data);\n\n" % cls.name
+
+
+        source += self._define_WmiInfo_struct()
+        source += "\n\n"
+
+        return source
+
+
     def generate_classes_typedef(self):
-        typedef = "typedef struct _%s_Data %s_Data;\n" % (self.name, self.name)
-        typedef += "typedef struct _%s %s;\n" % (self.name, self.name)
+        """Returns C string for typdefs"""
+
+        typedef = "typedef struct _%s %s;\n" % (self.name, self.name)
+
+        if self.common is not None:
+            typedef += "typedef struct _%s_Data %s_Data;\n" % (self.name, self.name)
+
+        for cls in self.versions:
+            typedef += "typedef struct _%s_Data %s_Data;\n" % (cls.name, cls.name)
 
         return typedef
 
 
-    def generate_classes_header(self):
-        name_upper = self.name.upper()
 
-        header = separator
-        header += " * %s\n" % self.name
-        header += " */\n"
-        header += "\n"
-        header += "#define %s_RESOURCE_URI \\\n" % name_upper
+    def _declare_data_structs(self):
+        """Returns string C code declaring data structs.
 
-        if self.name.startswith("Win32_") or self.name.startswith("CIM_"):
-            header += "    \"http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/%s\"\n" % self.name
-        else:
-            header += "    \"http://schemas.microsoft.com/wbem/wsman/1/wmi/root/virtualization/%s\"\n" % self.name
+        The *_Data structs are members of hypervObject data union. Each one has
+        corresponding *_TypeInfo that is used for wsman unserialization of
+        response XML into the *_Data structs. If there's a "common" member, it
+        won't have corresponding *_TypeInfo becuase this is a special case only
+        used to provide a common "view" of v1, v2 etc members
+        """
 
-        header += "\n"
-        header += "#define %s_CLASSNAME \\\n" % name_upper
-        header += "    \"%s\"\n" % self.name
-        header += "\n"
-        header += "#define %s_WQL_SELECT \\\n" % name_upper
-        header += "    \"select * from %s \"\n" % self.name
-        header += "\n"
-        header += "struct _%s_Data {\n" % self.name
+        header = ""
+        if self.common is not None:
+            header += "struct _%s_Data {\n" % self.name
+            for property in self.common:
+                header += property.generate_classes_header()
+            header += "};\n\n"
 
-        for property in self.properties:
-            header += property.generate_classes_header()
+        # Declare actual data struct for each versions
+        for cls in self.versions:
+            header += "#define %s_RESOURCE_URI \\\n" % cls.name.upper()
+            header += "    \"%s\"\n" % cls.uri_info.resourceUri
+            header += "\n"
+            header += "struct _%s_Data {\n" % cls.name
+            for property in cls.properties:
+                header += property.generate_classes_header()
+            header += "};\n\n"
+            header += "SER_DECLARE_TYPE(%s_Data);\n" % cls.name
 
-        header += "};\n"
-        header += "\n"
-        header += "SER_DECLARE_TYPE(%s_Data);\n" % self.name
-        header += "\n"
+        return header
+
+
+    def _declare_hypervObject_struct(self):
+        """Return string for C code declaring hypervObject instance"""
+
+        header = "\n/* must match hypervObject */\n"
         header += "struct _%s {\n" % self.name
-        header += "    XmlSerializerInfo *serializerInfo;\n"
-        header += "    %s_Data *data;\n" % self.name
+        header += "    union {\n"
+
+        # if there's common use it as "common" else first and only version is
+        # the "common" member
+        if self.common is not None:
+            header += "        %s_Data *common;\n" % self.name
+        else:
+            header += "        %s_Data *common;\n" % self.versions[0].name
+
+        for cls in self.versions:
+            header += "        %s_Data *%s;\n" % (cls.name, cls.version)
+
+        header += "    } data;\n"
+        header += "    hypervWmiClassInfoPtr info;\n"
         header += "    %s *next;\n" % self.name
         header += "};\n"
-        header += "\n"
-        header += "\n"
-        header += "\n"
+
+        header += "\n\n\n"
 
         return header
 
 
-    def generate_source(self):
-        name_upper = self.name.upper()
+    def _define_WmiInfo_struct(self):
+        """Return string for C code defining *_WmiInfo struct
 
-        source = separator
-        source += " * %s\n" % self.name
-        source += " */\n"
-        source += "\n"
-        source += "int\n"
-        source += "hypervGet%sList(hypervPrivate *priv, virBufferPtr query, %s **list)\n" \
-                  % (self.name.replace("_", ""), self.name)
-        source += "{\n"
-
-        if self.name.startswith("Win32_") or self.name.startswith("CIM_"):
-            source += "    return hypervEnumAndPull(priv, query, ROOT_CIMV2,\n"
-        else:
-            source += "    return hypervEnumAndPull(priv, query, ROOT_VIRTUALIZATION,\n"
+        Those structs hold info with meta-data needed to make wsman requests for
+        each version of WMI class
+        """
+
+        source = "hypervWmiClassInfoListPtr %s_WmiInfo = &(hypervWmiClassInfoList) {\n" % self.name
+        source += "    .count = %d,\n" % len(self.versions)
+        source += "    .objs = (hypervWmiClassInfoPtr []) {\n"
+
+        for cls in self.versions:
+            source += "        &(hypervWmiClassInfo) {\n"
+            source += "            .name = %s_CLASSNAME,\n" % self.name.upper()
+            if cls.version is not None:
+                source += "            .version = \"%s\",\n" % cls.version
+            else:
+                source += "            .version = NULL,\n"
+            source += "            .rootUri = %s,\n" % cls.uri_info.rootUri
+            source += "            .resourceUri = %s_RESOURCE_URI,\n" % cls.name.upper()
+            source += "            .serializerInfo = %s_Data_TypeInfo\n" % cls.name
+            source += "        },\n"
 
-        source += "                             %s_Data_TypeInfo,\n" % self.name
-        source += "                             %s_RESOURCE_URI,\n" % name_upper
-        source += "                             %s_CLASSNAME,\n" % name_upper
-        source += "                             (hypervObject **)list);\n"
-        source += "}\n"
-        source += "\n"
-        source += "\n"
-        source += "\n"
+        source += "    }\n"
+        source += "};\n"
 
         return source
 
 
-    def generate_classes_source(self):
-        name_upper = self.name.upper()
+    def _align_property_members(self):
+        """Identifies common properties in all class versions.
 
-        source = separator
-        source += " * %s\n" % self.name
-        source += " */\n"
-        source += "\n"
-        source += "SER_START_ITEMS(%s_Data)\n" % self.name
+        Makes sure that properties in all versions are ordered with common
+        members first and that they are in the same order. This makes the
+        generated C structs memory aligned and safe to access via the "common"
+        struct that "shares" members with v1, v2 etc.
+        """
 
-        for property in self.properties:
-            source += property.generate_classes_source(self.name)
+        num_classes = len(self.versions)
+        common = {}
+        property_info = {}
 
-        source += "SER_END_ITEMS(%s_Data);\n" % self.name
-        source += "\n"
-        source += "\n"
-        source += "\n"
+        if num_classes < 2:
+            return
+
+        # count property occurences in all class versions
+        for cls in self.versions:
+            for prop in cls.properties:
+                # consdered same if matches by name AND type
+                key = "%s_%s" % (prop.name, prop.type)
+
+                if key in property_info:
+                    property_info[key][1] += 1
+                else:
+                    property_info[key] = [prop, 1]
+
+        # isolate those that are common for all and keep track of their postions
+        pos = 0
+        for key in property_info:
+            info = property_info[key]
+            # exists in all class versions
+            if info[1] == num_classes:
+                common[info[0].name] = [info[0], pos]
+                pos += 1
+
+        # alter each versions's property list so that common members are first
+        # and in the same order as in the common dictionary
+        total = len(common)
+        for cls in self.versions:
+            index = 0
+            count = len(cls.properties)
+
+            while index < count:
+                prop = cls.properties[index]
+
+                # it's a "common" property
+                if prop.name in common:
+                    pos = common[prop.name][1]
+
+                    # move to the same position as in "common" dictionary
+                    if index != pos:
+                        tmp = cls.properties[pos]
+                        cls.properties[pos] = prop
+                        cls.properties[index] = tmp
+                    else:
+                        index += 1
+                else:
+                    index += 1
+
+        # finally, get common properties as list sorted by position in dictionary
+        tmp = sorted(common.values(), key=lambda x: x[1])
+        self.common = []
+        for x in tmp:
+            self.common.append(x[0])
+
+
+
+class ClassUriInfo:
+    """Prepares URI information needed for wsman requests."""
+
+    def __init__(self, wmi_name, version):
+        self.rootUri = "ROOT_CIMV2"
+        self.resourceUri = None
+        baseUri = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2"
+
+        if wmi_name.startswith("Msvm_"):
+            baseUri = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/virtualization"
+            self.rootUri = "ROOT_VIRTUALIZATION"
+
+            if version == "v2":
+                baseUri += "/v2"
+                self.rootUri = "ROOT_VIRTUALIZATION_V2"
+
+        self.resourceUri = "%s/%s" % (baseUri, wmi_name)
+
+
+
+class WmiClassVersion:
+    """Represents specific version of WMI class."""
+
+    def __init__(self, name, version, properties, uri_info):
+        self.name = name
+        self.version = version
+        self.properties = properties
+        self.uri_info = uri_info
 
-        return source
 
 
 class Property:
@@ -155,9 +335,13 @@ class Property:
                "string"   : "STR",
                "datetime" : "STR",
                "int8"     : "INT8",
+               "sint8"    : "INT8",
                "int16"    : "INT16",
+               "sint16"   : "INT16",
                "int32"    : "INT32",
+               "sint32"   : "INT32",
                "int64"    : "INT64",
+               "sint64"   : "INT64",
                "uint8"    : "UINT8",
                "uint16"   : "UINT16",
                "uint32"   : "UINT32",
@@ -217,8 +401,15 @@ def parse_class(block):
     assert header_items[0] == "class"
 
     name = header_items[1]
-
     properties = []
+    version = None
+    wmi_name = name
+    ns_separator = name.find(wmi_version_separator)
+
+    if ns_separator != -1:
+        version = name[:ns_separator]
+        wmi_name = name[ns_separator + 1:]
+        name = "%s_%s" % (wmi_name, version)
 
     for line in block[1:]:
         # expected format: <type> <name>
@@ -236,7 +427,13 @@ def parse_class(block):
         properties.append(Property(type=items[0], name=items[1],
                                    is_array=is_array))
 
-    return Class(name=name, properties=properties)
+    cls = WmiClassVersion(name=name, version=version, properties=properties,
+                          uri_info=ClassUriInfo(wmi_name, version))
+
+    if wmi_name in wmi_classes_by_name:
+        wmi_classes_by_name[wmi_name].versions.append(cls)
+    else:
+        wmi_classes_by_name[wmi_name] = WmiClass(wmi_name, [cls])
 
 
 
@@ -248,15 +445,12 @@ def main():
         input_filename = os.path.join(os.getcwd(), "hyperv_wmi_generator.input")
         output_dirname = os.getcwd()
 
-    header = open_and_print(os.path.join(output_dirname, "hyperv_wmi.generated.h"))
-    source = open_and_print(os.path.join(output_dirname, "hyperv_wmi.generated.c"))
     classes_typedef = open_and_print(os.path.join(output_dirname, "hyperv_wmi_classes.generated.typedef"))
     classes_header = open_and_print(os.path.join(output_dirname, "hyperv_wmi_classes.generated.h"))
     classes_source = open_and_print(os.path.join(output_dirname, "hyperv_wmi_classes.generated.c"))
 
     # parse input file
     number = 0
-    classes_by_name = {}
     block = None
 
     for line in file(input_filename, "rb").readlines():
@@ -279,8 +473,7 @@ def main():
         if block is not None:
             if line == "end":
                 if block[0][1].startswith("class"):
-                    cls = parse_class(block)
-                    classes_by_name[cls.name] = cls
+                    parse_class(block)
 
                 block = None
             else:
@@ -289,21 +482,20 @@ def main():
     # write output files
     notice = "/* Generated by hyperv_wmi_generator.py */\n\n\n\n"
 
-    header.write(notice)
-    source.write(notice)
     classes_typedef.write(notice)
     classes_header.write(notice)
     classes_source.write(notice)
 
-    names = classes_by_name.keys()
+    names = wmi_classes_by_name.keys()
     names.sort()
 
     for name in names:
-        header.write(classes_by_name[name].generate_header())
-        source.write(classes_by_name[name].generate_source())
-        classes_typedef.write(classes_by_name[name].generate_classes_typedef())
-        classes_header.write(classes_by_name[name].generate_classes_header())
-        classes_source.write(classes_by_name[name].generate_classes_source())
+        cls = wmi_classes_by_name[name]
+        cls.prepare()
+
+        classes_typedef.write(cls.generate_classes_typedef())
+        classes_header.write(cls.generate_classes_header())
+        classes_source.write(cls.generate_classes_source())