| Server IP : 185.208.173.17 / Your IP : 87.236.161.98 Web Server : Microsoft-IIS/10.0 System : Windows NT SRV8576125506 10.0 build 26100 (Windows Server 2016) AMD64 User : IUSR ( 0) PHP Version : 7.4.13 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/Program Files/Cloudbase Solutions/Cloudbase-Init/Python/Lib/site-packages/wmi/ |
Upload File : |
# Copyright 2015 Cloudbase Solutions Srl
# All Rights Reserved.
#
# 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.
import abc
import ctypes
import datetime
import importlib
import re
import six
import struct
import weakref
import mi
from mi import mi_error
import pbr.version
try:
import eventlet
from eventlet import patcher
from eventlet import tpool
# If eventlet is installed and the 'thread' module is patched, we'll make
# sure that other greenthreads will not be blocked while WMI operations
# are performed by using tpool.
# This behavior can be disabled by using the following flag.
EVENTLET_NONBLOCKING_MODE_ENABLED = patcher.is_monkey_patched('thread')
except ImportError:
eventlet = None
EVENTLET_NONBLOCKING_MODE_ENABLED = False
__all__ = ['__version__']
version_info = pbr.version.VersionInfo('PyMI')
try:
__version__ = version_info.version_string()
except AttributeError:
__version__ = None
def avoid_blocking_call(f):
# Performs blocking calls in a different thread using tpool.execute
# when called from a greenthread.
# Note that eventlet.getcurrent will always return a greenlet object.
# Still, in case of a greenthread, the parent greenlet will always be the
# hub loop greenlet.
def wrapper(*args, **kwargs):
if (EVENTLET_NONBLOCKING_MODE_ENABLED and
eventlet.getcurrent().parent):
return tpool.execute(f, *args, **kwargs)
else:
return f(*args, **kwargs)
return wrapper
def _get_eventlet_original(module_name):
if eventlet:
return eventlet.patcher.original(module_name)
else:
return importlib.import_module(module_name)
# Default operation timeout in seconds.
# In order to enable it, this value must be set.
DEFAULT_OPERATION_TIMEOUT = None
WBEM_E_PROVIDER_NOT_CAPABLE = 0x80041024
class x_wmi(Exception):
def __init__(self, info="", com_error=None):
self.info = info
self.com_error = com_error
def __str__(self):
return "<x_wmi: %s %s>" % (
self.info or "Unexpected COM Error",
self.com_error or "(no underlying exception)"
)
class x_wmi_timed_out(x_wmi):
pass
class com_error(Exception):
def __init__(self, hresult, strerror, excepinfo, argerror):
self.hresult = hresult
self.strerror = strerror
self.excepinfo = excepinfo
self.argerror = argerror
def unsigned_to_signed(unsigned):
signed, = struct.unpack("l", struct.pack("L", unsigned))
return signed
def mi_to_wmi_exception(func):
def func_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except mi.error as ex:
d = ex.args[0]
hresult = unsigned_to_signed(d.get("error_code", 0))
err_msg = d.get("message") or ""
com_ex = com_error(
hresult, err_msg,
(0, None, err_msg, None, None, hresult),
None)
if(isinstance(ex, mi.timeouterror)):
raise x_wmi_timed_out(err_msg, com_ex)
else:
raise x_wmi(err_msg, com_ex)
return func_wrapper
_app = None
def _get_app():
global _app
if not _app:
_app = mi.Application()
return _app
class _Method(object):
def __init__(self, conn, target, method_name):
self._conn = conn
self._target = target
self._method_name = method_name
self._params = self._conn._get_method_params(target, method_name)
@avoid_blocking_call
@mi_to_wmi_exception
def __call__(self, *args, **kwargs):
return self._conn.invoke_method(
self._target, self._method_name, *args, **kwargs)
def __str__(self):
try:
args = []
# an MI object's length is the number of attributes the object it
# represents has.
for i in range(len(self._params)):
key, value_type, value = self._params.get_element(i)
args.append(key)
obj_string = '<function %s (%s)>' % (
self._method_name, ', '.join(args))
return obj_string
except Exception:
return super(_BaseEntity, self).__str__()
def __repr__(self):
return self.__str__()
class _Path(object):
"""
Provides a SWbemObjectPath replacement.
"""
def __init__(self, item):
self._item = item
def __str__(self):
return self.Path
@property
def Authority(self):
raise NotImplementedError()
@property
def Class(self):
return self._item.get_class_name()
@property
def DisplayName(self):
return self.Path()
@property
def IsClass(self):
return isinstance(self._item, _Class)
@property
def IsSingleton(self):
raise NotImplementedError()
@property
def Keys(self):
raise NotImplementedError()
@property
def Locale(self):
raise NotImplementedError()
@property
def Namespace(self):
return self._item.get_namespace()
@property
def ParentNamespace(self):
raise NotImplementedError()
@property
def Path(self):
return self._item.get_path()
@property
def RelPath(self):
path = self._item.get_path()
return path[path.find(':') + 1:]
@property
def Security_(self):
raise NotImplementedError()
@property
def Server(self):
return self._item.get_server_name()
@six.add_metaclass(abc.ABCMeta)
class _BaseEntity(object):
_convert_references = False
@abc.abstractmethod
def get_wrapped_object(self):
pass
@abc.abstractmethod
def get_class_name(self):
pass
@abc.abstractmethod
def get_class(self):
pass
def __str__(self):
try:
obj = self.get_wrapped_object()
obj_string = 'instance of %s\n{' % obj.get_class_name()
# an MI object's length is the number of attributes the object it
# represents has.
for i in range(len(obj)):
key, value_type, value = obj.get_element(i)
if value_type == mi.MI_STRING:
value = '"%s"' % value
obj_string += "\n\t%s = %s;" % (key, value)
obj_string += '\n};'
return obj_string
except Exception:
return super(_BaseEntity, self).__str__()
def __repr__(self):
try:
obj = self.get_wrapped_object()
return '<pymi_object: %s>' % obj.get_path()
except Exception:
return super(_BaseEntity, self).__repr__()
@mi_to_wmi_exception
def __getattr__(self, name):
try:
# If the class is an association class, certain of its properties
# are references which contain the paths to the associated objecs.
# The WMI module translates automatically into WMI objects those
# class properties that are references. To maintain the
# compatibility with the WMI module, those class properties that
# are references are translated into objects.
obj = self.get_wrapped_object()
return self._conn._wrap_element(
*obj.get_element(name),
convert_references=self._convert_references)
except mi.error:
try:
return _Method(self._conn, self, name)
except mi.error as err:
if err.args[0].get('mi_result') == (
mi_error.MI_RESULT_METHOD_NOT_FOUND):
err_msg = ("'%(cls_name)s' has no attribute "
"'%(attr_name)s'.")
raise AttributeError(
err_msg % dict(cls_name=self.get_class_name(),
attr_name=name))
else:
raise
@mi_to_wmi_exception
def path(self):
return _Path(self.get_wrapped_object())
class _Instance(_BaseEntity):
_convert_references = True
def __init__(self, conn, instance, use_conn_weak_ref=False):
if use_conn_weak_ref:
object.__setattr__(self, "_conn_ref", weakref.ref(conn))
else:
object.__setattr__(self, "_conn_ref", conn)
object.__setattr__(self, "_instance", instance)
object.__setattr__(self, "_cls_name", None)
@property
def _conn(self):
if isinstance(self._conn_ref, weakref.ref):
return self._conn_ref()
else:
return self._conn_ref
def get_wrapped_object(self):
return self._instance
def get_class_name(self):
if not self._cls_name:
object.__setattr__(self, '_cls_name',
self._instance.get_class_name())
return self._cls_name
def get_class(self):
class_name = self.get_class_name()
return self._conn.get_class(class_name)
@mi_to_wmi_exception
def __setattr__(self, name, value):
_, el_type, _ = self._instance.get_element(name)
self._instance[six.text_type(name)] = self._conn._unwrap_element(
el_type, value)
@mi_to_wmi_exception
def associators(self, wmi_association_class=u"", wmi_result_class=u"",
operation_options=None):
return self._conn.get_associators(
self, wmi_association_class, wmi_result_class,
operation_options)
@mi_to_wmi_exception
def path_(self):
return self._instance.get_path()
@mi_to_wmi_exception
def GetText_(self, text_format):
return self._conn.serialize_instance(self)
@mi_to_wmi_exception
def put(self, operation_options=None):
if not self._instance.get_path():
self._conn.create_instance(self, operation_options)
else:
self._conn.modify_instance(self, operation_options)
@mi_to_wmi_exception
def Delete_(self, operation_options=None):
self._conn.delete_instance(self, operation_options)
@mi_to_wmi_exception
def set(self, **kwargs):
for k, v in kwargs.items():
self.__setattr__(k, v)
class _Class(_BaseEntity):
def __init__(self, conn, class_name, cls):
self._conn = conn
self.class_name = six.text_type(class_name)
self._cls = cls
def get_wrapped_object(self):
return self._cls
def get_class_name(self):
return self.class_name
def get_class(self):
return self
@mi_to_wmi_exception
def __call__(self, *argc, **argv):
operation_options = argv.pop("operation_options", None)
fields = ""
for i, v in enumerate(argc):
if i > 0:
raise ValueError('Invalid argument')
if not isinstance(v, list):
raise ValueError('Invalid argument')
# TODO: sanitize input
fields = ", ".join(v)
if not fields:
fields = "*"
# TODO: sanitize input
filter = " and ".join(
"%(k)s = '%(v)s'" % {'k': k, 'v': v} for k, v in argv.items())
if filter:
where = " where %s" % filter
else:
where = ""
wql = (u"select %(fields)s from %(class_name)s%(where)s" %
{"fields": fields,
"class_name": self.class_name,
"where": where})
return self._conn.query(
wql, operation_options=operation_options)
@mi_to_wmi_exception
def new(self):
return self._conn.new_instance_from_class(self)
@mi_to_wmi_exception
def watch_for(self, raw_wql=None, notification_type="operation",
wmi_class=None, delay_secs=1, fields=[], **where_clause):
return _EventWatcher(self._conn, six.text_type(raw_wql))
class _EventWatcher(object):
def __init__(self, conn, wql):
native_threading = _get_eventlet_original('threading')
self._conn = conn
self._events_queue = []
self._error = None
self._event = native_threading.Event()
self._operation = conn.subscribe(
wql, self._indication_result, self.close)
self._operation_finished = native_threading.Event()
def _process_events(self):
if self._error:
err = self._error
self._error = None
raise x_wmi(info=err[1])
if self._events_queue:
return self._events_queue.pop(0)
@avoid_blocking_call
def __call__(self, timeout_ms=-1):
while True:
try:
event = self._process_events()
if event:
return event
timeout = timeout_ms / 1000.0 if timeout_ms else None
if not self._event.wait(timeout):
raise x_wmi_timed_out()
self._event.clear()
finally:
if (not self._operation or
not self._operation.has_more_results()):
self.close()
raise x_wmi("No more events")
def _indication_result(self, instance, bookmark, machine_id, more_results,
result_code, error_string, error_details):
if not more_results:
self._operation_finished.set()
if instance:
event = _Instance(self._conn,
instance[u"TargetInstance"].clone(),
use_conn_weak_ref=True)
try:
previous_inst = _Instance(
self._conn, instance[u'PreviousInstance'].clone(),
use_conn_weak_ref=True)
object.__setattr__(event, 'previous', previous_inst)
except (mi.error, AttributeError):
# The 'PreviousInstance' attribute may be missing, for
# example in case of a creation event or simply
# because this field was not requested.
pass
self._events_queue.append(event)
if error_details:
self._error = (
result_code, error_string,
_Instance(
self._conn, error_details.clone(),
use_conn_weak_ref=True))
self._event.set()
@avoid_blocking_call
def _wait_for_operation_cancel(self):
self._operation_finished.wait()
def close(self):
if self._operation:
self._operation.cancel()
# Those operations are asynchronous. We'll need to wait for the
# subscription to be canceled before deallocating objects,
# otherwise MI can crash when receiving further events. We rely on
# the fact that an event will be emitted once the subscription is
# canceled.
self._wait_for_operation_cancel()
self._operation.close()
self._event.set()
self._operation = None
self._timeout_ms = None
self._events_queue = []
self._conn = None
def __del__(self):
self.close()
class _Connection(object):
def __init__(self, computer_name=".", ns="root/cimv2", locale_name=None,
protocol=mi.PROTOCOL_WMIDCOM, cache_classes=True,
operation_timeout=None, user="", password="",
user_cert_thumbprint="", auth_type="", transport=None):
self._ns = six.text_type(ns)
self._app = _get_app()
self._protocol = six.text_type(protocol)
self._computer_name = six.text_type(computer_name)
self._transport = transport
self._locale_name = locale_name
self._op_timeout = operation_timeout or DEFAULT_OPERATION_TIMEOUT
self._user = user
self._password = password
self._auth_type = auth_type
self._cert_thumbprint = user_cert_thumbprint
self._set_destination_options()
self._session = self._app.create_session(
computer_name=self._computer_name,
protocol=self._protocol,
destination_options=self._destination_options)
self._cache_classes = cache_classes
self._class_cache = {}
self._method_params_cache = {}
self._notify_on_close = []
def _set_destination_options(self):
self._destination_options = self._app.create_destination_options()
if self._locale_name:
self._destination_options.set_ui_locale(
locale_name=six.text_type(self._locale_name))
if self._op_timeout is not None:
timeout = datetime.timedelta(0, self._op_timeout, 0)
self._destination_options.set_timeout(timeout)
if self._transport:
self._destination_options.set_transport(self._transport)
if self._user or self._cert_thumbprint:
user, domain = self._get_username_and_domain()
self._destination_options.add_credentials(
self._auth_type, domain, user, self._password,
self._cert_thumbprint)
def _get_username_and_domain(self):
username = self._user.replace("/", "\\")
if "\\" in username:
domain, user = username.split("\\")
elif "@" in username:
user, domain = username.split("@")
else:
user, domain = username, ""
return user, domain
def _close(self):
for callback in self._notify_on_close:
callback()
self._notify_on_close = []
self._session = None
self._app = None
@mi_to_wmi_exception
def __del__(self):
self._close()
@mi_to_wmi_exception
def __getattr__(self, name):
return self.get_class(six.text_type(name))
def _get_instances(self, op):
l = []
i = op.get_next_instance()
while i is not None:
l.append(_Instance(self, i.clone()))
i = op.get_next_instance()
return l
def _get_mi_operation_options(self, operation_options=None):
if not operation_options:
return
mi_op_options = self._app.create_operation_options()
if operation_options.get('operation_timeout') is not None:
operation_timeout = operation_options['operation_timeout']
timeout = datetime.timedelta(0, operation_timeout, 0)
mi_op_options.set_timeout(timeout)
for option in operation_options.get('custom_options', []):
# The value_type must be a MI type, such as MI_Array.
# The value object will then be 'casted' to that type.
option_value = self._unwrap_element(option['value_type'],
option['value'])
mi_op_options.set_custom_option(
name=six.text_type(option['name']),
value_type=option['value_type'],
value=option_value,
must_comply=option.get('must_comply', True))
return mi_op_options
@mi_to_wmi_exception
@avoid_blocking_call
def query(self, wql, operation_options=None):
wql = wql.replace("\\", "\\\\")
operation_options = self._get_mi_operation_options(
operation_options=operation_options)
with self._session.exec_query(
ns=self._ns, query=six.text_type(wql),
operation_options=operation_options) as q:
return self._get_instances(q)
@mi_to_wmi_exception
@avoid_blocking_call
def get_associators(self, instance, wmi_association_class=u"",
wmi_result_class=u"",
operation_options=None):
operation_options = self._get_mi_operation_options(
operation_options=operation_options)
with self._session.get_associators(
ns=self._ns, instance=instance._instance,
assoc_class=six.text_type(wmi_association_class),
result_class=six.text_type(wmi_result_class),
operation_options=operation_options) as q:
return self._get_instances(q)
def _get_method_params(self, target, method_name):
params = None
class_name = None
if self._cache_classes:
class_name = target.get_class_name()
params = self._method_params_cache.get((class_name, method_name))
if params is not None:
params = params.clone()
else:
mi_class = target.get_class().get_wrapped_object()
params = self._app.create_method_params(
mi_class, six.text_type(method_name))
if self._cache_classes:
self._method_params_cache[(class_name, method_name)] = params
params = params.clone()
return params
@mi_to_wmi_exception
def invoke_method(self, target, method_name, *args, **kwargs):
mi_target = target.get_wrapped_object()
params = self._get_method_params(target, method_name)
operation_options = self._get_mi_operation_options(
operation_options=kwargs.pop('operation_options', None))
for i, v in enumerate(args):
_, el_type, _ = params.get_element(i)
params[i] = self._unwrap_element(el_type, v)
for k, v in kwargs.items():
_, el_type, _ = params.get_element(k)
params[k] = self._unwrap_element(el_type, v)
if not params:
params = None
with self._session.invoke_method(
mi_target, six.text_type(method_name), params,
operation_options) as op:
l = []
r = op.get_next_instance()
elements = []
for i in six.moves.range(0, len(r)):
elements.append(r.get_element(i))
# Sort the output params by name before returning their values.
# The WINRM and WMIDCOM protocols behave differently in how
# returned elements are ordered. This hack aligns with the WMIDCOM
# behaviour to retain compatibility with the wmi.py module.
for element in sorted(elements, key=lambda element: element[0]):
# Workaround to avoid including the return value if the method
# returns void, as there's no direct way to determine it.
# This won't work if the method is expected to return a
# boolean value!!
if element != ('ReturnValue', mi.MI_BOOLEAN, True):
l.append(self._wrap_element(*element))
return tuple(l)
@mi_to_wmi_exception
@avoid_blocking_call
def new_instance_from_class(self, cls):
return _Instance(
self, self._app.create_instance_from_class(
cls.class_name, cls.get_wrapped_object()))
@mi_to_wmi_exception
def serialize_instance(self, instance):
with self._app.create_serializer() as s:
return s.serialize_instance(instance._instance)
@mi_to_wmi_exception
def get_class(self, class_name):
cls = None
if self._cache_classes:
cls = self._class_cache.get(class_name)
if cls is None:
cls = self._get_mi_class(class_name)
if self._cache_classes and cls:
self._class_cache[class_name] = cls
if cls is not None:
return _Class(self, class_name, cls)
@avoid_blocking_call
def _get_mi_class(self, class_name):
with self._session.get_class(
ns=self._ns, class_name=class_name) as op:
cls = op.get_next_class()
cls = cls.clone() if cls is not None else cls
return cls
@mi_to_wmi_exception
@avoid_blocking_call
def get_instance(self, class_name, key):
c = self.get_class(class_name)
key_instance = self.new_instance_from_class(c)
for k, v in key.items():
key_instance._instance[six.text_type(k)] = v
with self._session.get_instance(
self._ns, key_instance._instance) as op:
instance = op.get_next_instance()
if instance:
return _Instance(self, instance.clone())
@mi_to_wmi_exception
@avoid_blocking_call
def create_instance(self, instance, operation_options=None):
operation_options = self._get_mi_operation_options(
operation_options=operation_options)
self._session.create_instance(self._ns, instance._instance,
operation_options)
@mi_to_wmi_exception
@avoid_blocking_call
def modify_instance(self, instance, operation_options=None):
operation_options = self._get_mi_operation_options(
operation_options=operation_options)
self._session.modify_instance(self._ns, instance._instance,
operation_options)
@mi_to_wmi_exception
@avoid_blocking_call
def _delete_instance(self, session, instance, operation_options=None):
operation_options = self._get_mi_operation_options(
operation_options=operation_options)
session.delete_instance(self._ns, instance._instance,
operation_options)
def delete_instance(self, instance, operation_options=None):
try:
self._delete_instance(self._session, instance,
operation_options)
except x_wmi as exc:
# Deleting an instance using WMIDCOM can fail with
# WBEM_E_PROVIDER_NOT_CAPABLE.
# One affected WMI class is root/cimv2:WT_Host, there may
# be others as well.
err = ctypes.c_uint(exc.com_error.hresult).value
if (err == WBEM_E_PROVIDER_NOT_CAPABLE and
self._protocol != mi.PROTOCOL_WINRM):
tmp_session = self._app.create_session(
computer_name=self._computer_name,
protocol=mi.PROTOCOL_WINRM,
destination_options=self._destination_options)
self._delete_instance(tmp_session, instance,
operation_options)
else:
raise
@mi_to_wmi_exception
def subscribe(self, query, indication_result_callback, close_callback):
op = self._session.subscribe(
self._ns, six.text_type(query), indication_result_callback)
self._notify_on_close.append(close_callback)
return op
@mi_to_wmi_exception
def watch_for(self, raw_wql=None, notification_type="operation",
wmi_class=None, delay_secs=1, fields=[], **where_clause):
return _EventWatcher(self, six.text_type(raw_wql))
def _wrap_element(self, name, el_type, value, convert_references=False):
if isinstance(value, mi.Instance):
if el_type == mi.MI_INSTANCE:
return _Instance(self, value.clone())
elif el_type == mi.MI_REFERENCE:
if convert_references:
# Reload the object to populate all properties
return WMI(value.get_path(),
locale_name=self._locale_name,
operation_timeout=self._op_timeout,
user=self._user,
password=self._password,
user_cert_thumbprint=self._cert_thumbprint,
auth_type=self._auth_type,
transport=self._transport,
protocol=self._protocol)
return value.get_path()
else:
raise Exception(
"Unsupported instance element type: %s" % el_type)
if isinstance(value, (tuple, list)):
if el_type == mi.MI_REFERENCEA:
return tuple([i.get_path() for i in value])
elif el_type == mi.MI_INSTANCEA:
return tuple([_Instance(self, i.clone()) for i in value])
else:
return tuple(value)
else:
return value
def _unwrap_element(self, el_type, value):
if value is not None:
if el_type == mi.MI_REFERENCE:
instance = WMI(value,
locale_name=self._locale_name,
operation_timeout=self._op_timeout,
user=self._user,
password=self._password,
user_cert_thumbprint=self._cert_thumbprint,
auth_type=self._auth_type,
transport=self._transport,
protocol=self._protocol)
if instance is None:
raise Exception("Reference not found: %s" % value)
return instance._instance
elif el_type == mi.MI_INSTANCE:
return value._instance
elif el_type == mi.MI_BOOLEAN:
if isinstance(value, (str, six.text_type)):
return value.lower() in ['true', 'yes', '1']
else:
return value
elif el_type & mi.MI_ARRAY:
l = []
for item in value:
l.append(self._unwrap_element(el_type ^ mi.MI_ARRAY,
item))
return tuple(l)
else:
return value
def _parse_moniker(moniker):
PROTOCOL = "winmgmts:"
computer_name = '.'
namespace = None
path = None
class_name = None
key = None
m = re.match("(?:" + PROTOCOL + r")?//([^/]+)/([^:]*)(?::(.*))?", moniker)
if m:
computer_name, namespace, path = m.groups()
if path:
m = re.match("([^.]+).(.*)", path)
if m:
key = {}
class_name, kvs = m.groups()
for kv in kvs.split(","):
m = re.match("([^=]+)=\"(.*)\"", kv)
if not m:
m = re.match("([^=]+)=(.*)", kv)
name, value = m.groups()
# TODO: improve unescaping
key[name] = value.replace("//", "\\")
else:
class_name = path
else:
namespace = moniker
return (computer_name, namespace, class_name, key)
@mi_to_wmi_exception
def WMI(moniker="root/cimv2", privileges=None, locale_name=None, computer="",
user="", password="", user_cert_thumbprint="",
auth_type=mi.MI_AUTH_TYPE_DEFAULT, operation_timeout=None,
transport=None, protocol=mi.PROTOCOL_WMIDCOM):
computer_name, ns, class_name, key = _parse_moniker(
moniker.replace("\\", "/"))
if computer_name == '.':
computer_name = computer or '.'
conn = _Connection(computer_name=computer_name, ns=ns,
locale_name=locale_name,
operation_timeout=operation_timeout,
user=user, password=password,
user_cert_thumbprint=user_cert_thumbprint,
auth_type=auth_type,
transport=transport,
protocol=protocol)
if not class_name:
# Perform a simple operation to ensure the connection works.
# This is needed for compatibility with the WMI module.
conn.__provider
return conn
else:
return conn.get_instance(class_name, key)