# Copyright 2016 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. """ Base WMI utility class. """ import importlib import sys import threading import time from oslo_log import log as logging from oslo_utils import reflection if sys.platform == 'win32': import wmi LOG = logging.getLogger(__name__) class BaseUtils(object): _WMI_CONS = {} def _get_wmi_obj(self, moniker, **kwargs): return wmi.WMI(moniker=moniker, **kwargs) def _get_wmi_conn(self, moniker, **kwargs): if sys.platform != 'win32': return None if kwargs: return self._get_wmi_obj(moniker, **kwargs) if moniker in self._WMI_CONS: return self._WMI_CONS[moniker] wmi_conn = self._get_wmi_obj(moniker) self._WMI_CONS[moniker] = wmi_conn return wmi_conn class BaseUtilsVirt(BaseUtils): _wmi_namespace = '//%s/root/virtualization/v2' _os_version = None _old_wmi = None def __init__(self, host='.'): self._vs_man_svc_attr = None self._host = host self._conn_attr = None self._compat_conn_attr = None @property def _conn(self): if not self._conn_attr: self._conn_attr = self._get_wmi_conn( self._wmi_namespace % self._host) return self._conn_attr @property def _compat_conn(self): if not self._compat_conn_attr: if not BaseUtilsVirt._os_version: # hostutils cannot be used for this, it would end up in # a circular import. os_version = wmi.WMI().Win32_OperatingSystem()[0].Version BaseUtilsVirt._os_version = list( map(int, os_version.split('.'))) if BaseUtilsVirt._os_version >= [6, 3]: self._compat_conn_attr = self._conn else: self._compat_conn_attr = self._get_wmi_compat_conn( moniker=self._wmi_namespace % self._host) return self._compat_conn_attr @property def _vs_man_svc(self): if self._vs_man_svc_attr: return self._vs_man_svc_attr vs_man_svc = self._compat_conn.Msvm_VirtualSystemManagementService()[0] if BaseUtilsVirt._os_version >= [6, 3]: # NOTE(claudiub): caching this property on Windows / Hyper-V Server # 2012 (using the old WMI) can lead to memory leaks. PyMI doesn't # have those issues, so we can safely cache it. self._vs_man_svc_attr = vs_man_svc return vs_man_svc def _get_wmi_compat_conn(self, moniker, **kwargs): # old WMI should be used on Windows / Hyper-V Server 2012 whenever # .GetText_ is used (e.g.: AddResourceSettings). PyMI's and WMI's # .GetText_ have different results. if not BaseUtilsVirt._old_wmi: old_wmi_path = "%s.py" % wmi.__path__[0] spec = importlib.util.spec_from_file_location('old_wmi', old_wmi_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) BaseUtilsVirt._old_wmi = module return BaseUtilsVirt._old_wmi.WMI(moniker=moniker, **kwargs) def _get_wmi_obj(self, moniker, compatibility_mode=False, **kwargs): if not BaseUtilsVirt._os_version: # hostutils cannot be used for this, it would end up in # a circular import. os_version = wmi.WMI().Win32_OperatingSystem()[0].Version BaseUtilsVirt._os_version = list(map(int, os_version.split('.'))) if not compatibility_mode or BaseUtilsVirt._os_version >= [6, 3]: return wmi.WMI(moniker=moniker, **kwargs) return self._get_wmi_compat_conn(moniker=moniker, **kwargs) class SynchronizedMeta(type): """Use an rlock to synchronize all class methods.""" def __init__(cls, cls_name, bases, attrs): super(SynchronizedMeta, cls).__init__(cls_name, bases, attrs) rlock = threading.RLock() for attr_name in attrs: attr = getattr(cls, attr_name) if callable(attr): decorated = SynchronizedMeta._synchronize( attr, cls_name, rlock) setattr(cls, attr_name, decorated) @staticmethod def _synchronize(func, cls_name, rlock): def wrapper(*args, **kwargs): f_qual_name = reflection.get_callable_name(func) t_request = time.time() try: with rlock: t_acquire = time.time() LOG.debug("Method %(method_name)s acquired rlock. " "Waited %(time_wait)0.3fs", dict(method_name=f_qual_name, time_wait=t_acquire - t_request)) return func(*args, **kwargs) finally: t_release = time.time() LOG.debug("Method %(method_name)s released rlock. " "Held %(time_held)0.3fs", dict(method_name=f_qual_name, time_held=t_release - t_acquire)) return wrapper