VirtualBox

source: vbox/trunk/src/VBox/Main/glue/vboxapi.py@ 60682

Last change on this file since 60682 was 60463, checked in by vboxsync, 9 years ago

added CDDL headers to some Python bindings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.2 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vboxapi.py 60463 2016-04-12 16:21:27Z vboxsync $
3"""
4VirtualBox Python API Glue.
5"""
6
7__copyright__ = \
8 """
9 Copyright (C) 2009-2016 Oracle Corporation
10
11 This file is part of VirtualBox Open Source Edition (OSE), as
12 available from http://www.virtualbox.org. This file is free software;
13 you can redistribute it and/or modify it under the terms of the GNU
14 General Public License (GPL) as published by the Free Software
15 Foundation, in version 2 as it comes in the "COPYING" file of the
16 VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18
19 The contents of this file may alternatively be used under the terms
20 of the Common Development and Distribution License Version 1.0
21 (CDDL) only, as it comes in the "COPYING.CDDL" file of the
22 VirtualBox OSE distribution, in which case the provisions of the
23 CDDL are applicable instead of those of the GPL.
24
25 You may elect to license modified versions of this file under the
26 terms and conditions of either the GPL or the CDDL or both.
27 """
28__version__ = "$Revision: 60463 $"
29
30
31# Note! To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes'
32
33
34# Standard Python imports.
35import os
36import sys
37import traceback
38
39
40if sys.version_info >= (3, 0):
41 xrange = range
42 long = int
43
44#
45# Globals, environment and sys.path changes.
46#
47import platform;
48VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
49VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
50
51if VBoxBinDir is None:
52 if platform.system() == 'Darwin':
53 VBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS'
54 else: # Will be set by the installer
55 VBoxBinDir = "%VBOX_INSTALL_PATH%"
56else:
57 VBoxBinDir = os.path.abspath(VBoxBinDir)
58
59if VBoxSdkDir is None:
60 if platform.system() == 'Darwin':
61 VBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk'
62 else: # Will be set by the installer
63 VBoxSdkDir = "%VBOX_SDK_PATH%"
64else:
65 VBoxSdkDir = os.path.abspath(VBoxSdkDir)
66
67os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir
68os.environ["VBOX_SDK_PATH"] = VBoxSdkDir
69sys.path.append(VBoxBinDir)
70
71
72#
73# Import the generated VirtualBox constants.
74#
75from .VirtualBox_constants import VirtualBoxReflectionInfo
76
77
78class PerfCollector(object):
79 """ This class provides a wrapper over IPerformanceCollector in order to
80 get more 'pythonic' interface.
81
82 To begin collection of metrics use setup() method.
83
84 To get collected data use query() method.
85
86 It is possible to disable metric collection without changing collection
87 parameters with disable() method. The enable() method resumes metric
88 collection.
89 """
90
91 def __init__(self, mgr, vbox):
92 """ Initializes the instance.
93
94 """
95 self.mgr = mgr
96 self.isMscom = (mgr.type == 'MSCOM')
97 self.collector = vbox.performanceCollector
98
99 def setup(self, names, objects, period, nsamples):
100 """ Discards all previously collected values for the specified
101 metrics, sets the period of collection and the number of retained
102 samples, enables collection.
103 """
104 self.collector.setupMetrics(names, objects, period, nsamples)
105
106 def enable(self, names, objects):
107 """ Resumes metric collection for the specified metrics.
108 """
109 self.collector.enableMetrics(names, objects)
110
111 def disable(self, names, objects):
112 """ Suspends metric collection for the specified metrics.
113 """
114 self.collector.disableMetrics(names, objects)
115
116 def query(self, names, objects):
117 """ Retrieves collected metric values as well as some auxiliary
118 information. Returns an array of dictionaries, one dictionary per
119 metric. Each dictionary contains the following entries:
120 'name': metric name
121 'object': managed object this metric associated with
122 'unit': unit of measurement
123 'scale': divide 'values' by this number to get float numbers
124 'values': collected data
125 'values_as_string': pre-processed values ready for 'print' statement
126 """
127 # Get around the problem with input arrays returned in output
128 # parameters (see #3953) for MSCOM.
129 if self.isMscom:
130 (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
131 indices, lengths) = self.collector.queryMetricsData(names, objects)
132 else:
133 (values, names_out, objects_out, units, scales, sequence_numbers,
134 indices, lengths) = self.collector.queryMetricsData(names, objects)
135 out = []
136 for i in xrange(0, len(names_out)):
137 scale = int(scales[i])
138 if scale != 1:
139 fmt = '%.2f%s'
140 else:
141 fmt = '%d %s'
142 out.append({
143 'name': str(names_out[i]),
144 'object': str(objects_out[i]),
145 'unit': str(units[i]),
146 'scale': scale,
147 'values': [int(values[j]) for j in xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))],
148 'values_as_string': '[' + ', '.join([fmt % (int(values[j]) / scale, units[i]) for j in
149 xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))]) + ']'
150 })
151 return out
152
153
154#
155# Attribute hacks.
156#
157def ComifyName(name):
158 return name[0].capitalize() + name[1:]
159
160
161## This is for saving the original DispatchBaseClass __getattr__ and __setattr__
162# method references.
163_g_dCOMForward = {
164 'getattr': None,
165 'setattr': None,
166}
167
168
169def _CustomGetAttr(self, sAttr):
170 """ Our getattr replacement for DispatchBaseClass. """
171 # Fastpath.
172 oRet = self.__class__.__dict__.get(sAttr)
173 if oRet is not None:
174 return oRet
175
176 # Try case-insensitivity workaround for class attributes (COM methods).
177 sAttrLower = sAttr.lower()
178 for k in list(self.__class__.__dict__.keys()):
179 if k.lower() == sAttrLower:
180 setattr(self.__class__, sAttr, self.__class__.__dict__[k])
181 return getattr(self, k)
182
183 # Slow path.
184 try:
185 return _g_dCOMForward['getattr'](self, ComifyName(sAttr))
186 except AttributeError:
187 return _g_dCOMForward['getattr'](self, sAttr)
188
189
190def _CustomSetAttr(self, sAttr, oValue):
191 """ Our setattr replacement for DispatchBaseClass. """
192 try:
193 return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue)
194 except AttributeError:
195 return _g_dCOMForward['setattr'](self, sAttr, oValue)
196
197
198class PlatformBase(object):
199 """
200 Base class for the platform specific code.
201 """
202
203 def __init__(self, aoParams):
204 _ = aoParams
205
206 def getVirtualBox(self):
207 """
208 Gets a the IVirtualBox singleton.
209 """
210 return None
211
212 def getSessionObject(self, oIVBox):
213 """
214 Get a session object that can be used for opening machine sessions.
215
216 The oIVBox parameter is an getVirtualBox() return value, i.e. an
217 IVirtualBox reference.
218
219 See also openMachineSession.
220 """
221 _ = oIVBox
222 return None
223
224 def getType(self):
225 """ Returns the platform type (class name sans 'Platform'). """
226 return None
227
228 def isRemote(self):
229 """
230 Returns True if remote (web services) and False if local (COM/XPCOM).
231 """
232 return False
233
234 def getArray(self, oInterface, sAttrib):
235 """
236 Retrives the value of the array attribute 'sAttrib' from
237 interface 'oInterface'.
238
239 This is for hiding platform specific differences in attributes
240 returning arrays.
241 """
242 _ = oInterface
243 _ = sAttrib
244 return None
245
246 def setArray(self, oInterface, sAttrib, aoArray):
247 """
248 Sets the value (aoArray) of the array attribute 'sAttrib' in
249 interface 'oInterface'.
250
251 This is for hiding platform specific differences in attributes
252 setting arrays.
253 """
254 _ = oInterface
255 _ = sAttrib
256 _ = aoArray
257 return None
258
259 def initPerThread(self):
260 """
261 Does backend specific initialization for the calling thread.
262 """
263 return True
264
265 def deinitPerThread(self):
266 """
267 Does backend specific uninitialization for the calling thread.
268 """
269 return True
270
271 def createListener(self, oImplClass, dArgs):
272 """
273 Instantiates and wraps an active event listener class so it can be
274 passed to an event source for registration.
275
276 oImplClass is a class (type, not instance) which implements
277 IEventListener.
278
279 dArgs is a dictionary with string indexed variables. This may be
280 modified by the method to pass platform specific parameters. Can
281 be None.
282
283 This currently only works on XPCOM. COM support is not possible due to
284 shortcuts taken in the COM bridge code, which is not under our control.
285 Use passive listeners for COM and web services.
286 """
287 _ = oImplClass
288 _ = dArgs
289 raise Exception("No active listeners for this platform")
290
291 def waitForEvents(self, cMsTimeout):
292 """
293 Wait for events to arrive and process them.
294
295 The timeout (cMsTimeout) is in milliseconds for how long to wait for
296 events to arrive. A negative value means waiting for ever, while 0
297 does not wait at all.
298
299 Returns 0 if events was processed.
300 Returns 1 if timed out or interrupted in some way.
301 Returns 2 on error (like not supported for web services).
302
303 Raises an exception if the calling thread is not the main thread (the one
304 that initialized VirtualBoxManager) or if the time isn't an integer.
305 """
306 _ = cMsTimeout
307 return 2
308
309 def interruptWaitEvents(self):
310 """
311 Interrupt a waitForEvents call.
312 This is normally called from a worker thread to wake up the main thread.
313
314 Returns True on success, False on failure.
315 """
316 return False
317
318 def deinit(self):
319 """
320 Unitializes the platform specific backend.
321 """
322 return None
323
324 def queryInterface(self, oIUnknown, sClassName):
325 """
326 IUnknown::QueryInterface wrapper.
327
328 oIUnknown is who to ask.
329 sClassName is the name of the interface we're asking for.
330 """
331 return None
332
333 #
334 # Error (exception) access methods.
335 #
336
337 def xcptGetStatus(self, oXcpt):
338 """
339 Returns the COM status code from the VBox API given exception.
340 """
341 return None
342
343 def xcptIsDeadInterface(self, oXcpt):
344 """
345 Returns True if the exception indicates that the interface is dead, False if not.
346 """
347 return False
348
349 def xcptIsEqual(self, oXcpt, hrStatus):
350 """
351 Checks if the exception oXcpt is equal to the COM/XPCOM status code
352 hrStatus.
353
354 The oXcpt parameter can be any kind of object, we'll just return True
355 if it doesn't behave like a our exception class.
356
357 Will not raise any exception as long as hrStatus and self are not bad.
358 """
359 try:
360 hrXcpt = self.xcptGetStatus(oXcpt)
361 except AttributeError:
362 return False
363 if hrXcpt == hrStatus:
364 return True
365
366 # Fudge for 32-bit signed int conversion.
367 if 0x7fffffff < hrStatus <= 0xffffffff and hrXcpt < 0:
368 if (hrStatus - 0x100000000) == hrXcpt:
369 return True
370 return False
371
372 def xcptGetMessage(self, oXcpt):
373 """
374 Returns the best error message found in the COM-like exception.
375 Returns None to fall back on xcptToString.
376 Raises exception if oXcpt isn't our kind of exception object.
377 """
378 return None
379
380 def xcptGetBaseXcpt(self):
381 """
382 Returns the base exception class.
383 """
384 return None
385
386 def xcptSetupConstants(self, oDst):
387 """
388 Copy/whatever all error constants onto oDst.
389 """
390 return oDst
391
392 @staticmethod
393 def xcptCopyErrorConstants(oDst, oSrc):
394 """
395 Copy everything that looks like error constants from oDst to oSrc.
396 """
397 for sAttr in dir(oSrc):
398 if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'):
399 oAttr = getattr(oSrc, sAttr)
400 if type(oAttr) is int:
401 setattr(oDst, sAttr, oAttr)
402 return oDst
403
404
405class PlatformMSCOM(PlatformBase):
406 """
407 Platform specific code for MS COM.
408 """
409
410 ## @name VirtualBox COM Typelib definitions (should be generate)
411 #
412 # @remarks Must be updated when the corresponding VirtualBox.xidl bits
413 # are changed. Fortunately this isn't very often.
414 # @{
415 VBOX_TLB_GUID = '{D7569351-1750-46F0-936E-BD127D5BC264}'
416 VBOX_TLB_LCID = 0
417 VBOX_TLB_MAJOR = 1
418 VBOX_TLB_MINOR = 3
419 ## @}
420
421 def __init__(self, dParams):
422 PlatformBase.__init__(self, dParams)
423
424 #
425 # Since the code runs on all platforms, we have to do a lot of
426 # importing here instead of at the top of the file where it's normally located.
427 #
428 from win32com import universal
429 from win32com.client import gencache, DispatchBaseClass
430 from win32com.client import constants, getevents
431 import win32com
432 import pythoncom
433 import win32api
434 import winerror
435 from win32con import DUPLICATE_SAME_ACCESS
436 from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess
437 import threading
438
439 self.winerror = winerror
440
441 pid = GetCurrentProcess()
442 self.tid = GetCurrentThreadId()
443 handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS)
444 self.handles = []
445 self.handles.append(handle)
446
447 # Hack the COM dispatcher base class so we can modify method and
448 # attribute names to match those in xpcom.
449 if _g_dCOMForward['setattr'] is None:
450 _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
451 _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
452 setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
453 setattr(DispatchBaseClass, '__setattr__', _CustomSetAttr)
454
455 # Hack the exception base class so the users doesn't need to check for
456 # XPCOM or COM and do different things.
457 ## @todo
458
459 #
460 # Make sure the gencache is correct (we don't quite follow the COM
461 # versioning rules).
462 #
463 self.flushGenPyCache(win32com.client.gencache)
464 win32com.client.gencache.EnsureDispatch('VirtualBox.Session')
465 win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox')
466
467 self.oIntCv = threading.Condition()
468 self.fInterrupted = False
469
470 _ = dParams
471
472 def flushGenPyCache(self, oGenCache):
473 """
474 Flushes VBox related files in the win32com gen_py cache.
475
476 This is necessary since we don't follow the typelib versioning rules
477 that everyeone else seems to subscribe to.
478 """
479 #
480 # The EnsureModule method have broken validation code, it doesn't take
481 # typelib module directories into account. So we brute force them here.
482 # (It's possible the directory approach is from some older pywin
483 # version or the result of runnig makepy or gencache manually, but we
484 # need to cover it as well.)
485 #
486 sName = oGenCache.GetGeneratedFileName(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID,
487 self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR)
488 sGenPath = oGenCache.GetGeneratePath()
489 if len(sName) > 36 and len(sGenPath) > 5:
490 sTypelibPath = os.path.join(sGenPath, sName)
491 if os.path.isdir(sTypelibPath):
492 import shutil
493 shutil.rmtree(sTypelibPath, ignore_errors=True)
494
495 #
496 # Ensure that our typelib is valid.
497 #
498 return oGenCache.EnsureModule(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR)
499
500 def getSessionObject(self, oIVBox):
501 _ = oIVBox
502 import win32com
503 from win32com.client import Dispatch
504 return win32com.client.Dispatch("VirtualBox.Session")
505
506 def getVirtualBox(self):
507 import win32com
508 from win32com.client import Dispatch
509 return win32com.client.Dispatch("VirtualBox.VirtualBox")
510
511 def getType(self):
512 return 'MSCOM'
513
514 def getArray(self, oInterface, sAttrib):
515 return oInterface.__getattr__(sAttrib)
516
517 def setArray(self, oInterface, sAttrib, aoArray):
518 #
519 # HACK ALERT!
520 #
521 # With pywin32 build 218, we're seeing type mismatch errors here for
522 # IGuestSession::environmentChanges (safearray of BSTRs). The Dispatch
523 # object (_oleobj_) seems to get some type conversion wrong and COM
524 # gets upset. So, we redo some of the dispatcher work here, picking
525 # the missing type information from the getter.
526 #
527 oOleObj = getattr(oInterface, '_oleobj_');
528 aPropMapGet = getattr(oInterface, '_prop_map_get_');
529 aPropMapPut = getattr(oInterface, '_prop_map_put_');
530 sComAttrib = sAttrib if sAttrib in aPropMapGet else ComifyName(sAttrib);
531 try:
532 aArgs, aDefaultArgs = aPropMapPut[sComAttrib];
533 aGetArgs = aPropMapGet[sComAttrib];
534 except KeyError: # fallback.
535 return oInterface.__setattr__(sAttrib, aoArray);
536
537 import pythoncom;
538 oOleObj.InvokeTypes(aArgs[0], # dispid
539 aArgs[1], # LCID
540 aArgs[2], # DISPATCH_PROPERTYPUT
541 (pythoncom.VT_HRESULT, 0), # retType - or void?
542 (aGetArgs[2],), # argTypes - trick: we get the type from the getter.
543 aoArray,); # The array
544
545 def initPerThread(self):
546 import pythoncom
547 pythoncom.CoInitializeEx(0)
548
549 def deinitPerThread(self):
550 import pythoncom
551 pythoncom.CoUninitialize()
552
553 def createListener(self, oImplClass, dArgs):
554 if True:
555 raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
556 'returns new gateway objects all the time, thus breaking EventQueue '
557 'assumptions about the listener interface pointer being constants between calls ')
558 # Did this code ever really work?
559 d = {}
560 d['BaseClass'] = oImplClass
561 d['dArgs'] = dArgs
562 d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
563 d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR
564 d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR
565 str_ = ""
566 str_ += "import win32com.server.util\n"
567 str_ += "import pythoncom\n"
568
569 str_ += "class ListenerImpl(BaseClass):\n"
570 str_ += " _com_interfaces_ = ['IEventListener']\n"
571 str_ += " _typelib_guid_ = tlb_guid\n"
572 str_ += " _typelib_version_ = tlb_major, tlb_minor\n"
573 str_ += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
574 # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
575 str_ += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
576
577 # capitalized version of listener method
578 str_ += " HandleEvent=BaseClass.handleEvent\n"
579 str_ += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
580 str_ += "result = win32com.server.util.wrap(ListenerImpl())\n"
581 exec(str_, d, d)
582 return d['result']
583
584 def waitForEvents(self, timeout):
585 from win32api import GetCurrentThreadId
586 from win32event import INFINITE
587 from win32event import MsgWaitForMultipleObjects, \
588 QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
589 from pythoncom import PumpWaitingMessages
590 import types
591
592 if not isinstance(timeout, int):
593 raise TypeError("The timeout argument is not an integer")
594 if self.tid != GetCurrentThreadId():
595 raise Exception("wait for events from the same thread you inited!")
596
597 if timeout < 0:
598 cMsTimeout = INFINITE
599 else:
600 cMsTimeout = timeout
601 rc = MsgWaitForMultipleObjects(self.handles, 0, cMsTimeout, QS_ALLINPUT)
602 if WAIT_OBJECT_0 <= rc < WAIT_OBJECT_0 + len(self.handles):
603 # is it possible?
604 rc = 2
605 elif rc == WAIT_OBJECT_0 + len(self.handles):
606 # Waiting messages
607 PumpWaitingMessages()
608 rc = 0
609 else:
610 # Timeout
611 rc = 1
612
613 # check for interruption
614 self.oIntCv.acquire()
615 if self.fInterrupted:
616 self.fInterrupted = False
617 rc = 1
618 self.oIntCv.release()
619
620 return rc
621
622 def interruptWaitEvents(self):
623 """
624 Basically a python implementation of NativeEventQueue::postEvent().
625
626 The magic value must be in sync with the C++ implementation or this
627 won't work.
628
629 Note that because of this method we cannot easily make use of a
630 non-visible Window to handle the message like we would like to do.
631 """
632 from win32api import PostThreadMessage
633 from win32con import WM_USER
634
635 self.oIntCv.acquire()
636 self.fInterrupted = True
637 self.oIntCv.release()
638 try:
639 PostThreadMessage(self.tid, WM_USER, None, 0xf241b819)
640 except:
641 return False
642 return True
643
644 def deinit(self):
645 import pythoncom
646 from win32file import CloseHandle
647
648 for h in self.handles:
649 if h is not None:
650 CloseHandle(h)
651 self.handles = None
652 pythoncom.CoUninitialize()
653 pass
654
655 def queryInterface(self, oIUnknown, sClassName):
656 from win32com.client import CastTo
657 return CastTo(oIUnknown, sClassName)
658
659 def xcptGetStatus(self, oXcpt):
660 # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
661 # empirical info on it so far.
662 hrXcpt = oXcpt.hresult
663 if hrXcpt == self.winerror.DISP_E_EXCEPTION:
664 try:
665 hrXcpt = oXcpt.excepinfo[5]
666 except:
667 pass
668 return hrXcpt
669
670 def xcptIsDeadInterface(self, oXcpt):
671 return self.xcptGetStatus(oXcpt) in [
672 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
673 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
674 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
675 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
676 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
677 ]
678
679 def xcptGetMessage(self, oXcpt):
680 if hasattr(oXcpt, 'excepinfo'):
681 try:
682 if len(oXcpt.excepinfo) >= 3:
683 sRet = oXcpt.excepinfo[2]
684 if len(sRet) > 0:
685 return sRet[0:]
686 except:
687 pass
688 if hasattr(oXcpt, 'strerror'):
689 try:
690 sRet = oXcpt.strerror
691 if len(sRet) > 0:
692 return sRet
693 except:
694 pass
695 return None
696
697 def xcptGetBaseXcpt(self):
698 import pythoncom
699
700 return pythoncom.com_error
701
702 def xcptSetupConstants(self, oDst):
703 import winerror
704
705 oDst = self.xcptCopyErrorConstants(oDst, winerror)
706
707 # XPCOM compatability constants.
708 oDst.NS_OK = oDst.S_OK
709 oDst.NS_ERROR_FAILURE = oDst.E_FAIL
710 oDst.NS_ERROR_ABORT = oDst.E_ABORT
711 oDst.NS_ERROR_NULL_POINTER = oDst.E_POINTER
712 oDst.NS_ERROR_NO_INTERFACE = oDst.E_NOINTERFACE
713 oDst.NS_ERROR_INVALID_ARG = oDst.E_INVALIDARG
714 oDst.NS_ERROR_OUT_OF_MEMORY = oDst.E_OUTOFMEMORY
715 oDst.NS_ERROR_NOT_IMPLEMENTED = oDst.E_NOTIMPL
716 oDst.NS_ERROR_UNEXPECTED = oDst.E_UNEXPECTED
717 return oDst
718
719
720class PlatformXPCOM(PlatformBase):
721 """
722 Platform specific code for XPCOM.
723 """
724
725 def __init__(self, dParams):
726 PlatformBase.__init__(self, dParams)
727 sys.path.append(VBoxSdkDir + '/bindings/xpcom/python/')
728 import xpcom.vboxxpcom
729 import xpcom
730 import xpcom.components
731 _ = dParams
732
733 def getSessionObject(self, oIVBox):
734 _ = oIVBox
735 import xpcom.components
736 return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
737
738 def getVirtualBox(self):
739 import xpcom.components
740 return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance()
741
742 def getType(self):
743 return 'XPCOM'
744
745 def getArray(self, oInterface, sAttrib):
746 return oInterface.__getattr__('get' + ComifyName(sAttrib))()
747
748 def setArray(self, oInterface, sAttrib, aoArray):
749 return oInterface.__getattr__('set' + ComifyName(sAttrib))(aoArray)
750
751 def initPerThread(self):
752 import xpcom
753 xpcom._xpcom.AttachThread()
754
755 def deinitPerThread(self):
756 import xpcom
757 xpcom._xpcom.DetachThread()
758
759 def createListener(self, oImplClass, dArgs):
760 d = {}
761 d['BaseClass'] = oImplClass
762 d['dArgs'] = dArgs
763 str = ""
764 str += "import xpcom.components\n"
765 str += "class ListenerImpl(BaseClass):\n"
766 str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
767 str += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
768 str += "result = ListenerImpl()\n"
769 exec(str, d, d)
770 return d['result']
771
772 def waitForEvents(self, timeout):
773 import xpcom
774 return xpcom._xpcom.WaitForEvents(timeout)
775
776 def interruptWaitEvents(self):
777 import xpcom
778 return xpcom._xpcom.InterruptWait()
779
780 def deinit(self):
781 import xpcom
782 xpcom._xpcom.DeinitCOM()
783
784 def queryInterface(self, oIUnknown, sClassName):
785 import xpcom.components
786 return oIUnknown.queryInterface(getattr(xpcom.components.interfaces, sClassName))
787
788 def xcptGetStatus(self, oXcpt):
789 return oXcpt.errno
790
791 def xcptIsDeadInterface(self, oXcpt):
792 return self.xcptGetStatus(oXcpt) in [
793 0x80004004, -2147467260, # NS_ERROR_ABORT
794 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
795 ]
796
797 def xcptGetMessage(self, oXcpt):
798 if hasattr(oXcpt, 'msg'):
799 try:
800 sRet = oXcpt.msg
801 if len(sRet) > 0:
802 return sRet
803 except:
804 pass
805 return None
806
807 def xcptGetBaseXcpt(self):
808 import xpcom
809 return xpcom.Exception
810
811 def xcptSetupConstants(self, oDst):
812 import xpcom
813 oDst = self.xcptCopyErrorConstants(oDst, xpcom.nsError)
814
815 # COM compatability constants.
816 oDst.E_ACCESSDENIED = -2147024891 # see VBox/com/defs.h
817 oDst.S_OK = oDst.NS_OK
818 oDst.E_FAIL = oDst.NS_ERROR_FAILURE
819 oDst.E_ABORT = oDst.NS_ERROR_ABORT
820 oDst.E_POINTER = oDst.NS_ERROR_NULL_POINTER
821 oDst.E_NOINTERFACE = oDst.NS_ERROR_NO_INTERFACE
822 oDst.E_INVALIDARG = oDst.NS_ERROR_INVALID_ARG
823 oDst.E_OUTOFMEMORY = oDst.NS_ERROR_OUT_OF_MEMORY
824 oDst.E_NOTIMPL = oDst.NS_ERROR_NOT_IMPLEMENTED
825 oDst.E_UNEXPECTED = oDst.NS_ERROR_UNEXPECTED
826 oDst.DISP_E_EXCEPTION = -2147352567 # For COM compatability only.
827 return oDst
828
829
830class PlatformWEBSERVICE(PlatformBase):
831 """
832 VirtualBox Web Services API specific code.
833 """
834
835 def __init__(self, dParams):
836 PlatformBase.__init__(self, dParams)
837 # Import web services stuff. Fix the sys.path the first time.
838 sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
839 if sWebServLib not in sys.path:
840 sys.path.append(sWebServLib)
841 import VirtualBox_wrappers
842 from VirtualBox_wrappers import IWebsessionManager2
843
844 # Initialize instance variables from parameters.
845 if dParams is not None:
846 self.user = dParams.get("user", "")
847 self.password = dParams.get("password", "")
848 self.url = dParams.get("url", "")
849 else:
850 self.user = ""
851 self.password = ""
852 self.url = None
853 self.vbox = None
854 self.wsmgr = None
855
856 #
857 # Base class overrides.
858 #
859
860 def getSessionObject(self, oIVBox):
861 return self.wsmgr.getSessionObject(oIVBox)
862
863 def getVirtualBox(self):
864 return self.connect(self.url, self.user, self.password)
865
866 def getType(self):
867 return 'WEBSERVICE'
868
869 def isRemote(self):
870 """ Returns True if remote VBox host, False if local. """
871 return True
872
873 def getArray(self, oInterface, sAttrib):
874 return oInterface.__getattr__(sAttrib)
875
876 def setArray(self, oInterface, sAttrib, aoArray):
877 return oInterface.__setattr__(sAttrib, aoArray)
878
879 def waitForEvents(self, timeout):
880 # Webservices cannot do that yet
881 return 2
882
883 def interruptWaitEvents(self, timeout):
884 # Webservices cannot do that yet
885 return False
886
887 def deinit(self):
888 try:
889 self.disconnect()
890 except:
891 pass
892
893 def queryInterface(self, oIUnknown, sClassName):
894 d = {}
895 d['oIUnknown'] = oIUnknown
896 str = ""
897 str += "from VirtualBox_wrappers import " + sClassName + "\n"
898 str += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
899 # wrong, need to test if class indeed implements this interface
900 exec(str, d, d)
901 return d['result']
902
903 #
904 # Web service specific methods.
905 #
906
907 def connect(self, url, user, passwd):
908 if self.vbox is not None:
909 self.disconnect()
910 from VirtualBox_wrappers import IWebsessionManager2
911
912 if url is None:
913 url = ""
914 self.url = url
915 if user is None:
916 user = ""
917 self.user = user
918 if passwd is None:
919 passwd = ""
920 self.password = passwd
921 self.wsmgr = IWebsessionManager2(self.url)
922 self.vbox = self.wsmgr.logon(self.user, self.password)
923 if not self.vbox.handle:
924 raise Exception("cannot connect to '" + self.url + "' as '" + self.user + "'")
925 return self.vbox
926
927 def disconnect(self):
928 if self.vbox is not None and self.wsmgr is not None:
929 self.wsmgr.logoff(self.vbox)
930 self.vbox = None
931 self.wsmgr = None
932
933
934## The current (last) exception class.
935# This is reinitalized whenever VirtualBoxManager is called, so it will hold
936# the reference to the error exception class for the last platform/style that
937# was used. Most clients does talk to multiple VBox instance on different
938# platforms at the same time, so this should be sufficent for most uses and
939# be way simpler to use than VirtualBoxManager::oXcptClass.
940CurXctpClass = None
941
942
943class VirtualBoxManager(object):
944 """
945 VirtualBox API manager class.
946
947 The API users will have to instantiate this. If no parameters are given,
948 it will default to interface with the VirtualBox running on the local
949 machine. sStyle can be None (default), MSCOM, XPCOM or WEBSERVICES. Most
950 users will either be specifying None or WEBSERVICES.
951
952 The dPlatformParams is an optional dictionary for passing parameters to the
953 WEBSERVICE backend.
954 """
955
956 class Statuses(object):
957 def __init__(self):
958 pass
959
960 def __init__(self, sStyle=None, dPlatformParams=None):
961 if sStyle is None:
962 if sys.platform == 'win32':
963 sStyle = "MSCOM"
964 else:
965 sStyle = "XPCOM"
966 if sStyle == 'XPCOM':
967 self.platform = PlatformXPCOM(dPlatformParams)
968 elif sStyle == 'MSCOM':
969 self.platform = PlatformMSCOM(dPlatformParams)
970 elif sStyle == 'WEBSERVICE':
971 self.platform = PlatformWEBSERVICE(dPlatformParams)
972 else:
973 raise Exception('Unknown sStyle=%s' % (sStyle,))
974 self.style = sStyle
975 self.type = self.platform.getType()
976 self.remote = self.platform.isRemote()
977 ## VirtualBox API constants (for webservices, enums are symbolic).
978 self.constants = VirtualBoxReflectionInfo(sStyle == "WEBSERVICE")
979
980 ## Status constants.
981 self.statuses = self.platform.xcptSetupConstants(VirtualBoxManager.Statuses())
982 ## @todo Add VBOX_E_XXX to statuses? They're already in constants...
983 ## Dictionary for errToString, built on demand.
984 self._dErrorValToName = None
985
986 ## The exception class for the selected platform.
987 self.oXcptClass = self.platform.xcptGetBaseXcpt()
988 global CurXcptClass
989 CurXcptClass = self.oXcptClass
990
991 # Get the virtualbox singleton.
992 try:
993 self.vbox = self.platform.getVirtualBox()
994 except NameError:
995 print_("Installation problem: check that appropriate libs in place")
996 traceback.print_exc()
997 raise
998 except Exception:
999 _, e, _ = sys.exc_info()
1000 print_("init exception: ", e)
1001 traceback.print_exc()
1002 if self.remote:
1003 self.vbox = None
1004 else:
1005 raise e
1006
1007 def __del__(self):
1008 self.deinit()
1009
1010 def getPythonApiRevision(self):
1011 """
1012 Returns a Python API revision number.
1013 This will be incremented when features are added to this file.
1014 """
1015 return 3
1016
1017 @property
1018 def mgr(self):
1019 """
1020 This used to be an attribute referring to a session manager class with
1021 only one method called getSessionObject. It moved into this class.
1022 """
1023 return self;
1024
1025 #
1026 # Wrappers for self.platform methods.
1027 #
1028 def getVirtualBox(self):
1029 """ See PlatformBase::getVirtualBox(). """
1030 return self.platform.getVirtualBox()
1031
1032 def getSessionObject(self, oIVBox):
1033 """ See PlatformBase::getSessionObject(). """
1034 return self.platform.getSessionObject(oIVBox)
1035
1036 def getArray(self, oInterface, sAttrib):
1037 """ See PlatformBase::getArray(). """
1038 return self.platform.getArray(oInterface, sAttrib)
1039
1040 def setArray(self, oInterface, sAttrib, aoArray):
1041 """ See PlatformBase::setArray(). """
1042 return self.platform.setArray(oInterface, sAttrib, aoArray)
1043
1044 def createListener(self, oImplClass, dArgs=None):
1045 """ See PlatformBase::createListener(). """
1046 return self.platform.createListener(oImplClass, dArgs)
1047
1048 def waitForEvents(self, cMsTimeout):
1049 """ See PlatformBase::waitForEvents(). """
1050 return self.platform.waitForEvents(cMsTimeout)
1051
1052 def interruptWaitEvents(self):
1053 """ See PlatformBase::interruptWaitEvents(). """
1054 return self.platform.interruptWaitEvents()
1055
1056 def queryInterface(self, oIUnknown, sClassName):
1057 """ See PlatformBase::queryInterface(). """
1058 return self.platform.queryInterface(oIUnknown, sClassName)
1059
1060 #
1061 # Init and uninit.
1062 #
1063 def initPerThread(self):
1064 """ See PlatformBase::deinitPerThread(). """
1065 self.platform.initPerThread()
1066
1067 def deinitPerThread(self):
1068 """ See PlatformBase::deinitPerThread(). """
1069 return self.platform.deinitPerThread()
1070
1071 def deinit(self):
1072 """
1073 For unitializing the manager.
1074 Do not access it after calling this method.
1075 """
1076 if hasattr(self, "vbox"):
1077 del self.vbox
1078 self.vbox = None
1079 if hasattr(self, "platform"):
1080 self.platform.deinit()
1081 self.platform = None
1082 return True
1083
1084 #
1085 # Utility methods.
1086 #
1087 def openMachineSession(self, oIMachine, fPermitSharing=True):
1088 """
1089 Attempts to open the a session to the machine.
1090 Returns a session object on success.
1091 Raises exception on failure.
1092 """
1093 oSession = self.getSessionObject(self.vbox);
1094 if fPermitSharing:
1095 eType = self.constants.LockType_Shared
1096 else:
1097 eType = self.constants.LockType_Write
1098 oIMachine.lockMachine(oSession, eType)
1099 return oSession
1100
1101 def closeMachineSession(self, oSession):
1102 """
1103 Closes a session opened by openMachineSession.
1104 Ignores None parameters.
1105 """
1106 if oSession is not None:
1107 oSession.unlockMachine()
1108 return True
1109
1110 def getPerfCollector(self, oIVBox):
1111 """
1112 Returns a helper class (PerfCollector) for accessing performance
1113 collector goodies. See PerfCollector for details.
1114 """
1115 return PerfCollector(self, oIVBox)
1116
1117 def getBinDir(self):
1118 """
1119 Returns the VirtualBox binary directory.
1120 """
1121 global VBoxBinDir
1122 return VBoxBinDir
1123
1124 def getSdkDir(self):
1125 """
1126 Returns the VirtualBox SDK directory.
1127 """
1128 global VBoxSdkDir
1129 return VBoxSdkDir
1130
1131 #
1132 # Error code utilities.
1133 #
1134 ## @todo port to webservices!
1135 def xcptGetStatus(self, oXcpt=None):
1136 """
1137 Gets the status code from an exception. If the exception parameter
1138 isn't specified, the current exception is examined.
1139 """
1140 if oXcpt is None:
1141 oXcpt = sys.exc_info()[1]
1142 return self.platform.xcptGetStatus(oXcpt)
1143
1144 def xcptIsDeadInterface(self, oXcpt=None):
1145 """
1146 Returns True if the exception indicates that the interface is dead,
1147 False if not. If the exception parameter isn't specified, the current
1148 exception is examined.
1149 """
1150 if oXcpt is None:
1151 oXcpt = sys.exc_info()[1]
1152 return self.platform.xcptIsDeadInterface(oXcpt)
1153
1154 def xcptIsOurXcptKind(self, oXcpt=None):
1155 """
1156 Checks if the exception is one that could come from the VBox API. If
1157 the exception parameter isn't specified, the current exception is
1158 examined.
1159 """
1160 if self.oXcptClass is None: # @todo find the exception class for web services!
1161 return False
1162 if oXcpt is None:
1163 oXcpt = sys.exc_info()[1]
1164 return isinstance(oXcpt, self.oXcptClass)
1165
1166 def xcptIsEqual(self, oXcpt, hrStatus):
1167 """
1168 Checks if the exception oXcpt is equal to the COM/XPCOM status code
1169 hrStatus.
1170
1171 The oXcpt parameter can be any kind of object, we'll just return True
1172 if it doesn't behave like a our exception class. If it's None, we'll
1173 query the current exception and examine that.
1174
1175 Will not raise any exception as long as hrStatus and self are not bad.
1176 """
1177 if oXcpt is None:
1178 oXcpt = sys.exc_info()[1]
1179 return self.platform.xcptIsEqual(oXcpt, hrStatus)
1180
1181 def xcptIsNotEqual(self, oXcpt, hrStatus):
1182 """
1183 Negated xcptIsEqual.
1184 """
1185 return not self.xcptIsEqual(oXcpt, hrStatus)
1186
1187 def xcptToString(self, hrStatusOrXcpt=None):
1188 """
1189 Converts the specified COM status code, or the status code of the
1190 specified exception, to a C constant string. If the parameter isn't
1191 specified (is None), the current exception is examined.
1192 """
1193
1194 # Deal with exceptions.
1195 if hrStatusOrXcpt is None or self.xcptIsOurXcptKind(hrStatusOrXcpt):
1196 hrStatus = self.xcptGetStatus(hrStatusOrXcpt)
1197 else:
1198 hrStatus = hrStatusOrXcpt
1199
1200 # Build the dictionary on demand.
1201 if self._dErrorValToName is None:
1202 dErrorValToName = dict()
1203 for sKey in dir(self.statuses):
1204 if sKey[0].isupper():
1205 oValue = getattr(self.statuses, sKey)
1206 if type(oValue) is int:
1207 dErrorValToName[oValue] = sKey
1208 self._dErrorValToName = dErrorValToName
1209
1210 # Do the lookup, falling back on formatting the status number.
1211 try:
1212 sStr = self._dErrorValToName[int(hrStatus)]
1213 except KeyError:
1214 hrLong = long(hrStatus)
1215 sStr = '%#x (%d)' % (hrLong, hrLong)
1216 return sStr
1217
1218 def xcptGetMessage(self, oXcpt=None):
1219 """
1220 Returns the best error message found in the COM-like exception. If the
1221 exception parameter isn't specified, the current exception is examined.
1222 """
1223 if oXcpt is None:
1224 oXcpt = sys.exc_info()[1]
1225 sRet = self.platform.xcptGetMessage(oXcpt)
1226 if sRet is None:
1227 sRet = self.xcptToString(oXcpt)
1228 return sRet
1229
1230 # Legacy, remove in a day or two.
1231 errGetStatus = xcptGetStatus
1232 errIsDeadInterface = xcptIsDeadInterface
1233 errIsOurXcptKind = xcptIsOurXcptKind
1234 errGetMessage = xcptGetMessage
1235
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette