mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-07 04:07:17 +00:00
The struct doesn't contain any secrets to clear before freeing and even if it did VIR_DISPOSE_N wouldn't help as the struct contains only pointers thus the actual memory pointing to isn't sanitized. Just free the params array pointer and then the struct itself. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
1608 lines
49 KiB
C
1608 lines
49 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 <wsman-xml.h>
|
|
#include <wsman-xml-binding.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 "virlog.h"
|
|
#include "virxml.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_HYPERV
|
|
|
|
#define HYPERV_JOB_TIMEOUT_MS 300000
|
|
|
|
VIR_LOG_INIT("hyperv.hyperv_wmi");
|
|
|
|
int
|
|
hypervGetWmiClassList(hypervPrivate *priv, hypervWmiClassInfoPtr 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:
|
|
* @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(const char *method,
|
|
const char *selector,
|
|
hypervWmiClassInfoPtr info)
|
|
{
|
|
hypervInvokeParamsListPtr params = NULL;
|
|
|
|
params = g_new0(hypervInvokeParamsList, 1);
|
|
|
|
params->params = g_new0(hypervParam, HYPERV_DEFAULT_PARAM_COUNT);
|
|
|
|
params->method = method;
|
|
params->ns = info->rootUri;
|
|
params->resourceUri = info->resourceUri;
|
|
params->selector = selector;
|
|
params->nbParams = 0;
|
|
params->nbAvailParams = HYPERV_DEFAULT_PARAM_COUNT;
|
|
|
|
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"));
|
|
}
|
|
}
|
|
|
|
g_free(params->params);
|
|
g_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)
|
|
{
|
|
hypervParamPtr p = NULL;
|
|
|
|
if (hypervCheckParams(params) < 0)
|
|
return -1;
|
|
|
|
p = ¶ms->params[params->nbParams];
|
|
p->type = HYPERV_SIMPLE_PARAM;
|
|
|
|
p->simple.name = name;
|
|
p->simple.value = value;
|
|
|
|
params->nbParams++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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,
|
|
virBufferPtr query,
|
|
hypervWmiClassInfoPtr classInfo)
|
|
{
|
|
hypervParamPtr p = NULL;
|
|
|
|
if (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:
|
|
* @info: WmiInfo of the object type to serialize
|
|
*
|
|
* Instantiates a GHashTable 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 GHashTable on success, otherwise NULL.
|
|
*/
|
|
GHashTable *
|
|
hypervCreateEmbeddedParam(hypervWmiClassInfoPtr classInfo)
|
|
{
|
|
size_t i;
|
|
size_t count;
|
|
g_autoptr(GHashTable) table = NULL;
|
|
XmlSerializerInfo *typeinfo = NULL;
|
|
|
|
typeinfo = classInfo->serializerInfo;
|
|
|
|
/* loop through the items to find out how many fields there are */
|
|
for (count = 0; typeinfo[count].name != NULL; count++)
|
|
;
|
|
|
|
table = virHashNew(NULL);
|
|
if (table == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; typeinfo[i].name != NULL; i++) {
|
|
XmlSerializerInfo *item = &typeinfo[i];
|
|
|
|
if (virHashAddEntry(table, item->name, NULL) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
return g_steal_pointer(&table);
|
|
}
|
|
|
|
|
|
/**
|
|
* hypervSetEmbeddedProperty:
|
|
* @table: hash table allocated earlier by hypervCreateEmbeddedParam()
|
|
* @name: name of the property
|
|
* @value: value of the property
|
|
*
|
|
* For given table of properties, set property of @name to @value.
|
|
* Please note, that the hash table does NOT become owner of the @value and
|
|
* thus caller must ensure the pointer validity.
|
|
*
|
|
* Returns: 0 on success,
|
|
* -1 otherwise.
|
|
*/
|
|
int
|
|
hypervSetEmbeddedProperty(GHashTable *table,
|
|
const char *name,
|
|
const char *value)
|
|
{
|
|
return virHashUpdateEntry(table, name, (void*) value);
|
|
}
|
|
|
|
|
|
/*
|
|
* hypervAddEmbeddedParam:
|
|
* @params: Params list to add to
|
|
* @name: Name of the parameter
|
|
* @table: pointer to table of properties to add
|
|
* @info: WmiInfo of the object to serialize
|
|
*
|
|
* Add a GHashTable containing object properties as an embedded param to
|
|
* an invocation list.
|
|
*
|
|
* Upon successfull return the @table is consumed and the pointer is cleared out.
|
|
*
|
|
* Returns -1 on failure, 0 on success.
|
|
*/
|
|
int
|
|
hypervAddEmbeddedParam(hypervInvokeParamsListPtr params,
|
|
const char *name,
|
|
GHashTable **table,
|
|
hypervWmiClassInfoPtr classInfo)
|
|
{
|
|
hypervParamPtr p = NULL;
|
|
|
|
if (hypervCheckParams(params) < 0)
|
|
return -1;
|
|
|
|
p = ¶ms->params[params->nbParams];
|
|
p->type = HYPERV_EMBEDDED_PARAM;
|
|
p->embedded.name = name;
|
|
p->embedded.table = g_steal_pointer(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(GHashTable *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)
|
|
{
|
|
g_autofree char *method = g_strdup_printf("%s_INPUT", params->method);
|
|
g_auto(WsXmlDocH) invokeXmlDocRoot = ws_xml_create_doc(NULL, method);
|
|
WsXmlNodeH xmlNodeMethod = NULL;
|
|
|
|
if (!invokeXmlDocRoot) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not instantiate XML document"));
|
|
return -1;
|
|
}
|
|
|
|
xmlNodeMethod = xml_parser_get_root(invokeXmlDocRoot);
|
|
if (!xmlNodeMethod) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get root node of XML document"));
|
|
return -1;
|
|
}
|
|
|
|
/* add resource URI as namespace */
|
|
ws_xml_set_ns(xmlNodeMethod, params->resourceUri, "p");
|
|
|
|
*docRoot = g_steal_pointer(&invokeXmlDocRoot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
WsXmlNodeH xmlNodeParam = NULL,
|
|
xmlNodeTemp = NULL,
|
|
xmlNodeAddr = NULL,
|
|
xmlNodeRef = NULL;
|
|
g_auto(WsXmlDocH) xmlDocResponse = NULL;
|
|
g_autoptr(client_opt_t) options = NULL;
|
|
g_autoptr(filter_t) filter = NULL;
|
|
g_autofree char *enumContext = NULL;
|
|
g_autofree 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"));
|
|
return -1;
|
|
}
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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)
|
|
return -1;
|
|
|
|
/* 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)
|
|
return -1;
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(xmlNodeTemp = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ENUMERATION,
|
|
WSENUM_PULL_RESP))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get response"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(xmlNodeTemp = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ENUMERATION, WSENUM_ITEMS))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get response items"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(xmlNodeTemp = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ADDRESSING, WSA_EPR))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get EPR items"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(xmlNodeAddr = ws_xml_get_child(xmlNodeTemp, 0, XML_NS_ADDRESSING,
|
|
WSA_ADDRESS))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not get EPR address"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!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"));
|
|
return -1;
|
|
}
|
|
|
|
ws_xml_duplicate_tree(xmlNodeParam, xmlNodeAddr);
|
|
ws_xml_duplicate_tree(xmlNodeParam, xmlNodeRef);
|
|
|
|
/* we did it! */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hypervSerializeEmbeddedParam(hypervParamPtr p, const char *resourceUri,
|
|
WsXmlNodeH *methodNode)
|
|
{
|
|
WsXmlNodeH xmlNodeInstance = NULL,
|
|
xmlNodeProperty = NULL,
|
|
xmlNodeParam = NULL,
|
|
xmlNodeArray = NULL;
|
|
g_auto(WsXmlDocH) xmlDocTemp = NULL;
|
|
g_auto(WsXmlDocH) xmlDocCdata = NULL;
|
|
g_autofree char *cdataContent = NULL;
|
|
xmlNodePtr xmlNodeCdata = NULL;
|
|
hypervWmiClassInfoPtr classInfo = p->embedded.info;
|
|
g_autofree 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);
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(xmlNodeInstance = xml_parser_get_root(xmlDocTemp))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get temp xml doc root"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
/* retrieve parameters out of hash table */
|
|
numKeys = virHashSize(p->embedded.table);
|
|
items = virHashGetItems(p->embedded.table, NULL, false);
|
|
if (!items) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not read embedded param hash table"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(ws_xml_add_node_attr(xmlNodeProperty, NULL, "NAME", name))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add attribute to XML node"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(ws_xml_add_node_attr(xmlNodeProperty, NULL, "TYPE", property->type))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not add attribute to XML node"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* 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"));
|
|
return -1;
|
|
}
|
|
|
|
/* we did it! */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* hypervInvokeMethod:
|
|
* @priv: hypervPrivate object associated with the connection
|
|
* @paramsPtr: pointer to object containing the all necessary information for
|
|
* method invocation (consumed on invocation)
|
|
* @res: Optional out parameter to contain the response XML.
|
|
*
|
|
* Performs an invocation described by object at @paramsPtr, and optionally
|
|
* returns the XML containing the result.
|
|
*
|
|
* Please note that, object at @paramsPtr is consumed by this function and the
|
|
* pointer is cleared out, regardless of returning success or failure.
|
|
*
|
|
* Returns -1 on failure, 0 on success.
|
|
*/
|
|
int
|
|
hypervInvokeMethod(hypervPrivate *priv,
|
|
hypervInvokeParamsListPtr *paramsPtr,
|
|
WsXmlDocH *res)
|
|
{
|
|
g_autoptr(hypervInvokeParamsList) params = *paramsPtr;
|
|
size_t i = 0;
|
|
int returnCode;
|
|
g_auto(WsXmlDocH) paramsDocRoot = NULL;
|
|
g_autoptr(client_opt_t) options = NULL;
|
|
g_auto(WsXmlDocH) response = NULL;
|
|
WsXmlNodeH methodNode = NULL;
|
|
g_autofree char *returnValue_xpath = NULL;
|
|
g_autofree char *jobcode_instance_xpath = NULL;
|
|
g_autofree char *returnValue = NULL;
|
|
g_autofree char *instanceID = NULL;
|
|
bool completed = false;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_ConcreteJob) job = NULL;
|
|
int jobState = -1;
|
|
hypervParamPtr p = NULL;
|
|
int timeout = HYPERV_JOB_TIMEOUT_MS;
|
|
|
|
*paramsPtr = NULL;
|
|
|
|
if (hypervCreateInvokeXmlDoc(params, ¶msDocRoot) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create XML document"));
|
|
return -1;
|
|
}
|
|
|
|
methodNode = xml_parser_get_root(paramsDocRoot);
|
|
if (!methodNode) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get root of XML document"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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)
|
|
return -1;
|
|
break;
|
|
case HYPERV_EPR_PARAM:
|
|
if (hypervSerializeEprParam(p, priv, params->resourceUri,
|
|
&methodNode) < 0)
|
|
return -1;
|
|
break;
|
|
case HYPERV_EMBEDDED_PARAM:
|
|
if (hypervSerializeEmbeddedParam(p, params->resourceUri,
|
|
&methodNode) < 0)
|
|
return -1;
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Unknown parameter type"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Invoke the method and get the response */
|
|
|
|
options = wsmc_options_init();
|
|
if (!options) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not init options"));
|
|
return -1;
|
|
}
|
|
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 */
|
|
returnValue_xpath = g_strdup_printf("/s:Envelope/s:Body/p:%s_OUTPUT/p:ReturnValue",
|
|
params->method);
|
|
|
|
returnValue = ws_xml_get_xpath_value(response, returnValue_xpath);
|
|
if (!returnValue) {
|
|
g_autofree char *faultReason_xpath = g_strdup("/s:Envelope/s:Body/s:Fault/s:Reason/s:Text");
|
|
g_autofree char *faultReason = ws_xml_get_xpath_value(response, faultReason_xpath);
|
|
|
|
if (faultReason)
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("WS-Management fault during %s invocation: %s"),
|
|
params->method, faultReason);
|
|
else
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get return value for %s invocation"),
|
|
params->method);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (virStrToLong_i(returnValue, NULL, 10, &returnCode) < 0)
|
|
return -1;
|
|
|
|
if (returnCode == CIM_RETURNCODE_TRANSITION_STARTED) {
|
|
jobcode_instance_xpath = g_strdup_printf("/s:Envelope/s:Body/p:%s_OUTPUT/p:Job/a:ReferenceParameters/"
|
|
"w:SelectorSet/w:Selector[@Name='InstanceID']",
|
|
params->method);
|
|
|
|
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);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
virBufferEscapeSQL(&query,
|
|
MSVM_CONCRETEJOB_WQL_SELECT
|
|
"WHERE InstanceID = '%s'", instanceID);
|
|
|
|
if (hypervGetWmiClass(Msvm_ConcreteJob, &job) < 0 || !job)
|
|
return -1;
|
|
|
|
jobState = job->data->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((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:
|
|
return -1;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Unknown invocation state"));
|
|
return -1;
|
|
}
|
|
}
|
|
if (!completed && timeout < 0) {
|
|
virReportError(VIR_ERR_OPERATION_TIMEOUT,
|
|
_("Timeout waiting for %s invocation"), params->method);
|
|
return -1;
|
|
}
|
|
} 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);
|
|
return -1;
|
|
}
|
|
|
|
if (res)
|
|
*res = g_steal_pointer(&response);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Object
|
|
*/
|
|
|
|
/* This function guarantees that wqlQuery->query is reset, even on failure */
|
|
int
|
|
hypervEnumAndPull(hypervPrivate *priv, hypervWqlQueryPtr wqlQuery,
|
|
hypervObject **list)
|
|
{
|
|
WsSerializerContextH serializerContext;
|
|
g_autoptr(client_opt_t) options = NULL;
|
|
g_autofree char *query_string = NULL;
|
|
hypervWmiClassInfoPtr wmiInfo = wqlQuery->info;
|
|
g_autoptr(filter_t) filter = NULL;
|
|
g_auto(WsXmlDocH) response = NULL;
|
|
g_autofree char *enumContext = NULL;
|
|
g_autoptr(hypervObject) head = NULL;
|
|
hypervObject *tail = NULL;
|
|
WsXmlNodeH node = 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;
|
|
}
|
|
|
|
serializerContext = wsmc_get_serialization_context(priv->client);
|
|
|
|
options = wsmc_options_init();
|
|
|
|
if (options == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not initialize options"));
|
|
return -1;
|
|
}
|
|
|
|
filter = filter_create_simple(WSM_WQL_FILTER_DIALECT, query_string);
|
|
|
|
if (filter == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not create filter"));
|
|
return -1;
|
|
}
|
|
|
|
response = wsmc_action_enumerate(priv->client, wmiInfo->rootUri, options,
|
|
filter);
|
|
|
|
if (hypervVerifyResponse(priv->client, response, "enumeration") < 0)
|
|
return -1;
|
|
|
|
enumContext = wsmc_get_enum_context(response);
|
|
|
|
ws_xml_destroy_doc(response);
|
|
response = NULL;
|
|
|
|
while (enumContext != NULL && *enumContext != '\0') {
|
|
XML_TYPE_PTR data = NULL;
|
|
|
|
response = wsmc_action_pull(priv->client, wmiInfo->resourceUri, options,
|
|
filter, enumContext);
|
|
|
|
if (hypervVerifyResponse(priv->client, response, "pull") < 0)
|
|
return -1;
|
|
|
|
node = ws_xml_get_soap_body(response);
|
|
|
|
if (node == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not lookup SOAP body"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
object = g_new0(hypervObject, 1);
|
|
object->info = wmiInfo;
|
|
object->data = data;
|
|
object->priv = priv;
|
|
|
|
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;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
hypervFreeObject(void *object)
|
|
{
|
|
hypervObject *next;
|
|
WsSerializerContextH serializerContext;
|
|
|
|
if (object == NULL)
|
|
return;
|
|
|
|
serializerContext = wsmc_get_serialization_context(((hypervObject *)object)->priv->client);
|
|
|
|
while (object != NULL) {
|
|
next = ((hypervObject *)object)->next;
|
|
|
|
((hypervObject *)object)->priv = NULL;
|
|
|
|
if (ws_serializer_free_mem(serializerContext, ((hypervObject *)object)->data,
|
|
((hypervObject *)object)->info->serializerInfo) < 0) {
|
|
VIR_ERROR(_("Could not free deserialized data"));
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Msvm_ComputerSystem
|
|
*/
|
|
|
|
int
|
|
hypervInvokeMsvmComputerSystemRequestStateChange(virDomainPtr domain,
|
|
int requestedState)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
g_auto(WsXmlDocH) response = NULL;
|
|
g_autoptr(client_opt_t) options = NULL;
|
|
g_autofree char *selector = NULL;
|
|
g_autofree char *properties = NULL;
|
|
g_autofree char *returnValue = NULL;
|
|
int returnCode;
|
|
g_autofree char *instanceID = NULL;
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
g_autoptr(Msvm_ConcreteJob) concreteJob = NULL;
|
|
bool completed = false;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
selector = g_strdup_printf("Name=%s&CreationClassName=Msvm_ComputerSystem", uuid_string);
|
|
properties = g_strdup_printf("RequestedState=%d", requestedState);
|
|
|
|
options = wsmc_options_init();
|
|
|
|
if (options == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not initialize options"));
|
|
return -1;
|
|
}
|
|
|
|
wsmc_add_selectors_from_str(options, selector);
|
|
wsmc_add_prop_from_str(options, properties);
|
|
|
|
/* Invoke method */
|
|
response = wsmc_action_invoke(priv->client, MSVM_COMPUTERSYSTEM_RESOURCE_URI,
|
|
options, "RequestStateChange", NULL);
|
|
|
|
if (hypervVerifyResponse(priv->client, response, "invocation") < 0)
|
|
return -1;
|
|
|
|
/* 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");
|
|
return -1;
|
|
}
|
|
|
|
if (virStrToLong_i(returnValue, NULL, 10, &returnCode) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse return code from '%s'"), returnValue);
|
|
return -1;
|
|
}
|
|
|
|
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");
|
|
return -1;
|
|
}
|
|
|
|
/* FIXME: Poll every 100ms until the job completes or fails. There
|
|
* seems to be no other way than polling. */
|
|
while (!completed) {
|
|
virBufferAsprintf(&query,
|
|
MSVM_CONCRETEJOB_WQL_SELECT
|
|
"WHERE InstanceID = '%s'", instanceID);
|
|
|
|
if (hypervGetWmiClass(Msvm_ConcreteJob, &concreteJob) < 0)
|
|
return -1;
|
|
|
|
if (concreteJob == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not lookup %s for %s invocation"),
|
|
"Msvm_ConcreteJob", "RequestStateChange");
|
|
return -1;
|
|
}
|
|
|
|
switch (concreteJob->data->JobState) {
|
|
case MSVM_CONCRETEJOB_JOBSTATE_NEW:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_STARTING:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_RUNNING:
|
|
case MSVM_CONCRETEJOB_JOBSTATE_SHUTTING_DOWN:
|
|
hypervFreeObject((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");
|
|
return -1;
|
|
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Concrete job for %s invocation is in unknown state"),
|
|
"RequestStateChange");
|
|
return -1;
|
|
}
|
|
}
|
|
} 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);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervMsvmComputerSystemEnabledStateToDomainState
|
|
(Msvm_ComputerSystem *computerSystem)
|
|
{
|
|
switch (computerSystem->data->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->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->Name, uuid) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse UUID from string '%s'"),
|
|
computerSystem->data->Name);
|
|
return -1;
|
|
}
|
|
|
|
if (hypervIsMsvmComputerSystemActive(computerSystem, NULL))
|
|
id = computerSystem->data->ProcessID;
|
|
|
|
*domain = virGetDomain(conn, computerSystem->data->ElementName, uuid, id);
|
|
|
|
return *domain ? 0 : -1;
|
|
}
|
|
|
|
|
|
int
|
|
hypervMsvmComputerSystemFromUUID(hypervPrivate *priv, const char *uuid,
|
|
Msvm_ComputerSystem **computerSystem)
|
|
{
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
|
|
|
|
if (!computerSystem || *computerSystem) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
|
return -1;
|
|
}
|
|
|
|
virBufferEscapeSQL(&query,
|
|
MSVM_COMPUTERSYSTEM_WQL_SELECT
|
|
"WHERE " MSVM_COMPUTERSYSTEM_WQL_VIRTUAL
|
|
"AND Name = '%s'", uuid);
|
|
|
|
if (hypervGetWmiClass(Msvm_ComputerSystem, computerSystem) < 0)
|
|
return -1;
|
|
|
|
if (!*computerSystem) {
|
|
virReportError(VIR_ERR_NO_DOMAIN, _("No domain with UUID %s"), uuid);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervMsvmComputerSystemFromDomain(virDomainPtr domain,
|
|
Msvm_ComputerSystem **computerSystem)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuidString[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virUUIDFormat(domain->uuid, uuidString);
|
|
|
|
return hypervMsvmComputerSystemFromUUID(priv, uuidString, computerSystem);
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Generic "Get WMI class list" helpers
|
|
*/
|
|
|
|
int
|
|
hypervGetMsvmVirtualSystemSettingDataFromUUID(hypervPrivate *priv,
|
|
const char *uuid_string,
|
|
Msvm_VirtualSystemSettingData **list)
|
|
{
|
|
g_auto(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 (hypervGetWmiClass(Msvm_VirtualSystemSettingData, list) < 0 || !*list)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define hypervGetSettingData(type, id, out) \
|
|
g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER; \
|
|
virBufferEscapeSQL(&query, \
|
|
"ASSOCIATORS OF {Msvm_VirtualSystemSettingData.InstanceID='%s'} " \
|
|
"WHERE AssocClass = Msvm_VirtualSystemSettingDataComponent " \
|
|
"ResultClass = " #type, \
|
|
id); \
|
|
if (hypervGetWmiClass(type, out) < 0) \
|
|
return -1
|
|
|
|
|
|
int
|
|
hypervGetResourceAllocationSD(hypervPrivate *priv,
|
|
const char *id,
|
|
Msvm_ResourceAllocationSettingData **data)
|
|
{
|
|
hypervGetSettingData(Msvm_ResourceAllocationSettingData, id, data);
|
|
|
|
if (!*data) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not look up resource allocation setting data with virtual system instance ID '%s'"),
|
|
id);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervGetProcessorSD(hypervPrivate *priv,
|
|
const char *id,
|
|
Msvm_ProcessorSettingData **data)
|
|
{
|
|
hypervGetSettingData(Msvm_ProcessorSettingData, id, data);
|
|
|
|
if (!*data) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not look up processor setting data with virtual system instance ID '%s'"),
|
|
id);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervGetMemorySD(hypervPrivate *priv,
|
|
const char *vssd_instanceid,
|
|
Msvm_MemorySettingData **list)
|
|
{
|
|
hypervGetSettingData(Msvm_MemorySettingData, vssd_instanceid, list);
|
|
|
|
if (!*list)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervGetStorageAllocationSD(hypervPrivate *priv,
|
|
const char *id,
|
|
Msvm_StorageAllocationSettingData **data)
|
|
{
|
|
hypervGetSettingData(Msvm_StorageAllocationSettingData, id, data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervGetSerialPortSD(hypervPrivate *priv,
|
|
const char *id,
|
|
Msvm_SerialPortSettingData **data)
|
|
{
|
|
hypervGetSettingData(Msvm_SerialPortSettingData, id, data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervGetSyntheticEthernetPortSD(hypervPrivate *priv,
|
|
const char *id,
|
|
Msvm_SyntheticEthernetPortSettingData **data)
|
|
{
|
|
hypervGetSettingData(Msvm_SyntheticEthernetPortSettingData, id, data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervGetEthernetPortAllocationSD(hypervPrivate *priv,
|
|
const char *id,
|
|
Msvm_EthernetPortAllocationSettingData **data)
|
|
{
|
|
hypervGetSettingData(Msvm_EthernetPortAllocationSettingData, id, data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Msvm_VirtualSystemManagementService
|
|
*/
|
|
|
|
int
|
|
hypervMsvmVSMSAddResourceSettings(virDomainPtr domain,
|
|
GHashTable **resourceSettingsPtr,
|
|
hypervWmiClassInfoPtr wmiInfo,
|
|
WsXmlDocH *response)
|
|
{
|
|
hypervPrivate *priv = domain->conn->privateData;
|
|
char uuid_string[VIR_UUID_STRING_BUFLEN];
|
|
g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
|
|
g_autoptr(GHashTable) resourceSettings = *resourceSettingsPtr;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
g_auto(virBuffer) eprQuery = VIR_BUFFER_INITIALIZER;
|
|
|
|
*resourceSettingsPtr = NULL;
|
|
|
|
virUUIDFormat(domain->uuid, uuid_string);
|
|
|
|
if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, &vssd) < 0)
|
|
return -1;
|
|
|
|
virBufferEscapeSQL(&eprQuery,
|
|
MSVM_VIRTUALSYSTEMSETTINGDATA_WQL_SELECT "WHERE InstanceID='%s'",
|
|
vssd->data->InstanceID);
|
|
|
|
params = hypervCreateInvokeParamsList("AddResourceSettings",
|
|
MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
|
|
Msvm_VirtualSystemManagementService_WmiInfo);
|
|
|
|
if (!params)
|
|
return -1;
|
|
|
|
if (hypervAddEprParam(params, "AffectedConfiguration",
|
|
&eprQuery, Msvm_VirtualSystemSettingData_WmiInfo) < 0)
|
|
return -1;
|
|
|
|
if (hypervAddEmbeddedParam(params, "ResourceSettings", &resourceSettings, wmiInfo) < 0) {
|
|
hypervFreeEmbeddedParam(resourceSettings);
|
|
return -1;
|
|
}
|
|
|
|
if (hypervInvokeMethod(priv, ¶ms, response) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hypervMsvmVSMSModifyResourceSettings(hypervPrivate *priv,
|
|
GHashTable **resourceSettingsPtr,
|
|
hypervWmiClassInfoPtr wmiInfo)
|
|
{
|
|
g_autoptr(GHashTable) resourceSettings = *resourceSettingsPtr;
|
|
g_autoptr(hypervInvokeParamsList) params = NULL;
|
|
|
|
*resourceSettingsPtr = NULL;
|
|
|
|
params = hypervCreateInvokeParamsList("ModifyResourceSettings",
|
|
MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR,
|
|
Msvm_VirtualSystemManagementService_WmiInfo);
|
|
|
|
if (!params)
|
|
return -1;
|
|
|
|
if (hypervAddEmbeddedParam(params, "ResourceSettings", &resourceSettings, wmiInfo) < 0) {
|
|
hypervFreeEmbeddedParam(resourceSettings);
|
|
return -1;
|
|
}
|
|
|
|
if (hypervInvokeMethod(priv, ¶ms, NULL) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|