VirtualBox

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

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

vboxapi.py: s/type_/eType/g

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