[libvirt] [PATCH v3 00/12] hyperv: add support for Hyper-V 2012 and newer.

Changes since v2[1]: * address the issue with incorrect request URI used in invoke * update driver doc * add news entry [1] https://www.redhat.com/archives/libvir-list/2017-April/msg00018.html Dawid Zamirski (12): hyperv: store WMI version in hypervPrivate. hyperv: introduce hypervWmiClassInfo struct. hyperv: update hypervObject struct. hyperv: add hypervWqlQuery struct. hyperv: make hypervEnumAndPull use hypervWqlQuery hyperv: update generator input file. hyperv: update wmi code generator. hyperv: add helper for getting WMI class lists. hyperv: port rest of the driver to new stucts. hyperv: add hypervInitConnection. hyperv: update driver documentation. news: update for Hyper-V 2012+ support. docs/drvhyperv.html.in | 2 +- docs/news.xml | 11 + src/Makefile.am | 2 - src/hyperv/hyperv_driver.c | 168 ++++++++------- src/hyperv/hyperv_private.h | 8 +- src/hyperv/hyperv_wmi.c | 173 +++++++++++---- src/hyperv/hyperv_wmi.h | 56 ++++- src/hyperv/hyperv_wmi_classes.h | 30 +++ src/hyperv/hyperv_wmi_generator.input | 239 ++++++++++++++++++--- src/hyperv/hyperv_wmi_generator.py | 385 +++++++++++++++++++++++++--------- 10 files changed, 822 insertions(+), 252 deletions(-) -- 2.9.3

Hyper-V 2012+ uses a new "v2" version of Msvm_* WMI classes so we will store that info in hypervPrivate so that it is easily accessbile in the driver API callbacks and handled accordingly. --- src/hyperv/hyperv_private.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hyperv/hyperv_private.h b/src/hyperv/hyperv_private.h index 574bb5f..4e70699 100644 --- a/src/hyperv/hyperv_private.h +++ b/src/hyperv/hyperv_private.h @@ -28,11 +28,17 @@ # include "hyperv_util.h" # include "openwsman.h" -typedef struct _hypervPrivate hypervPrivate; +typedef enum _hypervWmiVersion hypervWmiVersion; +enum _hypervWmiVersion { + HYPERV_WMI_VERSION_V1, + HYPERV_WMI_VERSION_V2, +}; +typedef struct _hypervPrivate hypervPrivate; struct _hypervPrivate { hypervParsedUri *parsedUri; WsManClient *client; + hypervWmiVersion wmiVersion; }; #endif /* __HYPERV_PRIVATE_H__ */ -- 2.9.3

This struct is to be used to carry all the information necessary to issue wsman requests for given WMI class. Those will be defined by the generator code (as lists) so that they are handy for the driver code to "extract" needed info depending on which hyper-v we're connected to. For example: hypervWmiClassInfoListPtr Msvm_ComputerSystem_WmiInfo = { .count = 2 { { .name = "Msvm_ComputerSystem", .version = "v1", .rootUri = "http://asdf.com", ... }, { .name = "Msvm_ComputerSystem", .version = "v2", .rootUri = "http://asdf.com/v2", ... }, } }; Then the driver code will grab either "v1" or "v2" to pass info wsman API, depending on hypervPrivate->wmiVersion value. --- src/hyperv/hyperv_wmi_classes.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/hyperv/hyperv_wmi_classes.h b/src/hyperv/hyperv_wmi_classes.h index ac7a3b8..b0f3e3c 100644 --- a/src/hyperv/hyperv_wmi_classes.h +++ b/src/hyperv/hyperv_wmi_classes.h @@ -87,6 +87,28 @@ enum _Msvm_ConcreteJob_JobState { }; +typedef struct _hypervWmiClassInfo hypervWmiClassInfo; +typedef hypervWmiClassInfo *hypervWmiClassInfoPtr; +struct _hypervWmiClassInfo { + /* The WMI class name */ + const char *name; + /* The version of the WMI class as in "v1" or "v2" */ + const char *version; + /* The URI for wsman enumerate request */ + const char *rootUri; + /* The namespace URI for XML serialization */ + const char *resourceUri; + /* The wsman serializer info - one of the *_TypeInfo structs */ + XmlSerializerInfo *serializerInfo; +}; + + +typedef struct _hypervWmiClassInfoList hypervWmiClassInfoList; +typedef hypervWmiClassInfoList *hypervWmiClassInfoListPtr; +struct _hypervWmiClassInfoList { + size_t count; + hypervWmiClassInfoPtr *objs; +}; # include "hyperv_wmi_classes.generated.h" -- 2.9.3

Currently named as hypervObjecUnified to keep code compilable/functional until all bits are in place. This struct is a result of unserializing WMI request response. Therefore, it needs to be able to deal with different "versions" of the same WMI class. To accomplish this, the "data" member was turned in to a union which: * has a "common" member that contains only WMI class fields that are safe to access and are present in all "versions". This is ensured by the code generator that takes care of proper struct memory alignment between "common", "v1", "v2" etc members. This memeber is to be used by the driver code wherever the API implementation can be shared for all supported hyper-v versions. * the "v1" and "v2" member can be used by the driver code to handle version specific cases. Example: Msvm_ComputerSystem *vm = NULL; ... hypervGetVirtualMachineList(priv, wqlQuery, *vm); ... /* safe for "v1" and "v2" */ char *vmName = vm->data.common->Name; /* or if one really needs special handling for "v2" */ if (priv->wmiVersion == HYPERV_WMI_VERSION_V2) { char *foo = vm->data.v2->SomeV2OnlyField; } In other words, driver should not concern itself with existence of "v1" or "v2" of WMI class unless absolutely necessary. --- src/hyperv/hyperv_wmi.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h index ce1fe05..5fc36e8 100644 --- a/src/hyperv/hyperv_wmi.h +++ b/src/hyperv/hyperv_wmi.h @@ -41,6 +41,25 @@ int hypervVerifyResponse(WsManClient *client, WsXmlDocH response, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Object */ +typedef struct _hypervObjectUnified hypervObjectUnified; +struct _hypervObjectUnified { + /* 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 + * conditional checks based on which version was returned as long as it + * only needs to read common values. The alignment of structs is ensured + * by the generator. + */ + union { + XML_TYPE_PTR common; + XML_TYPE_PTR v1; + XML_TYPE_PTR v2; + } data; + /* The info used to make wsman request */ + hypervWmiClassInfoPtr info; + + hypervObjectUnified *next; +}; struct _hypervObject { XmlSerializerInfo *serializerInfo; -- 2.9.3

Since this is the last commit that will compile in the series, the remaining patches should be squashed into this one - they are kept separate only for code review purposes. This struct is to be passed to enumerate-and-pull wsman request (to run "Select" queries) and provides the hypervWmiClassInfoListPtr instance from which we can extract the version specific info using the new hypervGetWmiClassInfo function (currently unused) --- src/hyperv/hyperv_wmi.c | 35 +++++++++++++++++++++++++++++++++++ src/hyperv/hyperv_wmi.h | 8 ++++++++ 2 files changed, 43 insertions(+) diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index b2b5243..069bcc9 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -45,6 +45,41 @@ #define VIR_FROM_THIS VIR_FROM_HYPERV +static int ATTRIBUTE_UNUSED +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; +} int hypervVerifyResponse(WsManClient *client, WsXmlDocH response, diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h index 5fc36e8..12b94af 100644 --- a/src/hyperv/hyperv_wmi.h +++ b/src/hyperv/hyperv_wmi.h @@ -30,6 +30,7 @@ # include "openwsman.h" +# define HYPERV_WQL_QUERY_INITIALIZER {NULL, NULL} typedef struct _hypervObject hypervObject; @@ -61,6 +62,13 @@ struct _hypervObjectUnified { hypervObjectUnified *next; }; +typedef struct _hypervWqlQuery hypervWqlQuery; +typedef hypervWqlQuery *hypervWqlQueryPtr; +struct _hypervWqlQuery { + const char *query; + hypervWmiClassInfoListPtr info; +}; + struct _hypervObject { XmlSerializerInfo *serializerInfo; XML_TYPE_PTR data; -- 2.9.3

This enables this function to handle "v1" and "v2" WMI requests. Since this commit and the ones that follow should be squashed on previous one: * rename hypervObjectUnified -> hypervObject as we've already broken compilation here so there's no point in keeping those in parallel anymore. * do not mark hypervGetWmiClassInfo as unused --- src/hyperv/hyperv_wmi.c | 40 ++++++++++++++++++---------------------- src/hyperv/hyperv_wmi.h | 17 ++++------------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index 069bcc9..5cac58d 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -45,7 +45,7 @@ #define VIR_FROM_THIS VIR_FROM_HYPERV -static int ATTRIBUTE_UNUSED +static int hypervGetWmiClassInfo(hypervPrivate *priv, hypervWmiClassInfoListPtr list, hypervWmiClassInfoPtr *info) { @@ -143,14 +143,13 @@ hypervVerifyResponse(WsManClient *client, WsXmlDocH response, /* This function guarantees that query is freed, 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; @@ -160,18 +159,14 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root, XML_TYPE_PTR data = NULL; hypervObject *object; - if (virBufferCheckError(query) < 0) { - virBufferFreeAndReset(query); - return -1; - } - query_string = virBufferContentAndReset(query); - if (list == NULL || *list != NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); - VIR_FREE(query_string); return -1; } + if (hypervGetWmiClassInfo(priv, wqlQuery->info, &wmiInfo) < 0) + return -1; + serializerContext = wsmc_get_serialization_context(priv->client); options = wsmc_options_init(); @@ -182,7 +177,7 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root, goto cleanup; } - filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, query_string); + filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, wqlQuery->query); if (filter == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -190,7 +185,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; @@ -201,7 +197,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) @@ -231,11 +227,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", @@ -246,8 +243,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; @@ -283,13 +280,12 @@ 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 } - VIR_FREE(query_string); ws_xml_destroy_doc(response); VIR_FREE(enumContext); hypervFreeObject(priv, head); diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h index 12b94af..8ce32a9 100644 --- a/src/hyperv/hyperv_wmi.h +++ b/src/hyperv/hyperv_wmi.h @@ -32,7 +32,6 @@ # define HYPERV_WQL_QUERY_INITIALIZER {NULL, NULL} -typedef struct _hypervObject hypervObject; int hypervVerifyResponse(WsManClient *client, WsXmlDocH response, const char *detail); @@ -42,8 +41,8 @@ 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 @@ -59,7 +58,7 @@ struct _hypervObjectUnified { /* The info used to make wsman request */ hypervWmiClassInfoPtr info; - hypervObjectUnified *next; + hypervObject *next; }; typedef struct _hypervWqlQuery hypervWqlQuery; @@ -69,15 +68,7 @@ struct _hypervWqlQuery { hypervWmiClassInfoListPtr info; }; -struct _hypervObject { - XmlSerializerInfo *serializerInfo; - XML_TYPE_PTR data; - hypervObject *next; -}; - -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); -- 2.9.3

2017-04-04 1:52 GMT+02:00 Dawid Zamirski <dzamirski@datto.com>:
This enables this function to handle "v1" and "v2" WMI requests.
Since this commit and the ones that follow should be squashed on previous one: * rename hypervObjectUnified -> hypervObject as we've already broken compilation here so there's no point in keeping those in parallel anymore. * do not mark hypervGetWmiClassInfo as unused --- src/hyperv/hyperv_wmi.c | 40 ++++++++++++++++++---------------------- src/hyperv/hyperv_wmi.h | 17 ++++------------- 2 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index 069bcc9..5cac58d 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -45,7 +45,7 @@ #define VIR_FROM_THIS VIR_FROM_HYPERV
-static int ATTRIBUTE_UNUSED +static int hypervGetWmiClassInfo(hypervPrivate *priv, hypervWmiClassInfoListPtr list, hypervWmiClassInfoPtr *info) { @@ -143,14 +143,13 @@ hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
/* This function guarantees that query is freed, even on failure */
You changes broke the contract of this function as stated in the comment above. You're changes make hypervEnumAndPull leak the query string now stored in hypervWqlQuery. I'd change hypervWqlQuery to hold the query string as a virBuffer (not a virBufferPtr) and then instead of using virBufferContentAndReset to create the query string before calling hypervEnumAndPull just make all the callers build the query in the virBuffer stored in the hypervWqlQuery struct directly. Then hypervEnumAndPull can keep all its virBuffer handling and ensure again that the query string is freed properly in all cases.
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; @@ -160,18 +159,14 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root, XML_TYPE_PTR data = NULL; hypervObject *object;
- if (virBufferCheckError(query) < 0) { - virBufferFreeAndReset(query); - return -1; - } - query_string = virBufferContentAndReset(query); -
Don't remove this but feed it with &wqlQuery->query instead of query
if (list == NULL || *list != NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); - VIR_FREE(query_string); return -1; }
+ if (hypervGetWmiClassInfo(priv, wqlQuery->info, &wmiInfo) < 0)
Need to free query_string here.
+ return -1; + serializerContext = wsmc_get_serialization_context(priv->client);
options = wsmc_options_init(); @@ -182,7 +177,7 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root, goto cleanup; }
- filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, query_string); + filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, wqlQuery->query); if (filter == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -190,7 +185,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; @@ -201,7 +197,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) @@ -231,11 +227,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", @@ -246,8 +243,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;
@@ -283,13 +280,12 @@ 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 }
- VIR_FREE(query_string); ws_xml_destroy_doc(response); VIR_FREE(enumContext); hypervFreeObject(priv, head);
-- Matthias Bolte http://photron.blogspot.com

On Tue, 2017-04-04 at 23:10 +0200, Matthias Bolte wrote:
2017-04-04 1:52 GMT+02:00 Dawid Zamirski <dzamirski@datto.com>:
This enables this function to handle "v1" and "v2" WMI requests.
Since this commit and the ones that follow should be squashed on previous one: * rename hypervObjectUnified -> hypervObject as we've already broken compilation here so there's no point in keeping those in parallel anymore. * do not mark hypervGetWmiClassInfo as unused --- src/hyperv/hyperv_wmi.c | 40 ++++++++++++++++++------------------- --- src/hyperv/hyperv_wmi.h | 17 ++++------------- 2 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index 069bcc9..5cac58d 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -45,7 +45,7 @@ #define VIR_FROM_THIS VIR_FROM_HYPERV
-static int ATTRIBUTE_UNUSED +static int hypervGetWmiClassInfo(hypervPrivate *priv, hypervWmiClassInfoListPtr list, hypervWmiClassInfoPtr *info) { @@ -143,14 +143,13 @@ hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
/* This function guarantees that query is freed, even on failure */
You changes broke the contract of this function as stated in the comment above. You're changes make hypervEnumAndPull leak the query string now stored in hypervWqlQuery.
I'd change hypervWqlQuery to hold the query string as a virBuffer (not a virBufferPtr) and then instead of using virBufferContentAndReset to create the query string before calling hypervEnumAndPull just make all the callers build the query in the virBuffer stored in the hypervWqlQuery struct directly.
Then hypervEnumAndPull can keep all its virBuffer handling and ensure again that the query string is freed properly in all cases.
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; @@ -160,18 +159,14 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root, XML_TYPE_PTR data = NULL; hypervObject *object;
- if (virBufferCheckError(query) < 0) { - virBufferFreeAndReset(query); - return -1; - } - query_string = virBufferContentAndReset(query); -
Don't remove this but feed it with &wqlQuery->query instead of query
if (list == NULL || *list != NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); - VIR_FREE(query_string); return -1; }
+ if (hypervGetWmiClassInfo(priv, wqlQuery->info, &wmiInfo) < 0)
Need to free query_string here.
Shouldn't this goto cleanup to also free &wql->query with virtBufferFreeAndReset?
+ return -1; + serializerContext = wsmc_get_serialization_context(priv-
client);
options = wsmc_options_init(); @@ -182,7 +177,7 @@ hypervEnumAndPull(hypervPrivate *priv, virBufferPtr query, const char *root, goto cleanup; }
- filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, query_string); + filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, wqlQuery->query); if (filter == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -190,7 +185,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; @@ -201,7 +197,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) @@ -231,11 +227,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", @@ -246,8 +243,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;
@@ -283,13 +280,12 @@ 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 }
- VIR_FREE(query_string); ws_xml_destroy_doc(response); VIR_FREE(enumContext); hypervFreeObject(priv, head);

Adds defintiions for "v2" WMI class variants that are needed by the driver to handle both hyperv 2008 and 2012+ --- src/hyperv/hyperv_wmi_generator.input | 239 +++++++++++++++++++++++++++++----- 1 file changed, 206 insertions(+), 33 deletions(-) diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input index 209a9ff..d7f819e 100644 --- a/src/hyperv/hyperv_wmi_generator.input +++ b/src/hyperv/hyperv_wmi_generator.input @@ -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 -- 2.9.3

This patch updates the code generator that outputs C headers and code for WMI classes. It has been updated to handle multiple versions (or namespaces) of the same class which were introduced with Hyperv 2012+ --- src/hyperv/hyperv_wmi_generator.py | 385 +++++++++++++++++++++++++++---------- 1 file changed, 288 insertions(+), 97 deletions(-) diff --git a/src/hyperv/hyperv_wmi_generator.py b/src/hyperv/hyperv_wmi_generator.py index 8c62882..f2c9cde 100755 --- a/src/hyperv/hyperv_wmi_generator.py +++ b/src/hyperv/hyperv_wmi_generator.py @@ -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, identfies + 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 struc 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" - 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" + 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 += " }\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" proptery + 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", @@ -189,8 +373,6 @@ class Property: return " SER_NS_%s(%s_RESOURCE_URI, \"%s\", 1),\n" \ % (Property.typemap[self.type], class_name.upper(), self.name) - - def open_and_print(filename): if filename.startswith("./"): print " GEN " + filename[2:] @@ -217,8 +399,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 +425,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 +443,13 @@ 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(): @@ -268,7 +461,7 @@ def main(): line = line.lstrip().rstrip() if len(line) < 1: - continue + continue if line.startswith("class"): if block is not None: @@ -279,8 +472,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 +481,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()) -- 2.9.3

2017-04-04 1:52 GMT+02:00 Dawid Zamirski <dzamirski@datto.com>:
This patch updates the code generator that outputs C headers and code for WMI classes. It has been updated to handle multiple versions (or namespaces) of the same class which were introduced with Hyperv 2012+ --- src/hyperv/hyperv_wmi_generator.py | 385 +++++++++++++++++++++++++++---------- 1 file changed, 288 insertions(+), 97 deletions(-)
diff --git a/src/hyperv/hyperv_wmi_generator.py b/src/hyperv/hyperv_wmi_generator.py index 8c62882..f2c9cde 100755 --- a/src/hyperv/hyperv_wmi_generator.py +++ b/src/hyperv/hyperv_wmi_generator.py @@ -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
s/Hyper-v/Hyper-V/
+ 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, identfies
s/identfies/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 struc member alignment
s/struc/struct/
+ 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"
- 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" + 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 += " }\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" proptery
s/proptery/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", @@ -189,8 +373,6 @@ class Property: return " SER_NS_%s(%s_RESOURCE_URI, \"%s\", 1),\n" \ % (Property.typemap[self.type], class_name.upper(), self.name)
- - def open_and_print(filename): if filename.startswith("./"): print " GEN " + filename[2:] @@ -217,8 +399,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 +425,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 +443,13 @@ 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")) +
Unnecessary whitespace addition.
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 +
Why did you drop this comment?
number = 0 - classes_by_name = {} block = None
for line in file(input_filename, "rb").readlines(): @@ -268,7 +461,7 @@ def main(): line = line.lstrip().rstrip()
if len(line) < 1: - continue + continue
Unnecessary whitespace change. -- Matthias Bolte http://photron.blogspot.com

Those used to be auto-generated and are hand-written now intead. The reason being that those are not very useful and better replacements are in order once the driver itself implements more of the API and common patterns start to emerge. --- src/Makefile.am | 2 - src/hyperv/hyperv_wmi.c | 81 +++++++++++++++++++++++++++++++++++++---- src/hyperv/hyperv_wmi.h | 24 +++++++++++- src/hyperv/hyperv_wmi_classes.h | 8 ++++ 4 files changed, 104 insertions(+), 11 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 75e4344..99f5229 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -910,8 +910,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 diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index 5cac58d..c2d64ba 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -36,12 +36,6 @@ #define WS_SERIALIZER_FREE_MEM_WORKS 0 -#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 VIR_FROM_THIS VIR_FROM_HYPERV @@ -81,6 +75,18 @@ hypervGetWmiClassInfo(hypervPrivate *priv, hypervWmiClassInfoListPtr list, return -1; } +static int +hypervGetWmiClassList(hypervPrivate *priv, hypervWmiClassInfoListPtr wmiInfo, + virBufferPtr query, hypervObject **wmiClass) +{ + hypervWqlQuery wqlQuery = HYPERV_WQL_QUERY_INITIALIZER; + + wqlQuery.info = wmiInfo; + wqlQuery.query = virBufferContentAndReset(query); + + return hypervEnumAndPull(priv, &wqlQuery, wmiClass); +} + int hypervVerifyResponse(WsManClient *client, WsXmlDocH response, const char *detail) @@ -694,5 +700,66 @@ hypervMsvmComputerSystemFromDomain(virDomainPtr domain, } +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); +} + -#include "hyperv_wmi.generated.c" +int +hypervGetMsvmMemorySettingDataList(hypervPrivate *priv, virBufferPtr query, + Msvm_MemorySettingData **list) +{ + return hypervGetWmiClassList(priv, Msvm_MemorySettingData_WmiInfo, query, + (hypervObject **) list); +} diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h index 8ce32a9..2d90a3b 100644 --- a/src/hyperv/hyperv_wmi.h +++ b/src/hyperv/hyperv_wmi.h @@ -109,7 +109,29 @@ enum _Msvm_ReturnCode { const char *hypervReturnCodeToString(int returnCode); +/* Generic "Get WMI class list" */ +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 @@ -133,6 +155,4 @@ int hypervMsvmComputerSystemFromDomain(virDomainPtr domain, -# include "hyperv_wmi.generated.h" - #endif /* __HYPERV_WMI_H__ */ diff --git a/src/hyperv/hyperv_wmi_classes.h b/src/hyperv/hyperv_wmi_classes.h index b0f3e3c..0b8e29c 100644 --- a/src/hyperv/hyperv_wmi_classes.h +++ b/src/hyperv/hyperv_wmi_classes.h @@ -28,6 +28,14 @@ # 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/*" /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Msvm_ComputerSystem -- 2.9.3

basically s/data->/data.common->/ Because the data member of hypervObject is a union, get the data via the "common" member everywhere - existing driver does not require special handling for v1 vs v2 separately. --- src/hyperv/hyperv_driver.c | 76 +++++++++++++++++++++++----------------------- src/hyperv/hyperv_wmi.c | 21 ++++++++----- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index b642a02..5d01c1f 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -240,7 +240,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 +282,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 +295,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 +313,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 +331,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 +372,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 +532,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 +560,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 +666,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 +676,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 +687,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 +697,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 +709,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 +803,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 +813,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 +824,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 +834,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 +846,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 +854,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 +930,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 +1154,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 +1177,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 +1280,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) || diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c index c2d64ba..5d258a6 100644 --- a/src/hyperv/hyperv_wmi.c +++ b/src/hyperv/hyperv_wmi.c @@ -439,6 +439,8 @@ 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); @@ -447,6 +449,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) { @@ -459,7 +464,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) @@ -508,7 +513,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: @@ -567,7 +572,7 @@ int hypervMsvmComputerSystemEnabledStateToDomainState (Msvm_ComputerSystem *computerSystem) { - switch (computerSystem->data->EnabledState) { + switch (computerSystem->data.common->EnabledState) { case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_UNKNOWN: return VIR_DOMAIN_NOSTATE; @@ -607,7 +612,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; @@ -652,17 +657,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; } -- 2.9.3

This function detects hyperv version by issuing a simple query using "v2" namespace and falling back to "v1". --- src/hyperv/hyperv_driver.c | 92 ++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 5d01c1f..0913517 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -55,7 +55,62 @@ hypervFreePrivate(hypervPrivate **priv) VIR_FREE(*priv); } +static int +hypervInitConnection(virConnectPtr conn, hypervPrivate *priv, + char *username, char *password) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + hypervWqlQuery query = 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"); + + virBufferAddLit(&buf, MSVM_COMPUTERSYSTEM_WQL_SELECT); + virBufferAddLit(&buf, "WHERE "); + virBufferAddLit(&buf, MSVM_COMPUTERSYSTEM_WQL_PHYSICAL); + + query.query = virBufferContentAndReset(&buf); + query.info = Msvm_ComputerSystem_WmiInfo; + + /* try query using V2 namespace (for Hyperv 2012+) */ + priv->wmiVersion = HYPERV_WMI_VERSION_V2; + + if (hypervEnumAndPull(priv, &query, &computerSystem) < 0) { + /* fall back to V1 namespace (for Hyper-v 2008) */ + priv->wmiVersion = HYPERV_WMI_VERSION_V1; + + if (hypervEnumAndPull(priv, &query, &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 +122,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 +200,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")); - 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); + if (hypervInitConnection(conn, priv, username, password) < 0) goto cleanup; - } conn->privateData = priv; priv = NULL; @@ -191,7 +212,6 @@ hypervConnectOpen(virConnectPtr conn, virConnectAuthPtr auth, hypervFreePrivate(&priv); VIR_FREE(username); VIR_FREE(password); - hypervFreeObject(priv, (hypervObject *)computerSystem); return result; } -- 2.9.3

--- docs/drvhyperv.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/drvhyperv.html.in b/docs/drvhyperv.html.in index 7acf86f..e87d8cb 100644 --- a/docs/drvhyperv.html.in +++ b/docs/drvhyperv.html.in @@ -5,7 +5,7 @@ <h1>Microsoft Hyper-V hypervisor driver</h1> <ul id="toc"></ul> <p> - The libvirt Microsoft Hyper-V driver can manage Hyper-V 2008 R2. + The libvirt Microsoft Hyper-V driver can manage Hyper-V 2008 R2 and newer. </p> -- 2.9.3

--- docs/news.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/news.xml b/docs/news.xml index 9c0dcfd..39631c3 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -37,6 +37,17 @@ <section title="New features"> </section> <section title="Improvements"> + <change> + <summary> + The libvirt Hyper-V driver now supports Hyper-V 2012 and newer. + </summary> + <description> + Starting with Hyper-V 2012 the API has changed causing the existing + driver to be unable to send and process requests properly. This has + been resolved by adding abstractions to handle the differences and + ease handling such breaks if they happen in the future. + </description> + </change> </section> <section title="Bug fixes"> </section> -- 2.9.3

2017-04-04 1:52 GMT+02:00 Dawid Zamirski <dzamirski@datto.com>:
--- docs/news.xml | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/docs/news.xml b/docs/news.xml index 9c0dcfd..39631c3 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -37,6 +37,17 @@ <section title="New features"> </section> <section title="Improvements"> + <change> + <summary> + The libvirt Hyper-V driver now supports Hyper-V 2012 and newer.
Maybe drop "libvirt" from this line, as it seems redundant to me. -- Matthias Bolte http://photron.blogspot.com

On Tue, 2017-04-04 at 23:20 +0200, Matthias Bolte wrote:
2017-04-04 1:52 GMT+02:00 Dawid Zamirski <dzamirski@datto.com>:
--- docs/news.xml | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/docs/news.xml b/docs/news.xml index 9c0dcfd..39631c3 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -37,6 +37,17 @@ <section title="New features"> </section> <section title="Improvements"> + <change> + <summary> + The libvirt Hyper-V driver now supports Hyper-V 2012 and newer.
Maybe drop "libvirt" from this line, as it seems redundant to me.
All feedback for the series should be addressed in v4: https://www.redhat.com/archives/libvir-list/2017-April/msg00165.html
participants (2)
-
Dawid Zamirski
-
Matthias Bolte