VirtualBox

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

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

ValidationKit: fixed location of the GA .iso on Solaris

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

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