2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00
ovs/lib/wmi.c
Alin Gabriel Serdean 58b34b0016 Switch from internal port to all ports defined
This patch changes the way we try to figure out if a port is defined on a given switch.

Instead of looking only in the internal ports defined switch to all ports defined.

This caused issues when trying to add a Hyper-V container port to a given OVS bridge.

Reported-by: Danting Liu <dantingl@vmware.com>
Signed-off-by: Alin Gabriel Serdean <aserdean@ovn.org>
Acked-by: Anand Kumar <kumaranand@vmware.com>
2019-08-13 11:23:47 +03:00

1273 lines
37 KiB
C

/*
* Copyright (c) 2016 Cloudbase Solutions Srl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "wmi.h"
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include "openvswitch/vlog.h"
#include "util.h"
VLOG_DEFINE_THIS_MODULE(wmi);
/* WMI Job values. */
enum job_status
{
job_starting = 3,
job_running = 4,
job_completed = 7,
job_wait = 4096
};
static char *
sanitize_port_name(char *name)
{
char *p1, *p2;
p1 = p2 = name;
while (*p1) {
if ((*p1) == '\'' || (*p1) == '\"') {
p1++;
} else {
*p2 = *p1;
p2++;
p1++;
}
}
*p2 = '\0';
return name;
}
/* This function will output the appropriate message for a given HRESULT.*/
static void
get_hres_error(HRESULT hres)
{
char *error_msg = NULL;
if (FACILITY_WINDOWS == HRESULT_FACILITY(hres)) {
hres = HRESULT_CODE(hres);
}
VLOG_ERR("%s", ovs_format_message(hres));
}
static boolean
check_return_value(HRESULT hres)
{
if (FAILED(hres)) {
get_hres_error(hres);
return false;
}
return true;
}
static HRESULT
get_variant_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
VARIANT *value)
{
HRESULT hres;
VariantInit(value);
hres = pcls_obj->lpVtbl->Get(pcls_obj, field_name, 0, value, 0, 0);
if (FAILED(hres)) {
VariantClear(value);
}
return hres;
}
/* This function retrieves the uint16_t value from a given class object with
* the field name field_name. */
static HRESULT
get_uint16_t_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
uint16_t *value)
{
VARIANT vt_prop;
HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
*value = V_UI2(&vt_prop);
return hres;
}
/* This function retrieves the unsigned int values from a given class object
* with the field name field_name. */
static HRESULT
get_uint_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
unsigned int *value)
{
VARIANT vt_prop;
HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
*value = V_UI4(&vt_prop);
return hres;
}
/* This function retrieves the unsigned short value from a given class object
* with the field name field_name. */
static HRESULT
get_ushort_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
unsigned short *value)
{
VARIANT vt_prop;
HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
*value = V_UI2(&vt_prop);
return hres;
}
/* This function retrieves the BSTR value from a given class object with
* the field name field_name, to a preallocated destination dest and with the
* maximum length max_dest_lgth. */
static HRESULT
get_str_value(IWbemClassObject *pcls_obj, wchar_t *field_name, wchar_t *dest,
int max_dest_lgth)
{
VARIANT vt_prop;
HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
if (wcscpy_s(dest, max_dest_lgth, vt_prop.bstrVal)) {
VariantClear(&vt_prop);
VLOG_WARN("get_str_value, wcscpy_s failed :%s", ovs_strerror(errno));
return WBEM_E_FAILED;
}
VariantClear(&vt_prop);
return S_OK;
}
/* This function waits for a WMI job to finish and retrieves the error code
* if the job failed */
static HRESULT
wait_for_job(IWbemServices *psvc, wchar_t *job_path)
{
IWbemClassObject *pcls_obj = NULL;
HRESULT retval = 0;
uint16_t job_state = 0;
uint16_t error = 0;
do {
if(!check_return_value(psvc->lpVtbl->GetObject(psvc, job_path, 0, NULL,
&pcls_obj, NULL))) {
retval = WBEM_E_FAILED;
break;
}
retval = get_uint16_t_value(pcls_obj, L"JobState", &job_state);
if (FAILED(retval)) {
break;
}
if (job_state == job_starting || job_state == job_running) {
Sleep(200);
} else if (job_state == job_completed) {
break;
} else {
/* Error occurred. */
retval = get_uint16_t_value(pcls_obj, L"ErrorCode", &error);
if (FAILED(retval)) {
break;
}
VLOG_WARN("Job failed with error: %d", error);
retval = WBEM_E_FAILED;;
break;
}
if (pcls_obj != NULL) {
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
}
} while(TRUE);
if (pcls_obj != NULL) {
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
}
return retval;
}
/* This function will initialize DCOM retrieving the WMI locator's ploc and
* the context associated to it. */
static boolean
initialize_wmi(IWbemLocator **ploc, IWbemContext **pcontext)
{
HRESULT hres = 0;
/* Initialize COM. */
hres = CoInitialize(NULL);
if (FAILED(hres)) {
return false;
}
/* Initialize COM security. */
hres = CoInitializeSecurity(NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL);
if (FAILED(hres)) {
return false;
}
/* Fill context. */
hres = CoCreateInstance(&CLSID_WbemContext,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IWbemContext,
(void**)pcontext);
if (FAILED(hres)) {
return false;
}
fill_context(*pcontext);
/* Initialize locator's (ploc) to WMI. */
hres = CoCreateInstance(&CLSID_WbemLocator,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IWbemLocator,
(LPVOID *)ploc);
if (FAILED(hres)) {
return false;
}
return true;
}
/* This function connects the WMI locator's ploc to a given WMI provider
* defined in server and also sets the required security levels for a local
* connection to it. */
static boolean
connect_set_security(IWbemLocator *ploc, IWbemContext *pcontext,
wchar_t *server, IWbemServices **psvc)
{
HRESULT hres = 0;
/* Connect to server. */
hres = ploc->lpVtbl->ConnectServer(ploc,
server,
NULL,
NULL,
0,
0,
0,
pcontext,
psvc);
if (FAILED(hres)) {
return false;
}
/* Set security levels. */
hres = CoSetProxyBlanket((IUnknown *) *psvc,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE);
if (FAILED(hres)) {
return false;
}
return true;
}
/* This function retrieves the first class object of a given enumeration
* outputted by a query and fails if it could not retrieve the object or there
* was no object to retrieve */
static boolean
get_first_element(IEnumWbemClassObject *penumerate,
IWbemClassObject **pcls_obj)
{
unsigned long retval = 0;
if (penumerate == NULL) {
VLOG_WARN("Enumeration Class Object is NULL. Cannot get the first"
"object");
return false;
}
HRESULT hres = penumerate->lpVtbl->Next(penumerate, WBEM_INFINITE, 1,
pcls_obj, &retval);
if (!check_return_value(hres) || retval == 0) {
return false;
}
return true;
}
/* This function is a wrapper that transforms a char * into a wchar_t * */
static boolean
tranform_wide(char *name, wchar_t *wide_name)
{
unsigned long size = strlen(name) + 1;
long long ret = 0;
if (wide_name == NULL) {
VLOG_WARN("Provided wide string is NULL");
return false;
}
ret = mbstowcs(wide_name, name, size);
if (ret == -1) {
VLOG_WARN("Invalid multibyte character is encountered");
return false;
} else if (ret == size) {
VLOG_WARN("Returned wide string not NULL terminated");
return false;
}
return true;
}
#define WMI_QUERY_COUNT 2048
/* This function will delete a switch internal port with a given name as input
* executing "RemoveResourceSettings" as per documentation:
* https://msdn.microsoft.com/en-us/library/hh850277%28v=vs.85%29.aspx
* allocating the data and populating the needed fields to execute the
* method */
boolean
delete_wmi_port(char *name)
{
HRESULT hres = 0;
boolean retval = true;
IWbemLocator *ploc = NULL;
IWbemServices *psvc = NULL;
IWbemContext *pcontext = NULL;
IWbemClassObject *pclass_instance = NULL;
IWbemClassObject *pinput_params = NULL;
IWbemClassObject *pcls_obj = NULL;
IWbemClassObject *pout_params = NULL;
IEnumWbemClassObject *penumerate = NULL;
sanitize_port_name(name);
VARIANT vt_prop;
VARIANT variant_array;
wchar_t *wide_name = NULL;
VariantInit(&vt_prop);
VariantInit(&variant_array);
LONG count[1];
SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
if (psa == NULL) {
VLOG_WARN("Could not allocate memory for a SAFEARRAY");
retval = false;
goto error;
}
if (!initialize_wmi(&ploc, &pcontext)) {
VLOG_WARN("Could not initialize DCOM");
retval = false;
goto error;
}
if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2",
&psvc)) {
VLOG_WARN("Could not connect and set security for virtualization");
retval = false;
goto error;
}
/* Get the port with the element name equal to the name input. */
wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * from "
L"Msvm_EthernetPortAllocationSettingData WHERE ElementName = \"" ;
wide_name = xmalloc((strlen(name) + 1) * sizeof(wchar_t));
if (!tranform_wide(name, wide_name)) {
retval = false;
goto error;
}
wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
internal_port_query,
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
/* Get the element path on the switch which will be deleted. */
if (!get_first_element(penumerate, &pcls_obj)) {
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
/* Get the class object and the parameters it can have. */
hres = psvc->lpVtbl->GetObject(psvc,
L"Msvm_VirtualEthernetSwitchManagementService", 0, NULL, &pcls_obj,
NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = pcls_obj->lpVtbl->GetMethod(pcls_obj, L"RemoveResourceSettings", 0,
&pinput_params, NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
&pclass_instance);
if (FAILED(hres)) {
retval = false;
goto error;
}
count[0] = 0;
hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal);
if (FAILED(hres)) {
retval = false;
goto error;
}
VariantClear(&vt_prop);
VariantInit(&vt_prop);
variant_array.vt = VT_ARRAY | VT_BSTR;
variant_array.parray = psa;
hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", 0,
&variant_array, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
/* Get the object of the Msvm_VirtualEthernetSwitchManagementService which
* we need to invoke the port deletion. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
L"SELECT * FROM "
L"Msvm_VirtualEthernetSwitchManagementService",
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &pcls_obj)) {
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
/* Invoke the delete port method. */
hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal,
L"RemoveResourceSettings", 0,
pcontext, pclass_instance, &pout_params,
NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
VariantClear(&vt_prop);
VariantInit(&vt_prop);
hres = pout_params->lpVtbl->Get(pout_params, L"ReturnValue", 0,
&vt_prop, NULL, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
unsigned int retvalue = 0;
hres = get_uint_value(pout_params, L"ReturnValue", &retvalue);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (retvalue != 0 && retvalue != job_wait) {
retval = false;
goto error;
}
if (retvalue == job_wait) {
WCHAR job_path[2048];
hres = get_str_value(pout_params, L"Job", job_path,
sizeof(job_path) / sizeof(WCHAR));
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = wait_for_job(psvc, job_path);
if (FAILED(hres)) {
retval = false;
}
}
error:
VariantClear(&vt_prop);
if (pcontext != NULL) {
pcontext->lpVtbl->Release(pcontext);
pcontext = NULL;
}
if (psa != NULL) {
SafeArrayDestroy(psa);
psa = NULL;
}
if (pcls_obj != NULL) {
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
}
if (wide_name != NULL) {
free(wide_name);
wide_name = NULL;
}
if (!retval) {
get_hres_error(hres);
}
if (pinput_params != NULL) {
pinput_params->lpVtbl->Release(pinput_params);
pinput_params = NULL;
}
if (pout_params != NULL) {
pout_params->lpVtbl->Release(pout_params);
pout_params = NULL;
}
if (psvc != NULL) {
psvc->lpVtbl->Release(psvc);
psvc = NULL;
}
if (ploc != NULL) {
ploc->lpVtbl->Release(ploc);
ploc = NULL;
}
if (pclass_instance != NULL) {
pclass_instance->lpVtbl->Release(pclass_instance);
pclass_instance = NULL;
}
if (penumerate != NULL) {
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
}
CoUninitialize();
return retval;
}
/* This function will create an internal port on the switch given a given name
* executing the method AddResourceSettings as per documentation:
* https://msdn.microsoft.com/en-us/library/hh850019%28v=vs.85%29.aspx.
* It will verify if the port is already defined, in which case it will use
* the specific port, and if the forwarding extension "Open vSwitch Extension"
* is enabled and running only on a single switch.
* After the port is created and bound to the switch we will disable the
* created net adapter and rename it to match the OVS bridge name .*/
boolean
create_wmi_port(char *name) {
HRESULT hres = 0;
boolean retval = true;
BSTR text_object_string = NULL;
IWbemLocator *ploc = NULL;
IWbemContext *pcontext = NULL;
IWbemServices *psvc = NULL;
IEnumWbemClassObject *penumerate = NULL;
IWbemClassObject *default_settings_data = NULL;
IWbemClassObject *default_system = NULL;
IWbemClassObject *pcls_obj = NULL;
IWbemClassObject *pclass = NULL;
IWbemClassObject *pinput_params = NULL;
IWbemClassObject *pclass_instance = NULL;
IWbemObjectTextSrc *text_object = NULL;
IWbemClassObject *pout_params = NULL;
wchar_t *wide_name = NULL;
VARIANT vt_prop;
VARIANT switch_setting_path;
VARIANT new_name;
SAFEARRAY *psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
VARIANT variant_array;
LONG count[1];
VariantInit(&vt_prop);
VariantInit(&switch_setting_path);
sanitize_port_name(name);
if (psa == NULL) {
VLOG_WARN("Could not allocate memory for a SAFEARRAY");
retval = false;
goto error;
}
if (!initialize_wmi(&ploc, &pcontext)) {
VLOG_WARN("Could not initialize DCOM");
retval = false;
goto error;
}
if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2",
&psvc)) {
VLOG_WARN("Could not connect and set security for virtualization");
retval = false;
goto error;
}
/* Check if the element already exists on the switch. */
wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * FROM "
L"CIM_EthernetPort WHERE ElementName = \"";
wide_name = xmalloc((strlen(name) + 1) * sizeof(wchar_t));
if (!tranform_wide(name, wide_name)) {
retval = false;
goto error;
}
wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
internal_port_query,
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("Port with name: %s already defined on the switch", name);
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
/* Check if the extension is enabled and running. Also check if the
* the extension is enabled on more than one switch. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
L"SELECT * "
L"FROM Msvm_EthernetSwitchExtension "
L"WHERE "
L"ElementName=\"Open vSwitch Extension\" "
L"AND EnabledState=2 "
L"AND HealthState=5",
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("Open vSwitch Extension is not enabled on any switch");
retval = false;
goto error;
}
wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
L"SELECT * FROM Msvm_VirtualEthernetSwitch WHERE Name = \"");
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"SystemName", 0,
&vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
wcscat_s(internal_port_query, WMI_QUERY_COUNT,
vt_prop.bstrVal);
VariantClear(&vt_prop);
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
if (get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("The extension is activated on more than one switch, "
"aborting operation. Please activate the extension on a "
"single switch");
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
if (pcls_obj != NULL) {
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
}
/* Get the switch object on which the extension is activated. */
wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
internal_port_query,
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("Could not get the switch object on which the extension is"
"activated");
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"ElementName", 0, &vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
L"SELECT * FROM Msvm_VirtualEthernetSwitchSettingData WHERE "
L"ElementName = \"");
wcscat_s(internal_port_query, WMI_QUERY_COUNT,
vt_prop.bstrVal);
VariantClear(&vt_prop);
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"Name", 0, &vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
/* Should be enough to give the InstanceID, from msdn documentation:
* Uniquely identifies an instance of this class. This property is
* inherited from CIM_SettingData and is always
* set to "Microsoft:GUID\DeviceSpecificData". */
wcscat_s(internal_port_query, WMI_QUERY_COUNT,
L"\" AND InstanceID = \"Microsoft:");
wcscat_s(internal_port_query, WMI_QUERY_COUNT,
vt_prop.bstrVal);
wcscat_s(internal_port_query, WMI_QUERY_COUNT,
L"\"");
VariantClear(&vt_prop);
/* Retrieve the Msvm_VirtualEthernetSwitchSettingData pinned to the switch
* object on which the extension is activated. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
internal_port_query,
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("Could not get the first "
"Msvm_VirtualEthernetSwitchSettingData object");
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &switch_setting_path,
0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
/* Retrieve a default allocation port. This object will be later filled
* with optional data to create an switch internal port. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
L"SELECT * FROM "
L"Msvm_EthernetPortAllocationSettingData "
L"WHERE InstanceID LIKE '%%%%\\\\Default' "
L"AND ResourceSubType = "
L"'Microsoft:Hyper-V:Ethernet Connection'",
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &default_settings_data)) {
VLOG_WARN("Could not retrieve default allocation port object");
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
/* Retrieve the default computer system on which the port allocation will
* be hosted.
* Instead of querying using Description, we can query using InstallDate.
* From MSDN documentation regarding InstallDate:
* The date and time the virtual machine configuration was created for
* a virtual machine, or Null, for a management operating system. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
L"SELECT * FROM Msvm_ComputerSystem WHERE "
L"InstallDate is NULL",
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &default_system)) {
VLOG_WARN("Could not retrieve default computer system object");
retval = false;
goto error;
}
hres = default_system->lpVtbl->Get(default_system, L"__PATH",
0, &vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
count[0] = 0;
hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal);
if (FAILED(hres)) {
retval = false;
goto error;
}
VariantClear(&vt_prop);
variant_array.vt = VT_ARRAY | VT_BSTR;
variant_array.parray = psa;
hres = default_settings_data->lpVtbl->Put(default_settings_data,
L"HostResource", 0,
&variant_array, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = psvc->lpVtbl->GetObject(psvc,
L"Msvm_VirtualEthernetSwitchManagementService",
0, NULL, &pclass, NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = pclass->lpVtbl->GetMethod(pclass, L"AddResourceSettings", 0,
&pinput_params, NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
&pclass_instance);
if (FAILED(hres)) {
retval = false;
goto error;
}
/* Store the switch setting path retrieved above in the affected
* configuration field of the class instance. */
hres = pclass_instance->lpVtbl->Put(pclass_instance,
L"AffectedConfiguration", 0,
&switch_setting_path, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
/* Store the port name in the ElementName field of the default allocation
* data. */
vt_prop.vt = VT_BSTR;
vt_prop.bstrVal = SysAllocString(wide_name);
hres = default_settings_data->lpVtbl->Put(default_settings_data,
L"ElementName", 0,
&vt_prop, 0);
VariantClear(&vt_prop);
if (FAILED(hres)) {
retval = false;
goto error;
}
/* Retrieve and store the serialized data of the modified default switch
* settings data. */
hres = CoCreateInstance(&CLSID_WbemObjectTextSrc,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IWbemObjectTextSrc,
(void**)&text_object);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = text_object->lpVtbl->GetText(text_object, 0,
default_settings_data,
WMI_OBJ_TEXT_WMI_DTD_2_0,
pcontext,
&text_object_string);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = SafeArrayDestroy(psa);
if (FAILED(hres)) {
VLOG_WARN("Could not clear the data of the array");
retval = false;
goto error;
}
psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
if (psa == NULL) {
VLOG_WARN("Could not allocate memory for a SAFEARRAY");
retval = false;
goto error;
}
count[0] = 0;
variant_array.parray = psa;
hres = SafeArrayPutElement(psa, count, text_object_string);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings",
0, &variant_array, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
/* Get the object of the switch service. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
L"SELECT * FROM "
L"Msvm_VirtualEthernetSwitchManagementService",
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (!get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("Could not get the object of the switch service");
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
/* Try to add the port to the switch. */
hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal,
L"AddResourceSettings", 0,
pcontext, pclass_instance, &pout_params,
NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
unsigned int retvalue = 0;
hres = get_uint_value(pout_params, L"ReturnValue", &retvalue);
if (FAILED(hres)) {
retval = false;
goto error;
}
if (retvalue != 0 && retvalue != job_wait) {
retval = false;
goto error;
}
if (retvalue == job_wait) {
WCHAR job_path[2048];
hres = get_str_value(pout_params, L"Job", job_path,
sizeof(job_path) / sizeof(WCHAR));
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = wait_for_job(psvc, job_path);
if (FAILED(hres)) {
retval = false;
goto error;
}
}
pclass->lpVtbl->Release(pclass);
pclass = NULL;
pclass_instance->lpVtbl->Release(pclass_instance);
pclass_instance = NULL;
pinput_params->lpVtbl->Release(pinput_params);
pinput_params = NULL;
psvc->lpVtbl->Release(psvc);
psvc = NULL;
VariantClear(&vt_prop);
if (!connect_set_security(ploc, pcontext, L"Root\\StandardCimv2",
&psvc)) {
VLOG_WARN("Could not connect and set security for CIM");
retval = false;
goto error;
}
wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
L"SELECT * FROM MSFT_NetAdapter WHERE Name LIKE '%%");
wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"%%'");
/* Get the object with the port name equal to name on the CIM. */
hres = psvc->lpVtbl->ExecQuery(psvc,
L"WQL",
internal_port_query,
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&penumerate);
if (!get_first_element(penumerate, &pcls_obj)) {
VLOG_WARN("Element name: %s not found in CIM", name);
retval = false;
goto error;
}
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
/* Disable the adapter with port name equal with name. */
hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Disable", 0,
pcontext, NULL, NULL, NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = psvc->lpVtbl->GetObject(psvc, L"MSFT_NetAdapter", 0, NULL, &pclass,
NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = pclass->lpVtbl->GetMethod(pclass, L"Rename", 0, &pinput_params,
NULL);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
&pclass_instance);
if (FAILED(hres)) {
retval = false;
goto error;
}
VariantInit(&new_name);
new_name.vt = VT_BSTR;
new_name.bstrVal = wide_name;
hres = pclass_instance->lpVtbl->Put(pclass_instance, L"NewName", 0,
&new_name, 0);
if (FAILED(hres)) {
retval = false;
goto error;
}
hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Rename", 0,
pcontext, pclass_instance, NULL, NULL);
if (FAILED(hres)) {
retval = false;
}
error:
if (text_object_string != NULL) {
SysFreeString(text_object_string);
text_object_string = NULL;
}
if (psa != NULL) {
SafeArrayDestroy(psa);
psa = NULL;
}
if (ploc != NULL) {
ploc->lpVtbl->Release(ploc);
ploc = NULL;
}
if (pcontext != NULL) {
pcontext->lpVtbl->Release(pcontext);
pcontext = NULL;
}
if (psvc != NULL) {
psvc->lpVtbl->Release(psvc);
psvc = NULL;
}
if (penumerate != NULL) {
penumerate->lpVtbl->Release(penumerate);
penumerate = NULL;
}
if (default_settings_data != NULL) {
default_settings_data->lpVtbl->Release(default_settings_data);
default_settings_data = NULL;
}
if (default_system != NULL) {
default_system->lpVtbl->Release(default_system);
default_system = NULL;
}
if (pcls_obj != NULL) {
pcls_obj->lpVtbl->Release(pcls_obj);
pcls_obj = NULL;
}
if (pclass != NULL) {
pclass->lpVtbl->Release(pclass);
pclass = NULL;
}
if (pinput_params != NULL) {
pinput_params->lpVtbl->Release(pinput_params);
pinput_params = NULL;
}
if (pclass_instance != NULL) {
pclass_instance->lpVtbl->Release(pclass_instance);
pclass_instance = NULL;
}
if (text_object != NULL) {
text_object->lpVtbl->Release(text_object);
text_object = NULL;
}
if (pout_params != NULL) {
pout_params->lpVtbl->Release(pout_params);
pout_params = NULL;
}
if (wide_name != NULL) {
free(wide_name);
wide_name = NULL;
}
VariantClear(&vt_prop);
VariantClear(&switch_setting_path);
if (!retval) {
get_hres_error(hres);
}
CoUninitialize();
return retval;
}