VirtualBox

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

Last change on this file since 67954 was 67049, checked in by vboxsync, 7 years ago

Main/glue/vboxapi.py: clean up, remove vbox attribute, eliminate unnecessary parameter of getSessionObject (it is a completely ignored parameter with a default parameter to make life a little simpler for API clients in Python to deal with the previous version still)
Frontends/VBoxShell: adapt to the cleanup, reduce the unnecessary variations in the code dealing with sessions
ValidationKit etc.: adapt to the cleanup

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