diff options
Diffstat (limited to 'src/VBox/Main/glue/vboxapi.py')
-rw-r--r--[-rwxr-xr-x] | src/VBox/Main/glue/vboxapi.py | 1057 |
1 files changed, 834 insertions, 223 deletions
diff --git a/src/VBox/Main/glue/vboxapi.py b/src/VBox/Main/glue/vboxapi.py index 66bc71bc..3cda567d 100755..100644 --- a/src/VBox/Main/glue/vboxapi.py +++ b/src/VBox/Main/glue/vboxapi.py @@ -1,5 +1,12 @@ +# -*- coding: utf-8 -*- +# $Id: vboxapi.py $ """ -Copyright (C) 2009-2012 Oracle Corporation +VirtualBox Python API Glue. +""" + +__copyright__ = \ +""" +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; @@ -9,30 +16,47 @@ 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. """ +__version__ = "$Revision: 91042 $" + + +# Note! To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes' -import sys,os + +# Standard Python imports. +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) +# +# Globals, environment and sys.path changes. +# +VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None) +VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None) -if VboxBinDir is None: +if VBoxBinDir is None: # Will be set by the installer - VboxBinDir = "%VBOX_INSTALL_PATH%" + VBoxBinDir = "%VBOX_INSTALL_PATH%" +else: + VBoxBinDir = os.path.abspath(VBoxBinDir); -if VboxSdkDir is None: +if VBoxSdkDir is None: # Will be set by the installer - VboxSdkDir = "%VBOX_SDK_PATH%" + VBoxSdkDir = "%VBOX_SDK_PATH%" +else: + VBoxSdkDir = os.path.abspath(VBoxSdkDir); + +os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir +os.environ["VBOX_SDK_PATH"] = VBoxSdkDir +sys.path.append(VBoxBinDir) -os.environ["VBOX_PROGRAM_PATH"] = VboxBinDir -os.environ["VBOX_SDK_PATH"] = VboxSdkDir -sys.path.append(VboxBinDir) +# +# Import the generated VirtualBox constants. +# from VirtualBox_constants import VirtualBoxReflectionInfo -class PerfCollector: + +class PerfCollector(object): """ This class provides a wrapper over IPerformanceCollector in order to get more 'pythonic' interface. @@ -106,36 +130,265 @@ class PerfCollector: }) return out +# +# Attribute hacks. +# 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) + return name[0].capitalize() + name[1:] + + +## This is for saving the original DispatchBaseClass __getattr__ and __setattr__ +# method references. +_g_dCOMForward = { + 'getattr': None, + 'setattr': None, +} + +def _CustomGetAttr(self, sAttr): + """ Our getattr replacement for DispatchBaseClass. """ + # Fastpath. + oRet = self.__class__.__dict__.get(sAttr); + if oRet != None: + return oRet; + + # Try case-insensitivity workaround for class attributes (COM methods). + sAttrLower = sAttr.lower(); + for sKey in self.__class__.__dict__.keys(): + if sKey.lower() == sAttrLower: + self.__class__.__dict__[sAttr] = self.__class__.__dict__[sKey] + return getattr(self, sKey) + + # Slow path. try: - return _COMForward['getattr'](self,ComifyName(attr)) + return _g_dCOMForward['getattr'](self, ComifyName(sAttr)) except AttributeError: - return _COMForward['getattr'](self,attr) + return _g_dCOMForward['getattr'](self, sAttr) -def CustomSetAttr(self, attr, value): +def _CustomSetAttr(self, sAttr, oValue): + """ Our setattr replacement for DispatchBaseClass. """ try: - return _COMForward['setattr'](self, ComifyName(attr), value) + return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue) except AttributeError: - return _COMForward['setattr'](self, attr, value) + return _g_dCOMForward['setattr'](self, sAttr, oValue) + + + +class PlatformBase(object): + """ + Base class for the platform specific code. + """ + + def __init__(self, aoParams): + _ = aoParams; + + def getVirtualBox(self): + """ + Gets a the IVirtualBox singleton. + """ + return None; + + def getSessionObject(self, oIVBox): + """ + Get a session object that can be used for opening machine sessions. + + The oIVBox parameter is an getVirtualBox() return value, i.e. an + IVirtualBox reference. + + See also openMachineSession. + """ + _ = oIVBox; + return None; + + def getType(self): + """ Returns the platform type (class name sans 'Platform'). """ + return None; + + def isRemote(self): + """ + Returns True if remote (web services) and False if local (COM/XPCOM). + """ + return False + + def getArray(self, oInterface, sAttrib): + """ + Retrives the value of the array attribute 'sAttrib' from + interface 'oInterface'. + + This is for hiding platform specific differences in attributes + returning arrays. + """ + _ = oInterface; + _ = sAttrib; + return None; + + def initPerThread(self): + """ + Does backend specific initialization for the calling thread. + """ + return True; + + def deinitPerThread(self): + """ + Does backend specific uninitialization for the calling thread. + """ + return True; + + def createListener(self, oImplClass, dArgs): + """ + Instantiates and wraps an active event listener class so it can be + passed to an event source for registration. + + oImplClass is a class (type, not instance) which implements + IEventListener. + + dArgs is a dictionary with string indexed variables. This may be + modified by the method to pass platform specific parameters. Can + be None. + + This currently only works on XPCOM. COM support is not possible due to + shortcuts taken in the COM bridge code, which is not under our control. + Use passive listeners for COM and web services. + """ + _ = oImplClass; + _ = dArgs; + raise Exception("No active listeners for this platform"); + return None; + + def waitForEvents(self, cMsTimeout): + """ + Wait for events to arrive and process them. + + The timeout (cMsTimeout) is in milliseconds for how long to wait for + events to arrive. 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. + """ + _ = cMsTimeout; + return 2; + + def interruptWaitEvents(self): + """ + Interrupt a waitForEvents call. + This is normally called from a worker thread to wake up the main thread. + + Returns True on success, False on failure. + """ + return False; + + def deinit(self): + """ + Unitializes the platform specific backend. + """ + return None; + + def queryInterface(self, oIUnknown, sClassName): + """ + IUnknown::QueryInterface wrapper. + + oIUnknown is who to ask. + sClassName is the name of the interface we're asking for. + """ + return None; + + # + # Error (exception) access methods. + # + + def xcptGetStatus(self, oXcpt): + """ + Returns the COM status code from the VBox API given exception. + """ + return None; + + def xcptIsDeadInterface(self, oXcpt): + """ + Returns True if the exception indicates that the interface is dead, False if not. + """ + return False; + + def xcptIsEqual(self, oXcpt, hrStatus): + """ + Checks if the exception oXcpt is equal to the COM/XPCOM status code + hrStatus. + + The oXcpt parameter can be any kind of object, we'll just return True + if it doesn't behave like a our exception class. + + Will not raise any exception as long as hrStatus and self are not bad. + """ + try: + hrXcpt = self.xcptGetStatus(oXcpt); + except AttributeError: + return False; + if hrXcpt == hrStatus: + return True; + + # Fudge for 32-bit signed int conversion. + if hrStatus > 0x7fffffff and hrStatus <= 0xffffffff and hrXcpt < 0: + if (hrStatus - 0x100000000) == hrXcpt: + return True; + return False; + + def xcptGetMessage(self, oXcpt): + """ + Returns the best error message found in the COM-like exception. + Returns None to fall back on xcptToString. + Raises exception if oXcpt isn't our kind of exception object. + """ + return None; + + def xcptGetBaseXcpt(self): + """ + Returns the base exception class. + """ + return None; + + def xcptSetupConstants(self, oDst): + """ + Copy/whatever all error constants onto oDst. + """ + return oDst; + + @staticmethod + def xcptCopyErrorConstants(oDst, oSrc): + """ + Copy everything that looks like error constants from oDst to oSrc. + """ + for sAttr in dir(oSrc): + if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'): + oAttr = getattr(oSrc, sAttr); + if type(oAttr) is int: + setattr(oDst, sAttr, oAttr); + return oDst; + + + +class PlatformMSCOM(PlatformBase): + """ + Platform specific code for MS COM. + """ + + ## @name VirtualBox COM Typelib definitions (should be generate) + # + # @remarks Must be updated when the corresponding VirtualBox.xidl bits + # are changed. Fortunately this isn't very often. + # @{ + VBOX_TLB_GUID = '{D7569351-1750-46F0-936E-BD127D5BC264}' + VBOX_TLB_LCID = 0 + VBOX_TLB_MAJOR = 1 + VBOX_TLB_MINOR = 3 + ## @} + + + class ConstantFake(object): + """ Class to fake access to constants in style of foo.bar.boo """ -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 @@ -172,7 +425,7 @@ class PlatformMSCOM: else: name = attr return win32com.client.constants.__getattr__(name) - except AttributeError,e: + except AttributeError, e: fake = PlatformMSCOM.ConstantFake(self, attr) consts[attr] = fake return fake @@ -189,39 +442,90 @@ class PlatformMSCOM: raise AttributeError try: return win32com.client.constants.__getattr__(a) - except AttributeError,e: + 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, dParams): + PlatformBase.__init__(self, dParams); - 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): + # + # Since the code runs on all platforms, we have to do a lot of + # importing here instead of at the top of the file where it's normally located. + # + from win32com import universal + from win32com.client import gencache, DispatchBaseClass + from win32com.client import constants, getevents + import win32com + import pythoncom + import win32api + import winerror + from win32con import DUPLICATE_SAME_ACCESS + from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess + import threading + + self.winerror = winerror; + + pid = GetCurrentProcess() + self.tid = GetCurrentThreadId() + handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS) + self.handles = [] + self.handles.append(handle) + + # Hack the COM dispatcher base class so we can modify method and + # attribute names to match those in xpcom. + if _g_dCOMForward['setattr'] is None: + _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__'] + DispatchBaseClass.__dict__['__getattr__'] = _CustomGetAttr + _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__'] + DispatchBaseClass.__dict__['__setattr__'] = _CustomSetAttr + + # Hack the exception base class so the users doesn't need to check for + # XPCOM or COM and do different things. + ## @todo + + # + # Make sure the gencache is correct (we don't quite follow the COM + # versioning rules). + # + self.flushGenPyCache(win32com.client.gencache); + win32com.client.gencache.EnsureDispatch('VirtualBox.Session'); + win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox'); + + self.oIntCv = threading.Condition() + self.fInterrupted = False; + + _ = dParams; + + def flushGenPyCache(self, oGenCache): + """ + Flushes VBox related files in the win32com gen_py cache. + + This is necessary since we don't follow the typelib versioning rules + that everyeone else seems to subscribe to. + """ + # + # The EnsureModule method have broken validation code, it doesn't take + # typelib module directories into account. So we brute force them here. + # (It's possible the directory approach is from some older pywin + # version or the result of runnig makepy or gencache manually, but we + # need to cover it as well.) + # + sName = oGenCache.GetGeneratedFileName(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, + self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR); + sGenPath = oGenCache.GetGeneratePath(); + if len(sName) > 36 and len(sGenPath) > 5: + sTypelibPath = os.path.join(sGenPath, sName); + if os.path.isdir(sTypelibPath): + import shutil; + shutil.rmtree(sTypelibPath, ignore_errors = True); + + # + # Ensure that our typelib is valid. + # + return oGenCache.EnsureModule(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR); + + def getSessionObject(self, oIVBox): + _ = oIVBox import win32com from win32com.client import Dispatch return win32com.client.Dispatch("VirtualBox.Session") @@ -234,11 +538,8 @@ class PlatformMSCOM: def getType(self): return 'MSCOM' - def getRemote(self): - return False - - def getArray(self, obj, field): - return obj.__getattr__(field) + def getArray(self, oInterface, sAttrib): + return oInterface.__getattr__(sAttrib) def initPerThread(self): import pythoncom @@ -248,11 +549,18 @@ class PlatformMSCOM: import pythoncom pythoncom.CoUninitialize() - def createListener(self, impl, arg): + def createListener(self, oImplClass, dArgs): + if True: + raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() ' + 'returns new gateway objects all the time, thus breaking EventQueue ' + 'assumptions about the listener interface pointer being constants between calls '); + # Did this code ever really work? d = {} - d['BaseClass'] = impl - d['arg'] = arg - d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID + d['BaseClass'] = oImplClass + d['dArgs'] = dArgs + 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" @@ -260,16 +568,16 @@ class PlatformMSCOM: str += "class ListenerImpl(BaseClass):\n" str += " _com_interfaces_ = ['IEventListener']\n" str += " _typelib_guid_ = tlb_guid\n" - str += " _typelib_version_ = 1, 0\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 += " def __init__(self): BaseClass.__init__(self, dArgs)\n" str += "result = win32com.server.util.wrap(ListenerImpl())\n" - exec (str,d,d) + exec(str, d, d) return d['result'] def waitForEvents(self, timeout): @@ -285,8 +593,10 @@ class PlatformMSCOM: if (self.tid != GetCurrentThreadId()): raise Exception("wait for events from the same thread you inited!") - if timeout < 0: cMsTimeout = INFINITE - else: cMsTimeout = timeout + 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? @@ -310,7 +620,7 @@ class PlatformMSCOM: def interruptWaitEvents(self): """ - Basically a python implementation of EventQueue::postEvent(). + Basically a python implementation of NativeEventQueue::postEvent(). The magic value must be in sync with the C++ implementation or this won't work. @@ -340,18 +650,83 @@ class PlatformMSCOM: pythoncom.CoUninitialize() pass - def queryInterface(self, obj, klazzName): + def queryInterface(self, oIUnknown, sClassName): from win32com.client import CastTo - return CastTo(obj, klazzName) + return CastTo(oIUnknown, sClassName) + + def xcptGetStatus(self, oXcpt): + # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only + # empirical info on it so far. + hrXcpt = oXcpt.hresult + if hrXcpt == self.winerror.DISP_E_EXCEPTION: + try: hrXcpt = oXcpt.excepinfo[5]; + except: pass; + return hrXcpt; + + def xcptIsDeadInterface(self, oXcpt): + return self.xcptGetStatus(oXcpt) in [ + 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE. + 0x800706be, -2147023170, # RPC_S_CALL_FAILED. + 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE. + 0x80010108, -2147417848, # RPC_E_DISCONNECTED. + 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF + ]; + + + def xcptGetMessage(self, oXcpt): + if hasattr(oXcpt, 'excepinfo'): + try: + if len(oXcpt.excepinfo) >= 3: + sRet = oXcpt.excepinfo[2]; + if len(sRet) > 0: + return sRet[0:]; + except: + pass; + if hasattr(oXcpt, 'strerror'): + try: + sRet = oXcpt.strerror; + if len(sRet) > 0: + return sRet; + except: + pass; + return None; + + def xcptGetBaseXcpt(self): + import pythoncom; + return pythoncom.com_error; + + def xcptSetupConstants(self, oDst): + import winerror; + oDst = self.xcptCopyErrorConstants(oDst, winerror); + + # XPCOM compatability constants. + oDst.NS_OK = oDst.S_OK; + oDst.NS_ERROR_FAILURE = oDst.E_FAIL; + oDst.NS_ERROR_ABORT = oDst.E_ABORT; + oDst.NS_ERROR_NULL_POINTER = oDst.E_POINTER; + oDst.NS_ERROR_NO_INTERFACE = oDst.E_NOINTERFACE; + oDst.NS_ERROR_INVALID_ARG = oDst.E_INVALIDARG; + oDst.NS_ERROR_OUT_OF_MEMORY = oDst.E_OUTOFMEMORY; + oDst.NS_ERROR_NOT_IMPLEMENTED = oDst.E_NOTIMPL; + oDst.NS_ERROR_UNEXPECTED = oDst.E_UNEXPECTED; + return oDst; + + +class PlatformXPCOM(PlatformBase): + """ + Platform specific code for XPCOM. + """ -class PlatformXPCOM: - def __init__(self, params): - sys.path.append(VboxSdkDir+'/bindings/xpcom/python/') + def __init__(self, dParams): + PlatformBase.__init__(self, dParams); + sys.path.append(VBoxSdkDir+'/bindings/xpcom/python/') import xpcom.vboxxpcom import xpcom import xpcom.components + _ = dParams; - def getSessionObject(self, vbox): + def getSessionObject(self, oIVBox): + _ = oIVBox; import xpcom.components return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance() @@ -362,11 +737,8 @@ class PlatformXPCOM: def getType(self): return 'XPCOM' - def getRemote(self): - return False - - def getArray(self, obj, field): - return obj.__getattr__('get'+ComifyName(field))() + def getArray(self, oInterface, sAttrib): + return oInterface.__getattr__('get'+ComifyName(sAttrib))() def initPerThread(self): import xpcom @@ -376,17 +748,17 @@ class PlatformXPCOM: import xpcom xpcom._xpcom.DetachThread() - def createListener(self, impl, arg): + def createListener(self, oImplClass, dArgs): d = {} - d['BaseClass'] = impl - d['arg'] = arg + d['BaseClass'] = oImplClass + d['dArgs'] = dArgs 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 += " def __init__(self): BaseClass.__init__(self, dArgs)\n" str += "result = ListenerImpl()\n" - exec (str,d,d) + exec (str, d, d) return d['result'] def waitForEvents(self, timeout): @@ -401,33 +773,126 @@ class PlatformXPCOM: import xpcom xpcom._xpcom.DeinitCOM() - def queryInterface(self, obj, klazzName): + def queryInterface(self, oIUnknown, sClassName): import xpcom.components - return obj.queryInterface(getattr(xpcom.components.interfaces, klazzName)) + return oIUnknown.queryInterface(getattr(xpcom.components.interfaces, sClassName)) + + def xcptGetStatus(self, oXcpt): + return oXcpt.errno; -class PlatformWEBSERVICE: - def __init__(self, params): - sys.path.append(os.path.join(VboxSdkDir,'bindings', 'webservice', 'python', 'lib')) - #import VirtualBox_services + def xcptIsDeadInterface(self, oXcpt): + return self.xcptGetStatus(oXcpt) in [ + 0x80004004, -2147467260, # NS_ERROR_ABORT + 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED) + ]; + + def xcptGetMessage(self, oXcpt): + if hasattr(oXcpt, 'msg'): + try: + sRet = oXcpt.msg; + if len(sRet) > 0: + return sRet; + except: + pass; + return None; + + def xcptGetBaseXcpt(self): + import xpcom; + return xpcom.Exception; + + def xcptSetupConstants(self, oDst): + import xpcom; + oDst = self.xcptCopyErrorConstants(oDst, xpcom.nsError); + + # COM compatability constants. + oDst.E_ACCESSDENIED = -2147024891; # see VBox/com/defs.h + oDst.S_OK = oDst.NS_OK; + oDst.E_FAIL = oDst.NS_ERROR_FAILURE; + oDst.E_ABORT = oDst.NS_ERROR_ABORT; + oDst.E_POINTER = oDst.NS_ERROR_NULL_POINTER; + oDst.E_NOINTERFACE = oDst.NS_ERROR_NO_INTERFACE; + oDst.E_INVALIDARG = oDst.NS_ERROR_INVALID_ARG; + oDst.E_OUTOFMEMORY = oDst.NS_ERROR_OUT_OF_MEMORY; + oDst.E_NOTIMPL = oDst.NS_ERROR_NOT_IMPLEMENTED; + oDst.E_UNEXPECTED = oDst.NS_ERROR_UNEXPECTED; + oDst.DISP_E_EXCEPTION = -2147352567; # For COM compatability only. + return oDst; + + +class PlatformWEBSERVICE(PlatformBase): + """ + VirtualBox Web Services API specific code. + """ + + def __init__(self, dParams): + PlatformBase.__init__(self, dParams); + # Import web services stuff. Fix the sys.path the first time. + sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib'); + if sWebServLib not in sys.path: + sys.path.append(sWebServLib); 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", "") + # Initialize instance variables from parameters. + if dParams is not None: + self.user = dParams.get("user", "") + self.password = dParams.get("password", "") + self.url = dParams.get("url", "") else: - self.user = "" + self.user = "" self.password = "" - self.url = None - self.vbox = None + self.url = None + self.vbox = None + self.wsmgr = None; + + # + # Base class overrides. + # - def getSessionObject(self, vbox): - return self.wsmgr.getSessionObject(vbox) + def getSessionObject(self, oIVBox): + return self.wsmgr.getSessionObject(oIVBox) def getVirtualBox(self): return self.connect(self.url, self.user, self.password) + def getType(self): + return 'WEBSERVICE' + + def isRemote(self): + """ Returns True if remote VBox host, False if local. """ + return True + + def getArray(self, oInterface, sAttrib): + return oInterface.__getattr__(sAttrib) + + 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, oIUnknown, sClassName): + d = {} + d['oIUnknown'] = oIUnknown + str = "" + str += "from VirtualBox_wrappers import "+sClassName+"\n" + str += "result = "+sClassName+"(oIUnknown.mgr, oIUnknown.handle)\n" + # wrong, need to test if class indeed implements this interface + exec (str, d, d) + return d['result'] + + # + # Web service specific methods. + # + def connect(self, url, user, passwd): if self.vbox is not None: self.disconnect() @@ -449,164 +914,310 @@ class PlatformWEBSERVICE: 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 + self.wsmgr.logoff(self.vbox) + self.vbox = None + self.wsmgr = None - def getType(self): - return 'WEBSERVICE' - def getRemote(self): - return True +## The current (last) exception class. +# This is reinitalized whenever VirtualBoxManager is called, so it will hold +# the reference to the error exception class for the last platform/style that +# was used. Most clients does talk to multiple VBox instance on different +# platforms at the same time, so this should be sufficent for most uses and +# be way simpler to use than VirtualBoxManager::oXcptClass. +CurXctpClass = None; - 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 +class VirtualBoxManager(object): + """ + VirtualBox API manager class. - def queryInterface(self, obj, klazzName): - d = {} - d['obj'] = obj - str = "" - str += "from VirtualBox_wrappers import "+klazzName+"\n" - str += "result = "+klazzName+"(obj.mgr,obj.handle)\n" - # wrong, need to test if class indeed implements this interface - exec (str,d,d) - return d['result'] + The API users will have to instantiate this. If no parameters are given, + it will default to interface with the VirtualBox running on the local + machine. sStyle can be None (default), MSCOM, XPCOM or WEBSERVICES. Most + users will either be specifying None or WEBSERVICES. -class SessionManager: - def __init__(self, mgr): - self.mgr = mgr + The dPlatformParams is an optional dictionary for passing parameters to the + WEBSERVICE backend. + """ - def getSessionObject(self, vbox): - return self.mgr.platform.getSessionObject(vbox) + class Statuses(object): + def __init__(self): + pass; -class VirtualBoxManager: - def __init__(self, style, platparams): - if style is None: + def __init__(self, sStyle = None, dPlatformParams = None): + if sStyle is None: if sys.platform == 'win32': - style = "MSCOM" + sStyle = "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) - + sStyle = "XPCOM" + if sStyle == 'XPCOM': + self.platform = PlatformXPCOM(dPlatformParams); + elif sStyle == 'MSCOM': + self.platform = PlatformMSCOM(dPlatformParams); + elif sStyle == 'WEBSERVICE': + self.platform = PlatformWEBSERVICE(dPlatformParams); + else: + raise Exception('Unknown sStyle=%s' % (sStyle,)); + self.style = sStyle + self.type = self.platform.getType() + self.remote = self.platform.isRemote() + ## VirtualBox API constants (for webservices, enums are symbolic). + self.constants = VirtualBoxReflectionInfo(sStyle == "WEBSERVICE") + + ## Status constants. + self.statuses = self.platform.xcptSetupConstants(VirtualBoxManager.Statuses()); + ## @todo Add VBOX_E_XXX to statuses? They're already in constants... + ## Dictionary for errToString, built on demand. + self._dErrorValToName = None; + + ## The exception class for the selected platform. + self.oXcptClass = self.platform.xcptGetBaseXcpt(); + global CurXcptClass; + CurXcptClass = self.oXcptClass; + + # Get the virtualbox singleton. try: self.vbox = self.platform.getVirtualBox() - except NameError,ne: + except NameError, ne: print "Installation problem: check that appropriate libs in place" traceback.print_exc() raise ne - except Exception,e: - print "init exception: ",e + except Exception, e: + print "init exception: ", e traceback.print_exc() if self.remote: self.vbox = None else: raise e + ## @deprecated + # This used to refer to a session manager class with only one method + # called getSessionObject. The method has moved into this call. + self.mgr = self; + + def __del__(self): + self.deinit() - def getArray(self, obj, field): - return self.platform.getArray(obj, field) + def getPythonApiRevision(self): + """ + Returns a Python API revision number. + This will be incremented when features are added to this file. + """ + return 3; + + + # + # Wrappers for self.platform methods. + # def getVirtualBox(self): - return self.platform.getVirtualBox() + """ See PlatformBase::getVirtualBox(). """ + return self.platform.getVirtualBox() - def __del__(self): - self.deinit() + def getSessionObject(self, oIVBox): + """ See PlatformBase::getSessionObject(). """ + return self.platform.getSessionObject(oIVBox); + + def getArray(self, oInterface, sAttrib): + """ See PlatformBase::getArray(). """ + return self.platform.getArray(oInterface, sAttrib) + + def createListener(self, oImplClass, dArgs = None): + """ See PlatformBase::createListener(). """ + return self.platform.createListener(oImplClass, dArgs) + + def waitForEvents(self, cMsTimeout): + """ See PlatformBase::waitForEvents(). """ + return self.platform.waitForEvents(cMsTimeout) + + def interruptWaitEvents(self): + """ See PlatformBase::interruptWaitEvents(). """ + return self.platform.interruptWaitEvents() + + def queryInterface(self, oIUnknown, sClassName): + """ See PlatformBase::queryInterface(). """ + return self.platform.queryInterface(oIUnknown, sClassName) + + + # + # Init and uninit. + # + + def initPerThread(self): + """ See PlatformBase::deinitPerThread(). """ + self.platform.initPerThread() + + def deinitPerThread(self): + """ See PlatformBase::deinitPerThread(). """ + return self.platform.deinitPerThread() def deinit(self): + """ + For unitializing the manager. + Do not access it after calling this method. + """ if hasattr(self, "vbox"): del self.vbox self.vbox = None if hasattr(self, "platform"): self.platform.deinit() self.platform = None + return True; - 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 + # + # Utility methods. + # - def closeMachineSession(self, session): - if session is not None: - session.unlockMachine() + def openMachineSession(self, oIMachine, fPermitSharing = True): + """ + Attemts to open the a session to the machine. + Returns a session object on success. + Raises exception on failure. + """ + oSession = self.mgr.getSessionObject(self.vbox); + if fPermitSharing: + type = self.constants.LockType_Shared; + else: + type = self.constants.LockType_Write; + oIMachine.lockMachine(oSession, type); + return oSession; - def deinitPerThread(self): - self.platform.deinitPerThread() + def closeMachineSession(self, oSession): + """ + Closes a session opened by openMachineSession. + Ignores None parameters. + """ + if oSession is not None: + oSession.unlockMachine() + return True; - def createListener(self, impl, arg = None): - return self.platform.createListener(impl, arg) + def getPerfCollector(self, oIVBox): + """ + Returns a helper class (PerfCollector) for accessing performance + collector goodies. See PerfCollector for details. + """ + return PerfCollector(self, oIVBox) - def waitForEvents(self, timeout): + def getBinDir(self): """ - Wait for events to arrive and process them. + Returns the VirtualBox binary directory. + """ + global VBoxBinDir + return VBoxBinDir - The timeout is in milliseconds. A negative value means waiting for - ever, while 0 does not wait at all. + def getSdkDir(self): + """ + Returns the VirtualBox SDK directory. + """ + global VBoxSdkDir + return VBoxSdkDir - 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. + # + # Error code utilities. + # + + ## @todo port to webservices! + + def xcptGetStatus(self, oXcpt = None): + """ + Gets the status code from an exception. If the exception parameter + isn't specified, the current exception is examined. """ - return self.platform.waitForEvents(timeout) + if oXcpt is None: + oXcpt = sys.exc_info()[1]; + return self.platform.xcptGetStatus(oXcpt); - def interruptWaitEvents(self): + def xcptIsDeadInterface(self, oXcpt = None): """ - Interrupt a waitForEvents call. - This is normally called from a worker thread. + Returns True if the exception indicates that the interface is dead, + False if not. If the exception parameter isn't specified, the current + exception is examined. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1]; + return self.platform.xcptIsDeadInterface(oXcpt); - Returns True on success, False on failure. + def xcptIsOurXcptKind(self, oXcpt = None): """ - return self.platform.interruptWaitEvents() + Checks if the exception is one that could come from the VBox API. If + the exception parameter isn't specified, the current exception is + examined. + """ + if self.oXcptClass is None: ## @todo find the exception class for web services! + return False; + if oXcpt is None: + oXcpt = sys.exc_info()[1]; + return isinstance(oXcpt, self.oXcptClass); - def getPerfCollector(self, vbox): - return PerfCollector(self, vbox) + def xcptIsEqual(self, oXcpt, hrStatus): + """ + Checks if the exception oXcpt is equal to the COM/XPCOM status code + hrStatus. - def getBinDir(self): - global VboxBinDir - return VboxBinDir + The oXcpt parameter can be any kind of object, we'll just return True + if it doesn't behave like a our exception class. If it's None, we'll + query the current exception and examine that. - def getSdkDir(self): - global VboxSdkDir - return VboxSdkDir + Will not raise any exception as long as hrStatus and self are not bad. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1]; + return self.platform.xcptIsEqual(oXcpt, hrStatus); + + def xcptIsNotEqual(self, oXcpt, hrStatus): + """ + Negated xcptIsEqual. + """ + return not self.xcptIsEqual(oXcpt, hrStatus); + + def xcptToString(self, hrStatusOrXcpt = None): + """ + Converts the specified COM status code, or the status code of the + specified exception, to a C constant string. If the parameter isn't + specified (is None), the current exception is examined. + """ + + # Deal with exceptions. + if hrStatusOrXcpt is None or self.xcptIsOurXcptKind(hrStatusOrXcpt): + hrStatus = self.xcptGetStatus(hrStatusOrXcpt); + else: + hrStatus = hrStatusOrXcpt; + + # Build the dictionary on demand. + if self._dErrorValToName is None: + dErrorValToName = dict(); + for sKey in dir(self.statuses): + if sKey[0].isupper(): + oValue = getattr(self.statuses, sKey); + if type(oValue) is int: + dErrorValToName[oValue] = sKey; + self._dErrorValToName = dErrorValToName; + + # Do the lookup, falling back on formatting the status number. + try: + sStr = self._dErrorValToName[int(hrStatus)]; + except KeyError: + hrLong = long(hrStatus); + sStr = '%#x (%d)' % (hrLong, hrLong); + return sStr; + + def xcptGetMessage(self, oXcpt = None): + """ + Returns the best error message found in the COM-like exception. If the + exception parameter isn't specified, the current exception is examined. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1]; + sRet = self.platform.xcptGetMessage(oXcpt); + if sRet is None: + sRet = self.xcptToString(oXcpt); + return sRet; + + # Legacy, remove in a day or two. + errGetStatus = xcptGetStatus + errIsDeadInterface = xcptIsDeadInterface + errIsOurXcptKind = xcptIsOurXcptKind + errGetMessage = xcptGetMessage - def queryInterface(self, obj, klazzName): - return self.platform.queryInterface(obj, klazzName) |