VirtualBox

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

Last change on this file since 61224 was 61154, checked in by vboxsync, 9 years ago

vbox.py: Added more logging in area where there have been a few hangs lately (xpcom). E.g. test set 3520240.

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

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