""" Copyright (C) 2009-2013 Oracle Corporation This file is part of VirtualBox Open Source Edition (OSE), as available from http://www.virtualbox.org. This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL) as published by the Free Software Foundation, in version 2 as it comes in the "COPYING" file of the VirtualBox OSE distribution. VirtualBox OSE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. """ import sys, os import traceback # To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes' VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None) VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None) if VBoxBinDir is None: # Will be set by the installer VBoxBinDir = "%VBOX_INSTALL_PATH%" if VBoxSdkDir is None: # Will be set by the installer VBoxSdkDir = "%VBOX_SDK_PATH%" os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir os.environ["VBOX_SDK_PATH"] = VBoxSdkDir sys.path.append(VBoxBinDir) from VirtualBox_constants import VirtualBoxReflectionInfo class PerfCollector: """ This class provides a wrapper over IPerformanceCollector in order to get more 'pythonic' interface. To begin collection of metrics use setup() method. To get collected data use query() method. It is possible to disable metric collection without changing collection parameters with disable() method. The enable() method resumes metric collection. """ def __init__(self, mgr, vbox): """ Initializes the instance. """ self.mgr = mgr self.isMscom = (mgr.type == 'MSCOM') self.collector = vbox.performanceCollector def setup(self, names, objects, period, nsamples): """ Discards all previously collected values for the specified metrics, sets the period of collection and the number of retained samples, enables collection. """ self.collector.setupMetrics(names, objects, period, nsamples) def enable(self, names, objects): """ Resumes metric collection for the specified metrics. """ self.collector.enableMetrics(names, objects) def disable(self, names, objects): """ Suspends metric collection for the specified metrics. """ self.collector.disableMetrics(names, objects) def query(self, names, objects): """ Retrieves collected metric values as well as some auxiliary information. Returns an array of dictionaries, one dictionary per metric. Each dictionary contains the following entries: 'name': metric name 'object': managed object this metric associated with 'unit': unit of measurement 'scale': divide 'values' by this number to get float numbers 'values': collected data 'values_as_string': pre-processed values ready for 'print' statement """ # Get around the problem with input arrays returned in output # parameters (see #3953) for MSCOM. if self.isMscom: (values, names, objects, names_out, objects_out, units, scales, sequence_numbers, indices, lengths) = self.collector.queryMetricsData(names, objects) else: (values, names_out, objects_out, units, scales, sequence_numbers, indices, lengths) = self.collector.queryMetricsData(names, objects) out = [] for i in xrange(0, len(names_out)): scale = int(scales[i]) if scale != 1: fmt = '%.2f%s' else: fmt = '%d %s' out.append({ 'name':str(names_out[i]), 'object':str(objects_out[i]), 'unit':str(units[i]), 'scale':scale, 'values':[int(values[j]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))], 'values_as_string':'['+', '.join([fmt % (int(values[j])/scale, units[i]) for j in xrange(int(indices[i]), int(indices[i])+int(lengths[i]))])+']' }) return out def ComifyName(name): return name[0].capitalize()+name[1:] _COMForward = { 'getattr' : None, 'setattr' : None} def CustomGetAttr(self, attr): # fastpath if self.__class__.__dict__.get(attr) != None: return self.__class__.__dict__.get(attr) # try case-insensitivity workaround for class attributes (COM methods) for k in self.__class__.__dict__.keys(): if k.lower() == attr.lower(): self.__class__.__dict__[attr] = self.__class__.__dict__[k] return getattr(self, k) try: return _COMForward['getattr'](self, ComifyName(attr)) except AttributeError: return _COMForward['getattr'](self, attr) def CustomSetAttr(self, attr, value): try: return _COMForward['setattr'](self, ComifyName(attr), value) except AttributeError: return _COMForward['setattr'](self, attr, value) class PlatformMSCOM: # Class to fake access to constants in style of foo.bar.boo class ConstantFake: def __init__(self, parent, name): self.__dict__['_parent'] = parent self.__dict__['_name'] = name self.__dict__['_consts'] = {} try: self.__dict__['_depth']=parent.__dict__['_depth']+1 except: self.__dict__['_depth']=0 if self.__dict__['_depth'] > 4: raise AttributeError def __getattr__(self, attr): import win32com from win32com.client import constants if attr.startswith("__"): raise AttributeError consts = self.__dict__['_consts'] fake = consts.get(attr, None) if fake != None: return fake try: name = self.__dict__['_name'] parent = self.__dict__['_parent'] while parent != None: if parent._name is not None: name = parent._name+'_'+name parent = parent._parent if name is not None: name += "_" + attr else: name = attr return win32com.client.constants.__getattr__(name) except AttributeError, e: fake = PlatformMSCOM.ConstantFake(self, attr) consts[attr] = fake return fake class InterfacesWrapper: def __init__(self): self.__dict__['_rootFake'] = PlatformMSCOM.ConstantFake(None, None) def __getattr__(self, a): import win32com from win32com.client import constants if a.startswith("__"): raise AttributeError try: return win32com.client.constants.__getattr__(a) except AttributeError, e: return self.__dict__['_rootFake'].__getattr__(a) VBOX_TLB_GUID = '{46137EEC-703B-4FE5-AFD4-7C9BBBBA0259}' VBOX_TLB_LCID = 0 VBOX_TLB_MAJOR = 1 VBOX_TLB_MINOR = 0 def __init__(self, params): from win32com import universal from win32com.client import gencache, DispatchBaseClass from win32com.client import constants, getevents import win32com import pythoncom import win32api from win32con import DUPLICATE_SAME_ACCESS from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess import threading pid = GetCurrentProcess() self.tid = GetCurrentThreadId() handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS) self.handles = [] self.handles.append(handle) _COMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__'] DispatchBaseClass.__dict__['__getattr__'] = CustomGetAttr _COMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__'] DispatchBaseClass.__dict__['__setattr__'] = CustomSetAttr win32com.client.gencache.EnsureDispatch('VirtualBox.Session') win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox') self.oIntCv = threading.Condition() self.fInterrupted = False; def getSessionObject(self, vbox): import win32com from win32com.client import Dispatch return win32com.client.Dispatch("VirtualBox.Session") def getVirtualBox(self): import win32com from win32com.client import Dispatch return win32com.client.Dispatch("VirtualBox.VirtualBox") def getType(self): return 'MSCOM' def getRemote(self): return False def getArray(self, obj, field): return obj.__getattr__(field) def initPerThread(self): import pythoncom pythoncom.CoInitializeEx(0) def deinitPerThread(self): import pythoncom pythoncom.CoUninitialize() def createListener(self, impl, arg): d = {} d['BaseClass'] = impl d['arg'] = arg d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR str = "" str += "import win32com.server.util\n" str += "import pythoncom\n" str += "class ListenerImpl(BaseClass):\n" str += " _com_interfaces_ = ['IEventListener']\n" str += " _typelib_guid_ = tlb_guid\n" str += " _typelib_version_ = tlb_major, tlb_minor\n" str += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n" # Maybe we'd better implement Dynamic invoke policy, to be more flexible here str += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n" # capitalized version of listener method str += " HandleEvent=BaseClass.handleEvent\n" str += " def __init__(self): BaseClass.__init__(self, arg)\n" str += "result = win32com.server.util.wrap(ListenerImpl())\n" exec (str, d, d) return d['result'] def waitForEvents(self, timeout): from win32api import GetCurrentThreadId from win32event import INFINITE from win32event import MsgWaitForMultipleObjects, \ QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0 from pythoncom import PumpWaitingMessages import types if not isinstance(timeout, types.IntType): raise TypeError("The timeout argument is not an integer") if (self.tid != GetCurrentThreadId()): raise Exception("wait for events from the same thread you inited!") if timeout < 0: cMsTimeout = INFINITE else: cMsTimeout = timeout rc = MsgWaitForMultipleObjects(self.handles, 0, cMsTimeout, QS_ALLINPUT) if rc >= WAIT_OBJECT_0 and rc < WAIT_OBJECT_0+len(self.handles): # is it possible? rc = 2; elif rc==WAIT_OBJECT_0 + len(self.handles): # Waiting messages PumpWaitingMessages() rc = 0; else: # Timeout rc = 1; # check for interruption self.oIntCv.acquire() if self.fInterrupted: self.fInterrupted = False rc = 1; self.oIntCv.release() return rc; def interruptWaitEvents(self): """ Basically a python implementation of EventQueue::postEvent(). The magic value must be in sync with the C++ implementation or this won't work. Note that because of this method we cannot easily make use of a non-visible Window to handle the message like we would like to do. """ from win32api import PostThreadMessage from win32con import WM_USER self.oIntCv.acquire() self.fInterrupted = True self.oIntCv.release() try: PostThreadMessage(self.tid, WM_USER, None, 0xf241b819) except: return False; return True; def deinit(self): import pythoncom from win32file import CloseHandle for h in self.handles: if h is not None: CloseHandle(h) self.handles = None pythoncom.CoUninitialize() pass def queryInterface(self, obj, className): from win32com.client import CastTo return CastTo(obj, className) class PlatformXPCOM: def __init__(self, params): sys.path.append(VBoxSdkDir+'/bindings/xpcom/python/') import xpcom.vboxxpcom import xpcom import xpcom.components def getSessionObject(self, vbox): import xpcom.components return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance() def getVirtualBox(self): import xpcom.components return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance() def getType(self): return 'XPCOM' def getRemote(self): return False def getArray(self, obj, field): return obj.__getattr__('get'+ComifyName(field))() def initPerThread(self): import xpcom xpcom._xpcom.AttachThread() def deinitPerThread(self): import xpcom xpcom._xpcom.DetachThread() def createListener(self, impl, arg): d = {} d['BaseClass'] = impl d['arg'] = arg str = "" str += "import xpcom.components\n" str += "class ListenerImpl(BaseClass):\n" str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n" str += " def __init__(self): BaseClass.__init__(self, arg)\n" str += "result = ListenerImpl()\n" exec (str, d, d) return d['result'] def waitForEvents(self, timeout): import xpcom return xpcom._xpcom.WaitForEvents(timeout) def interruptWaitEvents(self): import xpcom return xpcom._xpcom.InterruptWait() def deinit(self): import xpcom xpcom._xpcom.DeinitCOM() def queryInterface(self, obj, className): import xpcom.components return obj.queryInterface(getattr(xpcom.components.interfaces, className)) class PlatformWEBSERVICE: def __init__(self, params): sys.path.append(os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')) #import VirtualBox_services import VirtualBox_wrappers from VirtualBox_wrappers import IWebsessionManager2 if params is not None: self.user = params.get("user", "") self.password = params.get("password", "") self.url = params.get("url", "") else: self.user = "" self.password = "" self.url = None self.vbox = None def getSessionObject(self, vbox): return self.wsmgr.getSessionObject(vbox) def getVirtualBox(self): return self.connect(self.url, self.user, self.password) def connect(self, url, user, passwd): if self.vbox is not None: self.disconnect() from VirtualBox_wrappers import IWebsessionManager2 if url is None: url = "" self.url = url if user is None: user = "" self.user = user if passwd is None: passwd = "" self.password = passwd self.wsmgr = IWebsessionManager2(self.url) self.vbox = self.wsmgr.logon(self.user, self.password) if not self.vbox.handle: raise Exception("cannot connect to '"+self.url+"' as '"+self.user+"'") return self.vbox def disconnect(self): if self.vbox is not None and self.wsmgr is not None: self.wsmgr.logoff(self.vbox) self.vbox = None self.wsmgr = None def getType(self): return 'WEBSERVICE' def getRemote(self): return True def getArray(self, obj, field): return obj.__getattr__(field) def initPerThread(self): pass def deinitPerThread(self): pass def createListener(self, impl, arg): raise Exception("no active listeners for webservices") def waitForEvents(self, timeout): # Webservices cannot do that yet return 2; def interruptWaitEvents(self, timeout): # Webservices cannot do that yet return False; def deinit(self): try: disconnect() except: pass def queryInterface(self, obj, className): d = {} d['obj'] = obj str = "" str += "from VirtualBox_wrappers import "+className+"\n" str += "result = "+className+"(obj.mgr, obj.handle)\n" # wrong, need to test if class indeed implements this interface exec (str, d, d) return d['result'] class SessionManager: def __init__(self, mgr): self.mgr = mgr def getSessionObject(self, vbox): return self.mgr.platform.getSessionObject(vbox) class VirtualBoxManager: def __init__(self, style, platparams): if style is None: if sys.platform == 'win32': style = "MSCOM" else: style = "XPCOM" exec "self.platform = Platform"+style+"(platparams)" # for webservices, enums are symbolic self.constants = VirtualBoxReflectionInfo(style == "WEBSERVICE") self.type = self.platform.getType() self.remote = self.platform.getRemote() self.style = style self.mgr = SessionManager(self) try: self.vbox = self.platform.getVirtualBox() except NameError, ne: print "Installation problem: check that appropriate libs in place" traceback.print_exc() raise ne except Exception, e: print "init exception: ", e traceback.print_exc() if self.remote: self.vbox = None else: raise e def getArray(self, obj, field): return self.platform.getArray(obj, field) def getVirtualBox(self): return self.platform.getVirtualBox() def __del__(self): self.deinit() def deinit(self): if hasattr(self, "vbox"): del self.vbox self.vbox = None if hasattr(self, "platform"): self.platform.deinit() self.platform = None def initPerThread(self): self.platform.initPerThread() def openMachineSession(self, mach, permitSharing = True): session = self.mgr.getSessionObject(self.vbox) if permitSharing: type = self.constants.LockType_Shared else: type = self.constants.LockType_Write mach.lockMachine(session, type) return session def closeMachineSession(self, session): if session is not None: session.unlockMachine() def deinitPerThread(self): self.platform.deinitPerThread() def createListener(self, impl, arg = None): return self.platform.createListener(impl, arg) def waitForEvents(self, timeout): """ Wait for events to arrive and process them. The timeout is in milliseconds. A negative value means waiting for ever, while 0 does not wait at all. Returns 0 if events was processed. Returns 1 if timed out or interrupted in some way. Returns 2 on error (like not supported for web services). Raises an exception if the calling thread is not the main thread (the one that initialized VirtualBoxManager) or if the time isn't an integer. """ return self.platform.waitForEvents(timeout) def interruptWaitEvents(self): """ Interrupt a waitForEvents call. This is normally called from a worker thread. Returns True on success, False on failure. """ return self.platform.interruptWaitEvents() def getPerfCollector(self, vbox): return PerfCollector(self, vbox) def getBinDir(self): global VBoxBinDir return VBoxBinDir def getSdkDir(self): global VBoxSdkDir return VBoxSdkDir def queryInterface(self, obj, className): return self.platform.queryInterface(obj, className)