VirtualBox

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

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

re-applied the Python 3 changes which were backed out in r105674 sans the changes in .cpp

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