VirtualBox

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

Last change on this file since 55909 was 55589, checked in by vboxsync, 10 years ago

vboxapi.py: We probably need to abstract setting array attributes too, just like with getting them. If nothing else, it make up a symmetric interface.

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