hyperv: add support for Hyper-V 2012 and newer

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);
}
This commit is contained in:
Dawid Zamirski 2017-04-04 18:26:08 -04:00 committed by Matthias Bolte
parent 87f2bd3c0c
commit 3372f8fb5b
7 changed files with 795 additions and 262 deletions

View 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

View 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) ||

View File

@ -36,15 +36,56 @@
#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
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"

View 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;
};
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,
typedef struct _hypervWqlQuery hypervWqlQuery;
typedef hypervWqlQuery *hypervWqlQueryPtr;
struct _hypervWqlQuery {
virBufferPtr query;
hypervWmiClassInfoListPtr info;
};
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__ */

View File

@ -27,6 +27,15 @@
# 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/*"
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

View 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

View File

@ -24,140 +24,324 @@ 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.
separator = "/* " + ("* " * 37) + "*\n"
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.
"""
class Class:
def __init__(self, name, properties):
def __init__(self, name, versions = []):
self.name = name
self.properties = properties
self.versions = versions
self.common = None
def generate_header(self):
name_upper = self.name.upper()
def prepare(self):
"""Prepares the class for code generation
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 += "\n"
header += "\n"
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)
return header
# 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)
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)
return typedef
# 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_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 += "#define %s_RESOURCE_URI \\\n" % name_upper
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
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 += " \"SELECT * FROM %s \"\n" % self.name
header += "\n"
header += "struct _%s_Data {\n" % self.name
header += "extern hypervWmiClassInfoListPtr %s_WmiInfo;\n\n" % self.name
for property in self.properties:
header += property.generate_classes_header()
header += "};\n"
header += "\n"
header += "SER_DECLARE_TYPE(%s_Data);\n" % self.name
header += "\n"
header += "struct _%s {\n" % self.name
header += " XmlSerializerInfo *serializerInfo;\n"
header += " %s_Data *data;\n" % self.name
header += " %s *next;\n" % self.name
header += "};\n"
header += "\n"
header += "\n"
header += "\n"
header += self._declare_data_structs()
header += self._declare_hypervObject_struct()
return header
def generate_source(self):
name_upper = self.name.upper()
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"
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"
return source
def generate_classes_source(self):
name_upper = self.name.upper()
"""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"
source += "\n"
source += "SER_START_ITEMS(%s_Data)\n" % self.name
for property in self.properties:
source += property.generate_classes_source(self.name)
for cls in self.versions:
source += "SER_START_ITEMS(%s_Data)\n" % cls.name
source += "SER_END_ITEMS(%s_Data);\n" % self.name
source += "\n"
source += "\n"
source += "\n"
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):
"""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 _declare_data_structs(self):
"""Returns string C code declaring data structs.
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 = ""
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"
# 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
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 += " 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\n\n"
return header
def _define_WmiInfo_struct(self):
"""Return string for C code defining *_WmiInfo struct
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 += " }\n"
source += "};\n"
return source
def _align_property_members(self):
"""Identifies common properties in all class versions.
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.
"""
num_classes = len(self.versions)
common = {}
property_info = {}
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
class Property:
typemap = {"boolean" : "BOOL",
"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())