VirtualBox

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

Last change on this file since 66617 was 66617, checked in by vboxsync, 8 years ago

VBox/Main: bugref:3300: VBoxSVC from terminal server session is not 'visible' - fixed issues in Python VirtualBox API

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

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