VirtualBox

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

Last change on this file since 47981 was 47981, checked in by vboxsync, 12 years ago

vboxapi.py: Started on error abstrations (testsuite needs errIsDeadInterface).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vboxapi.py 47981 2013-08-22 00:47:00Z 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: 47981 $"
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 errGetStatus(self, oXcpt):
300 """
301 Returns the COM status code from the VBox API given exception.
302 """
303 return None;
304
305 def errIsDeadInterface(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 errIsEqual(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.errGetStatus(oXcpt);
323 except AttributeError:
324 return False;
325 return hrXcpt == hrStatus;
326
327 def errGetMessage(self, oXcpt):
328 """
329 Returns the best error message found in the COM-like exception.
330 Returns None to fall back on errToString.
331 Raises exception if oXcpt isn't our kind of exception object.
332 """
333 return None;
334
335 def errGetBaseXcpt(self):
336 """
337 Returns the base exception class.
338 """
339 return None;
340
341 def errSetupConstants(self, oDst):
342 """
343 Copy/whatever all error constants onto oDst.
344 """
345 return oDst;
346
347 @staticmethod
348 def errCopyErrorConstants(oDst, oSrc):
349 """
350 Copy everything that looks like error constants from oDst to oSrc.
351 """
352 for sAttr in dir(oSrc):
353 if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'):
354 oAttr = getattr(oSrc, sAttr);
355 if type(oAttr) is int:
356 setattr(oDst, sAttr, oAttr);
357 return oDst;
358
359
360
361class PlatformMSCOM(PlatformBase):
362 """
363 Platform specific code for MS COM.
364 """
365
366 ## @name VirtualBox COM Typelib definitions (should be generate)
367 #
368 # @remarks Must be updated when the corresponding VirtualBox.xidl bits
369 # are changed. Fortunately this isn't very often.
370 # @{
371 VBOX_TLB_GUID = '{D7569351-1750-46F0-936E-BD127D5BC264}'
372 VBOX_TLB_LCID = 0
373 VBOX_TLB_MAJOR = 1
374 VBOX_TLB_MINOR = 3
375 ## @}
376
377
378 class ConstantFake(object):
379 """ Class to fake access to constants in style of foo.bar.boo """
380
381 def __init__(self, parent, name):
382 self.__dict__['_parent'] = parent
383 self.__dict__['_name'] = name
384 self.__dict__['_consts'] = {}
385 try:
386 self.__dict__['_depth']=parent.__dict__['_depth']+1
387 except:
388 self.__dict__['_depth']=0
389 if self.__dict__['_depth'] > 4:
390 raise AttributeError
391
392 def __getattr__(self, attr):
393 import win32com
394 from win32com.client import constants
395
396 if attr.startswith("__"):
397 raise AttributeError
398
399 consts = self.__dict__['_consts']
400
401 fake = consts.get(attr, None)
402 if fake != None:
403 return fake
404 try:
405 name = self.__dict__['_name']
406 parent = self.__dict__['_parent']
407 while parent != None:
408 if parent._name is not None:
409 name = parent._name+'_'+name
410 parent = parent._parent
411
412 if name is not None:
413 name += "_" + attr
414 else:
415 name = attr
416 return win32com.client.constants.__getattr__(name)
417 except AttributeError, e:
418 fake = PlatformMSCOM.ConstantFake(self, attr)
419 consts[attr] = fake
420 return fake
421
422
423 class InterfacesWrapper:
424 def __init__(self):
425 self.__dict__['_rootFake'] = PlatformMSCOM.ConstantFake(None, None)
426
427 def __getattr__(self, a):
428 import win32com
429 from win32com.client import constants
430 if a.startswith("__"):
431 raise AttributeError
432 try:
433 return win32com.client.constants.__getattr__(a)
434 except AttributeError, e:
435 return self.__dict__['_rootFake'].__getattr__(a)
436
437 def __init__(self, dParams):
438 PlatformBase.__init__(self, dParams);
439
440 #
441 # Since the code runs on all platforms, we have to do a lot of
442 # importing here instead of at the top of the file where it's normally located.
443 #
444 from win32com import universal
445 from win32com.client import gencache, DispatchBaseClass
446 from win32com.client import constants, getevents
447 import win32com
448 import pythoncom
449 import win32api
450 import winerror
451 from win32con import DUPLICATE_SAME_ACCESS
452 from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess
453 import threading
454
455 self.winerror = winerror;
456
457 pid = GetCurrentProcess()
458 self.tid = GetCurrentThreadId()
459 handle = DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS)
460 self.handles = []
461 self.handles.append(handle)
462
463 # Hack the COM dispatcher base class so we can modify method and
464 # attribute names to match those in xpcom.
465 if _g_dCOMForward['setattr'] is None:
466 _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
467 DispatchBaseClass.__dict__['__getattr__'] = _CustomGetAttr
468 _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
469 DispatchBaseClass.__dict__['__setattr__'] = _CustomSetAttr
470
471 # Hack the exception base class so the users doesn't need to check for
472 # XPCOM or COM and do different things.
473 ## @todo
474
475
476 win32com.client.gencache.EnsureDispatch('VirtualBox.Session')
477 win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox')
478
479 self.oIntCv = threading.Condition()
480 self.fInterrupted = False;
481
482 _ = dParams;
483
484 def getSessionObject(self, oIVBox):
485 _ = oIVBox
486 import win32com
487 from win32com.client import Dispatch
488 return win32com.client.Dispatch("VirtualBox.Session")
489
490 def getVirtualBox(self):
491 import win32com
492 from win32com.client import Dispatch
493 return win32com.client.Dispatch("VirtualBox.VirtualBox")
494
495 def getType(self):
496 return 'MSCOM'
497
498 def getArray(self, oInterface, sAttrib):
499 return oInterface.__getattr__(sAttrib)
500
501 def initPerThread(self):
502 import pythoncom
503 pythoncom.CoInitializeEx(0)
504
505 def deinitPerThread(self):
506 import pythoncom
507 pythoncom.CoUninitialize()
508
509 def createListener(self, oImplClass, dArgs):
510 if True:
511 raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
512 'returns new gateway objects all the time, thus breaking EventQueue '
513 'assumptions about the listener interface pointer being constants between calls ');
514 # Did this code ever really work?
515 d = {}
516 d['BaseClass'] = oImplClass
517 d['dArgs'] = dArgs
518 d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
519 d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR
520 d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR
521 str = ""
522 str += "import win32com.server.util\n"
523 str += "import pythoncom\n"
524
525 str += "class ListenerImpl(BaseClass):\n"
526 str += " _com_interfaces_ = ['IEventListener']\n"
527 str += " _typelib_guid_ = tlb_guid\n"
528 str += " _typelib_version_ = tlb_major, tlb_minor\n"
529 str += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
530 # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
531 str += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
532
533 # capitalized version of listener method
534 str += " HandleEvent=BaseClass.handleEvent\n"
535 str += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
536 str += "result = win32com.server.util.wrap(ListenerImpl())\n"
537 exec(str, d, d)
538 return d['result']
539
540 def waitForEvents(self, timeout):
541 from win32api import GetCurrentThreadId
542 from win32event import INFINITE
543 from win32event import MsgWaitForMultipleObjects, \
544 QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
545 from pythoncom import PumpWaitingMessages
546 import types
547
548 if not isinstance(timeout, types.IntType):
549 raise TypeError("The timeout argument is not an integer")
550 if (self.tid != GetCurrentThreadId()):
551 raise Exception("wait for events from the same thread you inited!")
552
553 if timeout < 0:
554 cMsTimeout = INFINITE
555 else:
556 cMsTimeout = timeout
557 rc = MsgWaitForMultipleObjects(self.handles, 0, cMsTimeout, QS_ALLINPUT)
558 if rc >= WAIT_OBJECT_0 and rc < WAIT_OBJECT_0+len(self.handles):
559 # is it possible?
560 rc = 2;
561 elif rc==WAIT_OBJECT_0 + len(self.handles):
562 # Waiting messages
563 PumpWaitingMessages()
564 rc = 0;
565 else:
566 # Timeout
567 rc = 1;
568
569 # check for interruption
570 self.oIntCv.acquire()
571 if self.fInterrupted:
572 self.fInterrupted = False
573 rc = 1;
574 self.oIntCv.release()
575
576 return rc;
577
578 def interruptWaitEvents(self):
579 """
580 Basically a python implementation of NativeEventQueue::postEvent().
581
582 The magic value must be in sync with the C++ implementation or this
583 won't work.
584
585 Note that because of this method we cannot easily make use of a
586 non-visible Window to handle the message like we would like to do.
587 """
588 from win32api import PostThreadMessage
589 from win32con import WM_USER
590 self.oIntCv.acquire()
591 self.fInterrupted = True
592 self.oIntCv.release()
593 try:
594 PostThreadMessage(self.tid, WM_USER, None, 0xf241b819)
595 except:
596 return False;
597 return True;
598
599 def deinit(self):
600 import pythoncom
601 from win32file import CloseHandle
602
603 for h in self.handles:
604 if h is not None:
605 CloseHandle(h)
606 self.handles = None
607 pythoncom.CoUninitialize()
608 pass
609
610 def queryInterface(self, oIUnknown, sClassName):
611 from win32com.client import CastTo
612 return CastTo(oIUnknown, sClassName)
613
614 def errGetStatus(self, oXcpt):
615 # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
616 # empirical info on it so far.
617 hrXcpt = oXcpt.hresult
618 if hrXcpt == self.winerror.DISP_E_EXCEPTION:
619 try: hrXcpt = oXcpt.excepinfo[5];
620 except: pass;
621 return hrXcpt;
622
623 def errIsDeadInterface(self, oXcpt):
624 return self.errGetStatus(oXcpt) in [
625 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
626 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
627 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
628 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
629 ];
630
631
632 def errGetMessage(self, oXcpt):
633 if hasattr(oXcpt, 'excepinfo'):
634 try:
635 if len(oXcpt.excepinfo) >= 3:
636 sRet = oXcpt.excepinfo[2];
637 if len(sRet) > 0:
638 return sRet[0:];
639 except:
640 pass;
641 if hasattr(oXcpt, 'strerror'):
642 try:
643 sRet = oXcpt.strerror;
644 if len(sRet) > 0:
645 return sRet;
646 except:
647 pass;
648 return None;
649
650 def errGetBaseXcpt(self):
651 import pythoncom;
652 return pythoncom.com_error;
653
654 def errSetupConstants(self, oDst):
655 import winerror;
656 oDst = self.errCopyErrorConstants(oDst, winerror);
657
658 # XPCOM compatability constants.
659 oDst.NS_OK = oDst.S_OK;
660 oDst.NS_ERROR_FAILURE = oDst.E_FAIL;
661 oDst.NS_ERROR_ABORT = oDst.E_ABORT;
662 oDst.NS_ERROR_NULL_POINTER = oDst.E_POINTER;
663 oDst.NS_ERROR_NO_INTERFACE = oDst.E_NOINTERFACE;
664 oDst.NS_ERROR_INVALID_ARG = oDst.E_INVALIDARG;
665 oDst.NS_ERROR_OUT_OF_MEMORY = oDst.E_OUTOFMEMORY;
666 oDst.NS_ERROR_NOT_IMPLEMENTED = oDst.E_NOTIMPL;
667 oDst.NS_ERROR_UNEXPECTED = oDst.E_UNEXPECTED;
668 return oDst;
669
670
671class PlatformXPCOM(PlatformBase):
672 """
673 Platform specific code for XPCOM.
674 """
675
676 def __init__(self, dParams):
677 PlatformBase.__init__(self, dParams);
678 sys.path.append(VBoxSdkDir+'/bindings/xpcom/python/')
679 import xpcom.vboxxpcom
680 import xpcom
681 import xpcom.components
682 _ = dParams;
683
684 def getSessionObject(self, oIVBox):
685 _ = oIVBox;
686 import xpcom.components
687 return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
688
689 def getVirtualBox(self):
690 import xpcom.components
691 return xpcom.components.classes["@virtualbox.org/VirtualBox;1"].createInstance()
692
693 def getType(self):
694 return 'XPCOM'
695
696 def getArray(self, oInterface, sAttrib):
697 return oInterface.__getattr__('get'+ComifyName(sAttrib))()
698
699 def initPerThread(self):
700 import xpcom
701 xpcom._xpcom.AttachThread()
702
703 def deinitPerThread(self):
704 import xpcom
705 xpcom._xpcom.DetachThread()
706
707 def createListener(self, oImplClass, dArgs):
708 d = {}
709 d['BaseClass'] = oImplClass
710 d['dArgs'] = dArgs
711 str = ""
712 str += "import xpcom.components\n"
713 str += "class ListenerImpl(BaseClass):\n"
714 str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
715 str += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
716 str += "result = ListenerImpl()\n"
717 exec (str, d, d)
718 return d['result']
719
720 def waitForEvents(self, timeout):
721 import xpcom
722 return xpcom._xpcom.WaitForEvents(timeout)
723
724 def interruptWaitEvents(self):
725 import xpcom
726 return xpcom._xpcom.InterruptWait()
727
728 def deinit(self):
729 import xpcom
730 xpcom._xpcom.DeinitCOM()
731
732 def queryInterface(self, oIUnknown, sClassName):
733 import xpcom.components
734 return oIUnknown.queryInterface(getattr(xpcom.components.interfaces, sClassName))
735
736 def errGetStatus(self, oXcpt):
737 return oXcpt.errno;
738
739 def errIsDeadInterface(self, oXcpt):
740 import winerror;
741 return self.errGetStatus(oXcpt) in [
742 0x80004004, -2147467260, # NS_ERROR_ABORT
743 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
744 ];
745
746 def errGetMessage(self, oXcpt):
747 if hasattr(oXcpt, 'msg'):
748 try:
749 sRet = oXcpt.msg;
750 if len(sRet) > 0:
751 return sRet;
752 except:
753 pass;
754 return None;
755
756 def errGetBaseXcpt(self):
757 import xpcom;
758 return xpcom.Exception;
759
760 def errSetupConstants(self, oDst):
761 import winerror;
762 oDst = self.errCopyErrorConstants(oDst, winerror);
763
764 # COM compatability constants.
765 oDst.E_ACCESSDENIED = -2147024891; # see VBox/com/defs.h
766 oDst.S_OK = oDst.NS_OK;
767 oDst.E_FAIL = oDst.NS_ERROR_FAILURE;
768 oDst.E_ABORT = oDst.NS_ERROR_ABORT;
769 oDst.E_POINTER = oDst.NS_ERROR_NULL_POINTER;
770 oDst.E_NOINTERFACE = oDst.NS_ERROR_NO_INTERFACE;
771 oDst.E_INVALIDARG = oDst.NS_ERROR_INVALID_ARG;
772 oDst.E_OUTOFMEMORY = oDst.NS_ERROR_OUT_OF_MEMORY;
773 oDst.E_NOTIMPL = oDst.NS_ERROR_NOT_IMPLEMENTED;
774 oDst.E_UNEXPECTED = oDst.NS_ERROR_UNEXPECTED;
775 oDst.DISP_E_EXCEPTION = -2147352567; # For COM compatability only.
776 return oDst;
777
778
779class PlatformWEBSERVICE(PlatformBase):
780 """
781 VirtualBox Web Services API specific code.
782 """
783
784 def __init__(self, dParams):
785 PlatformBase.__init__(self, dParams);
786 # Import web services stuff. Fix the sys.path the first time.
787 sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib');
788 if sWebServLib not in sys.path:
789 sys.path.append(sWebServLib);
790 import VirtualBox_wrappers
791 from VirtualBox_wrappers import IWebsessionManager2
792
793 # Initialize instance variables from parameters.
794 if dParams is not None:
795 self.user = dParams.get("user", "")
796 self.password = dParams.get("password", "")
797 self.url = dParams.get("url", "")
798 else:
799 self.user = ""
800 self.password = ""
801 self.url = None
802 self.vbox = None
803 self.wsmgr = None;
804
805 #
806 # Base class overrides.
807 #
808
809 def getSessionObject(self, oIVBox):
810 return self.wsmgr.getSessionObject(oIVBox)
811
812 def getVirtualBox(self):
813 return self.connect(self.url, self.user, self.password)
814
815 def getType(self):
816 return 'WEBSERVICE'
817
818 def isRemote(self):
819 """ Returns True if remote VBox host, False if local. """
820 return True
821
822 def getArray(self, oInterface, sAttrib):
823 return oInterface.__getattr__(sAttrib)
824
825 def waitForEvents(self, timeout):
826 # Webservices cannot do that yet
827 return 2;
828
829 def interruptWaitEvents(self, timeout):
830 # Webservices cannot do that yet
831 return False;
832
833 def deinit(self):
834 try:
835 disconnect()
836 except:
837 pass
838
839 def queryInterface(self, oIUnknown, sClassName):
840 d = {}
841 d['oIUnknown'] = oIUnknown
842 str = ""
843 str += "from VirtualBox_wrappers import "+sClassName+"\n"
844 str += "result = "+sClassName+"(oIUnknown.mgr, oIUnknown.handle)\n"
845 # wrong, need to test if class indeed implements this interface
846 exec (str, d, d)
847 return d['result']
848
849 #
850 # Web service specific methods.
851 #
852
853 def connect(self, url, user, passwd):
854 if self.vbox is not None:
855 self.disconnect()
856 from VirtualBox_wrappers import IWebsessionManager2
857 if url is None:
858 url = ""
859 self.url = url
860 if user is None:
861 user = ""
862 self.user = user
863 if passwd is None:
864 passwd = ""
865 self.password = passwd
866 self.wsmgr = IWebsessionManager2(self.url)
867 self.vbox = self.wsmgr.logon(self.user, self.password)
868 if not self.vbox.handle:
869 raise Exception("cannot connect to '"+self.url+"' as '"+self.user+"'")
870 return self.vbox
871
872 def disconnect(self):
873 if self.vbox is not None and self.wsmgr is not None:
874 self.wsmgr.logoff(self.vbox)
875 self.vbox = None
876 self.wsmgr = None
877
878
879## The current (last) exception class.
880# This is reinitalized whenever VirtualBoxManager is called, so it will hold
881# the reference to the error exception class for the last platform/style that
882# was used. Most clients does talk to multiple VBox instance on different
883# platforms at the same time, so this should be sufficent for most uses and
884# be way simpler to use than VirtualBoxManager::oXcptClass.
885CurXctpClass = None;
886
887
888class VirtualBoxManager(object):
889 """
890 VirtualBox API manager class.
891
892 The API users will have to instantiate this. If no parameters are given,
893 it will default to interface with the VirtualBox running on the local
894 machine. sStyle can be None (default), MSCOM, XPCOM or WEBSERVICES. Most
895 users will either be specifying None or WEBSERVICES.
896
897 The dPlatformParams is an optional dictionary for passing parameters to the
898 WEBSERVICE backend.
899 """
900
901 class Statuses(object):
902 def __init__(self):
903 pass;
904
905 def __init__(self, sStyle = None, dPlatformParams = None):
906 if sStyle is None:
907 if sys.platform == 'win32':
908 sStyle = "MSCOM"
909 else:
910 sStyle = "XPCOM"
911 if sStyle == 'XPCOM':
912 self.platform = PlatformXPCOM(dPlatformParams);
913 elif sStyle == 'MSCOM':
914 self.platform = PlatformMSCOM(dPlatformParams);
915 elif sStyle == 'WEBSERVICE':
916 self.platform = PlatformWEBSERVICE(dPlatformParams);
917 else:
918 raise Exception('Unknown sStyle=%s' % (sStyle,));
919 self.style = sStyle
920 self.type = self.platform.getType()
921 self.remote = self.platform.isRemote()
922 ## VirtualBox API constants (for webservices, enums are symbolic).
923 self.constants = VirtualBoxReflectionInfo(sStyle == "WEBSERVICE")
924
925 ## Status constants.
926 self.statuses = self.platform.errSetupConstants(VirtualBoxManager.Statuses());
927 ## @todo Add VBOX_E_XXX to statuses? They're already in constants...
928 ## Dictionary for errToString, built on demand.
929 self._dErrorValToName = None;
930
931 ## The exception class for the selected platform.
932 self.oXcptClass = self.platform.errGetBaseXcpt();
933 global CurXcptClass;
934 CurXcptClass = self.oXcptClass;
935
936 # Get the virtualbox singleton.
937 try:
938 self.vbox = self.platform.getVirtualBox()
939 except NameError, ne:
940 print "Installation problem: check that appropriate libs in place"
941 traceback.print_exc()
942 raise ne
943 except Exception, e:
944 print "init exception: ", e
945 traceback.print_exc()
946 if self.remote:
947 self.vbox = None
948 else:
949 raise e
950 ## @deprecated
951 # This used to refer to a session manager class with only one method
952 # called getSessionObject. The method has moved into this call.
953 self.mgr = self;
954
955 def __del__(self):
956 self.deinit()
957
958 def getPythonApiRevision(self):
959 """
960 Returns a Python API revision number.
961 This will be incremented when features are added to this file.
962 """
963 return 2;
964
965
966 #
967 # Wrappers for self.platform methods.
968 #
969
970 def getVirtualBox(self):
971 """ See PlatformBase::getVirtualBox(). """
972 return self.platform.getVirtualBox()
973
974 def getSessionObject(self, oIVBox):
975 """ See PlatformBase::getSessionObject(). """
976 return self.platform.getSessionObject(oIVBox);
977
978 def getArray(self, oInterface, sAttrib):
979 """ See PlatformBase::getArray(). """
980 return self.platform.getArray(oInterface, sAttrib)
981
982 def createListener(self, oImplClass, dArgs = None):
983 """ See PlatformBase::createListener(). """
984 return self.platform.createListener(oImplClass, dArgs)
985
986 def waitForEvents(self, cMsTimeout):
987 """ See PlatformBase::waitForEvents(). """
988 return self.platform.waitForEvents(cMsTimeout)
989
990 def interruptWaitEvents(self):
991 """ See PlatformBase::interruptWaitEvents(). """
992 return self.platform.interruptWaitEvents()
993
994 def queryInterface(self, oIUnknown, sClassName):
995 """ See PlatformBase::queryInterface(). """
996 return self.platform.queryInterface(oIUnknown, sClassName)
997
998
999 #
1000 # Init and uninit.
1001 #
1002
1003 def initPerThread(self):
1004 """ See PlatformBase::deinitPerThread(). """
1005 self.platform.initPerThread()
1006
1007 def deinitPerThread(self):
1008 """ See PlatformBase::deinitPerThread(). """
1009 return self.platform.deinitPerThread()
1010
1011 def deinit(self):
1012 """
1013 For unitializing the manager.
1014 Do not access it after calling this method.
1015 """
1016 if hasattr(self, "vbox"):
1017 del self.vbox
1018 self.vbox = None
1019 if hasattr(self, "platform"):
1020 self.platform.deinit()
1021 self.platform = None
1022 return True;
1023
1024
1025 #
1026 # Utility methods.
1027 #
1028
1029 def openMachineSession(self, oIMachine, fPermitSharing = True):
1030 """
1031 Attemts to open the a session to the machine.
1032 Returns a session object on success.
1033 Raises exception on failure.
1034 """
1035 oSession = self.mgr.getSessionObject(self.vbox);
1036 if fPermitSharing:
1037 type = self.constants.LockType_Shared;
1038 else:
1039 type = self.constants.LockType_Write;
1040 oIMachine.lockMachine(oSession, type);
1041 return oSession;
1042
1043 def closeMachineSession(self, oSession):
1044 """
1045 Closes a session opened by openMachineSession.
1046 Ignores None parameters.
1047 """
1048 if oSession is not None:
1049 oSession.unlockMachine()
1050 return True;
1051
1052 def getPerfCollector(self, oIVBox):
1053 """
1054 Returns a helper class (PerfCollector) for accessing performance
1055 collector goodies. See PerfCollector for details.
1056 """
1057 return PerfCollector(self, oIVBox)
1058
1059 def getBinDir(self):
1060 """
1061 Returns the VirtualBox binary directory.
1062 """
1063 global VBoxBinDir
1064 return VBoxBinDir
1065
1066 def getSdkDir(self):
1067 """
1068 Returns the VirtualBox SDK directory.
1069 """
1070 global VBoxSdkDir
1071 return VBoxSdkDir
1072
1073
1074 #
1075 # Error code utilities.
1076 #
1077
1078 ## @todo port to webservices!
1079
1080 def errGetStatus(self, oXcpt):
1081 """
1082 Gets the status code from an exception.
1083 """
1084 return self.platform.errGetStatus(oXcpt);
1085
1086 def errIsDeadInterface(self, oXcpt):
1087 """
1088 Returns True if the exception indicates that the interface is dead, False if not.
1089 """
1090 return self.platform.errIsDeadInterface(oXcpt);
1091
1092 def errIsOurXcptKind(self, oXcpt):
1093 """
1094 Checks if the exception is one that could come from the VBox API.
1095 """
1096 if self.oXcptClass is None: ## @todo find the exception class for web services!
1097 return False;
1098 return isinstance(oXcpt, self.oXcptClass);
1099
1100 def errIsEqual(self, oXcpt, hrStatus):
1101 """ See PlatformBase::errIsEqual(). """
1102 return self.platform.errIsEqual(oXcpt, hrStatus);
1103
1104 def errIsNotEqual(self, oXcpt, hrStatus):
1105 """
1106 Negated errIsEqual.
1107 """
1108 return self.errIsEqual(oXcpt, hrStatus);
1109
1110 def errToString(self, hrStatusOrXcpt):
1111 """
1112 Converts the specified COM status code, or the status code of the
1113 specified exception, to a C constant string.
1114 """
1115
1116 # Deal with exceptions.
1117 if self.errIsOurXcptKind(hrStatusOrXcpt):
1118 hrStatus = self.errGetStatus(hrStatusOrXcpt);
1119 else:
1120 hrStatus = hrStatusOrXcpt;
1121
1122 # Build the dictionary on demand.
1123 if self._dErrorValToName is None:
1124 dErrorValToName = dict();
1125 for sKey in dir(self.statuses):
1126 if sKey[0].isupper():
1127 oValue = getattr(self.statuses, sKey);
1128 if type(oValue) is int:
1129 dErrorValToName[oValue] = sKey;
1130 self._dErrorValToName = dErrorValToName;
1131
1132 # Do the lookup, falling back on formatting the status number.
1133 try:
1134 sStr = self._dErrorValToName[int(hrStatus)];
1135 except KeyError:
1136 hrLong = long(hrStatus);
1137 sStr = '%#x (%d)' % (hrLong, hrLong);
1138 return sStr;
1139
1140 def errGetMessage(self, oXcpt):
1141 """
1142 Returns the best error message found in the COM-like exception.
1143 """
1144 sRet = self.platform.errGetMessage(oXcpt);
1145 if sRet is None:
1146 sRet = self.errToString(oXcpt);
1147 return sRet;
1148
Note: See TracBrowser for help on using the repository browser.

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