VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testdriver/vbox.py@ 55818

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

ValidationKit: adapt to renamed API attribute

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 138.9 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 55818 2015-05-12 10:00:06Z vboxsync $
3# pylint: disable=C0302
4
5"""
6VirtualBox Specific base testdriver.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2015 Oracle Corporation
12
13This file is part of VirtualBox Open Source Edition (OSE), as
14available from http://www.virtualbox.org. This file is free software;
15you can redistribute it and/or modify it under the terms of the GNU
16General Public License (GPL) as published by the Free Software
17Foundation, in version 2 as it comes in the "COPYING" file of the
18VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20
21The contents of this file may alternatively be used under the terms
22of the Common Development and Distribution License Version 1.0
23(CDDL) only, as it comes in the "COPYING.CDDL" file of the
24VirtualBox OSE distribution, in which case the provisions of the
25CDDL are applicable instead of those of the GPL.
26
27You may elect to license modified versions of this file under the
28terms and conditions of either the GPL or the CDDL or both.
29"""
30__version__ = "$Revision: 55818 $"
31
32
33# Standard Python imports.
34import os
35import platform
36import sys
37import threading
38import time
39import traceback
40import datetime
41
42# Figure out where the validation kit lives and make sure it's in the path.
43try: __file__
44except: __file__ = sys.argv[0];
45g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
46if g_ksValidationKitDir not in sys.path:
47 sys.path.append(g_ksValidationKitDir);
48
49# Validation Kit imports.
50from common import utils;
51from testdriver import base;
52from testdriver import reporter;
53from testdriver import vboxcon;
54from testdriver import vboxtestvms;
55
56
57#
58# Exception and Error Unification Hacks.
59# Note! This is pretty gross stuff. Be warned!
60# TODO: Find better ways of doing these things, preferrably in vboxapi.
61#
62
63ComException = None; # pylint: disable=C0103
64__fnComExceptionGetAttr__ = None; # pylint: disable=C0103
65
66def __MyDefaultGetAttr(oSelf, sName):
67 """ __getattribute__/__getattr__ default fake."""
68 try:
69 oAttr = oSelf.__dict__[sName];
70 except:
71 oAttr = dir(oSelf)[sName];
72 return oAttr;
73
74def __MyComExceptionGetAttr(oSelf, sName):
75 """ ComException.__getattr__ wrapper - both XPCOM and COM. """
76 try:
77 oAttr = __fnComExceptionGetAttr__(oSelf, sName);
78 except AttributeError:
79 if platform.system() == 'Windows':
80 if sName == 'errno':
81 oAttr = __fnComExceptionGetAttr__(oSelf, 'hresult');
82 elif sName == 'msg':
83 oAttr = __fnComExceptionGetAttr__(oSelf, 'strerror');
84 else:
85 raise;
86 else:
87 if sName == 'hresult':
88 oAttr = __fnComExceptionGetAttr__(oSelf, 'errno');
89 elif sName == 'strerror':
90 oAttr = __fnComExceptionGetAttr__(oSelf, 'msg');
91 elif sName == 'excepinfo':
92 oAttr = None;
93 elif sName == 'argerror':
94 oAttr = None;
95 else:
96 raise;
97 #print '__MyComExceptionGetAttr(,%s) -> "%s"' % (sName, oAttr);
98 return oAttr;
99
100def __deployExceptionHacks__(oNativeComExceptionClass):
101 """
102 Deploys the exception and error hacks that helps unifying COM and XPCOM
103 exceptions and errors.
104 """
105 global ComException # pylint: disable=C0103
106 global __fnComExceptionGetAttr__ # pylint: disable=C0103
107
108 # Hook up our attribute getter for the exception class (ASSUMES new-style).
109 if __fnComExceptionGetAttr__ is None:
110 try:
111 __fnComExceptionGetAttr__ = getattr(oNativeComExceptionClass, '__getattr__');
112 except:
113 try:
114 __fnComExceptionGetAttr__ = getattr(oNativeComExceptionClass, '__getattribute__');
115 except:
116 __fnComExceptionGetAttr__ = __MyDefaultGetAttr;
117 setattr(oNativeComExceptionClass, '__getattr__', __MyComExceptionGetAttr)
118
119 # Make the modified classes accessible (are there better ways to do this?)
120 ComException = oNativeComExceptionClass
121 return None;
122
123
124
125#
126# Utility functions.
127#
128
129def isIpAddrValid(sIpAddr):
130 """
131 Checks if a IPv4 address looks valid. This will return false for
132 localhost and similar.
133 Returns True / False.
134 """
135 if sIpAddr is None: return False;
136 if len(sIpAddr.split('.')) != 4: return False;
137 if sIpAddr.endswith('.0'): return False;
138 if sIpAddr.endswith('.255'): return False;
139 if sIpAddr.startswith('127.'): return False;
140 if sIpAddr.startswith('169.254.'): return False;
141 if sIpAddr.startswith('192.0.2.'): return False;
142 if sIpAddr.startswith('224.0.0.'): return False;
143 return True;
144
145def stringifyErrorInfo(oErrInfo):
146 """
147 Stringifies the error information in a IVirtualBoxErrorInfo object.
148
149 Returns string with error info.
150 """
151 try:
152 rc = oErrInfo.resultCode;
153 sText = oErrInfo.text;
154 sIid = oErrInfo.interfaceID;
155 sComponent = oErrInfo.component;
156 except:
157 sRet = 'bad error object (%s)?' % (oErrInfo,);
158 traceback.print_exc();
159 else:
160 sRet = 'rc=%s text="%s" IID=%s component=%s' % (ComError.toString(rc), sText, sIid, sComponent);
161 return sRet;
162
163def reportError(oErr, sText):
164 """
165 Report a VirtualBox error on oErr. oErr can be IVirtualBoxErrorInfo
166 or IProgress. Anything else is ignored.
167
168 Returns the same a reporter.error().
169 """
170 try:
171 oErrObj = oErr.errorInfo; # IProgress.
172 except:
173 oErrObj = oErr;
174 reporter.error(sText);
175 return reporter.error(stringifyErrorInfo(oErrObj));
176
177
178#
179# Classes
180#
181
182class ComError(object):
183 """
184 Unified COM and XPCOM status code repository.
185 This works more like a module than a class since it's replacing a module.
186 """
187
188 # The VBOX_E_XXX bits:
189 __VBOX_E_BASE = -2135228416;
190 VBOX_E_OBJECT_NOT_FOUND = __VBOX_E_BASE + 1;
191 VBOX_E_INVALID_VM_STATE = __VBOX_E_BASE + 2;
192 VBOX_E_VM_ERROR = __VBOX_E_BASE + 3;
193 VBOX_E_FILE_ERROR = __VBOX_E_BASE + 4;
194 VBOX_E_IPRT_ERROR = __VBOX_E_BASE + 5;
195 VBOX_E_PDM_ERROR = __VBOX_E_BASE + 6;
196 VBOX_E_INVALID_OBJECT_STATE = __VBOX_E_BASE + 7;
197 VBOX_E_HOST_ERROR = __VBOX_E_BASE + 8;
198 VBOX_E_NOT_SUPPORTED = __VBOX_E_BASE + 9;
199 VBOX_E_XML_ERROR = __VBOX_E_BASE + 10;
200 VBOX_E_INVALID_SESSION_STATE = __VBOX_E_BASE + 11;
201 VBOX_E_OBJECT_IN_USE = __VBOX_E_BASE + 12;
202 VBOX_E_DONT_CALL_AGAIN = __VBOX_E_BASE + 13;
203
204 # Reverse lookup table.
205 dDecimalToConst = {}; # pylint: disable=C0103
206
207 def __init__(self):
208 raise base.GenError('No instances, please');
209
210 @staticmethod
211 def copyErrors(oNativeComErrorClass):
212 """
213 Copy all error codes from oNativeComErrorClass to this class and
214 install compatability mappings.
215 """
216
217 # First, add the VBOX_E_XXX constants to dDecimalToConst.
218 for sAttr in dir(ComError):
219 if sAttr.startswith('VBOX_E'):
220 oAttr = getattr(ComError, sAttr);
221 ComError.dDecimalToConst[oAttr] = sAttr;
222
223 # Copy all error codes from oNativeComErrorClass to this class.
224 for sAttr in dir(oNativeComErrorClass):
225 if sAttr[0].isupper():
226 oAttr = getattr(oNativeComErrorClass, sAttr);
227 setattr(ComError, sAttr, oAttr);
228 if type(oAttr) is int:
229 ComError.dDecimalToConst[oAttr] = sAttr;
230
231 # Install mappings to the other platform.
232 if platform.system() == 'Windows':
233 ComError.NS_OK = ComError.S_OK;
234 ComError.NS_ERROR_FAILURE = ComError.E_FAIL;
235 ComError.NS_ERROR_ABORT = ComError.E_ABORT;
236 ComError.NS_ERROR_NULL_POINTER = ComError.E_POINTER;
237 ComError.NS_ERROR_NO_INTERFACE = ComError.E_NOINTERFACE;
238 ComError.NS_ERROR_INVALID_ARG = ComError.E_INVALIDARG;
239 ComError.NS_ERROR_OUT_OF_MEMORY = ComError.E_OUTOFMEMORY;
240 ComError.NS_ERROR_NOT_IMPLEMENTED = ComError.E_NOTIMPL;
241 ComError.NS_ERROR_UNEXPECTED = ComError.E_UNEXPECTED;
242 else:
243 ComError.E_ACCESSDENIED = -2147024891; # see VBox/com/defs.h
244 ComError.S_OK = ComError.NS_OK;
245 ComError.E_FAIL = ComError.NS_ERROR_FAILURE;
246 ComError.E_ABORT = ComError.NS_ERROR_ABORT;
247 ComError.E_POINTER = ComError.NS_ERROR_NULL_POINTER;
248 ComError.E_NOINTERFACE = ComError.NS_ERROR_NO_INTERFACE;
249 ComError.E_INVALIDARG = ComError.NS_ERROR_INVALID_ARG;
250 ComError.E_OUTOFMEMORY = ComError.NS_ERROR_OUT_OF_MEMORY;
251 ComError.E_NOTIMPL = ComError.NS_ERROR_NOT_IMPLEMENTED;
252 ComError.E_UNEXPECTED = ComError.NS_ERROR_UNEXPECTED;
253 ComError.DISP_E_EXCEPTION = -2147352567; # For COM compatability only.
254 return True;
255
256 @staticmethod
257 def equal(oXcpt, hr):
258 """
259 Checks if the ComException e is not equal to the COM status code hr.
260 This takes DISP_E_EXCEPTION & excepinfo into account.
261
262 This method can be used with any Exception derivate, however it will
263 only return True for classes similar to the two ComException variants.
264 """
265 if platform.system() == 'Windows':
266 # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
267 # empirical info on it so far.
268 try:
269 hrXcpt = oXcpt.hresult;
270 except AttributeError:
271 return False;
272 if hrXcpt == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None:
273 hrXcpt = oXcpt.excepinfo[5];
274 else:
275 try:
276 hrXcpt = oXcpt.errno;
277 except AttributeError:
278 return False;
279 return hrXcpt == hr;
280
281 @staticmethod
282 def notEqual(oXcpt, hr):
283 """
284 Checks if the ComException e is not equal to the COM status code hr.
285 See equal() for more details.
286 """
287 return not ComError.equal(oXcpt, hr)
288
289 @staticmethod
290 def toString(hr):
291 """
292 Converts the specified COM status code to a string.
293 """
294 try:
295 sStr = ComError.dDecimalToConst[int(hr)];
296 except KeyError:
297 hrLong = long(hr);
298 sStr = '%#x (%d)' % (hrLong, hrLong);
299 return sStr;
300
301
302class Build(object): # pylint: disable=R0903
303 """
304 A VirtualBox build.
305
306 Note! After dropping the installation of VBox from this code and instead
307 realizing that with the vboxinstall.py wrapper driver, this class is
308 of much less importance and contains unnecessary bits and pieces.
309 """
310
311 def __init__(self, oDriver, strInstallPath):
312 """
313 Construct a build object from a build file name and/or install path.
314 """
315 # Initialize all members first.
316 self.oDriver = oDriver;
317 self.sInstallPath = strInstallPath;
318 self.sSdkPath = None;
319 self.sSrcRoot = None;
320 self.sKind = None;
321 self.sDesignation = None;
322 self.sType = None;
323 self.sOs = None;
324 self.sArch = None;
325 self.sGuestAdditionsIso = None;
326
327 # Figure out the values as best we can.
328 if strInstallPath is None:
329 #
330 # Both parameters are None, which means we're falling back on a
331 # build in the development tree.
332 #
333 self.sKind = "development";
334
335 if self.sType is None:
336 self.sType = os.environ.get("KBUILD_TYPE", os.environ.get("BUILD_TYPE", "release"));
337 if self.sOs is None:
338 self.sOs = os.environ.get("KBUILD_TARGET", os.environ.get("BUILD_TARGET", oDriver.sHost));
339 if self.sArch is None:
340 self.sArch = os.environ.get("KBUILD_TARGET_ARCH", os.environ.get("BUILD_TARGET_ARCH", oDriver.sHostArch));
341
342 sOut = os.path.join('out', self.sOs + '.' + self.sArch, self.sType);
343 sSearch = os.environ.get('VBOX_TD_DEV_TREE', os.path.dirname(__file__)); # Env.var. for older trees or testboxscript.
344 sCandidat = None;
345 for i in range(0, 10): # pylint: disable=W0612
346 sBldDir = os.path.join(sSearch, sOut);
347 if os.path.isdir(sBldDir):
348 sCandidat = os.path.join(sBldDir, 'bin', 'VBoxSVC' + base.exeSuff());
349 if os.path.isfile(sCandidat):
350 self.sSdkPath = os.path.join(sBldDir, 'bin/sdk');
351 break;
352 sCandidat = os.path.join(sBldDir, 'dist/VirtualBox.app/Contents/MacOS/VBoxSVC');
353 if os.path.isfile(sCandidat):
354 self.sSdkPath = os.path.join(sBldDir, 'dist/sdk');
355 break;
356 sSearch = os.path.abspath(os.path.join(sSearch, '..'));
357 if sCandidat is None or not os.path.isfile(sCandidat):
358 raise base.GenError();
359 self.sInstallPath = os.path.abspath(os.path.dirname(sCandidat));
360 self.sSrcRoot = os.path.abspath(sSearch);
361
362 self.sDesignation = os.environ.get('TEST_BUILD_DESIGNATION', None);
363 if self.sDesignation is None:
364 try:
365 oFile = utils.openNoInherit(os.path.join(self.sSrcRoot, sOut, 'revision.kmk'), 'r');
366 except:
367 pass;
368 else:
369 s = oFile.readline();
370 oFile.close();
371 import re;
372 oMatch = re.search("VBOX_SVN_REV=(\\d+)", s);
373 if oMatch is not None:
374 self.sDesignation = oMatch.group(1);
375
376 if self.sDesignation is None:
377 self.sDesignation = 'XXXXX'
378 else:
379 #
380 # We've been pointed to an existing installation, this could be
381 # in the out dir of a svn checkout, untarred VBoxAll or a real
382 # installation directory.
383 #
384 self.sKind = "preinstalled";
385 self.sType = "release";
386 self.sOs = oDriver.sHost;
387 self.sArch = oDriver.sHostArch;
388 self.sInstallPath = os.path.abspath(strInstallPath);
389 self.sSdkPath = os.path.join(self.sInstallPath, 'sdk');
390 self.sSrcRoot = None;
391 self.sDesignation = os.environ.get('TEST_BUILD_DESIGNATION', 'XXXXX');
392 ## @todo Much more work is required here.
393
394 # Do some checks.
395 sVMMR0 = os.path.join(self.sInstallPath, 'VMMR0.r0');
396 if not os.path.isfile(sVMMR0) and utils.getHostOs() == 'solaris': # solaris is special.
397 sVMMR0 = os.path.join(self.sInstallPath, 'amd64' if utils.getHostArch() == 'amd64' else 'i386', 'VMMR0.r0');
398 if not os.path.isfile(sVMMR0):
399 raise base.GenError('%s is missing' % (sVMMR0,));
400
401 # Guest additions location is different on windows for some _stupid_ reason.
402 if self.sOs == 'win' and self.sKind != 'development':
403 self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
404 elif self.sOs == 'darwin':
405 self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
406 else:
407 self.sGuestAdditionsIso = '%s/additions/VBoxGuestAdditions.iso' % (self.sInstallPath,);
408
409 # __init__ end;
410
411 def dump(self):
412 """ Status dumper for debugging. """
413 print >> sys.stderr, "testdriver.vbox.Build: sInstallPath= '%s'" % self.sInstallPath;
414 print >> sys.stderr, "testdriver.vbox.Build: sSdkPath = '%s'" % self.sSdkPath;
415 print >> sys.stderr, "testdriver.vbox.Build: sSrcRoot = '%s'" % self.sSrcRoot;
416 print >> sys.stderr, "testdriver.vbox.Build: sKind = '%s'" % self.sKind;
417 print >> sys.stderr, "testdriver.vbox.Build: sDesignation= '%s'" % self.sDesignation;
418 print >> sys.stderr, "testdriver.vbox.Build: sType = '%s'" % self.sType;
419 print >> sys.stderr, "testdriver.vbox.Build: sOs = '%s'" % self.sOs;
420 print >> sys.stderr, "testdriver.vbox.Build: sArch = '%s'" % self.sArch;
421
422 def isDevBuild(self):
423 """ Returns True if it's development build (kind), otherwise False. """
424 return self.sKind == 'development';
425
426
427class EventHandlerBase(object):
428 """
429 Base class for both Console and VirtualBox event handlers.
430 """
431
432 def __init__(self, dArgs, fpApiVer, sName = None):
433 self.oVBoxMgr = dArgs['oVBoxMgr'];
434 self.oEventSrc = dArgs['oEventSrc']; # Console/VirtualBox for < 3.3
435 self.oListener = dArgs['oListener'];
436 self.fPassive = self.oListener != None;
437 self.sName = sName
438 self.fShutdown = False;
439 self.oThread = None;
440 self.fpApiVer = fpApiVer;
441
442 def threadForPassiveMode(self):
443 """
444 The thread procedure for the event processing thread.
445 """
446 assert self.fPassive is not None;
447 while not self.fShutdown:
448 try:
449 oEvt = self.oEventSrc.getEvent(self.oListener, 500);
450 except:
451 if not self.oVBoxMgr.xcptIsDeadInterface(): reporter.logXcpt();
452 else: reporter.log('threadForPassiveMode/%s: interface croaked (ignored)' % (self.sName,));
453 break;
454 if oEvt:
455 self.handleEvent(oEvt);
456 try:
457 self.oEventSrc.eventProcessed(self.oListener, oEvt);
458 except:
459 reporter.logXcpt();
460 break;
461 self.unregister(fWaitForThread = False);
462 return None;
463
464 def startThreadForPassiveMode(self):
465 """
466 Called when working in passive mode.
467 """
468 self.oThread = threading.Thread(target = self.threadForPassiveMode, \
469 args=(), name=('PAS-%s' % (self.sName,)));
470 self.oThread.setDaemon(True)
471 self.oThread.start();
472 return None;
473
474 def unregister(self, fWaitForThread = True):
475 """
476 Unregister the event handler.
477 """
478 self.fShutdown = True;
479
480 fRc = False;
481 if self.oEventSrc is not None:
482 if self.fpApiVer < 3.3:
483 try:
484 self.oEventSrc.unregisterCallback(self.oListener);
485 fRc = True;
486 except:
487 reporter.errorXcpt('unregisterCallback failed on %s' % (self.oListener,));
488 else:
489 try:
490 self.oEventSrc.unregisterListener(self.oListener);
491 fRc = True;
492 except:
493 if self.oVBoxMgr.xcptIsDeadInterface():
494 reporter.log('unregisterListener failed on %s because of dead interface (%s)'
495 % (self.oListener, self.oVBoxMgr.xcptToString(),));
496 else:
497 reporter.errorXcpt('unregisterListener failed on %s' % (self.oListener,));
498
499 if self.oThread is not None \
500 and self.oThread != threading.current_thread():
501 self.oThread.join();
502 self.oThread = None;
503
504 _ = fWaitForThread;
505 return fRc;
506
507 def handleEvent(self, oEvt):
508 """
509 Compatibility wrapper that child classes implement.
510 """
511 _ = oEvt;
512 return None;
513
514 @staticmethod
515 def registerDerivedEventHandler(oVBoxMgr, fpApiVer, oSubClass, dArgsCopy,
516 oSrcParent, sSrcParentNm, sICallbackNm,
517 fMustSucceed = True, sLogSuffix = ''):
518 """
519 Registers the callback / event listener.
520 """
521 dArgsCopy['oVBoxMgr'] = oVBoxMgr;
522 dArgsCopy['oListener'] = None;
523 if fpApiVer < 3.3:
524 dArgsCopy['oEventSrc'] = oSrcParent;
525 try:
526 oRet = oVBoxMgr.createCallback(sICallbackNm, oSubClass, dArgsCopy);
527 except:
528 reporter.errorXcpt('%s::registerCallback(%s) failed%s' % (sSrcParentNm, oRet, sLogSuffix));
529 else:
530 try:
531 oSrcParent.registerCallback(oRet);
532 return oRet;
533 except Exception, oXcpt:
534 if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
535 reporter.errorXcpt('%s::registerCallback(%s)%s' % (sSrcParentNm, oRet, sLogSuffix));
536 else:
537 fPassive = sys.platform == 'win32'; # or webservices.
538 try:
539 oEventSrc = oSrcParent.eventSource;
540 dArgsCopy['oEventSrc'] = oEventSrc;
541 if not fPassive:
542 oListener = oRet = oVBoxMgr.createListener(oSubClass, dArgsCopy);
543 else:
544 oListener = oEventSrc.createListener();
545 dArgsCopy['oListener'] = oListener;
546 oRet = oSubClass(dArgsCopy);
547 except:
548 reporter.errorXcpt('%s::eventSource.createListener(%s) failed%s' % (sSrcParentNm, oListener, sLogSuffix));
549 else:
550 try:
551 oEventSrc.registerListener(oListener, [vboxcon.VBoxEventType_Any], not fPassive);
552 except Exception, oXcpt:
553 if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
554 reporter.errorXcpt('%s::eventSource.registerListener(%s) failed%s' \
555 % (sSrcParentNm, oListener, sLogSuffix));
556 else:
557 if not fPassive:
558 if sys.platform == 'win32':
559 from win32com.server.util import unwrap # pylint: disable=F0401
560 oRet = unwrap(oRet);
561 oRet.oListener = oListener;
562 else:
563 oRet.startThreadForPassiveMode();
564 return oRet;
565 return None;
566
567
568
569
570class ConsoleEventHandlerBase(EventHandlerBase):
571 """
572 Base class for handling IConsole events.
573
574 The class has IConsoleCallback (<=3.2) compatible callback methods which
575 the user can override as needed.
576
577 Note! This class must not inherit from object or we'll get type errors in VBoxPython.
578 """
579 def __init__(self, dArgs, sName = None):
580 self.oSession = dArgs['oSession'];
581 self.oConsole = dArgs['oConsole'];
582 if sName is None:
583 sName = self.oSession.sName;
584 EventHandlerBase.__init__(self, dArgs, self.oSession.fpApiVer, sName);
585
586
587 # pylint: disable=C0111,R0913,W0613
588 def onMousePointerShapeChange(self, fVisible, fAlpha, xHot, yHot, cx, cy, abShape):
589 reporter.log2('onMousePointerShapeChange/%s' % (self.sName));
590 def onMouseCapabilityChange(self, fSupportsAbsolute, *aArgs): # Extra argument was added in 3.2.
591 reporter.log2('onMouseCapabilityChange/%s' % (self.sName));
592 def onKeyboardLedsChange(self, fNumLock, fCapsLock, fScrollLock):
593 reporter.log2('onKeyboardLedsChange/%s' % (self.sName));
594 def onStateChange(self, eState):
595 reporter.log2('onStateChange/%s' % (self.sName));
596 def onAdditionsStateChange(self):
597 reporter.log2('onAdditionsStateChange/%s' % (self.sName));
598 def onNetworkAdapterChange(self, oNic):
599 reporter.log2('onNetworkAdapterChange/%s' % (self.sName));
600 def onSerialPortChange(self, oPort):
601 reporter.log2('onSerialPortChange/%s' % (self.sName));
602 def onParallelPortChange(self, oPort):
603 reporter.log2('onParallelPortChange/%s' % (self.sName));
604 def onStorageControllerChange(self):
605 reporter.log2('onStorageControllerChange/%s' % (self.sName));
606 def onMediumChange(self, attachment):
607 reporter.log2('onMediumChange/%s' % (self.sName));
608 def onCPUChange(self, iCpu, fAdd):
609 reporter.log2('onCPUChange/%s' % (self.sName));
610 def onVRDPServerChange(self):
611 reporter.log2('onVRDPServerChange/%s' % (self.sName));
612 def onRemoteDisplayInfoChange(self):
613 reporter.log2('onRemoteDisplayInfoChange/%s' % (self.sName));
614 def onUSBControllerChange(self):
615 reporter.log2('onUSBControllerChange/%s' % (self.sName));
616 def onUSBDeviceStateChange(self, oDevice, fAttached, oError):
617 reporter.log2('onUSBDeviceStateChange/%s' % (self.sName));
618 def onSharedFolderChange(self, fGlobal):
619 reporter.log2('onSharedFolderChange/%s' % (self.sName));
620 def onRuntimeError(self, fFatal, sErrId, sMessage):
621 reporter.log2('onRuntimeError/%s' % (self.sName));
622 def onCanShowWindow(self):
623 reporter.log2('onCanShowWindow/%s' % (self.sName));
624 return True
625 def onShowWindow(self):
626 reporter.log2('onShowWindow/%s' % (self.sName));
627 return None;
628
629 # pylint: enable=C0111,R0913,W0613
630
631 def handleEvent(self, oEvt):
632 """
633 Compatibility wrapper.
634 """
635 try:
636 oEvtBase = self.oVBoxMgr.queryInterface(oEvt, 'IEvent');
637 eType = oEvtBase.type;
638 except:
639 reporter.logXcpt();
640 return None;
641 if eType == vboxcon.VBoxEventType_OnRuntimeError:
642 try:
643 oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IRuntimeErrorEvent');
644 return self.onRuntimeError(oEvtIt.fatal, oEvtIt.id, oEvtIt.message)
645 except:
646 reporter.logXcpt();
647 ## @todo implement the other events.
648 if eType != vboxcon.VBoxEventType_OnMousePointerShapeChanged:
649 reporter.log2('%s/%s' % (str(eType), self.sName));
650 return None;
651
652
653class VirtualBoxEventHandlerBase(EventHandlerBase):
654 """
655 Base class for handling IVirtualBox events.
656
657 The class has IConsoleCallback (<=3.2) compatible callback methods which
658 the user can override as needed.
659
660 Note! This class must not inherit from object or we'll get type errors in VBoxPython.
661 """
662 def __init__(self, dArgs, sName = "emanon"):
663 self.oVBoxMgr = dArgs['oVBoxMgr'];
664 self.oVBox = dArgs['oVBox'];
665 EventHandlerBase.__init__(self, dArgs, self.oVBox.fpApiVer, sName);
666
667 # pylint: disable=C0111,W0613
668 def onMachineStateChange(self, sMachineId, eState):
669 pass;
670 def onMachineDataChange(self, sMachineId):
671 pass;
672 def onExtraDataCanChange(self, sMachineId, sKey, sValue):
673 # The COM bridge does tuples differently. Not very funny if you ask me... ;-)
674 if self.oVBoxMgr.type == 'MSCOM':
675 return '', 0, True;
676 return True, ''
677 def onExtraDataChange(self, sMachineId, sKey, sValue):
678 pass;
679 def onMediumRegistered(self, sMediumId, eMediumType, fRegistered):
680 pass;
681 def onMachineRegistered(self, sMachineId, fRegistered):
682 pass;
683 def onSessionStateChange(self, sMachineId, eState):
684 pass;
685 def onSnapshotTaken(self, sMachineId, sSnapshotId):
686 pass;
687 def onSnapshotDiscarded(self, sMachineId, sSnapshotId):
688 pass;
689 def onSnapshotChange(self, sMachineId, sSnapshotId):
690 pass;
691 def onGuestPropertyChange(self, sMachineId, sName, sValue, sFlags):
692 pass;
693 # pylint: enable=C0111,W0613
694
695 def handleEvent(self, oEvt):
696 """
697 Compatibility wrapper.
698 """
699 try:
700 oEvtBase = self.oVBoxMgr.queryInterface(oEvt, 'IEvent');
701 eType = oEvtBase.type;
702 except:
703 reporter.logXcpt();
704 return None;
705 if eType == vboxcon.VBoxEventType_OnMachineStateChanged:
706 try:
707 oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IMachineStateChangedEvent');
708 return self.onMachineStateChange(oEvtIt.machineId, oEvtIt.state)
709 except:
710 reporter.logXcpt();
711 elif eType == vboxcon.VBoxEventType_OnGuestPropertyChanged:
712 try:
713 oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IGuestPropertyChangedEvent');
714 return self.onGuestPropertyChange(oEvtIt.machineId, oEvtIt.name, oEvtIt.value, oEvtIt.flags);
715 except:
716 reporter.logXcpt();
717 ## @todo implement the other events.
718 reporter.log2('%s/%s' % (str(eType), self.sName));
719 return None;
720
721
722class SessionConsoleEventHandler(ConsoleEventHandlerBase):
723 """
724 For catching machine state changes and waking up the task machinery at that point.
725 """
726 def __init__(self, dArgs):
727 ConsoleEventHandlerBase.__init__(self, dArgs);
728
729 def onMachineStateChange(self, sMachineId, eState): # pylint: disable=W0613
730 """ Just interrupt the wait loop here so it can check again. """
731 self.oVBoxMgr.interruptWaitEvents();
732
733
734
735class TestDriver(base.TestDriver): # pylint: disable=R0902
736 """
737 This is the VirtualBox test driver.
738 """
739
740 def __init__(self):
741 base.TestDriver.__init__(self);
742 self.fImportedVBoxApi = False;
743 self.fpApiVer = 3.2;
744 self.oBuild = None;
745 self.oVBoxMgr = None;
746 self.oVBox = None;
747 self.aoRemoteSessions = [];
748 self.aoVMs = []; ## @todo not sure if this list will be of any use.
749 self.oTestVmManager = vboxtestvms.TestVmManager(self.sResourcePath);
750 self.oTestVmSet = vboxtestvms.TestVmSet();
751 self.sSessionTypeDef = 'headless';
752 self.sSessionType = self.sSessionTypeDef;
753 self.fEnableVrdp = True;
754 self.uVrdpBasePortDef = 6000;
755 self.uVrdpBasePort = self.uVrdpBasePortDef;
756 self.sDefBridgedNic = None;
757 self.fUseDefaultSvc = False;
758 self.sLogSelfGroups = '';
759 self.sLogSelfFlags = 'time';
760 self.sLogSelfDest = '';
761 self.sLogSessionGroups = '';
762 self.sLogSessionFlags = 'time';
763 self.sLogSessionDest = '';
764 self.sLogSvcGroups = '';
765 self.sLogSvcFlags = 'time';
766 self.sLogSvcDest = '';
767 self.sSelfLogFile = None;
768 self.sVBoxSvcLogFile = None;
769 self.oVBoxSvcProcess = None;
770 self.sVBoxSvcPidFile = None;
771 self.fVBoxSvcInDebugger = False;
772 self.sVBoxValidationKit = None;
773 self.sVBoxValidationKitIso = None;
774 self.sVBoxBootSectors = None;
775 self.fAlwaysUploadLogs = False;
776 self.fAlwaysUploadScreenshots = False;
777
778 # Quietly detect build and validation kit.
779 self._detectBuild(False);
780 self._detectValidationKit(False);
781
782 # Make sure all debug logs goes to the scratch area unless
783 # specified otherwise (more of this later on).
784 if 'VBOX_LOG_DEST' not in os.environ:
785 os.environ['VBOX_LOG_DEST'] = 'dir=%s' % (self.sScratchPath);
786
787 def dump(self):
788 """
789 Dump object state, for debugging.
790 """
791 base.TestDriver.dump(self);
792 print >> sys.stderr, "testdriver.vbox: fImportedVBoxApi = '%s'" % self.fImportedVBoxApi;
793 print >> sys.stderr, "testdriver.vbox: fpApiVer = '%s'" % self.fpApiVer;
794 print >> sys.stderr, "testdriver.vbox: oBuild = '%s'" % self.oBuild;
795 print >> sys.stderr, "testdriver.vbox: oVBoxMgr = '%s'" % self.oVBoxMgr;
796 print >> sys.stderr, "testdriver.vbox: oVBox = '%s'" % self.oVBox;
797 print >> sys.stderr, "testdriver.vbox: aoRemoteSessions = '%s'" % self.aoRemoteSessions;
798 print >> sys.stderr, "testdriver.vbox: aoVMs = '%s'" % self.aoVMs;
799 print >> sys.stderr, "testdriver.vbox: sVBoxValidationKit = '%s'" % self.sVBoxValidationKit;
800 print >> sys.stderr, "testdriver.vbox: sVBoxValidationKitIso = '%s'" % self.sVBoxValidationKitIso;
801 print >> sys.stderr, "testdriver.vbox: sVBoxBootSectors = '%s'" % self.sVBoxBootSectors;
802 if self.oBuild is not None:
803 self.oBuild.dump();
804
805 def _detectBuild(self, fQuiet = False):
806 """
807 This is used internally to try figure a locally installed build when
808 running tests manually.
809 """
810 if self.oBuild is not None:
811 return True;
812
813 # Try dev build first since that's where I'll be using it first..
814 if True:
815 try:
816 self.oBuild = Build(self, None);
817 return True;
818 except base.GenError:
819 pass;
820
821 # Try default installation locations.
822 if self.sHost == 'win':
823 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
824 asLocs = [
825 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
826 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
827 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
828 ];
829 elif self.sHost == 'solaris':
830 asLocs = [ '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0', '/opt/VirtualBox' ];
831 elif self.sHost == 'darwin':
832 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
833 elif self.sHost == 'linux':
834 asLocs = [ '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0', '/opt/VirtualBox' ];
835 else:
836 asLocs = [ '/opt/VirtualBox' ];
837 if 'VBOX_INSTALL_PATH' in os.environ:
838 asLocs.insert(0, os.environ['VBOX_INSTALL_PATH']);
839
840 for sLoc in asLocs:
841 try:
842 self.oBuild = Build(self, sLoc);
843 return True;
844 except base.GenError:
845 pass;
846
847 if not fQuiet:
848 reporter.error('failed to find VirtualBox installation');
849 return False;
850
851 def _detectValidationKit(self, fQuiet = False):
852 """
853 This is used internally by the constructor to try locate an unzipped
854 VBox Validation Kit somewhere in the immediate proximity.
855 """
856 if self.sVBoxValidationKit is not None:
857 return True;
858
859 #
860 # Normally it's found where we're running from, which is the same as
861 # the script directly on the testboxes.
862 #
863 asCandidates = [self.sScriptPath, ];
864 if g_ksValidationKitDir not in asCandidates:
865 asCandidates.append(g_ksValidationKitDir);
866 if os.getcwd() not in asCandidates:
867 asCandidates.append(os.getcwd());
868 if self.oBuild is not None and self.oBuild.sInstallPath not in asCandidates:
869 asCandidates.append(self.oBuild.sInstallPath);
870
871 #
872 # When working out of the tree, we'll search the current directory
873 # as well as parent dirs.
874 #
875 for sDir in list(asCandidates):
876 for i in range(10):
877 sDir = os.path.dirname(sDir);
878 if sDir not in asCandidates:
879 asCandidates.append(sDir);
880
881 #
882 # Do the searching.
883 #
884 sCandidate = None;
885 for i in range(len(asCandidates)):
886 sCandidate = asCandidates[i];
887 if os.path.isfile(os.path.join(sCandidate, 'VBoxValidationKit.iso')):
888 break;
889 sCandidate = os.path.join(sCandidate, 'validationkit');
890 if os.path.isfile(os.path.join(sCandidate, 'VBoxValidationKit.iso')):
891 break;
892 sCandidate = None;
893
894 fRc = sCandidate is not None;
895 if fRc is False:
896 if not fQuiet:
897 reporter.error('failed to find VBox Validation Kit installation (candidates: %s)' % (asCandidates,));
898 sCandidate = os.path.join(self.sScriptPath, 'validationkit'); # Don't leave the values as None.
899
900 #
901 # Set the member values.
902 #
903 self.sVBoxValidationKit = sCandidate;
904 self.sVBoxValidationKitIso = os.path.join(sCandidate, 'VBoxValidationKit.iso');
905 self.sVBoxBootSectors = os.path.join(sCandidate, 'bootsectors');
906 return fRc;
907
908 def importVBoxApi(self):
909 """
910 Import the 'vboxapi' module from the VirtualBox build we're using and
911 instantiate the two basic objects.
912
913 This will try detect an development or installed build if no build has
914 been associated with the driver yet.
915 """
916 if self.fImportedVBoxApi:
917 return True;
918
919 ## @todo split up this messy function.
920
921 # Make sure we've got our own VirtualBox config and VBoxSVC (on XPCOM at least).
922 if not self.fUseDefaultSvc:
923 os.environ['VBOX_USER_HOME'] = os.path.join(self.sScratchPath, 'VBoxUserHome');
924 sUser = os.environ.get('USERNAME', os.environ.get('USER', os.environ.get('LOGNAME', 'unknown')));
925 os.environ['VBOX_IPC_SOCKETID'] = sUser + '-VBoxTest';
926
927 # Do the detecting.
928 self._detectBuild();
929 if self.oBuild is None:
930 return False;
931
932 # Avoid crashing when loading the 32-bit module (or whatever it is that goes bang).
933 if self.oBuild.sArch == 'x86' \
934 and self.sHost == 'darwin' \
935 and platform.architecture()[0] == '64bit' \
936 and self.oBuild.sKind == 'development' \
937 and os.getenv('VERSIONER_PYTHON_PREFER_32_BIT') != 'yes':
938 print "WARNING: 64-bit python on darwin, 32-bit VBox development build => crash"
939 print "WARNING: bash-3.2$ /usr/bin/python2.5 ./testdriver"
940 print "WARNING: or"
941 print "WARNING: bash-3.2$ VERSIONER_PYTHON_PREFER_32_BIT=yes ./testdriver"
942 return False;
943
944 # Start VBoxSVC and load the vboxapi bits.
945 if self._startVBoxSVC() is True:
946 assert(self.oVBoxSvcProcess is not None);
947
948 sSavedSysPath = sys.path;
949 self._setupVBoxApi();
950 sys.path = sSavedSysPath;
951
952 # Adjust the default machine folder.
953 if self.fImportedVBoxApi and not self.fUseDefaultSvc and self.fpApiVer >= 4.0:
954 sNewFolder = os.path.join(self.sScratchPath, 'VBoxUserHome', 'Machines');
955 try:
956 self.oVBox.systemProperties.defaultMachineFolder = sNewFolder;
957 except:
958 self.fImportedVBoxApi = False;
959 self.oVBoxMgr = None;
960 self.oVBox = None;
961 reporter.logXcpt("defaultMachineFolder exception (sNewFolder=%s)" % (sNewFolder,));
962
963 # Kill VBoxSVC on failure.
964 if self.oVBoxMgr is None:
965 self._stopVBoxSVC();
966 else:
967 assert(self.oVBoxSvcProcess is None);
968 return self.fImportedVBoxApi;
969
970 def _startVBoxSVC(self): # pylint: disable=R0915
971 """ Starts VBoxSVC. """
972 assert(self.oVBoxSvcProcess is None);
973
974 # Setup vbox logging for VBoxSVC now and start it manually. This way
975 # we can control both logging and shutdown.
976 self.sVBoxSvcLogFile = '%s/VBoxSVC-debug.log' % (self.sScratchPath,);
977 try: os.remove(self.sVBoxSvcLogFile);
978 except: pass;
979 os.environ['VBOX_LOG'] = self.sLogSvcGroups;
980 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSvcFlags,); # Append becuse of VBoxXPCOMIPCD.
981 if self.sLogSvcDest:
982 os.environ['VBOX_LOG_DEST'] = self.sLogSvcDest;
983 else:
984 os.environ['VBOX_LOG_DEST'] = 'file=%s' % (self.sVBoxSvcLogFile,);
985 os.environ['VBOXSVC_RELEASE_LOG_FLAGS'] = 'time append';
986
987 # Always leave a pid file behind so we can kill it during cleanup-before.
988 self.sVBoxSvcPidFile = '%s/VBoxSVC.pid' % (self.sScratchPath,);
989 fWritePidFile = True;
990
991 cMsFudge = 1;
992 sVBoxSVC = '%s/VBoxSVC' % (self.oBuild.sInstallPath,); ## @todo .exe and stuff.
993 if self.fVBoxSvcInDebugger:
994 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
995 # Start VBoxSVC in gdb in a new terminal.
996 #sTerm = '/usr/bin/gnome-terminal'; - doesn't work, some fork+exec stuff confusing us.
997 sTerm = '/usr/bin/xterm';
998 if not os.path.isfile(sTerm): sTerm = '/usr/X11/bin/xterm';
999 if not os.path.isfile(sTerm): sTerm = '/usr/X11R6/bin/xterm';
1000 if not os.path.isfile(sTerm): sTerm = '/usr/bin/xterm';
1001 if not os.path.isfile(sTerm): sTerm = 'xterm';
1002 sGdb = '/usr/bin/gdb';
1003 if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
1004 if not os.path.isfile(sGdb): sGdb = '/usr/sfw/bin/gdb';
1005 if not os.path.isfile(sGdb): sGdb = 'gdb';
1006 sGdbCmdLine = '%s --args %s --pidfile %s' % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
1007 reporter.log('term="%s" gdb="%s"' % (sTerm, sGdbCmdLine));
1008 os.environ['SHELL'] = self.sOrgShell; # Non-working shell may cause gdb and/or the term problems.
1009 self.oVBoxSvcProcess = base.Process.spawnp(sTerm, sTerm, '-e', sGdbCmdLine);
1010 os.environ['SHELL'] = self.sOurShell;
1011 if self.oVBoxSvcProcess is not None:
1012 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1013 sys.stdin.read(1);
1014 fWritePidFile = False;
1015
1016 elif self.sHost == 'win':
1017 sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows\\windbg.exe';
1018 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe';
1019 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows\\windbg.exe'; # Localization rulez! pylint: disable=C0301
1020 if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows (x64)\\windbg.exe';
1021 if not os.path.isfile(sWinDbg): sWinDbg = 'windbg'; # WinDbg must be in the path; better than nothing.
1022 # Assume that everything WinDbg needs is defined using the environment variables.
1023 # See WinDbg help for more information.
1024 reporter.log('windbg="%s"' % (sWinDbg));
1025 self.oVBoxSvcProcess = base.Process.spawn(sWinDbg, sWinDbg, sVBoxSVC + base.exeSuff());
1026 if self.oVBoxSvcProcess is not None:
1027 reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
1028 sys.stdin.read(1);
1029 fWritePidFile = False;
1030 ## @todo add a pipe interface similar to xpcom if feasible, i.e. if
1031 # we can get actual handle values for pipes in python.
1032
1033 else:
1034 reporter.error('Port me!');
1035 else: # Run without a debugger attached.
1036 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1037 #
1038 # XPCOM - We can use a pipe to let VBoxSVC notify us when it's ready.
1039 #
1040 iPipeR, iPipeW = os.pipe();
1041 os.environ['NSPR_INHERIT_FDS'] = 'vboxsvc:startup-pipe:5:0x%x' % (iPipeW,);
1042 reporter.log2("NSPR_INHERIT_FDS=%s" % (os.environ['NSPR_INHERIT_FDS']));
1043
1044 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC, '--auto-shutdown'); # SIGUSR1 requirement.
1045 try: # Try make sure we get the SIGINT and not VBoxSVC.
1046 os.setpgid(self.oVBoxSvcProcess.getPid(), 0); # pylint: disable=E1101
1047 os.setpgid(0, 0); # pylint: disable=E1101
1048 except:
1049 reporter.logXcpt();
1050
1051 os.close(iPipeW);
1052 try:
1053 sResponse = os.read(iPipeR, 32);
1054 except:
1055 reporter.logXcpt();
1056 sResponse = None;
1057 os.close(iPipeR);
1058
1059 if sResponse is None or sResponse.strip() != 'READY':
1060 reporter.error('VBoxSVC failed starting up... (sResponse=%s)' % (sResponse,));
1061 if not self.oVBoxSvcProcess.wait(5000):
1062 self.oVBoxSvcProcess.terminate(2500);
1063 self.oVBoxSvcProcess.wait(5000);
1064 self.oVBoxSvcProcess = None;
1065
1066 elif self.sHost == 'win':
1067 #
1068 # Windows - Just fudge it for now.
1069 #
1070 cMsFudge = 2000;
1071 self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC);
1072
1073 else:
1074 reporter.error('Port me!');
1075
1076 #
1077 # Enable automatic crash reporting if we succeeded.
1078 #
1079 if self.oVBoxSvcProcess is not None:
1080 self.oVBoxSvcProcess.enableCrashReporting('crash/report/svc', 'crash/dump/svc');
1081
1082 #
1083 # Fudge and pid file.
1084 #
1085 if self.oVBoxSvcProcess != None and not self.oVBoxSvcProcess.wait(cMsFudge):
1086 if fWritePidFile:
1087 iPid = self.oVBoxSvcProcess.getPid();
1088 try:
1089 oFile = utils.openNoInherit(self.sVBoxSvcPidFile, "w+");
1090 oFile.write('%s' % (iPid,));
1091 oFile.close();
1092 except:
1093 reporter.logXcpt('sPidFile=%s' % (self.sVBoxSvcPidFile,));
1094 reporter.log('VBoxSVC PID=%u' % (iPid,));
1095
1096 #
1097 # Finally add the task so we'll notice when it dies in a relatively timely manner.
1098 #
1099 self.addTask(self.oVBoxSvcProcess);
1100 else:
1101 self.oVBoxSvcProcess = None;
1102 try: os.remove(self.sVBoxSvcPidFile);
1103 except: pass;
1104
1105 return self.oVBoxSvcProcess != None;
1106
1107
1108 def _killVBoxSVCByPidFile(self, sPidFile):
1109 """ Kill a VBoxSVC given the pid from it's pid file. """
1110
1111 # Read the pid file.
1112 if not os.path.isfile(sPidFile):
1113 return False;
1114 try:
1115 oFile = utils.openNoInherit(sPidFile, "r");
1116 sPid = oFile.readline().strip();
1117 oFile.close();
1118 except:
1119 reporter.logXcpt('sPidfile=%s' % (sPidFile,));
1120 return False;
1121
1122 # Convert the pid to an integer and validate the range a little bit.
1123 try:
1124 iPid = long(sPid);
1125 except:
1126 reporter.logXcpt('sPidfile=%s sPid="%s"' % (sPidFile, sPid));
1127 return False;
1128 if iPid <= 0:
1129 reporter.log('negative pid - sPidfile=%s sPid="%s" iPid=%d' % (sPidFile, sPid, iPid));
1130 return False;
1131
1132 # Take care checking that it's VBoxSVC we're about to inhume.
1133 if base.processCheckPidAndName(iPid, "VBoxSVC") is not True:
1134 reporter.log('Ignoring stale VBoxSVC pid file (pid=%s)' % (iPid,));
1135 return False;
1136
1137 # Loop thru our different ways of getting VBoxSVC to terminate.
1138 for aHow in [ [ base.sendUserSignal1, 5000, 'Dropping VBoxSVC a SIGUSR1 hint...'], \
1139 [ base.processInterrupt, 5000, 'Dropping VBoxSVC a SIGINT hint...'], \
1140 [ base.processTerminate, 7500, 'VBoxSVC is still around, killing it...'] ]:
1141 reporter.log(aHow[2]);
1142 if aHow[0](iPid) is True:
1143 msStart = base.timestampMilli();
1144 while base.timestampMilli() - msStart < 5000 \
1145 and base.processExists(iPid):
1146 time.sleep(0.2);
1147
1148 fRc = not base.processExists(iPid);
1149 if fRc is True:
1150 break;
1151 if fRc:
1152 reporter.log('Successfully killed VBoxSVC (pid=%s)' % (iPid,));
1153 else:
1154 reporter.log('Failed to kill VBoxSVC (pid=%s)' % (iPid,));
1155 return fRc;
1156
1157 def _stopVBoxSVC(self):
1158 """
1159 Stops VBoxSVC. Try the polite way first.
1160 """
1161
1162 if self.oVBoxSvcProcess:
1163 self.removeTask(self.oVBoxSvcProcess);
1164 self.oVBoxSvcProcess.enableCrashReporting(None, None); # Disables it.
1165
1166 fRc = False;
1167 if self.oVBoxSvcProcess is not None \
1168 and not self.fVBoxSvcInDebugger:
1169 # by process object.
1170 if self.oVBoxSvcProcess.isRunning():
1171 reporter.log('Dropping VBoxSVC a SIGUSR1 hint...');
1172 if not self.oVBoxSvcProcess.sendUserSignal1() \
1173 or not self.oVBoxSvcProcess.wait(5000):
1174 reporter.log('Dropping VBoxSVC a SIGINT hint...');
1175 if not self.oVBoxSvcProcess.interrupt() \
1176 or not self.oVBoxSvcProcess.wait(5000):
1177 reporter.log('VBoxSVC is still around, killing it...');
1178 self.oVBoxSvcProcess.terminate();
1179 self.oVBoxSvcProcess.wait(7500);
1180 else:
1181 reporter.log('VBoxSVC is no longer running...');
1182 if not self.oVBoxSvcProcess.isRunning():
1183 self.oVBoxSvcProcess = None;
1184 else:
1185 # by pid file.
1186 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1187 return fRc;
1188
1189 def _setupVBoxApi(self):
1190 """
1191 Import and set up the vboxapi.
1192 The caller saves and restores sys.path.
1193 """
1194
1195 # Setup vbox logging for self (the test driver).
1196 self.sSelfLogFile = '%s/VBoxTestDriver.log' % (self.sScratchPath,);
1197 try: os.remove(self.sSelfLogFile);
1198 except: pass;
1199 os.environ['VBOX_LOG'] = self.sLogSelfGroups;
1200 os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSelfFlags, );
1201 if self.sLogSelfDest:
1202 os.environ['VBOX_LOG_DEST'] = self.sLogSelfDest;
1203 else:
1204 os.environ['VBOX_LOG_DEST'] = 'file=%s' % (self.sSelfLogFile,);
1205 os.environ['VBOX_RELEASE_LOG_FLAGS'] = 'time append';
1206
1207 # Hack the sys.path + environment so the vboxapi can be found.
1208 sys.path.insert(0, self.oBuild.sInstallPath);
1209 if self.oBuild.sSdkPath is not None:
1210 sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer'))
1211 sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'bindings', 'xpcom', 'python'))
1212 os.environ['VBOX_PROGRAM_PATH'] = self.oBuild.sInstallPath;
1213 reporter.log("sys.path: %s" % (sys.path));
1214
1215 try:
1216 # pylint: disable=F0401
1217 from vboxapi import VirtualBoxManager
1218 if self.sHost == 'win':
1219 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=E0611
1220 import winerror as NativeComErrorClass
1221 else:
1222 from xpcom import Exception as NativeComExceptionClass
1223 from xpcom import nsError as NativeComErrorClass
1224 # pylint: enable=F0401
1225 except:
1226 traceback.print_exc();
1227 return False;
1228
1229 __deployExceptionHacks__(NativeComExceptionClass)
1230 ComError.copyErrors(NativeComErrorClass);
1231
1232 # Create the manager.
1233 try:
1234 self.oVBoxMgr = VirtualBoxManager(None, None)
1235 except:
1236 self.oVBoxMgr = None;
1237 reporter.logXcpt('VirtualBoxManager exception');
1238 return False;
1239
1240 # Figure the API version.
1241 try:
1242 oVBox = self.oVBoxMgr.getVirtualBox();
1243 try:
1244 sVer = oVBox.version;
1245 except:
1246 reporter.logXcpt('Failed to get VirtualBox version, assuming 4.0.0');
1247 sVer = "4.0.0";
1248 if sVer.startswith("5.0") or (sVer.startswith("4.3.5") and len(sVer) == 6):
1249 self.fpApiVer = 5.0;
1250 elif sVer.startswith("4.3") or (sVer.startswith("4.2.5") and len(sVer) == 6):
1251 self.fpApiVer = 4.3;
1252 elif sVer.startswith("4.2."):
1253 self.fpApiVer = 4.2; ## @todo Fudge: Add (proper) 4.2 API support. Unmount medium etc?
1254 elif sVer.startswith("4.1.") or (sVer.startswith("4.0.5") and len(sVer) == 6):
1255 self.fpApiVer = 4.1;
1256 elif sVer.startswith("4.0."):
1257 self.fpApiVer = 4.0;
1258 elif sVer.startswith("3.2."):
1259 self.fpApiVer = 3.2;
1260 elif sVer.startswith("3.1."):
1261 self.fpApiVer = 3.1;
1262 elif sVer.startswith("3.0."):
1263 self.fpApiVer = 3.0;
1264 else:
1265 raise base.GenError('Unknown version "%s"' % (sVer,));
1266
1267 self._patchVBoxMgr();
1268
1269 from testdriver.vboxwrappers import VirtualBoxWrapper;
1270 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1271 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1272 vboxcon.fpApiVer = self.fpApiVer
1273 self.fImportedVBoxApi = True;
1274 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1275 except:
1276 self.oVBoxMgr = None;
1277 self.oVBox = None;
1278 reporter.logXcpt("getVirtualBox exception");
1279 return False;
1280 return True;
1281
1282 def _patchVBoxMgr(self):
1283 """
1284 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1285 """
1286
1287 def _xcptGetResult(oSelf, oXcpt = None):
1288 """ See vboxapi. """
1289 _ = oSelf;
1290 if oXcpt is None: oXcpt = sys.exc_info()[1];
1291 if sys.platform == 'win32':
1292 import winerror; # pylint: disable=F0401
1293 hrXcpt = oXcpt.hresult;
1294 if hrXcpt == winerror.DISP_E_EXCEPTION:
1295 hrXcpt = oXcpt.excepinfo[5];
1296 else:
1297 hrXcpt = oXcpt.error;
1298 return hrXcpt;
1299
1300 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1301 """ See vboxapi. """
1302 return oSelf.xcptGetStatus(oXcpt) in [
1303 0x80004004, -2147467260, # NS_ERROR_ABORT
1304 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1305 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1306 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1307 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1308 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1309 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1310 ];
1311
1312 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1313 """ See vboxapi. """
1314 _ = oSelf;
1315 if oXcpt is None: oXcpt = sys.exc_info()[1];
1316 if sys.platform == 'win32':
1317 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=F0401,E0611
1318 else:
1319 from xpcom import Exception as NativeComExceptionClass # pylint: disable=F0401
1320 return isinstance(oXcpt, NativeComExceptionClass);
1321
1322 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1323 """ See vboxapi. """
1324 hrXcpt = oSelf.xcptGetResult(oXcpt);
1325 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000;
1326
1327 def _xcptToString(oSelf, oXcpt):
1328 """ See vboxapi. """
1329 _ = oSelf;
1330 if oXcpt is None: oXcpt = sys.exc_info()[1];
1331 return str(oXcpt);
1332
1333 # Add utilities found in newer vboxapi revision.
1334 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1335 import types;
1336 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1337 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1338 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1339 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1340 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1341
1342
1343 def _teardownVBoxApi(self):
1344 """
1345 Drop all VBox object references and shutdown com/xpcom.
1346 """
1347 if not self.fImportedVBoxApi:
1348 return True;
1349
1350 self.aoRemoteSessions = [];
1351 self.aoVMs = [];
1352 self.oVBoxMgr = None;
1353 self.oVBox = None;
1354
1355 try:
1356 import gc
1357 gc.collect();
1358 except:
1359 reporter.logXcpt();
1360 self.fImportedVBoxApi = False;
1361
1362 if self.sHost == 'win':
1363 pass; ## TODO shutdown COM if possible/necessary?
1364 else:
1365 try:
1366 from xpcom import _xpcom as _xpcom; # pylint: disable=F0401
1367 hrc = _xpcom.NS_ShutdownXPCOM();
1368 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=W0212
1369 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=W0212
1370 if cObjs == 0 and cIfs == 0:
1371 reporter.log('actionCleanupAfter: NS_ShutdownXPCOM -> %s, nothing left behind.' % (hrc, ));
1372 else:
1373 reporter.log('actionCleanupAfter: NS_ShutdownXPCOM -> %s, leaving %s objects and %s interfaces behind...' \
1374 % (hrc, cObjs, cIfs));
1375 if hasattr(_xpcom, '_DumpInterfaces'):
1376 try:
1377 _xpcom._DumpInterfaces(); # pylint: disable=W0212
1378 except:
1379 reporter.logXcpt('actionCleanupAfter: _DumpInterfaces failed');
1380 except:
1381 reporter.logXcpt();
1382
1383 try:
1384 gc.collect();
1385 time.sleep(0.5); # fudge factory
1386 except:
1387 reporter.logXcpt();
1388 return True;
1389
1390 def _powerOffAllVms(self):
1391 """
1392 Tries to power off all running VMs.
1393 """
1394 for oSession in self.aoRemoteSessions:
1395 uPid = oSession.getPid();
1396 if uPid is not None:
1397 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1398 base.processKill(uPid);
1399 else:
1400 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1401 oSession.close();
1402 return None;
1403
1404
1405
1406 #
1407 # Build type, OS and arch getters.
1408 #
1409
1410 def getBuildType(self):
1411 """
1412 Get the build type.
1413 """
1414 if not self._detectBuild():
1415 return 'release';
1416 return self.oBuild.sType;
1417
1418 def getBuildOs(self):
1419 """
1420 Get the build OS.
1421 """
1422 if not self._detectBuild():
1423 return self.sHost;
1424 return self.oBuild.sOs;
1425
1426 def getBuildArch(self):
1427 """
1428 Get the build arch.
1429 """
1430 if not self._detectBuild():
1431 return self.sHostArch;
1432 return self.oBuild.sArch;
1433
1434 def getGuestAdditionsIso(self):
1435 """
1436 Get the path to the guest addition iso.
1437 """
1438 if not self._detectBuild():
1439 return None;
1440 return self.oBuild.sGuestAdditionsIso;
1441
1442 #
1443 # Override everything from the base class so the testdrivers don't have to
1444 # check whether we have overridden a method or not.
1445 #
1446
1447 def showUsage(self):
1448 rc = base.TestDriver.showUsage(self);
1449 reporter.log('');
1450 reporter.log('Generic VirtualBox Options:');
1451 reporter.log(' --vbox-session-type <type>');
1452 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1453 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1454 reporter.log(' --vrdp, --no-vrdp');
1455 reporter.log(' Enables VRDP, ports starting at 6000');
1456 reporter.log(' Default: --vrdp');
1457 reporter.log(' --vrdp-base-port <port>');
1458 reporter.log(' Sets the base for VRDP port assignments.');
1459 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1460 reporter.log(' --vbox-default-bridged-nic <interface>');
1461 reporter.log(' Sets the default interface for bridged networking.');
1462 reporter.log(' Default: autodetect');
1463 reporter.log(' --vbox-use-svc-defaults');
1464 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1465 reporter.log(' for automatically configuring the test VMs for debugging.');
1466 reporter.log(' --vbox-self-log');
1467 reporter.log(' The VBox logger group settings for the testdriver.');
1468 reporter.log(' --vbox-self-log-flags');
1469 reporter.log(' The VBox logger flags settings for the testdriver.');
1470 reporter.log(' --vbox-self-log-dest');
1471 reporter.log(' The VBox logger destination settings for the testdriver.');
1472 reporter.log(' --vbox-session-log');
1473 reporter.log(' The VM session logger group settings.');
1474 reporter.log(' --vbox-session-log-flags');
1475 reporter.log(' The VM session logger flags.');
1476 reporter.log(' --vbox-session-log-dest');
1477 reporter.log(' The VM session logger destination settings.');
1478 reporter.log(' --vbox-svc-log');
1479 reporter.log(' The VBoxSVC logger group settings.');
1480 reporter.log(' --vbox-svc-log-flags');
1481 reporter.log(' The VBoxSVC logger flag settings.');
1482 reporter.log(' --vbox-svc-log-dest');
1483 reporter.log(' The VBoxSVC logger destination settings.');
1484 reporter.log(' --vbox-log');
1485 reporter.log(' The VBox logger group settings for everyone.');
1486 reporter.log(' --vbox-log-flags');
1487 reporter.log(' The VBox logger flags settings for everyone.');
1488 reporter.log(' --vbox-log-dest');
1489 reporter.log(' The VBox logger destination settings for everyone.');
1490 reporter.log(' --vbox-svc-debug');
1491 reporter.log(' Start VBoxSVC in a debugger');
1492 reporter.log(' --vbox-always-upload-logs');
1493 reporter.log(' Whether to always upload log files, or only do so on failure.');
1494 reporter.log(' --vbox-always-upload-screenshots');
1495 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1496 if self.oTestVmSet is not None:
1497 self.oTestVmSet.showUsage();
1498 return rc;
1499
1500 def parseOption(self, asArgs, iArg): # pylint: disable=R0915
1501 if asArgs[iArg] == '--vbox-session-type':
1502 iArg += 1;
1503 if iArg >= len(asArgs):
1504 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1505 self.sSessionType = asArgs[iArg];
1506 elif asArgs[iArg] == '--vrdp':
1507 self.fEnableVrdp = True;
1508 elif asArgs[iArg] == '--no-vrdp':
1509 self.fEnableVrdp = False;
1510 elif asArgs[iArg] == '--vrdp-base-port':
1511 iArg += 1;
1512 if iArg >= len(asArgs):
1513 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1514 try: self.uVrdpBasePort = int(asArgs[iArg]);
1515 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer', asArgs[iArg]);
1516 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1517 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)', asArgs[iArg]);
1518 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1519 iArg += 1;
1520 if iArg >= len(asArgs):
1521 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1522 self.sDefBridgedNic = asArgs[iArg];
1523 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1524 self.fUseDefaultSvc = True;
1525 elif asArgs[iArg] == '--vbox-self-log':
1526 iArg += 1;
1527 if iArg >= len(asArgs):
1528 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1529 self.sLogSelfGroups = asArgs[iArg];
1530 elif asArgs[iArg] == '--vbox-self-log-flags':
1531 iArg += 1;
1532 if iArg >= len(asArgs):
1533 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1534 self.sLogSelfFlags = asArgs[iArg];
1535 elif asArgs[iArg] == '--vbox-self-log-dest':
1536 iArg += 1;
1537 if iArg >= len(asArgs):
1538 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1539 self.sLogSelfDest = asArgs[iArg];
1540 elif asArgs[iArg] == '--vbox-session-log':
1541 iArg += 1;
1542 if iArg >= len(asArgs):
1543 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1544 self.sLogSessionGroups = asArgs[iArg];
1545 elif asArgs[iArg] == '--vbox-session-log-flags':
1546 iArg += 1;
1547 if iArg >= len(asArgs):
1548 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1549 self.sLogSessionFlags = asArgs[iArg];
1550 elif asArgs[iArg] == '--vbox-session-log-dest':
1551 iArg += 1;
1552 if iArg >= len(asArgs):
1553 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1554 self.sLogSessionDest = asArgs[iArg];
1555 elif asArgs[iArg] == '--vbox-svc-log':
1556 iArg += 1;
1557 if iArg >= len(asArgs):
1558 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1559 self.sLogSvcGroups = asArgs[iArg];
1560 elif asArgs[iArg] == '--vbox-svc-log-flags':
1561 iArg += 1;
1562 if iArg >= len(asArgs):
1563 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1564 self.sLogSvcFlags = asArgs[iArg];
1565 elif asArgs[iArg] == '--vbox-svc-log-dest':
1566 iArg += 1;
1567 if iArg >= len(asArgs):
1568 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1569 self.sLogSvcDest = asArgs[iArg];
1570 elif asArgs[iArg] == '--vbox-log':
1571 iArg += 1;
1572 if iArg >= len(asArgs):
1573 raise base.InvalidOption('The "--vbox-log" takes an argument');
1574 self.sLogSelfGroups = asArgs[iArg];
1575 self.sLogSessionGroups = asArgs[iArg];
1576 self.sLogSvcGroups = asArgs[iArg];
1577 elif asArgs[iArg] == '--vbox-log-flags':
1578 iArg += 1;
1579 if iArg >= len(asArgs):
1580 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1581 self.sLogSelfFlags = asArgs[iArg];
1582 self.sLogSessionFlags = asArgs[iArg];
1583 self.sLogSvcFlags = asArgs[iArg];
1584 elif asArgs[iArg] == '--vbox-log-dest':
1585 iArg += 1;
1586 if iArg >= len(asArgs):
1587 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
1588 self.sLogSelfDest = asArgs[iArg];
1589 self.sLogSessionDest = asArgs[iArg];
1590 self.sLogSvcDest = asArgs[iArg];
1591 elif asArgs[iArg] == '--vbox-svc-debug':
1592 self.fVBoxSvcInDebugger = True;
1593 elif asArgs[iArg] == '--vbox-always-upload-logs':
1594 self.fAlwaysUploadLogs = True;
1595 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
1596 self.fAlwaysUploadScreenshots = True;
1597 else:
1598 # Relevant for selecting VMs to test?
1599 if self.oTestVmSet is not None:
1600 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
1601 if iRc != iArg:
1602 return iRc;
1603
1604 # Hand it to the base class.
1605 return base.TestDriver.parseOption(self, asArgs, iArg);
1606 return iArg + 1;
1607
1608 def completeOptions(self):
1609 return base.TestDriver.completeOptions(self);
1610
1611 def getResourceSet(self):
1612 if self.oTestVmSet is not None:
1613 return self.oTestVmSet.getResourceSet();
1614 return base.TestDriver.getResourceSet(self);
1615
1616 def actionExtract(self):
1617 return base.TestDriver.actionExtract(self);
1618
1619 def actionVerify(self):
1620 return base.TestDriver.actionVerify(self);
1621
1622 def actionConfig(self):
1623 return base.TestDriver.actionConfig(self);
1624
1625 def actionExecute(self):
1626 return base.TestDriver.actionExecute(self);
1627
1628 def actionCleanupBefore(self):
1629 """
1630 Kill any VBoxSVC left behind by a previous test run.
1631 """
1632 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1633 return base.TestDriver.actionCleanupBefore(self);
1634
1635 def actionCleanupAfter(self):
1636 """
1637 Clean up the VBox bits and then call the base driver.
1638
1639 If your test driver overrides this, it should normally call us at the
1640 end of the job.
1641 """
1642
1643 # Kill any left over VM processes.
1644 self._powerOffAllVms();
1645
1646 # Drop all VBox object references and shutdown xpcom then
1647 # terminating VBoxSVC, with extreme prejudice if need be.
1648 self._teardownVBoxApi();
1649 self._stopVBoxSVC();
1650
1651 # Add the VBoxSVC and testdriver debug+release log files.
1652 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
1653 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
1654 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
1655 self.sVBoxSvcLogFile = None;
1656
1657 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
1658 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
1659 self.sSelfLogFile = None;
1660
1661 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
1662 if os.path.isfile(sVBoxSvcRelLog):
1663 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
1664 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
1665 if os.path.isfile(sVBoxSvcRelLog + sSuff):
1666 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
1667 # Testbox debugging - START - TEMPORARY, REMOVE ASAP.
1668 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1669 try:
1670 print '> ls -la %s' % (os.path.join(self.sScratchPath, 'VBoxUserHome'),);
1671 utils.processCall(['ls', '-la', os.path.join(self.sScratchPath, 'VBoxUserHome')]);
1672 print '> ls -la %s' % (self.sScratchPath,);
1673 utils.processCall(['ls', '-la', self.sScratchPath]);
1674 except: pass;
1675 # Testbox debugging - END - TEMPORARY, REMOVE ASAP.
1676
1677 # Finally, call the base driver to wipe the scratch space.
1678 return base.TestDriver.actionCleanupAfter(self);
1679
1680 def actionAbort(self):
1681 """
1682 Terminate VBoxSVC if we've got a pid file.
1683 """
1684 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1685 return base.TestDriver.actionAbort(self);
1686
1687 def onExit(self, iRc):
1688 """
1689 Stop VBoxSVC if we've started it.
1690 """
1691 if self.oVBoxSvcProcess is not None:
1692 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
1693 self._powerOffAllVms();
1694 self._teardownVBoxApi();
1695 self._stopVBoxSVC();
1696 reporter.log('*** VBox API shutdown done.');
1697 return base.TestDriver.onExit(self, iRc);
1698
1699
1700 #
1701 # Task wait method override.
1702 #
1703
1704 def notifyAboutReadyTask(self, oTask):
1705 """
1706 Overriding base.TestDriver.notifyAboutReadyTask.
1707 """
1708 try:
1709 self.oVBoxMgr.interruptWaitEvents();
1710 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
1711 except:
1712 reporter.logXcpt('vbox.notifyAboutReadyTask');
1713 return base.TestDriver.notifyAboutReadyTask(self, oTask);
1714
1715 def waitForTasksSleepWorker(self, cMsTimeout):
1716 """
1717 Overriding base.TestDriver.waitForTasksSleepWorker.
1718 """
1719 try:
1720 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
1721 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
1722 return True;
1723 except KeyboardInterrupt:
1724 raise;
1725 except:
1726 reporter.logXcpt('vbox.waitForTasksSleepWorker');
1727 return False;
1728
1729 #
1730 # Utility methods.
1731 #
1732
1733 def processEvents(self, cMsTimeout = 0):
1734 """
1735 Processes events, returning after the first batch has been processed
1736 or the time limit has been reached.
1737
1738 Only Ctrl-C exception, no return.
1739 """
1740 try:
1741 self.oVBoxMgr.waitForEvents(cMsTimeout);
1742 except KeyboardInterrupt:
1743 raise;
1744 except:
1745 pass;
1746 return None;
1747
1748 def processPendingEvents(self):
1749 """ processEvents(0) - no waiting. """
1750 return self.processEvents(0);
1751
1752 def sleep(self, cSecs):
1753 """
1754 Sleep for a specified amount of time, processing XPCOM events all the while.
1755 """
1756 cMsTimeout = long(cSecs * 1000);
1757 msStart = base.timestampMilli();
1758 self.processEvents(0);
1759 while True:
1760 cMsElapsed = base.timestampMilli() - msStart;
1761 if cMsElapsed > cMsTimeout:
1762 break;
1763 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
1764 self.processEvents(cMsTimeout - cMsElapsed);
1765 return None;
1766
1767 def _logVmInfoUnsafe(self, oVM): # pylint: disable=R0915,R0912
1768 """
1769 Internal worker for logVmInfo that is wrapped in try/except.
1770
1771 This is copy, paste, search, replace and edit of infoCmd from vboxshell.py.
1772 """
1773 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId)
1774 reporter.log(" Name: %s" % (oVM.name));
1775 reporter.log(" ID: %s" % (oVM.id));
1776 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description));
1777 reporter.log(" Machine state: %s" % (oVM.state));
1778 reporter.log(" Session state: %s" % (oVM.sessionState));
1779 if self.fpApiVer >= 4.2:
1780 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID));
1781 else:
1782 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid));
1783 if self.fpApiVer >= 5.0:
1784 reporter.log(" Session Name: %s" % (oVM.sessionName));
1785 else:
1786 reporter.log(" Session Name: %s" % (oVM.sessionType));
1787 reporter.log(" CPUs: %s" % (oVM.CPUCount));
1788 reporter.log(" RAM: %sMB" % (oVM.memorySize));
1789 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize));
1790 reporter.log(" Monitors: %s" % (oVM.monitorCount));
1791 reporter.log(" Firmware: %s" % (oVM.firmwareType));
1792 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled)));
1793 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID)));
1794 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging)));
1795 if self.fpApiVer >= 4.2 and hasattr(vboxcon, 'CPUPropertyType_LongMode'):
1796 reporter.log(" Long-mode: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_LongMode)));
1797 if self.fpApiVer >= 3.2:
1798 reporter.log(" PAE: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_PAE)));
1799 reporter.log(" Synthetic CPU: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_Synthetic)));
1800 else:
1801 reporter.log(" PAE: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_PAE)));
1802 reporter.log(" Synthetic CPU: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_Synthetic)));
1803 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled));
1804 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled));
1805 if self.fpApiVer >= 3.2:
1806 if self.fpApiVer >= 4.2:
1807 reporter.log(" HPET: %s" % (oVM.HPETEnabled));
1808 else:
1809 reporter.log(" HPET: %s" % (oVM.hpetEnabled));
1810 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled));
1811 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled));
1812 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled));
1813 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort));
1814 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress));
1815 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword));
1816 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode));
1817 if self.fpApiVer >= 4.0:
1818 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled));
1819 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
1820 except: sPorts = "";
1821 reporter.log(" VRDP server ports: %s" % (sPorts));
1822 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary));
1823 else:
1824 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled));
1825 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports));
1826 reporter.log(" Last changed: %s" % (oVM.lastStateChange));
1827
1828 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
1829 if aoControllers:
1830 reporter.log(" Controllers:");
1831 for oCtrl in aoControllers:
1832 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType));
1833
1834 self.processPendingEvents();
1835 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
1836 if aoAttachments:
1837 reporter.log(" Attachments:");
1838 for oAtt in aoAttachments:
1839 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
1840 oMedium = oAtt.medium
1841 if oAtt.type == vboxcon.DeviceType_HardDisk:
1842 reporter.log(" %s: HDD" % sCtrl);
1843 reporter.log(" Id: %s" % (oMedium.id));
1844 reporter.log(" Name: %s" % (oMedium.name));
1845 reporter.log(" Format: %s" % (oMedium.format));
1846 reporter.log(" Location: %s" % (oMedium.location));
1847
1848 if oAtt.type == vboxcon.DeviceType_DVD:
1849 reporter.log(" %s: DVD" % sCtrl);
1850 if oMedium:
1851 reporter.log(" Id: %s" % (oMedium.id));
1852 reporter.log(" Name: %s" % (oMedium.name));
1853 if oMedium.hostDrive:
1854 reporter.log(" Host DVD %s" % (oMedium.location));
1855 if oAtt.passthrough:
1856 reporter.log(" [passthrough mode]");
1857 else:
1858 reporter.log(" Virtual image: %s" % (oMedium.location));
1859 reporter.log(" Size: %s" % (oMedium.size));
1860 else:
1861 reporter.log(" empty");
1862
1863 if oAtt.type == vboxcon.DeviceType_Floppy:
1864 reporter.log(" %s: Floppy" % sCtrl);
1865 if oMedium:
1866 reporter.log(" Id: %s" % (oMedium.id));
1867 reporter.log(" Name: %s" % (oMedium.name));
1868 if oMedium.hostDrive:
1869 reporter.log(" Host floppy: %s" % (oMedium.location));
1870 else:
1871 reporter.log(" Virtual image: %s" % (oMedium.location));
1872 reporter.log(" Size: %s" % (oMedium.size));
1873 else:
1874 reporter.log(" empty");
1875 self.processPendingEvents();
1876
1877 reporter.log(" Network Adapter:");
1878 for iSlot in range(0, 32):
1879 try: oNic = oVM.getNetworkAdapter(iSlot)
1880 except: break;
1881 if not oNic.enabled:
1882 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
1883 continue;
1884 if oNic.adapterType == vboxcon.NetworkAdapterType_Am79C973: sType = "PCNet";
1885 elif oNic.adapterType == vboxcon.NetworkAdapterType_Am79C970A: sType = "PCNetOld";
1886 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82545EM: sType = "E1000";
1887 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82540EM: sType = "E1000Desk";
1888 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82543GC: sType = "E1000Srv2";
1889 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio: sType = "Virtio";
1890 else: sType = "unknown %s" % (oNic.adapterType);
1891 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s" % \
1892 (iSlot, sType, oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
1893
1894 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
1895 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType));
1896 if self.fpApiVer >= 4.1:
1897 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
1898 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
1899 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType));
1900 if self.fpApiVer >= 4.1:
1901 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface));
1902 else:
1903 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
1904 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
1905 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType));
1906 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
1907 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
1908 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType));
1909 if self.fpApiVer >= 4.1:
1910 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface));
1911 else:
1912 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
1913 else:
1914 if self.fpApiVer >= 4.1:
1915 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
1916 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType));
1917 reporter.log(" generic-driver: %s" % (oNic.GenericDriver));
1918 else:
1919 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
1920 else:
1921 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
1922 if oNic.traceEnabled:
1923 reporter.log(" traceFile: %s" % (oNic.traceFile));
1924 self.processPendingEvents();
1925 return True;
1926
1927 def logVmInfo(self, oVM): # pylint: disable=R0915,R0912
1928 """
1929 Logs VM configuration details.
1930
1931 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
1932 """
1933 try:
1934 fRc = self._logVmInfoUnsafe(oVM);
1935 except:
1936 reporter.logXcpt();
1937 fRc = False;
1938 return fRc;
1939
1940 def logVmInfoByName(self, sName):
1941 """
1942 logVmInfo + getVmByName.
1943 """
1944 return self.logVmInfo(self.getVmByName(sName));
1945
1946 def tryFindGuestOsId(self, sIdOrDesc):
1947 """
1948 Takes a guest OS ID or Description and returns the ID.
1949 If nothing matching it is found, the input is returned unmodified.
1950 """
1951
1952 if self.fpApiVer >= 4.0:
1953 if sIdOrDesc == 'Solaris (64 bit)':
1954 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
1955
1956 try:
1957 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
1958 except:
1959 reporter.logXcpt();
1960 else:
1961 for oGuestOS in aoGuestTypes:
1962 try:
1963 sId = oGuestOS.id;
1964 sDesc = oGuestOS.description;
1965 except:
1966 reporter.logXcpt();
1967 else:
1968 if sIdOrDesc == sId or sIdOrDesc == sDesc:
1969 sIdOrDesc = sId;
1970 break;
1971 self.processPendingEvents();
1972 return sIdOrDesc
1973
1974 def resourceFindVmHd(self, sVmName, sFlavor):
1975 """
1976 Search the test resources for the most recent VM HD.
1977
1978 Returns path relative to the test resource root.
1979 """
1980 ## @todo implement a proper search algo here.
1981 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
1982
1983
1984 #
1985 # VM Api wrappers that logs errors, hides exceptions and other details.
1986 #
1987
1988 # pylint: disable=R0913,R0914
1989 def createTestVM(self, sName, iGroup, sHd = None, cMbRam = None, cCpus = 1, fVirtEx = None, fNestedPaging = None, \
1990 sDvdImage = None, sKind = "Other", fIoApic = None, fPae = None, fFastBootLogo = True, \
1991 eNic0Type = None, eNic0AttachType = None, sNic0NetName = 'default', sNic0MacAddr = 'grouped', \
1992 sFloppy = None, fNatForwardingForTxs = None, sHddControllerType = 'IDE Controller', \
1993 fVmmDevTestingPart = None, fVmmDevTestingMmio = False):
1994 """
1995 Creates a test VM with a immutable HD from the test resources.
1996 """
1997 if not self.importVBoxApi():
1998 return None;
1999
2000 # create + register the VM
2001 try:
2002 if self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2003 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2004 elif self.fpApiVer >= 4.0:
2005 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2006 elif self.fpApiVer >= 3.2:
2007 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2008 else:
2009 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2010 try:
2011 oVM.saveSettings();
2012 try:
2013 self.oVBox.registerMachine(oVM);
2014 except:
2015 raise;
2016 except:
2017 if self.fpApiVer >= 4.0:
2018 try:
2019 if self.fpApiVer >= 4.3:
2020 oProgress = oVM.deleteConfig(None);
2021 else:
2022 oProgress = oVM.delete(None);
2023 self.waitOnProgress(oProgress);
2024 except:
2025 reporter.logXcpt();
2026 else:
2027 try: oVM.deleteSettings();
2028 except: reporter.logXcpt();
2029 raise;
2030 except:
2031 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2032 return None;
2033
2034 # Configure the VM.
2035 fRc = True;
2036 oSession = self.openSession(oVM);
2037 if oSession is not None:
2038 fRc = oSession.setupPreferredConfig();
2039
2040 if fRc and cMbRam is not None :
2041 fRc = oSession.setRamSize(cMbRam);
2042 if fRc and cCpus is not None:
2043 fRc = oSession.setCpuCount(cCpus);
2044 if fRc and fVirtEx is not None:
2045 fRc = oSession.enableVirtEx(fVirtEx);
2046 if fRc and fNestedPaging is not None:
2047 fRc = oSession.enableNestedPaging(fNestedPaging);
2048 if fRc and fIoApic is not None:
2049 fRc = oSession.enableIoApic(fIoApic);
2050 if fRc and fPae is not None:
2051 fRc = oSession.enablePae(fPae);
2052 if fRc and sDvdImage is not None:
2053 fRc = oSession.attachDvd(sDvdImage);
2054 if fRc and sHd is not None:
2055 fRc = oSession.attachHd(sHd, sHddControllerType);
2056 if fRc and sFloppy is not None:
2057 fRc = oSession.attachFloppy(sFloppy);
2058 if fRc and eNic0Type is not None:
2059 fRc = oSession.setNicType(eNic0Type, 0);
2060 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2061 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2062 if fRc and sNic0MacAddr is not None:
2063 if sNic0MacAddr == 'grouped':
2064 sNic0MacAddr = '%02u' % (iGroup);
2065 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2066 if fRc and fNatForwardingForTxs is True:
2067 fRc = oSession.setupNatForwardingForTxs();
2068 if fRc and fFastBootLogo is not None:
2069 fRc = oSession.setupBootLogo(fFastBootLogo);
2070 if fRc and self.fEnableVrdp:
2071 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2072 if fRc and fVmmDevTestingPart is not None:
2073 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2074
2075 if fRc: fRc = oSession.saveSettings();
2076 if not fRc: oSession.discardSettings(True);
2077 oSession.close();
2078 if not fRc:
2079 try: self.oVBox.unregisterMachine(oVM.id);
2080 except: pass;
2081 if self.fpApiVer >= 4.0:
2082 try:
2083 if self.fpApiVer >= 4.3:
2084 oProgress = oVM.deleteConfig(None);
2085 else:
2086 oProgress = oVM.delete(None);
2087 self.waitOnProgress(oProgress);
2088 except:
2089 reporter.logXcpt();
2090 else:
2091 try: oVM.deleteSettings();
2092 except: reporter.logXcpt();
2093 return None;
2094
2095 # success.
2096 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2097 self.aoVMs.append(oVM);
2098 self.logVmInfo(oVM); # testing...
2099 return oVM;
2100 # pylint: enable=R0913,R0914
2101
2102 def addTestMachine(self, sNameOrId, fQuiet = False):
2103 """
2104 Adds an already existing (that is, configured) test VM to the
2105 test VM list.
2106 """
2107 # find + add the VM to the list.
2108 try:
2109 if self.fpApiVer >= 4.0:
2110 oVM = self.oVBox.findMachine(sNameOrId);
2111 else:
2112 reporter.error('Port me!'); ## @todo Add support for older version < 4.0.
2113 except:
2114 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2115 return None;
2116
2117 self.aoVMs.append(oVM);
2118 if not fQuiet:
2119 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2120 self.logVmInfo(oVM);
2121 return oVM;
2122
2123 def openSession(self, oVM):
2124 """
2125 Opens a session for the VM. Returns the a Session wrapper object that
2126 will automatically close the session when the wrapper goes out of scope.
2127
2128 On failure None is returned and an error is logged.
2129 """
2130 try:
2131 sUuid = oVM.id;
2132 except:
2133 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2134 return None;
2135
2136 # This loop is a kludge to deal with us racing the closing of the
2137 # direct session of a previous VM run. See waitOnDirectSessionClose.
2138 for i in range(10):
2139 try:
2140 if self.fpApiVer <= 3.2:
2141 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2142 else:
2143 oSession = self.oVBoxMgr.openMachineSession(oVM);
2144 break;
2145 except:
2146 if i == 9:
2147 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2148 return None;
2149 if i > 0:
2150 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2151 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2152 from testdriver.vboxwrappers import SessionWrapper;
2153 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2154
2155 def getVmByName(self, sName):
2156 """
2157 Get a test VM by name. Returns None if not found, logged.
2158 """
2159 # Look it up in our 'cache'.
2160 for oVM in self.aoVMs:
2161 try:
2162 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2163 if oVM.name == sName:
2164 return oVM;
2165 except:
2166 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2167
2168 # Look it up the standard way.
2169 return self.addTestMachine(sName, fQuiet = True);
2170
2171 def getVmByUuid(self, sUuid):
2172 """
2173 Get a test VM by uuid. Returns None if not found, logged.
2174 """
2175 # Look it up in our 'cache'.
2176 for oVM in self.aoVMs:
2177 try:
2178 if oVM.id == sUuid:
2179 return oVM;
2180 except:
2181 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2182
2183 # Look it up the standard way.
2184 return self.addTestMachine(sUuid, fQuiet = True);
2185
2186 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2187 """
2188 Waits for a progress object to complete. Returns the status code.
2189 """
2190 # Wait for progress no longer than cMsTimeout time period.
2191 tsStart = datetime.datetime.now()
2192 while True:
2193 self.processPendingEvents();
2194 try:
2195 if oProgress.completed:
2196 break;
2197 except:
2198 return -1;
2199 self.processPendingEvents();
2200
2201 tsNow = datetime.datetime.now()
2202 tsDelta = tsNow - tsStart
2203 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) / 1000) > cMsTimeout:
2204 if fErrorOnTimeout:
2205 reporter.errorTimeout('Timeout while waiting for progress.')
2206 return -1
2207
2208 try: oProgress.waitForCompletion(cMsInterval);
2209 except: return -2;
2210
2211 try: rc = oProgress.resultCode;
2212 except: rc = -2;
2213 self.processPendingEvents();
2214 return rc;
2215
2216 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2217 """
2218 Waits for the VM process to close it's current direct session.
2219
2220 Returns None.
2221 """
2222 # Get the original values so we're not subject to
2223 try:
2224 eCurState = oVM.sessionState;
2225 if self.fpApiVer >= 5.0:
2226 sCurName = sOrgName = oVM.sessionName;
2227 else:
2228 sCurName = sOrgName = oVM.sessionType;
2229 if self.fpApiVer >= 4.2:
2230 iCurPid = iOrgPid = oVM.sessionPID;
2231 else:
2232 iCurPid = iOrgPid = oVM.sessionPid;
2233 except Exception, oXcpt:
2234 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2235 reporter.logXcpt();
2236 self.processPendingEvents();
2237 return None;
2238 self.processPendingEvents();
2239
2240 msStart = base.timestampMilli();
2241 while iCurPid == iOrgPid \
2242 and sCurName == sOrgName \
2243 and sCurName != '' \
2244 and base.timestampMilli() - msStart < cMsTimeout \
2245 and ( eCurState == vboxcon.SessionState_Unlocking \
2246 or eCurState == vboxcon.SessionState_Spawning \
2247 or eCurState == vboxcon.SessionState_Locked):
2248 self.processEvents(1000);
2249 try:
2250 eCurState = oVM.sessionState;
2251 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2252 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2253 except Exception, oXcpt:
2254 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2255 reporter.logXcpt();
2256 break;
2257 self.processPendingEvents();
2258 self.processPendingEvents();
2259 return None;
2260
2261 def uploadStartupLogFile(self, oVM, sVmName):
2262 """
2263 Uploads the VBoxStartup.log when present.
2264 """
2265 fRc = True;
2266 try:
2267 sLogFile = os.path.join(oVM.logFolder, 'VBoxStartup.log');
2268 except:
2269 reporter.logXcpt();
2270 fRc = False;
2271 else:
2272 if os.path.isfile(sLogFile):
2273 reporter.addLogFile(sLogFile, 'log/release/vm', '%s startup log' % (sVmName, ),
2274 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
2275 return fRc;
2276
2277 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=R0914,R0915
2278 """
2279 Start the VM, returning the VM session and progress object on success.
2280 The session is also added to the task list and to the aoRemoteSessions set.
2281
2282 asEnv is a list of string on the putenv() form.
2283
2284 On failure (None, None) is returned and an error is logged.
2285 """
2286 # Massage and check the input.
2287 if sType is None:
2288 sType = self.sSessionType;
2289 if sName is None:
2290 try: sName = oVM.name;
2291 except: sName = 'bad-vm-handle';
2292 reporter.log2('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
2293 if oVM is None:
2294 return (None, None);
2295
2296 ## @todo Do this elsewhere.
2297 # Hack alert. Disables all annoying GUI popups.
2298 if sType == 'gui' and len(self.aoRemoteSessions) == 0:
2299 try:
2300 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
2301 if self.fpApiVer >= 3.2:
2302 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
2303 else:
2304 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
2305 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
2306 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
2307 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
2308 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
2309 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
2310 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
2311 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
2312 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
2313 except:
2314 reporter.logXcpt();
2315
2316 # The UUID for the name.
2317 try:
2318 sUuid = oVM.id;
2319 except:
2320 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
2321 return (None, None);
2322 self.processPendingEvents();
2323
2324 # Construct the environment.
2325 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
2326 try: os.remove(sLogFile);
2327 except: pass;
2328 if self.sLogSessionDest:
2329 sLogDest = self.sLogSessionDest;
2330 else:
2331 sLogDest = 'file=%s' % sLogFile;
2332 sEnv = 'VBOX_LOG=%s\nVBOX_LOG_FLAGS=%s\nVBOX_LOG_DEST=%s\nVBOX_RELEASE_LOG_FLAGS=append time' \
2333 % (self.sLogSessionGroups, self.sLogSessionFlags, sLogDest,);
2334 if sType == 'gui':
2335 sEnv += '\nVBOX_GUI_DBG_ENABLED=1'
2336 if asEnv is not None and len(asEnv) > 0:
2337 sEnv += '\n' + ('\n'.join(asEnv));
2338
2339 # Shortcuts for local testing.
2340 oProgress = oWrapped = None;
2341 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
2342 try:
2343 if oTestVM is not None \
2344 and oTestVM.fSnapshotRestoreCurrent is True:
2345 if oVM.state is vboxcon.MachineState_Running:
2346 reporter.log2('Machine "%s" already running.' % (sName,));
2347 oProgress = None;
2348 oWrapped = self.openSession(oVM);
2349 else:
2350 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
2351 oSessionWrapperRestore = self.openSession(oVM);
2352 if oSessionWrapperRestore is not None:
2353 oSnapshotCur = oVM.currentSnapshot;
2354 if oSnapshotCur is not None:
2355 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
2356 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
2357 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
2358 else:
2359 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
2360 oSessionWrapperRestore.close();
2361 except:
2362 reporter.errorXcpt();
2363 return (None, None);
2364
2365 # Open a remote session, wait for this operation to complete.
2366 # (The loop is a kludge to deal with us racing the closing of the
2367 # direct session of a previous VM run. See waitOnDirectSessionClose.)
2368 if oWrapped is None:
2369 for i in range(10):
2370 try:
2371 if self.fpApiVer < 4.3 \
2372 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
2373 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2374 else:
2375 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2376 if self.fpApiVer < 3.3:
2377 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, sEnv);
2378 else:
2379 oProgress = oVM.launchVMProcess(oSession, sType, sEnv);
2380 break;
2381 except:
2382 if i == 9:
2383 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
2384 return (None, None);
2385 oSession = None;
2386 if i >= 0:
2387 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=C0301
2388 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2389 if fWait and oProgress is not None:
2390 rc = self.waitOnProgress(oProgress);
2391 if rc < 0:
2392 self.waitOnDirectSessionClose(oVM, 5000);
2393 try:
2394 if oSession is not None:
2395 oSession.close();
2396 except: pass;
2397 reportError(oProgress, 'failed to open session for "%s"' % (sName));
2398 self.uploadStartupLogFile(oVM, sName);
2399 return (None, None);
2400 reporter.log2('waitOnProgress -> %s' % (rc,));
2401
2402 # Wrap up the session object and push on to the list before returning it.
2403 if oWrapped is None:
2404 from testdriver.vboxwrappers import SessionWrapper;
2405 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2406
2407 oWrapped.registerEventHandlerForTask();
2408 self.aoRemoteSessions.append(oWrapped);
2409 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
2410 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
2411 % (oWrapped, len(self.aoRemoteSessions) - 1,
2412 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
2413 self.addTask(oWrapped);
2414
2415 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
2416
2417 from testdriver.vboxwrappers import ProgressWrapper;
2418 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
2419 'starting %s' % (sName,)) if oProgress else None);
2420
2421 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
2422 """ Simplified version of startVmEx. """
2423 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
2424 return oSession;
2425
2426 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
2427 """
2428 Start the VM, returning the VM session and progress object on success.
2429 The session is also added to the task list and to the aoRemoteSessions set.
2430
2431 On failure (None, None) is returned and an error is logged.
2432 """
2433 oVM = self.getVmByName(sName);
2434 if oVM is None:
2435 return (None, None);
2436 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
2437
2438 def startVmByName(self, sName, sType=None, asEnv = None):
2439 """
2440 Start the VM, returning the VM session on success. The session is
2441 also added to the task list and to the aoRemoteSessions set.
2442
2443 On failure None is returned and an error is logged.
2444 """
2445 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
2446 return oSession;
2447
2448 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None):
2449 """
2450 Terminates the VM specified by oSession and adds the release logs to
2451 the test report.
2452
2453 This will try archive this by using powerOff, but will resort to
2454 tougher methods if that fails.
2455
2456 The session will always be removed from the task list.
2457 The session will be closed unless we fail to kill the process.
2458 The session will be removed from the remote session list if closed.
2459
2460 The progress object (a wrapper!) is for teleportation and similar VM
2461 operations, it will be attempted canceled before powering off the VM.
2462 Failures are logged but ignored.
2463 The progress object will always be removed from the task list.
2464
2465 Returns True if powerOff and session close both succeed.
2466 Returns False if on failure (logged), including when we successfully
2467 kill the VM process.
2468 """
2469 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
2470
2471 # Call getPid first to make sure the PID is cached in the wrapper.
2472 oSession.getPid();
2473
2474 #
2475 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
2476 #
2477 sLastScreenshotPath = None
2478 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
2479 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName)
2480 fRc = oSession.takeScreenshot(sLastScreenshotPath)
2481 if fRc is not True:
2482 sLastScreenshotPath = None
2483
2484 #
2485 # Terminate the VM
2486 #
2487
2488 # Cancel the progress object if specified.
2489 if oProgress is not None:
2490 if not oProgress.isCompleted() and oProgress.isCancelable():
2491 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
2492 try:
2493 oProgress.o.cancel();
2494 except:
2495 reporter.logXcpt();
2496 else:
2497 oProgress.wait();
2498 self.removeTask(oProgress);
2499
2500 # Check if the VM has terminated by it self before powering it off.
2501 fClose = True;
2502 fRc = oSession.pollTask();
2503 if fRc is not True:
2504 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
2505 fRc = oSession.powerOff(fFudgeOnFailure = False);
2506 if fRc is not True:
2507 # power off failed, try terminate it in a nice manner.
2508 fRc = False;
2509 uPid = oSession.getPid();
2510 if uPid is not None:
2511 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
2512 fClose = base.processTerminate(uPid);
2513 if fClose is True:
2514 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2515 fClose = oSession.waitForTask(1000);
2516
2517 if fClose is not True:
2518 # Being nice failed...
2519 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
2520 % (uPid, oSession.sName));
2521 fClose = base.processKill(uPid);
2522 if fClose is True:
2523 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2524 fClose = oSession.waitForTask(1000);
2525 if fClose is not True:
2526 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
2527
2528 # The final steps.
2529 if fClose is True:
2530 oSession.close();
2531 self.waitOnDirectSessionClose(oSession.oVM, 10000);
2532 try:
2533 eState = oSession.oVM.state;
2534 except:
2535 reporter.logXcpt();
2536 else:
2537 if eState == vboxcon.MachineState_Aborted:
2538 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
2539 self.removeTask(oSession);
2540
2541 #
2542 # Add the release log, debug log and a screenshot of the VM to the test report.
2543 #
2544 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2545 oSession.addLogsToReport();
2546
2547 # Add a screenshot if it has been requested and taken successfully.
2548 if sLastScreenshotPath is not None:
2549 if reporter.testErrorCount() > 0:
2550 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
2551 else:
2552 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
2553
2554 return fRc;
2555
2556
2557 #
2558 # Some information query functions (mix).
2559 #
2560 # Methods require the VBox API. If the information is provided by both
2561 # the testboxscript as well as VBox API, we'll check if it matches.
2562 #
2563
2564 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
2565 """
2566 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
2567
2568 Returns True / False.
2569 Raises exception on environment / host mismatch.
2570 """
2571 fEnv = os.environ.get(sEnvVar, None);
2572 if fEnv is not None:
2573 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
2574
2575 fVBox = None;
2576 self.importVBoxApi();
2577 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
2578 try:
2579 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
2580 except:
2581 if not fQuiet:
2582 reporter.logXcpt();
2583
2584 if fVBox is not None:
2585 if fEnv is not None:
2586 if fEnv != fVBox and not fQuiet:
2587 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
2588 % (fVBox, sEnum, fEnv, sEnvVar));
2589 return fEnv;
2590 return fVBox;
2591 if fEnv is not None:
2592 return fEnv;
2593 return False;
2594
2595 def hasHostHwVirt(self, fQuiet = False):
2596 """
2597 Checks if hardware assisted virtualization is supported by the host.
2598
2599 Returns True / False.
2600 Raises exception on environment / host mismatch.
2601 """
2602 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
2603
2604 def hasHostNestedPaging(self, fQuiet = False):
2605 """
2606 Checks if nested paging is supported by the host.
2607
2608 Returns True / False.
2609 Raises exception on environment / host mismatch.
2610 """
2611 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
2612 and self.hasHostHwVirt(fQuiet);
2613
2614 def hasHostLongMode(self, fQuiet = False):
2615 """
2616 Checks if the host supports 64-bit guests.
2617
2618 Returns True / False.
2619 Raises exception on environment / host mismatch.
2620 """
2621 # Note that the testboxscript doesn't export this variable atm.
2622 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
2623
2624 def getHostCpuCount(self, fQuiet = False):
2625 """
2626 Returns the number of CPUs on the host.
2627
2628 Returns True / False.
2629 Raises exception on environment / host mismatch.
2630 """
2631 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
2632 if cEnv is not None:
2633 cEnv = int(cEnv);
2634
2635 try:
2636 cVBox = self.oVBox.host.processorOnlineCount;
2637 except:
2638 if not fQuiet:
2639 reporter.logXcpt();
2640 cVBox = None;
2641
2642 if cVBox is not None:
2643 if cEnv is not None:
2644 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
2645 return cVBox;
2646 if cEnv is not None:
2647 return cEnv;
2648 return 1;
2649
2650 def _getHostCpuDesc(self, fQuiet = False):
2651 """
2652 Internal method used for getting the host CPU description from VBoxSVC.
2653 Returns description string, on failure an empty string is returned.
2654 """
2655 try:
2656 return self.oVBox.host.getProcessorDescription(0);
2657 except:
2658 if not fQuiet:
2659 reporter.logXcpt();
2660 return '';
2661
2662 def isHostCpuAmd(self, fQuiet = False):
2663 """
2664 Checks if the host CPU vendor is AMD.
2665
2666 Returns True / False.
2667 """
2668 sCpuDesc = self._getHostCpuDesc(fQuiet);
2669 return sCpuDesc.startswith("AMD") or sCpuDesc == 'AuthenticAMD';
2670
2671 def isHostCpuIntel(self, fQuiet = False):
2672 """
2673 Checks if the host CPU vendor is Intel.
2674
2675 Returns True / False.
2676 """
2677 sCpuDesc = self._getHostCpuDesc(fQuiet);
2678 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
2679
2680 def isHostCpuVia(self, fQuiet = False):
2681 """
2682 Checks if the host CPU vendor is VIA (or Centaur).
2683
2684 Returns True / False.
2685 """
2686 sCpuDesc = self._getHostCpuDesc(fQuiet);
2687 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
2688
2689
2690 #
2691 # Testdriver execution methods.
2692 #
2693
2694 def handleTask(self, oTask, sMethod):
2695 """
2696 Callback method for handling unknown tasks in the various run loops.
2697
2698 The testdriver should override this if it already tasks running when
2699 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
2700 Call super to handle unknown tasks.
2701
2702 Returns True if handled, False if not.
2703 """
2704 reporter.error('%s: unknown task %s' % (sMethod, oTask));
2705 return False;
2706
2707 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
2708 """
2709 Generic TXS task wrapper which waits both on the TXS and the session tasks.
2710
2711 Returns False on error, logged.
2712
2713 Returns task result on success.
2714 """
2715 # All async methods ends with the following to args.
2716 cMsTimeout = aArgs[-2];
2717 fIgnoreErrors = aArgs[-1];
2718
2719 fRemoveVm = self.addTask(oSession);
2720 fRemoveTxs = self.addTask(oTxsSession);
2721
2722 rc = fnAsync(*aArgs); # pylint: disable=W0142
2723 if rc is True:
2724 rc = False;
2725 oTask = self.waitForTasks(cMsTimeout + 1);
2726 if oTask is oTxsSession:
2727 if oTxsSession.isSuccess():
2728 rc = oTxsSession.getResult();
2729 elif fIgnoreErrors is True:
2730 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
2731 else:
2732 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
2733 else:
2734 oTxsSession.cancelTask();
2735 if oTask is None:
2736 if fIgnoreErrors is True:
2737 reporter.log( 'txsDoTask: The task timed out.');
2738 else:
2739 reporter.errorTimeout('txsDoTask: The task timed out.');
2740 elif oTask is oSession:
2741 reporter.error('txsDoTask: The VM terminated unexpectedly');
2742 else:
2743 if fIgnoreErrors is True:
2744 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
2745 else:
2746 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
2747 else:
2748 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
2749
2750 if fRemoveTxs:
2751 self.removeTask(oTxsSession);
2752 if fRemoveVm:
2753 self.removeTask(oSession);
2754 return rc;
2755
2756 # pylint: disable=C0111
2757
2758 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
2759 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
2760 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2761
2762 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
2763 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
2764 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2765
2766 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0700, cMsTimeout = 30000, fIgnoreErrors = False):
2767 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
2768 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2769
2770 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0700, cMsTimeout = 30000, fIgnoreErrors = False):
2771 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
2772 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2773
2774 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
2775 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
2776 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2777
2778 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
2779 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
2780 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2781
2782 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2783 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
2784 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2785
2786 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
2787 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
2788 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2789
2790 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
2791 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
2792 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2793
2794 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
2795 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
2796 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2797
2798 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2799 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
2800 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2801
2802 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
2803 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
2804 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2805
2806 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2807 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
2808 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2809
2810 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2811 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
2812 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2813
2814 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
2815 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
2816 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2817
2818 def txsDownloadFiles(self, oSession, oTxsSession, asFiles, fIgnoreErrors = False):
2819 """
2820 Convenience function to get files from the guest and stores it
2821 into the scratch directory for later (manual) review.
2822
2823 Returns True on success.
2824
2825 Returns False on failure, logged.
2826 """
2827 fRc = True;
2828 for sGstFile in asFiles:
2829 ## @todo Check for already existing files on the host and create a new
2830 # name for the current file to download.
2831 sTmpFile = os.path.join(self.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
2832 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
2833 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sTmpFile, 30 * 1000, fIgnoreErrors);
2834 try: os.unlink(sTmpFile);
2835 except: pass;
2836 if fRc:
2837 reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
2838 else:
2839 if fIgnoreErrors is not True:
2840 reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
2841 return fRc;
2842 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
2843 return True;
2844
2845 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2846 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
2847 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2848
2849 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
2850 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
2851 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2852
2853 # pylint: enable=C0111
2854
2855 def txsCdWait(self, oSession, oTxsSession, cMsTimeout = 30000, sFileCdWait = 'vboxtxs-readme.txt'):
2856 """
2857 Mostly an internal helper for txsRebootAndReconnectViaTcp and
2858 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
2859 ready. It does this by polling for a file it knows to exist on the CD.
2860
2861 Returns True on success.
2862
2863 Returns False on failure, logged.
2864 """
2865
2866 fRemoveVm = self.addTask(oSession);
2867 fRemoveTxs = self.addTask(oTxsSession);
2868 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
2869 msStart = base.timestampMilli();
2870 cMsTimeout2 = cMsTimeout;
2871 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFileCdWait), cMsTimeout2);
2872 if fRc is True:
2873 while True:
2874 # wait for it to complete.
2875 oTask = self.waitForTasks(cMsTimeout2 + 1);
2876 if oTask is not oTxsSession:
2877 oTxsSession.cancelTask();
2878 if oTask is None:
2879 reporter.errorTimeout('txsToCdWait: The task timed out (after %s ms).'
2880 % (base.timestampMilli() - msStart,));
2881 elif oTask is oSession:
2882 reporter.error('txsToCdWait: The VM terminated unexpectedly');
2883 else:
2884 reporter.error('txsToCdWait: An unknown task %s was returned' % (oTask,));
2885 fRc = False;
2886 break;
2887 if oTxsSession.isSuccess():
2888 break;
2889
2890 # Check for timeout.
2891 cMsElapsed = base.timestampMilli() - msStart;
2892 if cMsElapsed >= cMsTimeout:
2893 reporter.error('txsToCdWait: timed out');
2894 fRc = False;
2895 break;
2896
2897 # delay.
2898 self.sleep(1);
2899
2900 # resubmitt the task.
2901 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
2902 if cMsTimeout2 < 500:
2903 cMsTimeout2 = 500;
2904 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFileCdWait), cMsTimeout2);
2905 if fRc is not True:
2906 reporter.error('txsToCdWait: asyncIsFile failed');
2907 break;
2908 else:
2909 reporter.error('txsToCdWait: asyncIsFile failed');
2910
2911 if fRemoveTxs:
2912 self.removeTask(oTxsSession);
2913 if fRemoveVm:
2914 self.removeTask(oSession);
2915 return fRc;
2916
2917 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
2918 """
2919 Mostly an internal worker for connecting to TXS via TCP used by the
2920 *ViaTcp methods.
2921
2922 Returns a tuplet with True/False and TxsSession/None depending on the
2923 result. Errors are logged.
2924 """
2925
2926 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
2927 % (oSession, cMsTimeout, fNatForwardingForTxs));
2928
2929 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
2930 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
2931 if oTxsConnect is not None:
2932 self.addTask(oTxsConnect);
2933 fRemoveVm = self.addTask(oSession);
2934 oTask = self.waitForTasks(cMsTimeout + 1);
2935 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
2936 self.removeTask(oTxsConnect);
2937 if oTask is oTxsConnect:
2938 oTxsSession = oTxsConnect.getResult();
2939 if oTxsSession is not None:
2940 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
2941 return (True, oTxsSession);
2942
2943 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
2944 else:
2945 oTxsConnect.cancelTask();
2946 if oTask is None:
2947 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
2948 elif oTask is oSession:
2949 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
2950 else:
2951 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
2952 if fRemoveVm:
2953 self.removeTask(oSession);
2954 else:
2955 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
2956 return (False, None);
2957
2958 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
2959 cMsCdWait = 30000, sFileCdWait = 'vboxtxs-readme.txt', \
2960 fNatForwardingForTxs = False):
2961 """
2962 Starts the specified VM and tries to connect to its TXS via TCP.
2963 The VM will be powered off if TXS doesn't respond before the specified
2964 time has elapsed.
2965
2966 Returns a the VM and TXS sessions (a two tuple) on success. The VM
2967 session is in the task list, the TXS session is not.
2968 Returns (None, None) on failure, fully logged.
2969 """
2970
2971 # Zap the guest IP to make sure we're not getting a stale entry
2972 # (unless we're restoring the VM of course).
2973 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
2974 if oTestVM is None \
2975 or oTestVM.fSnapshotRestoreCurrent is False:
2976 try:
2977 oSession1 = self.openSession(self.getVmByName(sVmName));
2978 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
2979 oSession1.saveSettings(True);
2980 del oSession1;
2981 except:
2982 reporter.logXcpt();
2983
2984 # Start the VM.
2985 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
2986 oSession = self.startVmByName(sVmName);
2987 if oSession is not None:
2988 # Connect to TXS.
2989 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
2990 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
2991 if fRc is True:
2992 if fCdWait:
2993 # Wait for CD?
2994 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
2995 if fRc is not True:
2996 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
2997 if fRc is True:
2998 # Success!
2999 return (oSession, oTxsSession);
3000 else:
3001 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
3002 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
3003 self.terminateVmBySession(oSession);
3004 return (None, None);
3005
3006 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
3007 cMsCdWait = 30000, sFileCdWait = 'vboxtxs-readme.txt', fNatForwardingForTxs = False):
3008 """
3009 Executes the TXS reboot command
3010
3011 Returns A tuple of True and the new TXS session on success.
3012
3013 Returns A tuple of False and either the old TXS session or None on failure.
3014 """
3015 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
3016
3017 #
3018 # This stuff is a bit complicated because of rebooting being kind of
3019 # disruptive to the TXS and such... The protocol is that TXS will:
3020 # - ACK the reboot command.
3021 # - Shutdown the transport layer, implicitly disconnecting us.
3022 # - Execute the reboot operation.
3023 # - On failure, it will be re-init the transport layer and be
3024 # available pretty much immediately. UUID unchanged.
3025 # - On success, it will be respawed after the reboot (hopefully),
3026 # with a different UUID.
3027 #
3028 fRc = False;
3029 iStart = base.timestampMilli();
3030
3031 # Get UUID.
3032 cMsTimeout2 = min(60000, cMsTimeout);
3033 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
3034 if sUuidBefore is not False:
3035 # Reboot.
3036 cMsElapsed = base.timestampMilli() - iStart;
3037 cMsTimeout2 = cMsTimeout - cMsElapsed;
3038 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
3039 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3040 if fRc is True:
3041 # Reconnect.
3042 if fNatForwardingForTxs is True:
3043 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
3044 cMsElapsed = base.timestampMilli() - iStart;
3045 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
3046 if fRc is True:
3047 # Check the UUID.
3048 cMsElapsed = base.timestampMilli() - iStart;
3049 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
3050 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3051 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3052 if sUuidBefore is not False:
3053 if sUuidAfter != sUuidBefore:
3054 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
3055
3056 # Do CD wait if specified.
3057 if fCdWait:
3058 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3059 if fRc is not True:
3060 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
3061 else:
3062 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
3063 else:
3064 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
3065 else:
3066 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
3067 else:
3068 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
3069 else:
3070 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
3071 return (fRc, oTxsSession);
3072
3073 # pylint: disable=R0914,R0913
3074
3075 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = ""):
3076 """
3077 Executes the specified test task, waiting till it completes or times out.
3078
3079 The VM session (if any) must be in the task list.
3080
3081 Returns True if we executed the task and nothing abnormal happend.
3082 Query the process status from the TXS session.
3083
3084 Returns False if some unexpected task was signalled or we failed to
3085 submit the job.
3086 """
3087 reporter.testStart(sTestName);
3088 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3089
3090 # Submit the job.
3091 fRc = False;
3092 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3093 self.addTask(oTxsSession);
3094
3095 # Wait for the job to complete.
3096 while True:
3097 oTask = self.waitForTasks(cMsTimeout + 1);
3098 if oTask is None:
3099 reporter.log('txsRunTest: waitForTasks timed out');
3100 break;
3101 if oTask is oTxsSession:
3102 fRc = True;
3103 reporter.log('txsRunTest: isSuccess=%s getResult=%s' % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3104 break;
3105 if not self.handleTask(oTask, 'txsRunTest'):
3106 break;
3107
3108 self.removeTask(oTxsSession);
3109 if not oTxsSession.pollTask():
3110 oTxsSession.cancelTask();
3111 else:
3112 reporter.error('txsRunTest: asyncExec failed');
3113
3114 reporter.testDone();
3115 return fRc;
3116
3117 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
3118 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
3119 """
3120 Executes the specified test task, waiting till it completes or times out,
3121 redirecting stdin, stdout and stderr to the given objects.
3122
3123 The VM session (if any) must be in the task list.
3124
3125 Returns True if we executed the task and nothing abnormal happend.
3126 Query the process status from the TXS session.
3127
3128 Returns False if some unexpected task was signalled or we failed to
3129 submit the job.
3130 """
3131 reporter.testStart(sTestName);
3132 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3133
3134 # Submit the job.
3135 fRc = False;
3136 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
3137 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3138 self.addTask(oTxsSession);
3139
3140 # Wait for the job to complete.
3141 while True:
3142 oTask = self.waitForTasks(cMsTimeout + 1);
3143 if oTask is None:
3144 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
3145 break;
3146 if oTask is oTxsSession:
3147 fRc = True;
3148 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
3149 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3150 break;
3151 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
3152 break;
3153
3154 self.removeTask(oTxsSession);
3155 if not oTxsSession.pollTask():
3156 oTxsSession.cancelTask();
3157 else:
3158 reporter.error('txsRunTestRedirectStd: asyncExec failed');
3159
3160 reporter.testDone();
3161 return fRc;
3162
3163 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
3164 sExecName1, asArgs1,
3165 sExecName2, asArgs2,
3166 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
3167 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
3168 """
3169 Executes the specified test tasks, waiting till they complete or
3170 times out. The 1st task is started after the 2nd one.
3171
3172 The VM session (if any) must be in the task list.
3173
3174 Returns True if we executed the task and nothing abnormal happend.
3175 Query the process status from the TXS sessions.
3176
3177 Returns False if some unexpected task was signalled or we failed to
3178 submit the job.
3179 """
3180 reporter.testStart(sTestName);
3181
3182 # Submit the jobs.
3183 fRc = False;
3184 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
3185 self.adjustTimeoutMs(cMsTimeout)):
3186 self.addTask(oTxsSession1);
3187
3188 self.sleep(2); # fudge! grr
3189
3190 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
3191 self.adjustTimeoutMs(cMsTimeout)):
3192 self.addTask(oTxsSession2);
3193
3194 # Wait for the jobs to complete.
3195 cPendingJobs = 2;
3196 while True:
3197 oTask = self.waitForTasks(cMsTimeout + 1);
3198 if oTask is None:
3199 reporter.log('txsRunTest2: waitForTasks timed out');
3200 break;
3201
3202 if oTask is oTxsSession1 or oTask is oTxsSession2:
3203 if oTask is oTxsSession1: iTask = 1;
3204 else: iTask = 2;
3205 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
3206 % (iTask, oTask.isSuccess(), oTask.getResult()));
3207 self.removeTask(oTask);
3208 cPendingJobs -= 1;
3209 if cPendingJobs <= 0:
3210 fRc = True;
3211 break;
3212
3213 elif not self.handleTask(oTask, 'txsRunTest'):
3214 break;
3215
3216 self.removeTask(oTxsSession2);
3217 if not oTxsSession2.pollTask():
3218 oTxsSession2.cancelTask();
3219 else:
3220 reporter.error('txsRunTest2: asyncExec #2 failed');
3221
3222 self.removeTask(oTxsSession1);
3223 if not oTxsSession1.pollTask():
3224 oTxsSession1.cancelTask();
3225 else:
3226 reporter.error('txsRunTest2: asyncExec #1 failed');
3227
3228 reporter.testDone();
3229 return fRc;
3230
3231 # pylint: enable=R0914,R0913
3232
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