VirtualBox

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

Last change on this file since 48248 was 48045, checked in by vboxsync, 11 years ago

vboxapi.py: Inc revision.

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