mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-28 16:45:26 +00:00
0967708b81
The function now does not return an error so we can drop it fully. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
1644 lines
50 KiB
C
1644 lines
50 KiB
C
/*
|
|
* hyperv_wmi.c: general WMI over WSMAN related functions and structures for
|
|
* managing Microsoft Hyper-V hosts
|
|
*
|
|
* Copyright (C) 2017 Datto Inc
|
|
* Copyright (C) 2014 Red Hat, Inc.
|
|
* Copyright (C) 2011 Matthias Bolte <matthias.bolte@googlemail.com>
|
|
* Copyright (C) 2009 Michael Sievers <msievers83@googlemail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <wsman-soap.h>
|
|
|
|
#include "internal.h"
|
|
#include "virerror.h"
|
|
#include "datatypes.h"
|
|
#include "viralloc.h"
|
|
#include "viruuid.h"
|
|
#include "virbuffer.h"
|
|
#include "hyperv_private.h"
|
|
#include "hyperv_wmi.h"
|
|
#include "virstring.h"
|
|
#include "openwsman.h"
|
|
#include "virlog.h"
|
|
|
|
#define WS_SERIALIZER_FREE_MEM_WORKS 0
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_HYPERV
|
|
|
|
#define HYPERV_JOB_TIMEOUT_MS 300000
|
|
|
|
VIR_LOG_INIT("hyperv.hyperv_wmi");
|
|
|
|
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,
|
|
const char *detail)
|
|
{
|
|
int lastError = wsmc_get_last_error(client);
|
|
int responseCode = wsmc_get_response_code(client);
|
|
WsManFault *fault;
|
|
|
|
if (lastError != WS_LASTERR_OK) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Transport error during %s: %s (%d)"),
|
|
detail, wsman_transport_get_last_error_string(lastError),
|
|
lastError);
|
|
return -1;
|
|
}
|
|
|
|
/* Check the HTTP response code and report an error if it's not 200 (OK),
|
|
* 400 (Bad Request) or 500 (Internal Server Error) */
|
|
if (responseCode != 200 && responseCode != 400 && responseCode != 500) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unexpected HTTP response during %s: %d"),
|
|
detail, responseCode);
|
|
return -1;
|
|
}
|
|
|
|
if (response == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Empty response during %s"), detail);
|
|
return -1;
|
|
}
|
|
|
|
if (wsmc_check_for_fault(response)) {
|
|
fault = wsmc_fault_new();
|
|
|
|
if (fault == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
wsmc_get_fault_data(response, fault);
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("SOAP fault during %s: code '%s', subcode '%s', "
|
|
"reason '%s', detail '%s'"),
|
|
detail, NULLSTR(fault->code), NULLSTR(fault->subcode),
|
|
NULLSTR(fault->reason), NULLSTR(fault->fault_detail));
|
|
|
|
wsmc_fault_destroy(fault);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Methods to work with method invocation parameters
|
|
*/
|
|
|
|
/*
|
|
* hypervCreateInvokeParamsList:
|
|
* @priv: hypervPrivate object associated with the connection.
|
|
* @method: The name of the method you are calling
|
|
* @selector: The selector for the object you are invoking the method on
|
|
* @obj: The WmiInfo of the object class you are invoking the method on.
|
|
*
|
|
* Create a new InvokeParamsList object for the method call.
|
|
*
|
|
* Returns a pointer to the newly instantiated object on success, which should
|
|
* be freed by hypervInvokeMethod. Otherwise returns NULL.
|
|
*/
|
|
hypervInvokeParamsListPtr
|
|
hypervCreateInvokeParamsList(hypervPrivate *priv, const char *method,
|
|
const char *selector, hypervWmiClassInfoListPtr obj)
|
|
{
|
|
hypervInvokeParamsListPtr params = NULL;
|
|
hypervWmiClassInfoPtr info = NULL;
|
|
|
|
if (hypervGetWmiClassInfo(priv, obj, &info) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC(params) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC_N(params->params,
|
|
HYPERV_DEFAULT_PARAM_COUNT) < 0) {
|
|
VIR_FREE(params);
|
|
goto cleanup;
|
|
}
|
|
|
|
params->method = method;
|
|
params->ns = info->rootUri;
|
|
params->resourceUri = info->resourceUri;
|
|
params->selector = selector;
|
|
params->nbParams = 0;
|
|
params->nbAvailParams = HYPERV_DEFAULT_PARAM_COUNT;
|
|
|
|
cleanup:
|
|
return params;
|
|
}
|
|
|
|
/*
|
|
* hypervFreeInvokeParams:
|
|
* @params: Params object to be freed
|
|
*
|
|
*/
|
|
void
|
|
hypervFreeInvokeParams(hypervInvokeParamsListPtr params)
|
|
{
|
|
hypervParamPtr p = NULL;
|
|
size_t i = 0;
|
|
|
|
if (params == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < params->nbParams; i++) {
|
|
p = &(params->params[i]);
|
|
|
|
switch (p->type) {
|
|
case HYPERV_SIMPLE_PARAM:
|
|
break;
|
|
case HYPERV_EPR_PARAM:
|
|
virBufferFreeAndReset(p->epr.query);
|
|
break;
|
|
case HYPERV_EMBEDDED_PARAM:
|
|
hypervFreeEmbeddedParam(p->embedded.table);
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Invalid parameter type passed to free"));
|
|
}
|
|
}
|
|
|
|
VIR_DISPOSE_N(params->params, params->nbAvailParams);
|
|
VIR_FREE(params);
|
|
}
|
|
|
|
static inline int
|
|
hypervCheckParams(hypervInvokeParamsListPtr params)
|
|
{
|
|
if (params->nbParams + 1 > params->nbAvailParams) {
|
|
if (VIR_EXPAND_N(params->params, params->nbAvailParams, 5) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* hypervAddSimpleParam:
|
|
* @params: Params object to add to
|
|
* @name: Name of the parameter
|
|
* @value: Value of the parameter
|
|
*
|
|
* Add a param of type HYPERV_SIMPLE_PARAM, which is essentially a serialized
|
|
* key/value pair.
|
|
*
|
|
* Returns -1 on failure, 0 on success.
|
|
*/
|
|
int
|
|
hypervAddSimpleParam(hypervInvokeParamsListPtr params, const char *name,
|
|
const char *value)
|
|
{
|
|
int result = -1;
|
|
hypervParamPtr p = NULL;
|
|
|
|
if (hypervCheckParams(params) < 0)
|
|
goto cleanup;
|
|
|
|
p = ¶ms->params[params->nbParams];
|
|
p->type = HYPERV_SIMPLE_PARAM;
|
|
|
|
p->simple.name = name;
|
|
p->simple.value = value;
|
|
|
|
params->nbParams++;
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* hypervAddEprParam:
|
|
* @params: Params object to add to
|
|
* @name: Parameter name
|
|
* @priv: hypervPrivate object associated with the connection
|
|
* @query: WQL filter
|
|
* @eprInfo: WmiInfo of the object being filtered
|
|
*
|
|
* Adds an EPR param to the params list. Returns -1 on failure, 0 on success.
|
|
*/
|
|
int
|
|
hypervAddEprParam(hypervInvokeParamsListPtr params, const char *name,
|
|
hypervPrivate *priv, virBufferPtr query,
|
|
hypervWmiClassInfoListPtr eprInfo)
|
|
{
|
|
hypervParamPtr p = NULL;
|
|
hypervWmiClassInfoPtr classInfo = NULL;
|
|
|
|
if (hypervGetWmiClassInfo(priv, eprInfo, &classInfo) < 0 ||
|
|
hypervCheckParams(params) < 0)
|
|
return -1;
|
|
|
|
p = ¶ms->params[params->nbParams];
|
|
p->type = HYPERV_EPR_PARAM;
|
|
p->epr.name = name;
|
|
p->epr.query = query;
|
|
p->epr.info = classInfo;
|
|
params->nbParams++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* hypervCreateEmbeddedParam:
|
|
* @priv: hypervPrivate object associated with the connection
|
|
* @info: WmiInfo of the object type to serialize
|
|
*
|
|
* Instantiates a virHashTable pre-filled with all the properties pre-added
|
|
* a key/value pairs set to NULL. The user then sets only those properties that
|
|
* they wish to serialize, and passes the table via hypervAddEmbeddedParam.
|
|
*
|
|
* Returns a pointer to the virHashTable on success, otherwise NULL.
|
|
*/
|
|
virHashTablePtr
|
|
hypervCreateEmbeddedParam(hypervPrivate *priv, hypervWmiClassInfoListPtr info)
|
|
{
|
|
size_t i;
|
|
int count = 0;
|
|
virHashTablePtr table = NULL;
|
|
XmlSerializerInfo *typeinfo = NULL;
|
|
XmlSerializerInfo *item = NULL;
|
|
hypervWmiClassInfoPtr classInfo = NULL;
|
|
|
|
/* Get the typeinfo out of the class info list */
|
|
if (hypervGetWmiClassInfo(priv, info, &classInfo) < 0)
|
|
goto error;
|
|
|
|
typeinfo = classInfo->serializerInfo;
|
|
|
|
/* loop through the items to find out how many fields there are */
|
|
for (i = 0; typeinfo[i].name != NULL; i++) {}
|
|
count = i;
|
|
|
|
table = virHashCreate(count, NULL);
|
|
if (table == NULL)
|
|
goto error;
|
|
|
|
for (i = 0; typeinfo[i].name != NULL; i++) {
|
|
item = &typeinfo[i];
|
|
|
|
if (virHashAddEntry(table, item->name, NULL) < 0)
|
|
goto error;
|
|
}
|
|
|
|
return table;
|
|
|
|
error:
|
|
virHashFree(table);
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
hypervSetEmbeddedProperty(virHashTablePtr table, const char *name, char *value)
|
|
{
|
|
return virHashUpdateEntry(table, name, value);
|
|
}
|
|
|
|
/*
|
|
* hypervAddEmbeddedParam:
|
|
* @params: Params list to add to
|
|
* @priv: hypervPrivate object associated with the connection
|
|
* @name: Name of the parameter
|
|
* @table: table of properties to add
|
|
* @info: WmiInfo of the object to serialize
|
|
*
|
|
* Add a virHashTable containing object properties as an embedded param to
|
|
* an invocation list. Returns -1 on failure, 0 on success.
|
|
*/
|
|
int
|
|
hypervAddEmbeddedParam(hypervInvokeParamsListPtr params, hypervPrivate *priv,
|
|
const char *name, virHashTablePtr table, hypervWmiClassInfoListPtr info)
|
|
{
|
|
hypervParamPtr p = NULL;
|
|
hypervWmiClassInfoPtr classInfo = NULL;
|
|
|
|
if (hypervCheckParams(params) < 0)
|
|
return -1;
|
|
|
|
/* Get the typeinfo out of the class info list */
|
|
if (hypervGetWmiClassInfo(priv, info, &classInfo) < 0)
|
|
return -1;
|
|
|
|
p = ¶ms->params[params->nbParams];
|
|
p->type = HYPERV_EMBEDDED_PARAM;
|
|
p->embedded.name = name;
|
|
p->embedded.table = table;
|
|
p->embedded.info = classInfo;
|
|
params->nbParams++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* hypervFreeEmbeddedParam:
|
|
* @param: Pointer to embedded param to free
|
|
*
|
|
* Free the embedded param hash table.
|
|
*/
|
|
void
|
|
hypervFreeEmbeddedParam(virHashTablePtr p)
|
|
{
|
|
virHashFree(p);
|
|
}
|
|
|
|
/*
|
|
* Serializing parameters to XML and invoking methods
|
|
*/
|
|
|
|
static int
|
|
hypervGetCimTypeInfo(hypervCimTypePtr typemap, const char *name,
|
|
hypervCimTypePtr *property)
|
|
{
|
|
size_t i = 0;
|
|
while (typemap[i].name[0] != '\0') {
|
|
if (STREQ(typemap[i].name, name)) {
|
|
*property = &typemap[i];
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervCreateInvokeXmlDoc(hypervInvokeParamsListPtr params, WsXmlDocH *docRoot)
|
|
{
|
|
int result = -1;
|
|
char *method = NULL;
|
|
WsXmlNodeH xmlNodeMethod = NULL;
|
|
|
|
if (virAsprintf(&method, "%s_INPUT", params->method) < 0)
|
|
goto cleanup;
|
|
|
|
*docRoot = ws_xml_create_doc(NULL, method);
|
|
if (*docRoot == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not instantiate XML document"));
|
|
goto cleanup;
|
|
}
|
|
|
|
xmlNodeMethod = xml_parser_get_root(*docRoot);
|
|
if (xmlNodeMethod == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get root node of XML document"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add resource URI as namespace */
|
|
ws_xml_set_ns(xmlNodeMethod, params->resourceUri, "p");
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (result < 0 && *docRoot != NULL) {
|
|
ws_xml_destroy_doc(*docRoot);
|
|
*docRoot = NULL;
|
|
}
|
|
VIR_FREE(method);
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
hypervSerializeSimpleParam(hypervParamPtr p, const char *resourceUri,
|
|
WsXmlNodeH *methodNode)
|
|
{
|
|
WsXmlNodeH xmlNodeParam = NULL;
|
|
|
|
xmlNodeParam = ws_xml_add_child(*methodNode, resourceUri,
|
|
p->simple.name, p->simple.value);
|
|
if (xmlNodeParam == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create simple param"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hypervSerializeEprParam(hypervParamPtr p, hypervPrivate *priv,
|
|
const char *resourceUri, WsXmlNodeH *methodNode)
|
|
{
|
|
int result = -1;
|
|
WsXmlNodeH xmlNodeParam = NULL,
|
|
xmlNodeTemp = NULL,
|
|
xmlNodeAddr = NULL,
|
|
xmlNodeRef = NULL;
|
|
WsXmlDocH xmlDocResponse = NULL;
|
|
WsXmlNsH ns = NULL;
|
|
client_opt_t *options = NULL;
|
|
filter_t *filter = NULL;
|
|
char *enumContext = NULL;
|
|
char *query_string = NULL;
|
|
|
|
/* init and set up options */
|
|
options = wsmc_options_init();
|
|
if (!options) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not init options"));
|
|
goto cleanup;
|
|
}
|
|
wsmc_set_action_option(options, FLAG_ENUMERATION_ENUM_EPR);
|
|
|
|
query_string = virBufferContentAndReset(p->epr.query);
|
|
|
|
filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, query_string);
|
|
if (!filter) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not create WQL filter"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* enumerate based on the filter from this query */
|
|
xmlDocResponse = wsmc_action_enumerate(priv->client, p->epr.info->rootUri,
|
|
options, filter);
|
|
if (hypervVerifyResponse(priv->client, xmlDocResponse, "enumeration") < 0)
|
|
goto cleanup;
|
|
|
|
/* Get context */
|
|
enumContext = wsmc_get_enum_context(xmlDocResponse);
|
|
ws_xml_destroy_doc(xmlDocResponse);
|
|
|
|
/* Pull using filter and enum context */
|
|
xmlDocResponse = wsmc_action_pull(priv->client, resourceUri, options,
|
|
filter, enumContext);
|
|
|
|
if (hypervVerifyResponse(priv->client, xmlDocResponse, "pull") < 0)
|
|
goto cleanup;
|
|
|
|
/* drill down and extract EPR node children */
|
|
if (!(xmlNodeTemp = ws_xml_get_soap_body(xmlDocResponse))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get SOAP body"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeTemp = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ENUMERATION,
|
|
WSENUM_PULL_RESP))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get response"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeTemp = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ENUMERATION, WSENUM_ITEMS))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get response items"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeTemp = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ADDRESSING, WSA_EPR))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get EPR items"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeAddr = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ADDRESSING,
|
|
WSA_ADDRESS))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get EPR address"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeRef = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ADDRESSING,
|
|
WSA_REFERENCE_PARAMETERS))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not lookup EPR item reference parameters"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* now build a new xml doc with the EPR node children */
|
|
if (!(xmlNodeParam = ws_xml_add_child(*methodNode, resourceUri,
|
|
p->epr.name, NULL))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add child node to methodNode"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(ns = ws_xml_ns_add(xmlNodeParam,
|
|
"http://schemas.xmlsoap.org/ws/2004/08/addressing", "a"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not set namespace address for xmlNodeParam"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(ns = ws_xml_ns_add(xmlNodeParam,
|
|
"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd", "w"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not set wsman namespace address for xmlNodeParam"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ws_xml_duplicate_tree(xmlNodeParam, xmlNodeAddr);
|
|
ws_xml_duplicate_tree(xmlNodeParam, xmlNodeRef);
|
|
|
|
/* we did it! */
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (options != NULL)
|
|
wsmc_options_destroy(options);
|
|
if (filter != NULL)
|
|
filter_destroy(filter);
|
|
ws_xml_destroy_doc(xmlDocResponse);
|
|
VIR_FREE(enumContext);
|
|
VIR_FREE(query_string);
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
hypervSerializeEmbeddedParam(hypervParamPtr p, const char *resourceUri,
|
|
WsXmlNodeH *methodNode)
|
|
{
|
|
int result = -1;
|
|
WsXmlNodeH xmlNodeInstance = NULL,
|
|
xmlNodeProperty = NULL,
|
|
xmlNodeParam = NULL,
|
|
xmlNodeArray = NULL;
|
|
WsXmlDocH xmlDocTemp = NULL,
|
|
xmlDocCdata = NULL;
|
|
char *cdataContent = NULL;
|
|
xmlNodePtr xmlNodeCdata = NULL;
|
|
hypervWmiClassInfoPtr classInfo = p->embedded.info;
|
|
virHashKeyValuePairPtr items = NULL;
|
|
hypervCimTypePtr property = NULL;
|
|
ssize_t numKeys = -1;
|
|
int len = 0, i = 0;
|
|
|
|
if (!(xmlNodeParam = ws_xml_add_child(*methodNode, resourceUri, p->embedded.name,
|
|
NULL))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not add child node %s"),
|
|
p->embedded.name);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* create the temp xml doc */
|
|
|
|
/* start with the INSTANCE node */
|
|
if (!(xmlDocTemp = ws_xml_create_doc(NULL, "INSTANCE"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create temporary xml doc"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeInstance = xml_parser_get_root(xmlDocTemp))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get temp xml doc root"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add CLASSNAME node to INSTANCE node */
|
|
if (!(ws_xml_add_node_attr(xmlNodeInstance, NULL, "CLASSNAME",
|
|
classInfo->name))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add attribute to node"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* retrieve parameters out of hash table */
|
|
numKeys = virHashSize(p->embedded.table);
|
|
items = virHashGetItems(p->embedded.table, NULL);
|
|
if (!items) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not read embedded param hash table"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Add the parameters */
|
|
for (i = 0; i < numKeys; i++) {
|
|
const char *name = items[i].key;
|
|
const char *value = items[i].value;
|
|
|
|
if (value != NULL) {
|
|
if (hypervGetCimTypeInfo(classInfo->propertyInfo, name,
|
|
&property) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not read type information"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(xmlNodeProperty = ws_xml_add_child(xmlNodeInstance, NULL,
|
|
property->isArray ? "PROPERTY.ARRAY" : "PROPERTY",
|
|
NULL))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add child to XML node"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(ws_xml_add_node_attr(xmlNodeProperty, NULL, "NAME", name))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add attribute to XML node"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(ws_xml_add_node_attr(xmlNodeProperty, NULL, "TYPE", property->type))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add attribute to XML node"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* If this attribute is an array, add VALUE.ARRAY node */
|
|
if (property->isArray) {
|
|
if (!(xmlNodeArray = ws_xml_add_child(xmlNodeProperty, NULL,
|
|
"VALUE.ARRAY", NULL))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add child to XML node"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* add the child */
|
|
if (!(ws_xml_add_child(property->isArray ? xmlNodeArray : xmlNodeProperty,
|
|
NULL, "VALUE", value))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add child to XML node"));
|
|
goto cleanup;
|
|
}
|
|
|
|
xmlNodeArray = NULL;
|
|
xmlNodeProperty = NULL;
|
|
}
|
|
}
|
|
|
|
/* create CDATA node */
|
|
ws_xml_dump_memory_node_tree(xmlNodeInstance, &cdataContent, &len);
|
|
|
|
if (!(xmlNodeCdata = xmlNewCDataBlock((xmlDocPtr) xmlDocCdata,
|
|
(xmlChar *)cdataContent, len))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create CDATA element"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Add CDATA node to the doc root
|
|
*
|
|
* FIXME: there is no openwsman wrapper for xmlNewCDataBlock, so instead
|
|
* silence clang alignment warnings by casting to a void pointer first
|
|
*/
|
|
if (!(xmlAddChild((xmlNodePtr)(void *)xmlNodeParam, xmlNodeCdata))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add CDATA to doc root"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* we did it! */
|
|
result = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(items);
|
|
ws_xml_destroy_doc(xmlDocCdata);
|
|
ws_xml_destroy_doc(xmlDocTemp);
|
|
ws_xml_free_memory(cdataContent);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* hypervInvokeMethod:
|
|
* @priv: hypervPrivate object associated with the connection
|
|
* @params: object containing the all necessary information for method
|
|
* invocation
|
|
* @res: Optional out parameter to contain the response XML.
|
|
*
|
|
* Performs an invocation described by @params, and optionally returns the
|
|
* XML containing the result. Returns -1 on failure, 0 on success.
|
|
*/
|
|
int
|
|
hypervInvokeMethod(hypervPrivate *priv, hypervInvokeParamsListPtr params,
|
|
WsXmlDocH *res)
|
|
{
|
|
int result = -1;
|
|
size_t i = 0;
|
|
int returnCode;
|
|
WsXmlDocH paramsDocRoot = NULL;
|
|
client_opt_t *options = NULL;
|
|
WsXmlDocH response = NULL;
|
|
WsXmlNodeH methodNode = NULL;
|
|
char *returnValue_xpath = NULL;
|
|
char *jobcode_instance_xpath = NULL;
|
|
char *returnValue = NULL;
|
|
char *instanceID = NULL;
|
|
bool completed = false;
|
|
virBuffer query = VIR_BUFFER_INITIALIZER;
|
|
Msvm_ConcreteJob *job = NULL;
|
|
int jobState = -1;
|
|
hypervParamPtr p = NULL;
|
|
int timeout = HYPERV_JOB_TIMEOUT_MS;
|
|
|
|
if (hypervCreateInvokeXmlDoc(params, ¶msDocRoot) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create XML document"));
|
|
goto cleanup;
|
|
}
|
|
|
|
methodNode = xml_parser_get_root(paramsDocRoot);
|
|
if (!methodNode) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get root of XML document"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Serialize parameters */
|
|
for (i = 0; i < params->nbParams; i++) {
|
|
p = &(params->params[i]);
|
|
|
|
switch (p->type) {
|
|
case HYPERV_SIMPLE_PARAM:
|
|
if (hypervSerializeSimpleParam(p, params->resourceUri,
|
|
&methodNode) < 0)
|
|
goto cleanup;
|
|
break;
|
|
case HYPERV_EPR_PARAM:
|
|
if (hypervSerializeEprParam(p, priv, params->resourceUri,
|
|
&methodNode) < 0)
|
|
goto cleanup;
|
|
break;
|
|
case HYPERV_EMBEDDED_PARAM:
|
|
if (hypervSerializeEmbeddedParam(p, params->resourceUri,
|
|
&methodNode) < 0)
|
|
goto cleanup;
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Unknown parameter type"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Invoke the method and get the response */
|
|
|
|
options = wsmc_options_init();
|
|
if (!options) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not init options"));
|
|
goto cleanup;
|
|
}
|
|
wsmc_add_selectors_from_str(options, params->selector);
|
|
|
|
/* do the invoke */
|
|
response = wsmc_action_invoke(priv->client, params->resourceUri, options,
|
|
params->method, paramsDocRoot);
|
|
|
|
/* check return code of invocation */
|
|
if (virAsprintf(&returnValue_xpath, "/s:Envelope/s:Body/p:%s_OUTPUT/p:ReturnValue",
|
|
params->method) < 0)
|
|
goto cleanup;
|
|
|
|
returnValue = ws_xml_get_xpath_value(response, returnValue_xpath);
|
|
if (!returnValue) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get return value for %s invocation"),
|
|
params->method);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virStrToLong_i(returnValue, NULL, 10, &returnCode) < 0)
|
|
goto cleanup;
|
|
|
|
if (returnCode == CIM_RETURNCODE_TRANSITION_STARTED) {
|
|
if (virAsprintf(&jobcode_instance_xpath,
|
|
"/s:Envelope/s:Body/p:%s_OUTPUT/p:Job/a:ReferenceParameters/"
|
|
"w:SelectorSet/w:Selector[@Name='InstanceID']",
|
|
params->method) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
instanceID = ws_xml_get_xpath_value(response, jobcode_instance_xpath);
|
|
if (!instanceID) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get instance ID for %s invocation"),
|
|
params->method);
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Poll Hyper-V about the job until either the job completes or fails,
|
|
* or 5 minutes have elapsed.
|
|
*
|
|
* Windows has its own timeout on running WMI method calls (it calls
|
|
* these "jobs"), by default set to 1 minute. The administrator can
|
|
* change this to whatever they want, however, so we can't rely on it.
|
|
*
|
|
* Therefore, to avoid waiting in this loop for a very long-running job
|
|
* to complete, we instead bail after 5 minutes no matter what. NOTE that
|
|
* this does not mean that the remote job has terminated on the Windows
|
|
* side! That is up to Windows to control, we don't do anything about it.
|
|
*/
|
|
while (!completed && timeout >= 0) {
|
|
virBufferAddLit(&query, MSVM_CONCRETEJOB_WQL_SELECT);
|
|
virBufferEscapeSQL(&query, "where InstanceID = \"%s\"", instanceID);
|
|
|
|
if (hypervGetMsvmConcreteJobList(priv, &query, &job) < 0
|
|
|| job == NULL)
|
|
goto cleanup;
|
|
|
|
jobState = job->data.common->JobState;
|
|
switch (jobState) {
|
|
case MSVM_CONCRETEJOB_JOBSTATE_NEW:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_STARTING:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_RUNNING:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_SHUTTING_DOWN:
|
|
hypervFreeObject(priv, (hypervObject *)job);
|
|
job = NULL;
|
|
g_usleep(100 * 1000); /* sleep 100 ms */
|
|
timeout -= 100;
|
|
continue;
|
|
case MSVM_CONCRETEJOB_JOBSTATE_COMPLETED:
|
|
completed = true;
|
|
break;
|
|
case MSVM_CONCRETEJOB_JOBSTATE_TERMINATED:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_KILLED:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_EXCEPTION:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_SERVICE:
|
|
goto cleanup;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Unknown invocation state"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (!completed && timeout < 0) {
|
|
virReportError(VIR_ERR_OPERATION_TIMEOUT,
|
|
_("Timeout waiting for %s invocation"), params->method);
|
|
goto cleanup;
|
|
}
|
|
} else if (returnCode != CIM_RETURNCODE_COMPLETED_WITH_NO_ERROR) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("Invocation of %s returned an error: %s (%d)"),
|
|
params->method, hypervReturnCodeToString(returnCode),
|
|
returnCode);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (res)
|
|
*res = response;
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (options)
|
|
wsmc_options_destroy(options);
|
|
if (response && (!res))
|
|
ws_xml_destroy_doc(response);
|
|
if (paramsDocRoot)
|
|
ws_xml_destroy_doc(paramsDocRoot);
|
|
VIR_FREE(returnValue_xpath);
|
|
VIR_FREE(jobcode_instance_xpath);
|
|
VIR_FREE(returnValue);
|
|
VIR_FREE(instanceID);
|
|
virBufferFreeAndReset(&query);
|
|
hypervFreeObject(priv, (hypervObject *)job);
|
|
hypervFreeInvokeParams(params);
|
|
return result;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Object
|
|
*/
|
|
|
|
/* This function guarantees that wqlQuery->query is reset, even on failure */
|
|
int
|
|
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;
|
|
hypervObject *head = NULL;
|
|
hypervObject *tail = NULL;
|
|
WsXmlNodeH node = NULL;
|
|
XML_TYPE_PTR data = NULL;
|
|
hypervObject *object;
|
|
|
|
query_string = virBufferContentAndReset(wqlQuery->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)
|
|
goto cleanup;
|
|
|
|
serializerContext = wsmc_get_serialization_context(priv->client);
|
|
|
|
options = wsmc_options_init();
|
|
|
|
if (options == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not initialize options"));
|
|
goto cleanup;
|
|
}
|
|
|
|
filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, query_string);
|
|
|
|
if (filter == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create filter"));
|
|
goto cleanup;
|
|
}
|
|
|
|
response = wsmc_action_enumerate(priv->client, wmiInfo->rootUri, options,
|
|
filter);
|
|
|
|
if (hypervVerifyResponse(priv->client, response, "enumeration") < 0)
|
|
goto cleanup;
|
|
|
|
enumContext = wsmc_get_enum_context(response);
|
|
|
|
ws_xml_destroy_doc(response);
|
|
response = NULL;
|
|
|
|
while (enumContext != NULL && *enumContext != '\0') {
|
|
response = wsmc_action_pull(priv->client, wmiInfo->resourceUri, options,
|
|
filter, enumContext);
|
|
|
|
if (hypervVerifyResponse(priv->client, response, "pull") < 0)
|
|
goto cleanup;
|
|
|
|
node = ws_xml_get_soap_body(response);
|
|
|
|
if (node == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not lookup SOAP body"));
|
|
goto cleanup;
|
|
}
|
|
|
|
node = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_PULL_RESP);
|
|
|
|
if (node == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not lookup pull response"));
|
|
goto cleanup;
|
|
}
|
|
|
|
node = ws_xml_get_child(node, 0, XML_NS_ENUMERATION, WSENUM_ITEMS);
|
|
|
|
if (node == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not lookup pull response items"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ws_xml_get_child(node, 0, wmiInfo->resourceUri,
|
|
wmiInfo->name) == NULL)
|
|
break;
|
|
|
|
data = ws_deserialize(serializerContext, node, wmiInfo->serializerInfo,
|
|
wmiInfo->name, wmiInfo->resourceUri, NULL, 0, 0);
|
|
|
|
if (data == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not deserialize pull response item"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VIR_ALLOC(object) < 0)
|
|
goto cleanup;
|
|
|
|
object->info = wmiInfo;
|
|
object->data.common = data;
|
|
|
|
data = NULL;
|
|
|
|
if (head == NULL) {
|
|
head = object;
|
|
} else {
|
|
tail->next = object;
|
|
}
|
|
|
|
tail = object;
|
|
|
|
VIR_FREE(enumContext);
|
|
enumContext = wsmc_get_enum_context(response);
|
|
|
|
ws_xml_destroy_doc(response);
|
|
response = NULL;
|
|
}
|
|
|
|
*list = head;
|
|
head = NULL;
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (options != NULL)
|
|
wsmc_options_destroy(options);
|
|
|
|
if (filter != NULL)
|
|
filter_destroy(filter);
|
|
|
|
if (data != NULL) {
|
|
#if WS_SERIALIZER_FREE_MEM_WORKS
|
|
/* 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,
|
|
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);
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
hypervFreeObject(hypervPrivate *priv G_GNUC_UNUSED, hypervObject *object)
|
|
{
|
|
hypervObject *next;
|
|
#if WS_SERIALIZER_FREE_MEM_WORKS
|
|
WsSerializerContextH serializerContext;
|
|
#endif
|
|
|
|
if (object == NULL)
|
|
return;
|
|
|
|
#if WS_SERIALIZER_FREE_MEM_WORKS
|
|
serializerContext = wsmc_get_serialization_context(priv->client);
|
|
#endif
|
|
|
|
while (object != NULL) {
|
|
next = object->next;
|
|
|
|
#if WS_SERIALIZER_FREE_MEM_WORKS
|
|
/* FIXME: ws_serializer_free_mem is broken in openwsman <= 2.2.6,
|
|
* but this is not that critical, because openwsman keeps
|
|
* track of all allocations of the deserializer and frees
|
|
* 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.common,
|
|
object->info->serializerInfo) < 0) {
|
|
VIR_ERROR(_("Could not free deserialized data"));
|
|
}
|
|
#endif
|
|
|
|
VIR_FREE(object);
|
|
|
|
object = next;
|
|
}
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* CIM/Msvm_ReturnCode
|
|
*/
|
|
|
|
const char *
|
|
hypervReturnCodeToString(int returnCode)
|
|
{
|
|
switch (returnCode) {
|
|
case CIM_RETURNCODE_COMPLETED_WITH_NO_ERROR:
|
|
return _("Completed with no error");
|
|
|
|
case CIM_RETURNCODE_NOT_SUPPORTED:
|
|
return _("Not supported");
|
|
|
|
case CIM_RETURNCODE_UNKNOWN_ERROR:
|
|
return _("Unknown error");
|
|
|
|
case CIM_RETURNCODE_CANNOT_COMPLETE_WITHIN_TIMEOUT_PERIOD:
|
|
return _("Cannot complete within timeout period");
|
|
|
|
case CIM_RETURNCODE_FAILED:
|
|
return _("Failed");
|
|
|
|
case CIM_RETURNCODE_INVALID_PARAMETER:
|
|
return _("Invalid parameter");
|
|
|
|
case CIM_RETURNCODE_IN_USE:
|
|
return _("In use");
|
|
|
|
case CIM_RETURNCODE_TRANSITION_STARTED:
|
|
return _("Transition started");
|
|
|
|
case CIM_RETURNCODE_INVALID_STATE_TRANSITION:
|
|
return _("Invalid state transition");
|
|
|
|
case CIM_RETURNCODE_TIMEOUT_PARAMETER_NOT_SUPPORTED:
|
|
return _("Timeout parameter not supported");
|
|
|
|
case CIM_RETURNCODE_BUSY:
|
|
return _("Busy");
|
|
|
|
case MSVM_RETURNCODE_FAILED:
|
|
return _("Failed");
|
|
|
|
case MSVM_RETURNCODE_ACCESS_DENIED:
|
|
return _("Access denied");
|
|
|
|
case MSVM_RETURNCODE_NOT_SUPPORTED:
|
|
return _("Not supported");
|
|
|
|
case MSVM_RETURNCODE_STATUS_IS_UNKNOWN:
|
|
return _("Status is unknown");
|
|
|
|
case MSVM_RETURNCODE_TIMEOUT:
|
|
return _("Timeout");
|
|
|
|
case MSVM_RETURNCODE_INVALID_PARAMETER:
|
|
return _("Invalid parameter");
|
|
|
|
case MSVM_RETURNCODE_SYSTEM_IS_IN_USE:
|
|
return _("System is in use");
|
|
|
|
case MSVM_RETURNCODE_INVALID_STATE_FOR_THIS_OPERATION:
|
|
return _("Invalid state for this operation");
|
|
|
|
case MSVM_RETURNCODE_INCORRECT_DATA_TYPE:
|
|
return _("Incorrect data type");
|
|
|
|
case MSVM_RETURNCODE_SYSTEM_IS_NOT_AVAILABLE:
|
|
return _("System is not available");
|
|
|
|
case MSVM_RETURNCODE_OUT_OF_MEMORY:
|
|
return _("Out of memory");
|
|
|
|
default:
|
|
return _("Unknown return code");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* 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);
|
|
}
|
|
|
|
int hypervGetMsvmKeyboardList(hypervPrivate *priv, virBufferPtr query,
|
|
Msvm_Keyboard **list)
|
|
{
|
|
return hypervGetWmiClassList(priv, Msvm_Keyboard_WmiInfo, query,
|
|
(hypervObject **)list);
|
|
}
|
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Msvm_ComputerSystem
|
|
*/
|
|
|
|
int
|
|
hypervInvokeMsvmComputerSystemRequestStateChange(virDomainPtr domain,
|
|
int requestedState)
|
|
{
|
|
int result = -1;
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
WsXmlDocH response = NULL;
|
|
client_opt_t *options = NULL;
|
|
char *selector = NULL;
|
|
char *properties = NULL;
|
|
char *returnValue = NULL;
|
|
int returnCode;
|
|
char *instanceID = NULL;
|
|
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);
|
|
|
|
if (virAsprintf(&selector, "Name=%s&CreationClassName=Msvm_ComputerSystem",
|
|
uuid_string) < 0 ||
|
|
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) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not initialize options"));
|
|
goto cleanup;
|
|
}
|
|
|
|
wsmc_add_selectors_from_str(options, selector);
|
|
wsmc_add_prop_from_str(options, properties);
|
|
|
|
/* Invoke method */
|
|
response = wsmc_action_invoke(priv->client, resourceUri,
|
|
options, "RequestStateChange", NULL);
|
|
|
|
if (hypervVerifyResponse(priv->client, response, "invocation") < 0)
|
|
goto cleanup;
|
|
|
|
/* Check return value */
|
|
returnValue = ws_xml_get_xpath_value(response, (char *)"/s:Envelope/s:Body/p:RequestStateChange_OUTPUT/p:ReturnValue");
|
|
|
|
if (returnValue == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not lookup %s for %s invocation"),
|
|
"ReturnValue", "RequestStateChange");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virStrToLong_i(returnValue, NULL, 10, &returnCode) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse return code from '%s'"), returnValue);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (returnCode == CIM_RETURNCODE_TRANSITION_STARTED) {
|
|
/* Get concrete job object */
|
|
instanceID = ws_xml_get_xpath_value(response, (char *)"/s:Envelope/s:Body/p:RequestStateChange_OUTPUT/p:Job/a:ReferenceParameters/w:SelectorSet/w:Selector[@Name='InstanceID']");
|
|
|
|
if (instanceID == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not lookup %s for %s invocation"),
|
|
"InstanceID", "RequestStateChange");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* FIXME: Poll every 100ms until the job completes or fails. There
|
|
* seems to be no other way than polling. */
|
|
while (!completed) {
|
|
virBufferAddLit(&query, MSVM_CONCRETEJOB_WQL_SELECT);
|
|
virBufferAsprintf(&query, "where InstanceID = \"%s\"", instanceID);
|
|
|
|
if (hypervGetMsvmConcreteJobList(priv, &query, &concreteJob) < 0)
|
|
goto cleanup;
|
|
|
|
if (concreteJob == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not lookup %s for %s invocation"),
|
|
"Msvm_ConcreteJob", "RequestStateChange");
|
|
goto cleanup;
|
|
}
|
|
|
|
switch (concreteJob->data.common->JobState) {
|
|
case MSVM_CONCRETEJOB_JOBSTATE_NEW:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_STARTING:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_RUNNING:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_SHUTTING_DOWN:
|
|
hypervFreeObject(priv, (hypervObject *)concreteJob);
|
|
concreteJob = NULL;
|
|
|
|
g_usleep(100 * 1000);
|
|
continue;
|
|
|
|
case MSVM_CONCRETEJOB_JOBSTATE_COMPLETED:
|
|
completed = true;
|
|
break;
|
|
|
|
case MSVM_CONCRETEJOB_JOBSTATE_TERMINATED:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_KILLED:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_EXCEPTION:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_SERVICE:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Concrete job for %s invocation is in error state"),
|
|
"RequestStateChange");
|
|
goto cleanup;
|
|
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Concrete job for %s invocation is in unknown state"),
|
|
"RequestStateChange");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
} else if (returnCode != CIM_RETURNCODE_COMPLETED_WITH_NO_ERROR) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invocation of %s returned an error: %s (%d)"),
|
|
"RequestStateChange", hypervReturnCodeToString(returnCode),
|
|
returnCode);
|
|
goto cleanup;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
cleanup:
|
|
if (options != NULL)
|
|
wsmc_options_destroy(options);
|
|
|
|
ws_xml_destroy_doc(response);
|
|
VIR_FREE(selector);
|
|
VIR_FREE(properties);
|
|
VIR_FREE(returnValue);
|
|
VIR_FREE(instanceID);
|
|
hypervFreeObject(priv, (hypervObject *)concreteJob);
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
hypervMsvmComputerSystemEnabledStateToDomainState
|
|
(Msvm_ComputerSystem *computerSystem)
|
|
{
|
|
switch (computerSystem->data.common->EnabledState) {
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_UNKNOWN:
|
|
return VIR_DOMAIN_NOSTATE;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED:
|
|
return VIR_DOMAIN_RUNNING;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_DISABLED:
|
|
return VIR_DOMAIN_SHUTOFF;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_PAUSED:
|
|
return VIR_DOMAIN_PAUSED;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED: /* managed save */
|
|
return VIR_DOMAIN_SHUTOFF;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_STARTING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SNAPSHOTTING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SAVING:
|
|
return VIR_DOMAIN_RUNNING;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_STOPPING:
|
|
return VIR_DOMAIN_SHUTDOWN;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_PAUSING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_RESUMING:
|
|
return VIR_DOMAIN_RUNNING;
|
|
|
|
default:
|
|
return VIR_DOMAIN_NOSTATE;
|
|
}
|
|
}
|
|
|
|
bool
|
|
hypervIsMsvmComputerSystemActive(Msvm_ComputerSystem *computerSystem,
|
|
bool *in_transition)
|
|
{
|
|
if (in_transition != NULL)
|
|
*in_transition = false;
|
|
|
|
switch (computerSystem->data.common->EnabledState) {
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_UNKNOWN:
|
|
return false;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_ENABLED:
|
|
return true;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_DISABLED:
|
|
return false;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_PAUSED:
|
|
return true;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SUSPENDED: /* managed save */
|
|
return false;
|
|
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_STARTING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SNAPSHOTTING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_SAVING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_STOPPING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_PAUSING:
|
|
case MSVM_COMPUTERSYSTEM_ENABLEDSTATE_RESUMING:
|
|
if (in_transition != NULL)
|
|
*in_transition = true;
|
|
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int
|
|
hypervMsvmComputerSystemToDomain(virConnectPtr conn,
|
|
Msvm_ComputerSystem *computerSystem,
|
|
virDomainPtr *domain)
|
|
{
|
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
|
int id = -1;
|
|
|
|
if (domain == NULL || *domain != NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
|
return -1;
|
|
}
|
|
|
|
if (virUUIDParse(computerSystem->data.common->Name, uuid) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse UUID from string '%s'"),
|
|
computerSystem->data.common->Name);
|
|
return -1;
|
|
}
|
|
|
|
if (hypervIsMsvmComputerSystemActive(computerSystem, NULL))
|
|
id = computerSystem->data.common->ProcessID;
|
|
|
|
*domain = virGetDomain(conn, computerSystem->data.common->ElementName, uuid, id);
|
|
|
|
return *domain ? 0 : -1;
|
|
}
|
|
|
|
int
|
|
hypervMsvmComputerSystemFromDomain(virDomainPtr domain,
|
|
Msvm_ComputerSystem **computerSystem)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
virBuffer query = VIR_BUFFER_INITIALIZER;
|
|
|
|
if (computerSystem == NULL || *computerSystem != NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
|
return -1;
|
|
}
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_SELECT);
|
|
virBufferAddLit(&query, "where ");
|
|
virBufferAddLit(&query, MSVM_COMPUTERSYSTEM_WQL_VIRTUAL);
|
|
virBufferAsprintf(&query, "and Name = \"%s\"", uuid_string);
|
|
|
|
if (hypervGetMsvmComputerSystemList(priv, &query, computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (*computerSystem == NULL) {
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
_("No domain with UUID %s"), uuid_string);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Msvm_VirtualSystemSettingData
|
|
*/
|
|
|
|
int
|
|
hypervGetMsvmVirtualSystemSettingDataFromUUID(hypervPrivate *priv,
|
|
const char *uuid_string, Msvm_VirtualSystemSettingData **list)
|
|
{
|
|
virBuffer query = VIR_BUFFER_INITIALIZER;
|
|
|
|
virBufferAsprintf(&query,
|
|
"associators of "
|
|
"{Msvm_ComputerSystem.CreationClassName=\"Msvm_ComputerSystem\","
|
|
"Name=\"%s\"} "
|
|
"where AssocClass = Msvm_SettingsDefineState "
|
|
"ResultClass = Msvm_VirtualSystemSettingData",
|
|
uuid_string);
|
|
|
|
if (hypervGetWmiClassList(priv, Msvm_VirtualSystemSettingData_WmiInfo, &query,
|
|
(hypervObject **)list) < 0 || *list == NULL)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Msvm_MemorySettingData
|
|
*/
|
|
|
|
int
|
|
hypervGetMsvmMemorySettingDataFromVSSD(hypervPrivate *priv,
|
|
const char *vssd_instanceid, Msvm_MemorySettingData **list)
|
|
{
|
|
virBuffer query = VIR_BUFFER_INITIALIZER;
|
|
|
|
virBufferAsprintf(&query,
|
|
"associators of "
|
|
"{Msvm_VirtualSystemSettingData.InstanceID=\"%s\"} "
|
|
"where AssocClass = Msvm_VirtualSystemSettingDataComponent "
|
|
"ResultClass = Msvm_MemorySettingData",
|
|
vssd_instanceid);
|
|
|
|
if (hypervGetWmiClassList(priv, Msvm_MemorySettingData_WmiInfo, &query,
|
|
(hypervObject **)list) < 0 || *list == NULL)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|