VirtualBox

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

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

vbox.py: Try fix unregister() race in EventHandlerBase.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 142.6 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 60570 2016-04-19 11:56:33Z 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: 60570 $"
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 if not self.fShutdown:
504 self.fShutdown = True;
505
506 fRc = False;
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
1266 # Figure the API version.
1267 try:
1268 oVBox = self.oVBoxMgr.getVirtualBox();
1269 try:
1270 sVer = oVBox.version;
1271 except:
1272 reporter.logXcpt('Failed to get VirtualBox version, assuming 4.0.0');
1273 sVer = "4.0.0";
1274 if sVer.startswith("5.0") or (sVer.startswith("4.3.5") and len(sVer) == 6):
1275 self.fpApiVer = 5.0;
1276 elif sVer.startswith("4.3") or (sVer.startswith("4.2.5") and len(sVer) == 6):
1277 self.fpApiVer = 4.3;
1278 elif sVer.startswith("4.2."):
1279 self.fpApiVer = 4.2; ## @todo Fudge: Add (proper) 4.2 API support. Unmount medium etc?
1280 elif sVer.startswith("4.1.") or (sVer.startswith("4.0.5") and len(sVer) == 6):
1281 self.fpApiVer = 4.1;
1282 elif sVer.startswith("4.0."):
1283 self.fpApiVer = 4.0;
1284 elif sVer.startswith("3.2."):
1285 self.fpApiVer = 3.2;
1286 elif sVer.startswith("3.1."):
1287 self.fpApiVer = 3.1;
1288 elif sVer.startswith("3.0."):
1289 self.fpApiVer = 3.0;
1290 else:
1291 raise base.GenError('Unknown version "%s"' % (sVer,));
1292
1293 self._patchVBoxMgr();
1294
1295 from testdriver.vboxwrappers import VirtualBoxWrapper;
1296 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1297 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1298 vboxcon.fpApiVer = self.fpApiVer
1299 self.fImportedVBoxApi = True;
1300 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1301 except:
1302 self.oVBoxMgr = None;
1303 self.oVBox = None;
1304 reporter.logXcpt("getVirtualBox exception");
1305 return False;
1306 return True;
1307
1308 def _patchVBoxMgr(self):
1309 """
1310 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1311 """
1312
1313 def _xcptGetResult(oSelf, oXcpt = None):
1314 """ See vboxapi. """
1315 _ = oSelf;
1316 if oXcpt is None: oXcpt = sys.exc_info()[1];
1317 if sys.platform == 'win32':
1318 import winerror; # pylint: disable=F0401
1319 hrXcpt = oXcpt.hresult;
1320 if hrXcpt == winerror.DISP_E_EXCEPTION:
1321 hrXcpt = oXcpt.excepinfo[5];
1322 else:
1323 hrXcpt = oXcpt.error;
1324 return hrXcpt;
1325
1326 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1327 """ See vboxapi. """
1328 return oSelf.xcptGetStatus(oXcpt) in [
1329 0x80004004, -2147467260, # NS_ERROR_ABORT
1330 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1331 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1332 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1333 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1334 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1335 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1336 ];
1337
1338 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1339 """ See vboxapi. """
1340 _ = oSelf;
1341 if oXcpt is None: oXcpt = sys.exc_info()[1];
1342 if sys.platform == 'win32':
1343 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=F0401,E0611
1344 else:
1345 from xpcom import Exception as NativeComExceptionClass # pylint: disable=F0401
1346 return isinstance(oXcpt, NativeComExceptionClass);
1347
1348 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1349 """ See vboxapi. """
1350 hrXcpt = oSelf.xcptGetResult(oXcpt);
1351 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000;
1352
1353 def _xcptToString(oSelf, oXcpt):
1354 """ See vboxapi. """
1355 _ = oSelf;
1356 if oXcpt is None: oXcpt = sys.exc_info()[1];
1357 return str(oXcpt);
1358
1359 # Add utilities found in newer vboxapi revision.
1360 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1361 import types;
1362 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1363 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1364 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1365 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1366 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1367
1368
1369 def _teardownVBoxApi(self):
1370 """
1371 Drop all VBox object references and shutdown com/xpcom.
1372 """
1373 if not self.fImportedVBoxApi:
1374 return True;
1375
1376 self.aoRemoteSessions = [];
1377 self.aoVMs = [];
1378 self.oVBoxMgr = None;
1379 self.oVBox = None;
1380
1381 try:
1382 import gc
1383 gc.collect();
1384 except:
1385 reporter.logXcpt();
1386 self.fImportedVBoxApi = False;
1387
1388 if self.sHost == 'win':
1389 pass; ## TODO shutdown COM if possible/necessary?
1390 else:
1391 try:
1392 from xpcom import _xpcom as _xpcom; # pylint: disable=F0401
1393 hrc = _xpcom.NS_ShutdownXPCOM();
1394 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=W0212
1395 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=W0212
1396 if cObjs == 0 and cIfs == 0:
1397 reporter.log('actionCleanupAfter: NS_ShutdownXPCOM -> %s, nothing left behind.' % (hrc, ));
1398 else:
1399 reporter.log('actionCleanupAfter: NS_ShutdownXPCOM -> %s, leaving %s objects and %s interfaces behind...' \
1400 % (hrc, cObjs, cIfs));
1401 if hasattr(_xpcom, '_DumpInterfaces'):
1402 try:
1403 _xpcom._DumpInterfaces(); # pylint: disable=W0212
1404 except:
1405 reporter.logXcpt('actionCleanupAfter: _DumpInterfaces failed');
1406 except:
1407 reporter.logXcpt();
1408
1409 try:
1410 gc.collect();
1411 time.sleep(0.5); # fudge factory
1412 except:
1413 reporter.logXcpt();
1414 return True;
1415
1416 def _powerOffAllVms(self):
1417 """
1418 Tries to power off all running VMs.
1419 """
1420 for oSession in self.aoRemoteSessions:
1421 uPid = oSession.getPid();
1422 if uPid is not None:
1423 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1424 base.processKill(uPid);
1425 else:
1426 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1427 oSession.close();
1428 return None;
1429
1430
1431
1432 #
1433 # Build type, OS and arch getters.
1434 #
1435
1436 def getBuildType(self):
1437 """
1438 Get the build type.
1439 """
1440 if not self._detectBuild():
1441 return 'release';
1442 return self.oBuild.sType;
1443
1444 def getBuildOs(self):
1445 """
1446 Get the build OS.
1447 """
1448 if not self._detectBuild():
1449 return self.sHost;
1450 return self.oBuild.sOs;
1451
1452 def getBuildArch(self):
1453 """
1454 Get the build arch.
1455 """
1456 if not self._detectBuild():
1457 return self.sHostArch;
1458 return self.oBuild.sArch;
1459
1460 def getGuestAdditionsIso(self):
1461 """
1462 Get the path to the guest addition iso.
1463 """
1464 if not self._detectBuild():
1465 return None;
1466 return self.oBuild.sGuestAdditionsIso;
1467
1468 #
1469 # Override everything from the base class so the testdrivers don't have to
1470 # check whether we have overridden a method or not.
1471 #
1472
1473 def showUsage(self):
1474 rc = base.TestDriver.showUsage(self);
1475 reporter.log('');
1476 reporter.log('Generic VirtualBox Options:');
1477 reporter.log(' --vbox-session-type <type>');
1478 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1479 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1480 reporter.log(' --vrdp, --no-vrdp');
1481 reporter.log(' Enables VRDP, ports starting at 6000');
1482 reporter.log(' Default: --vrdp');
1483 reporter.log(' --vrdp-base-port <port>');
1484 reporter.log(' Sets the base for VRDP port assignments.');
1485 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1486 reporter.log(' --vbox-default-bridged-nic <interface>');
1487 reporter.log(' Sets the default interface for bridged networking.');
1488 reporter.log(' Default: autodetect');
1489 reporter.log(' --vbox-use-svc-defaults');
1490 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1491 reporter.log(' for automatically configuring the test VMs for debugging.');
1492 reporter.log(' --vbox-self-log');
1493 reporter.log(' The VBox logger group settings for the testdriver.');
1494 reporter.log(' --vbox-self-log-flags');
1495 reporter.log(' The VBox logger flags settings for the testdriver.');
1496 reporter.log(' --vbox-self-log-dest');
1497 reporter.log(' The VBox logger destination settings for the testdriver.');
1498 reporter.log(' --vbox-session-log');
1499 reporter.log(' The VM session logger group settings.');
1500 reporter.log(' --vbox-session-log-flags');
1501 reporter.log(' The VM session logger flags.');
1502 reporter.log(' --vbox-session-log-dest');
1503 reporter.log(' The VM session logger destination settings.');
1504 reporter.log(' --vbox-svc-log');
1505 reporter.log(' The VBoxSVC logger group settings.');
1506 reporter.log(' --vbox-svc-log-flags');
1507 reporter.log(' The VBoxSVC logger flag settings.');
1508 reporter.log(' --vbox-svc-log-dest');
1509 reporter.log(' The VBoxSVC logger destination settings.');
1510 reporter.log(' --vbox-log');
1511 reporter.log(' The VBox logger group settings for everyone.');
1512 reporter.log(' --vbox-log-flags');
1513 reporter.log(' The VBox logger flags settings for everyone.');
1514 reporter.log(' --vbox-log-dest');
1515 reporter.log(' The VBox logger destination settings for everyone.');
1516 reporter.log(' --vbox-svc-debug');
1517 reporter.log(' Start VBoxSVC in a debugger');
1518 reporter.log(' --vbox-always-upload-logs');
1519 reporter.log(' Whether to always upload log files, or only do so on failure.');
1520 reporter.log(' --vbox-always-upload-screenshots');
1521 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1522 if self.oTestVmSet is not None:
1523 self.oTestVmSet.showUsage();
1524 return rc;
1525
1526 def parseOption(self, asArgs, iArg): # pylint: disable=R0915
1527 if asArgs[iArg] == '--vbox-session-type':
1528 iArg += 1;
1529 if iArg >= len(asArgs):
1530 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1531 self.sSessionType = asArgs[iArg];
1532 elif asArgs[iArg] == '--vrdp':
1533 self.fEnableVrdp = True;
1534 elif asArgs[iArg] == '--no-vrdp':
1535 self.fEnableVrdp = False;
1536 elif asArgs[iArg] == '--vrdp-base-port':
1537 iArg += 1;
1538 if iArg >= len(asArgs):
1539 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1540 try: self.uVrdpBasePort = int(asArgs[iArg]);
1541 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer', asArgs[iArg]);
1542 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1543 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)', asArgs[iArg]);
1544 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1545 iArg += 1;
1546 if iArg >= len(asArgs):
1547 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1548 self.sDefBridgedNic = asArgs[iArg];
1549 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1550 self.fUseDefaultSvc = True;
1551 elif asArgs[iArg] == '--vbox-self-log':
1552 iArg += 1;
1553 if iArg >= len(asArgs):
1554 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1555 self.sLogSelfGroups = asArgs[iArg];
1556 elif asArgs[iArg] == '--vbox-self-log-flags':
1557 iArg += 1;
1558 if iArg >= len(asArgs):
1559 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1560 self.sLogSelfFlags = asArgs[iArg];
1561 elif asArgs[iArg] == '--vbox-self-log-dest':
1562 iArg += 1;
1563 if iArg >= len(asArgs):
1564 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1565 self.sLogSelfDest = asArgs[iArg];
1566 elif asArgs[iArg] == '--vbox-session-log':
1567 iArg += 1;
1568 if iArg >= len(asArgs):
1569 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1570 self.sLogSessionGroups = asArgs[iArg];
1571 elif asArgs[iArg] == '--vbox-session-log-flags':
1572 iArg += 1;
1573 if iArg >= len(asArgs):
1574 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1575 self.sLogSessionFlags = asArgs[iArg];
1576 elif asArgs[iArg] == '--vbox-session-log-dest':
1577 iArg += 1;
1578 if iArg >= len(asArgs):
1579 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1580 self.sLogSessionDest = asArgs[iArg];
1581 elif asArgs[iArg] == '--vbox-svc-log':
1582 iArg += 1;
1583 if iArg >= len(asArgs):
1584 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1585 self.sLogSvcGroups = asArgs[iArg];
1586 elif asArgs[iArg] == '--vbox-svc-log-flags':
1587 iArg += 1;
1588 if iArg >= len(asArgs):
1589 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1590 self.sLogSvcFlags = asArgs[iArg];
1591 elif asArgs[iArg] == '--vbox-svc-log-dest':
1592 iArg += 1;
1593 if iArg >= len(asArgs):
1594 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1595 self.sLogSvcDest = asArgs[iArg];
1596 elif asArgs[iArg] == '--vbox-log':
1597 iArg += 1;
1598 if iArg >= len(asArgs):
1599 raise base.InvalidOption('The "--vbox-log" takes an argument');
1600 self.sLogSelfGroups = asArgs[iArg];
1601 self.sLogSessionGroups = asArgs[iArg];
1602 self.sLogSvcGroups = asArgs[iArg];
1603 elif asArgs[iArg] == '--vbox-log-flags':
1604 iArg += 1;
1605 if iArg >= len(asArgs):
1606 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1607 self.sLogSelfFlags = asArgs[iArg];
1608 self.sLogSessionFlags = asArgs[iArg];
1609 self.sLogSvcFlags = asArgs[iArg];
1610 elif asArgs[iArg] == '--vbox-log-dest':
1611 iArg += 1;
1612 if iArg >= len(asArgs):
1613 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
1614 self.sLogSelfDest = asArgs[iArg];
1615 self.sLogSessionDest = asArgs[iArg];
1616 self.sLogSvcDest = asArgs[iArg];
1617 elif asArgs[iArg] == '--vbox-svc-debug':
1618 self.fVBoxSvcInDebugger = True;
1619 elif asArgs[iArg] == '--vbox-always-upload-logs':
1620 self.fAlwaysUploadLogs = True;
1621 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
1622 self.fAlwaysUploadScreenshots = True;
1623 else:
1624 # Relevant for selecting VMs to test?
1625 if self.oTestVmSet is not None:
1626 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
1627 if iRc != iArg:
1628 return iRc;
1629
1630 # Hand it to the base class.
1631 return base.TestDriver.parseOption(self, asArgs, iArg);
1632 return iArg + 1;
1633
1634 def completeOptions(self):
1635 return base.TestDriver.completeOptions(self);
1636
1637 def getResourceSet(self):
1638 if self.oTestVmSet is not None:
1639 return self.oTestVmSet.getResourceSet();
1640 return base.TestDriver.getResourceSet(self);
1641
1642 def actionExtract(self):
1643 return base.TestDriver.actionExtract(self);
1644
1645 def actionVerify(self):
1646 return base.TestDriver.actionVerify(self);
1647
1648 def actionConfig(self):
1649 return base.TestDriver.actionConfig(self);
1650
1651 def actionExecute(self):
1652 return base.TestDriver.actionExecute(self);
1653
1654 def actionCleanupBefore(self):
1655 """
1656 Kill any VBoxSVC left behind by a previous test run.
1657 """
1658 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1659 return base.TestDriver.actionCleanupBefore(self);
1660
1661 def actionCleanupAfter(self):
1662 """
1663 Clean up the VBox bits and then call the base driver.
1664
1665 If your test driver overrides this, it should normally call us at the
1666 end of the job.
1667 """
1668
1669 # Kill any left over VM processes.
1670 self._powerOffAllVms();
1671
1672 # Drop all VBox object references and shutdown xpcom then
1673 # terminating VBoxSVC, with extreme prejudice if need be.
1674 self._teardownVBoxApi();
1675 self._stopVBoxSVC();
1676
1677 # Add the VBoxSVC and testdriver debug+release log files.
1678 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
1679 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
1680 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
1681 self.sVBoxSvcLogFile = None;
1682
1683 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
1684 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
1685 self.sSelfLogFile = None;
1686
1687 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
1688 if os.path.isfile(sVBoxSvcRelLog):
1689 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
1690 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
1691 if os.path.isfile(sVBoxSvcRelLog + sSuff):
1692 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
1693 # Testbox debugging - START - TEMPORARY, REMOVE ASAP.
1694 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1695 try:
1696 print '> ls -la %s' % (os.path.join(self.sScratchPath, 'VBoxUserHome'),);
1697 utils.processCall(['ls', '-la', os.path.join(self.sScratchPath, 'VBoxUserHome')]);
1698 print '> ls -la %s' % (self.sScratchPath,);
1699 utils.processCall(['ls', '-la', self.sScratchPath]);
1700 except: pass;
1701 # Testbox debugging - END - TEMPORARY, REMOVE ASAP.
1702
1703 # Finally, call the base driver to wipe the scratch space.
1704 return base.TestDriver.actionCleanupAfter(self);
1705
1706 def actionAbort(self):
1707 """
1708 Terminate VBoxSVC if we've got a pid file.
1709 """
1710 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1711 return base.TestDriver.actionAbort(self);
1712
1713 def onExit(self, iRc):
1714 """
1715 Stop VBoxSVC if we've started it.
1716 """
1717 if self.oVBoxSvcProcess is not None:
1718 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
1719 self._powerOffAllVms();
1720 self._teardownVBoxApi();
1721 self._stopVBoxSVC();
1722 reporter.log('*** VBox API shutdown done.');
1723 return base.TestDriver.onExit(self, iRc);
1724
1725
1726 #
1727 # Task wait method override.
1728 #
1729
1730 def notifyAboutReadyTask(self, oTask):
1731 """
1732 Overriding base.TestDriver.notifyAboutReadyTask.
1733 """
1734 try:
1735 self.oVBoxMgr.interruptWaitEvents();
1736 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
1737 except:
1738 reporter.logXcpt('vbox.notifyAboutReadyTask');
1739 return base.TestDriver.notifyAboutReadyTask(self, oTask);
1740
1741 def waitForTasksSleepWorker(self, cMsTimeout):
1742 """
1743 Overriding base.TestDriver.waitForTasksSleepWorker.
1744 """
1745 try:
1746 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
1747 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
1748 return True;
1749 except KeyboardInterrupt:
1750 raise;
1751 except:
1752 reporter.logXcpt('vbox.waitForTasksSleepWorker');
1753 return False;
1754
1755 #
1756 # Utility methods.
1757 #
1758
1759 def processEvents(self, cMsTimeout = 0):
1760 """
1761 Processes events, returning after the first batch has been processed
1762 or the time limit has been reached.
1763
1764 Only Ctrl-C exception, no return.
1765 """
1766 try:
1767 self.oVBoxMgr.waitForEvents(cMsTimeout);
1768 except KeyboardInterrupt:
1769 raise;
1770 except:
1771 pass;
1772 return None;
1773
1774 def processPendingEvents(self):
1775 """ processEvents(0) - no waiting. """
1776 return self.processEvents(0);
1777
1778 def sleep(self, cSecs):
1779 """
1780 Sleep for a specified amount of time, processing XPCOM events all the while.
1781 """
1782 cMsTimeout = long(cSecs * 1000);
1783 msStart = base.timestampMilli();
1784 self.processEvents(0);
1785 while True:
1786 cMsElapsed = base.timestampMilli() - msStart;
1787 if cMsElapsed > cMsTimeout:
1788 break;
1789 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
1790 self.processEvents(cMsTimeout - cMsElapsed);
1791 return None;
1792
1793 def _logVmInfoUnsafe(self, oVM): # pylint: disable=R0915,R0912
1794 """
1795 Internal worker for logVmInfo that is wrapped in try/except.
1796
1797 This is copy, paste, search, replace and edit of infoCmd from vboxshell.py.
1798 """
1799 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId)
1800 reporter.log(" Name: %s" % (oVM.name));
1801 reporter.log(" ID: %s" % (oVM.id));
1802 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description));
1803 reporter.log(" Machine state: %s" % (oVM.state));
1804 reporter.log(" Session state: %s" % (oVM.sessionState));
1805 if self.fpApiVer >= 4.2:
1806 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID));
1807 else:
1808 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid));
1809 if self.fpApiVer >= 5.0:
1810 reporter.log(" Session Name: %s" % (oVM.sessionName));
1811 else:
1812 reporter.log(" Session Name: %s" % (oVM.sessionType));
1813 reporter.log(" CPUs: %s" % (oVM.CPUCount));
1814 reporter.log(" RAM: %sMB" % (oVM.memorySize));
1815 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize));
1816 reporter.log(" Monitors: %s" % (oVM.monitorCount));
1817 if oVM.firmwareType == vboxcon.FirmwareType_BIOS: sType = "BIOS";
1818 elif oVM.firmwareType == vboxcon.FirmwareType_EFI: sType = "EFI";
1819 elif oVM.firmwareType == vboxcon.FirmwareType_EFI32: sType = "EFI32";
1820 elif oVM.firmwareType == vboxcon.FirmwareType_EFI64: sType = "EFI64";
1821 elif oVM.firmwareType == vboxcon.FirmwareType_EFIDUAL: sType = "EFIDUAL";
1822 else: sType = "unknown %s" % (oVM.firmwareType);
1823 reporter.log(" Firmware: %s" % (sType));
1824 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled)));
1825 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID)));
1826 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging)));
1827 if self.fpApiVer >= 4.2 and hasattr(vboxcon, 'CPUPropertyType_LongMode'):
1828 reporter.log(" Long-mode: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_LongMode)));
1829 if self.fpApiVer >= 3.2:
1830 reporter.log(" PAE: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_PAE)));
1831 if self.fpApiVer < 5.0:
1832 reporter.log(" Synthetic CPU: %s" % (oVM.getCPUProperty(vboxcon.CPUPropertyType_Synthetic)));
1833 else:
1834 reporter.log(" PAE: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_PAE)));
1835 reporter.log(" Synthetic CPU: %s" % (oVM.getCpuProperty(vboxcon.CpuPropertyType_Synthetic)));
1836 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled));
1837 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled));
1838 if self.fpApiVer >= 3.2:
1839 if self.fpApiVer >= 4.2:
1840 reporter.log(" HPET: %s" % (oVM.HPETEnabled));
1841 else:
1842 reporter.log(" HPET: %s" % (oVM.hpetEnabled));
1843 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled));
1844 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled));
1845 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled));
1846 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort));
1847 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress));
1848 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword));
1849 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode));
1850 if self.fpApiVer >= 5.0:
1851 reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode));
1852 elif self.fpApiVer >= 4.3:
1853 reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode));
1854 if self.fpApiVer >= 4.0:
1855 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled));
1856 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
1857 except: sPorts = "";
1858 reporter.log(" VRDP server ports: %s" % (sPorts));
1859 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary));
1860 else:
1861 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled));
1862 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports));
1863 reporter.log(" Last changed: %s" % (oVM.lastStateChange));
1864
1865 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
1866 if aoControllers:
1867 reporter.log(" Controllers:");
1868 for oCtrl in aoControllers:
1869 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType));
1870 oAudioAdapter = oVM.audioAdapter;
1871 if oAudioAdapter.audioController == vboxcon.AudioControllerType_AC97: sType = "AC97";
1872 elif oAudioAdapter.audioController == vboxcon.AudioControllerType_SB16: sType = "SB16";
1873 elif oAudioAdapter.audioController == vboxcon.AudioControllerType_HDA: sType = "HDA";
1874 else: sType = "unknown %s" % (oAudioAdapter.audioController);
1875 reporter.log(" AudioController: %s" % (sType));
1876 reporter.log(" AudioEnabled: %s" % (oAudioAdapter.enabled));
1877
1878 self.processPendingEvents();
1879 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
1880 if aoAttachments:
1881 reporter.log(" Attachments:");
1882 for oAtt in aoAttachments:
1883 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
1884 oMedium = oAtt.medium
1885 if oAtt.type == vboxcon.DeviceType_HardDisk:
1886 reporter.log(" %s: HDD" % sCtrl);
1887 reporter.log(" Id: %s" % (oMedium.id));
1888 reporter.log(" Name: %s" % (oMedium.name));
1889 reporter.log(" Format: %s" % (oMedium.format));
1890 reporter.log(" Location: %s" % (oMedium.location));
1891
1892 if oAtt.type == vboxcon.DeviceType_DVD:
1893 reporter.log(" %s: DVD" % sCtrl);
1894 if oMedium:
1895 reporter.log(" Id: %s" % (oMedium.id));
1896 reporter.log(" Name: %s" % (oMedium.name));
1897 if oMedium.hostDrive:
1898 reporter.log(" Host DVD %s" % (oMedium.location));
1899 if oAtt.passthrough:
1900 reporter.log(" [passthrough mode]");
1901 else:
1902 reporter.log(" Virtual image: %s" % (oMedium.location));
1903 reporter.log(" Size: %s" % (oMedium.size));
1904 else:
1905 reporter.log(" empty");
1906
1907 if oAtt.type == vboxcon.DeviceType_Floppy:
1908 reporter.log(" %s: Floppy" % sCtrl);
1909 if oMedium:
1910 reporter.log(" Id: %s" % (oMedium.id));
1911 reporter.log(" Name: %s" % (oMedium.name));
1912 if oMedium.hostDrive:
1913 reporter.log(" Host floppy: %s" % (oMedium.location));
1914 else:
1915 reporter.log(" Virtual image: %s" % (oMedium.location));
1916 reporter.log(" Size: %s" % (oMedium.size));
1917 else:
1918 reporter.log(" empty");
1919 self.processPendingEvents();
1920
1921 reporter.log(" Network Adapter:");
1922 for iSlot in range(0, 32):
1923 try: oNic = oVM.getNetworkAdapter(iSlot)
1924 except: break;
1925 if not oNic.enabled:
1926 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
1927 continue;
1928 if oNic.adapterType == vboxcon.NetworkAdapterType_Am79C973: sType = "PCNet";
1929 elif oNic.adapterType == vboxcon.NetworkAdapterType_Am79C970A: sType = "PCNetOld";
1930 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82545EM: sType = "E1000";
1931 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82540EM: sType = "E1000Desk";
1932 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82543GC: sType = "E1000Srv2";
1933 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio: sType = "Virtio";
1934 else: sType = "unknown %s" % (oNic.adapterType);
1935 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s" % \
1936 (iSlot, sType, oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
1937
1938 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
1939 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType));
1940 if self.fpApiVer >= 4.1:
1941 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
1942 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
1943 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType));
1944 if self.fpApiVer >= 4.1:
1945 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface));
1946 else:
1947 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
1948 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
1949 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType));
1950 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
1951 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
1952 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType));
1953 if self.fpApiVer >= 4.1:
1954 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface));
1955 else:
1956 reporter.log(" hostInterface: %s" % (oNic.hostInterface));
1957 else:
1958 if self.fpApiVer >= 4.1:
1959 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
1960 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType));
1961 reporter.log(" generic-driver: %s" % (oNic.GenericDriver));
1962 else:
1963 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
1964 else:
1965 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType));
1966 if oNic.traceEnabled:
1967 reporter.log(" traceFile: %s" % (oNic.traceFile));
1968 self.processPendingEvents();
1969 return True;
1970
1971 def logVmInfo(self, oVM): # pylint: disable=R0915,R0912
1972 """
1973 Logs VM configuration details.
1974
1975 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
1976 """
1977 try:
1978 fRc = self._logVmInfoUnsafe(oVM);
1979 except:
1980 reporter.logXcpt();
1981 fRc = False;
1982 return fRc;
1983
1984 def logVmInfoByName(self, sName):
1985 """
1986 logVmInfo + getVmByName.
1987 """
1988 return self.logVmInfo(self.getVmByName(sName));
1989
1990 def tryFindGuestOsId(self, sIdOrDesc):
1991 """
1992 Takes a guest OS ID or Description and returns the ID.
1993 If nothing matching it is found, the input is returned unmodified.
1994 """
1995
1996 if self.fpApiVer >= 4.0:
1997 if sIdOrDesc == 'Solaris (64 bit)':
1998 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
1999
2000 try:
2001 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
2002 except:
2003 reporter.logXcpt();
2004 else:
2005 for oGuestOS in aoGuestTypes:
2006 try:
2007 sId = oGuestOS.id;
2008 sDesc = oGuestOS.description;
2009 except:
2010 reporter.logXcpt();
2011 else:
2012 if sIdOrDesc == sId or sIdOrDesc == sDesc:
2013 sIdOrDesc = sId;
2014 break;
2015 self.processPendingEvents();
2016 return sIdOrDesc
2017
2018 def resourceFindVmHd(self, sVmName, sFlavor):
2019 """
2020 Search the test resources for the most recent VM HD.
2021
2022 Returns path relative to the test resource root.
2023 """
2024 ## @todo implement a proper search algo here.
2025 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
2026
2027
2028 #
2029 # VM Api wrappers that logs errors, hides exceptions and other details.
2030 #
2031
2032 # pylint: disable=R0913,R0914,R0915
2033 def createTestVM(self, sName, iGroup, sHd = None, cMbRam = None, cCpus = 1, fVirtEx = None, fNestedPaging = None, \
2034 sDvdImage = None, sKind = "Other", fIoApic = None, fPae = None, fFastBootLogo = True, \
2035 eNic0Type = None, eNic0AttachType = None, sNic0NetName = 'default', sNic0MacAddr = 'grouped', \
2036 sFloppy = None, fNatForwardingForTxs = None, sHddControllerType = 'IDE Controller', \
2037 fVmmDevTestingPart = None, fVmmDevTestingMmio = False, sFirmwareType = 'bios'):
2038 """
2039 Creates a test VM with a immutable HD from the test resources.
2040 """
2041 if not self.importVBoxApi():
2042 return None;
2043
2044 # create + register the VM
2045 try:
2046 if self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2047 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2048 elif self.fpApiVer >= 4.0:
2049 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2050 elif self.fpApiVer >= 3.2:
2051 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2052 else:
2053 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2054 try:
2055 oVM.saveSettings();
2056 try:
2057 self.oVBox.registerMachine(oVM);
2058 except:
2059 raise;
2060 except:
2061 reporter.logXcpt();
2062 if self.fpApiVer >= 4.0:
2063 try:
2064 if self.fpApiVer >= 4.3:
2065 oProgress = oVM.deleteConfig([]);
2066 else:
2067 oProgress = oVM.delete(None);
2068 self.waitOnProgress(oProgress);
2069 except:
2070 reporter.logXcpt();
2071 else:
2072 try: oVM.deleteSettings();
2073 except: reporter.logXcpt();
2074 raise;
2075 except:
2076 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2077 return None;
2078
2079 # Configure the VM.
2080 fRc = True;
2081 oSession = self.openSession(oVM);
2082 if oSession is not None:
2083 fRc = oSession.setupPreferredConfig();
2084
2085 if fRc and cMbRam is not None :
2086 fRc = oSession.setRamSize(cMbRam);
2087 if fRc and cCpus is not None:
2088 fRc = oSession.setCpuCount(cCpus);
2089 if fRc and fVirtEx is not None:
2090 fRc = oSession.enableVirtEx(fVirtEx);
2091 if fRc and fNestedPaging is not None:
2092 fRc = oSession.enableNestedPaging(fNestedPaging);
2093 if fRc and fIoApic is not None:
2094 fRc = oSession.enableIoApic(fIoApic);
2095 if fRc and fPae is not None:
2096 fRc = oSession.enablePae(fPae);
2097 if fRc and sDvdImage is not None:
2098 fRc = oSession.attachDvd(sDvdImage);
2099 if fRc and sHd is not None:
2100 fRc = oSession.attachHd(sHd, sHddControllerType);
2101 if fRc and sFloppy is not None:
2102 fRc = oSession.attachFloppy(sFloppy);
2103 if fRc and eNic0Type is not None:
2104 fRc = oSession.setNicType(eNic0Type, 0);
2105 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2106 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2107 if fRc and sNic0MacAddr is not None:
2108 if sNic0MacAddr == 'grouped':
2109 sNic0MacAddr = '%02u' % (iGroup);
2110 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2111 if fRc and fNatForwardingForTxs is True:
2112 fRc = oSession.setupNatForwardingForTxs();
2113 if fRc and fFastBootLogo is not None:
2114 fRc = oSession.setupBootLogo(fFastBootLogo);
2115 if fRc and self.fEnableVrdp:
2116 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2117 if fRc and fVmmDevTestingPart is not None:
2118 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2119 if fRc and sFirmwareType == 'bios':
2120 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
2121 elif sFirmwareType == 'efi':
2122 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
2123
2124 if fRc: fRc = oSession.saveSettings();
2125 if not fRc: oSession.discardSettings(True);
2126 oSession.close();
2127 if not fRc:
2128 try: self.oVBox.unregisterMachine(oVM.id);
2129 except: pass;
2130 if self.fpApiVer >= 4.0:
2131 try:
2132 if self.fpApiVer >= 4.3:
2133 oProgress = oVM.deleteConfig([]);
2134 else:
2135 oProgress = oVM.delete(None);
2136 self.waitOnProgress(oProgress);
2137 except:
2138 reporter.logXcpt();
2139 else:
2140 try: oVM.deleteSettings();
2141 except: reporter.logXcpt();
2142 return None;
2143
2144 # success.
2145 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2146 self.aoVMs.append(oVM);
2147 self.logVmInfo(oVM); # testing...
2148 return oVM;
2149 # pylint: enable=R0913,R0914,R0915
2150
2151 def addTestMachine(self, sNameOrId, fQuiet = False):
2152 """
2153 Adds an already existing (that is, configured) test VM to the
2154 test VM list.
2155 """
2156 # find + add the VM to the list.
2157 try:
2158 if self.fpApiVer >= 4.0:
2159 oVM = self.oVBox.findMachine(sNameOrId);
2160 else:
2161 reporter.error('Port me!'); ## @todo Add support for older version < 4.0.
2162 except:
2163 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2164 return None;
2165
2166 self.aoVMs.append(oVM);
2167 if not fQuiet:
2168 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2169 self.logVmInfo(oVM);
2170 return oVM;
2171
2172 def openSession(self, oVM):
2173 """
2174 Opens a session for the VM. Returns the a Session wrapper object that
2175 will automatically close the session when the wrapper goes out of scope.
2176
2177 On failure None is returned and an error is logged.
2178 """
2179 try:
2180 sUuid = oVM.id;
2181 except:
2182 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2183 return None;
2184
2185 # This loop is a kludge to deal with us racing the closing of the
2186 # direct session of a previous VM run. See waitOnDirectSessionClose.
2187 for i in range(10):
2188 try:
2189 if self.fpApiVer <= 3.2:
2190 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2191 else:
2192 oSession = self.oVBoxMgr.openMachineSession(oVM);
2193 break;
2194 except:
2195 if i == 9:
2196 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2197 return None;
2198 if i > 0:
2199 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2200 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2201 from testdriver.vboxwrappers import SessionWrapper;
2202 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2203
2204 def getVmByName(self, sName):
2205 """
2206 Get a test VM by name. Returns None if not found, logged.
2207 """
2208 # Look it up in our 'cache'.
2209 for oVM in self.aoVMs:
2210 try:
2211 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2212 if oVM.name == sName:
2213 return oVM;
2214 except:
2215 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2216
2217 # Look it up the standard way.
2218 return self.addTestMachine(sName, fQuiet = True);
2219
2220 def getVmByUuid(self, sUuid):
2221 """
2222 Get a test VM by uuid. Returns None if not found, logged.
2223 """
2224 # Look it up in our 'cache'.
2225 for oVM in self.aoVMs:
2226 try:
2227 if oVM.id == sUuid:
2228 return oVM;
2229 except:
2230 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2231
2232 # Look it up the standard way.
2233 return self.addTestMachine(sUuid, fQuiet = True);
2234
2235 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2236 """
2237 Waits for a progress object to complete. Returns the status code.
2238 """
2239 # Wait for progress no longer than cMsTimeout time period.
2240 tsStart = datetime.datetime.now()
2241 while True:
2242 self.processPendingEvents();
2243 try:
2244 if oProgress.completed:
2245 break;
2246 except:
2247 return -1;
2248 self.processPendingEvents();
2249
2250 tsNow = datetime.datetime.now()
2251 tsDelta = tsNow - tsStart
2252 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) / 1000) > cMsTimeout:
2253 if fErrorOnTimeout:
2254 reporter.errorTimeout('Timeout while waiting for progress.')
2255 return -1
2256
2257 try: oProgress.waitForCompletion(cMsInterval);
2258 except: return -2;
2259
2260 try: rc = oProgress.resultCode;
2261 except: rc = -2;
2262 self.processPendingEvents();
2263 return rc;
2264
2265 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2266 """
2267 Waits for the VM process to close it's current direct session.
2268
2269 Returns None.
2270 """
2271 # Get the original values so we're not subject to
2272 try:
2273 eCurState = oVM.sessionState;
2274 if self.fpApiVer >= 5.0:
2275 sCurName = sOrgName = oVM.sessionName;
2276 else:
2277 sCurName = sOrgName = oVM.sessionType;
2278 if self.fpApiVer >= 4.2:
2279 iCurPid = iOrgPid = oVM.sessionPID;
2280 else:
2281 iCurPid = iOrgPid = oVM.sessionPid;
2282 except Exception, oXcpt:
2283 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2284 reporter.logXcpt();
2285 self.processPendingEvents();
2286 return None;
2287 self.processPendingEvents();
2288
2289 msStart = base.timestampMilli();
2290 while iCurPid == iOrgPid \
2291 and sCurName == sOrgName \
2292 and sCurName != '' \
2293 and base.timestampMilli() - msStart < cMsTimeout \
2294 and ( eCurState == vboxcon.SessionState_Unlocking \
2295 or eCurState == vboxcon.SessionState_Spawning \
2296 or eCurState == vboxcon.SessionState_Locked):
2297 self.processEvents(1000);
2298 try:
2299 eCurState = oVM.sessionState;
2300 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2301 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2302 except Exception, oXcpt:
2303 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2304 reporter.logXcpt();
2305 break;
2306 self.processPendingEvents();
2307 self.processPendingEvents();
2308 return None;
2309
2310 def uploadStartupLogFile(self, oVM, sVmName):
2311 """
2312 Uploads the VBoxStartup.log when present.
2313 """
2314 fRc = True;
2315 try:
2316 sLogFile = os.path.join(oVM.logFolder, 'VBoxStartup.log');
2317 except:
2318 reporter.logXcpt();
2319 fRc = False;
2320 else:
2321 if os.path.isfile(sLogFile):
2322 reporter.addLogFile(sLogFile, 'log/release/vm', '%s startup log' % (sVmName, ),
2323 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
2324 return fRc;
2325
2326 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=R0914,R0915
2327 """
2328 Start the VM, returning the VM session and progress object on success.
2329 The session is also added to the task list and to the aoRemoteSessions set.
2330
2331 asEnv is a list of string on the putenv() form.
2332
2333 On failure (None, None) is returned and an error is logged.
2334 """
2335 # Massage and check the input.
2336 if sType is None:
2337 sType = self.sSessionType;
2338 if sName is None:
2339 try: sName = oVM.name;
2340 except: sName = 'bad-vm-handle';
2341 reporter.log2('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
2342 if oVM is None:
2343 return (None, None);
2344
2345 ## @todo Do this elsewhere.
2346 # Hack alert. Disables all annoying GUI popups.
2347 if sType == 'gui' and len(self.aoRemoteSessions) == 0:
2348 try:
2349 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
2350 if self.fpApiVer >= 3.2:
2351 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
2352 else:
2353 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
2354 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
2355 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
2356 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
2357 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
2358 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
2359 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
2360 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
2361 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
2362 except:
2363 reporter.logXcpt();
2364
2365 # The UUID for the name.
2366 try:
2367 sUuid = oVM.id;
2368 except:
2369 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
2370 return (None, None);
2371 self.processPendingEvents();
2372
2373 # Construct the environment.
2374 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
2375 try: os.remove(sLogFile);
2376 except: pass;
2377 if self.sLogSessionDest:
2378 sLogDest = self.sLogSessionDest;
2379 else:
2380 sLogDest = 'file=%s' % sLogFile;
2381 sEnv = 'VBOX_LOG=%s\nVBOX_LOG_FLAGS=%s\nVBOX_LOG_DEST=%s\nVBOX_RELEASE_LOG_FLAGS=append time' \
2382 % (self.sLogSessionGroups, self.sLogSessionFlags, sLogDest,);
2383 if sType == 'gui':
2384 sEnv += '\nVBOX_GUI_DBG_ENABLED=1'
2385 if asEnv is not None and len(asEnv) > 0:
2386 sEnv += '\n' + ('\n'.join(asEnv));
2387
2388 # Shortcuts for local testing.
2389 oProgress = oWrapped = None;
2390 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
2391 try:
2392 if oTestVM is not None \
2393 and oTestVM.fSnapshotRestoreCurrent is True:
2394 if oVM.state is vboxcon.MachineState_Running:
2395 reporter.log2('Machine "%s" already running.' % (sName,));
2396 oProgress = None;
2397 oWrapped = self.openSession(oVM);
2398 else:
2399 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
2400 oSessionWrapperRestore = self.openSession(oVM);
2401 if oSessionWrapperRestore is not None:
2402 oSnapshotCur = oVM.currentSnapshot;
2403 if oSnapshotCur is not None:
2404 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
2405 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
2406 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
2407 else:
2408 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
2409 oSessionWrapperRestore.close();
2410 except:
2411 reporter.errorXcpt();
2412 return (None, None);
2413
2414 # Open a remote session, wait for this operation to complete.
2415 # (The loop is a kludge to deal with us racing the closing of the
2416 # direct session of a previous VM run. See waitOnDirectSessionClose.)
2417 if oWrapped is None:
2418 for i in range(10):
2419 try:
2420 if self.fpApiVer < 4.3 \
2421 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
2422 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2423 else:
2424 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=E1101
2425 if self.fpApiVer < 3.3:
2426 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, sEnv);
2427 else:
2428 oProgress = oVM.launchVMProcess(oSession, sType, sEnv);
2429 break;
2430 except:
2431 if i == 9:
2432 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
2433 return (None, None);
2434 oSession = None;
2435 if i >= 0:
2436 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=C0301
2437 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2438 if fWait and oProgress is not None:
2439 rc = self.waitOnProgress(oProgress);
2440 if rc < 0:
2441 self.waitOnDirectSessionClose(oVM, 5000);
2442 try:
2443 if oSession is not None:
2444 oSession.close();
2445 except: pass;
2446 reportError(oProgress, 'failed to open session for "%s"' % (sName));
2447 self.uploadStartupLogFile(oVM, sName);
2448 return (None, None);
2449 reporter.log2('waitOnProgress -> %s' % (rc,));
2450
2451 # Wrap up the session object and push on to the list before returning it.
2452 if oWrapped is None:
2453 from testdriver.vboxwrappers import SessionWrapper;
2454 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2455
2456 oWrapped.registerEventHandlerForTask();
2457 self.aoRemoteSessions.append(oWrapped);
2458 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
2459 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
2460 % (oWrapped, len(self.aoRemoteSessions) - 1,
2461 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
2462 self.addTask(oWrapped);
2463
2464 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
2465
2466 from testdriver.vboxwrappers import ProgressWrapper;
2467 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
2468 'starting %s' % (sName,)) if oProgress else None);
2469
2470 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
2471 """ Simplified version of startVmEx. """
2472 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
2473 return oSession;
2474
2475 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
2476 """
2477 Start the VM, returning the VM session and progress object on success.
2478 The session is also added to the task list and to the aoRemoteSessions set.
2479
2480 On failure (None, None) is returned and an error is logged.
2481 """
2482 oVM = self.getVmByName(sName);
2483 if oVM is None:
2484 return (None, None);
2485 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
2486
2487 def startVmByName(self, sName, sType=None, asEnv = None):
2488 """
2489 Start the VM, returning the VM session on success. The session is
2490 also added to the task list and to the aoRemoteSessions set.
2491
2492 On failure None is returned and an error is logged.
2493 """
2494 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
2495 return oSession;
2496
2497 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None):
2498 """
2499 Terminates the VM specified by oSession and adds the release logs to
2500 the test report.
2501
2502 This will try archive this by using powerOff, but will resort to
2503 tougher methods if that fails.
2504
2505 The session will always be removed from the task list.
2506 The session will be closed unless we fail to kill the process.
2507 The session will be removed from the remote session list if closed.
2508
2509 The progress object (a wrapper!) is for teleportation and similar VM
2510 operations, it will be attempted canceled before powering off the VM.
2511 Failures are logged but ignored.
2512 The progress object will always be removed from the task list.
2513
2514 Returns True if powerOff and session close both succeed.
2515 Returns False if on failure (logged), including when we successfully
2516 kill the VM process.
2517 """
2518 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
2519
2520 # Call getPid first to make sure the PID is cached in the wrapper.
2521 oSession.getPid();
2522
2523 #
2524 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
2525 #
2526 sLastScreenshotPath = None
2527 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
2528 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName)
2529 fRc = oSession.takeScreenshot(sLastScreenshotPath)
2530 if fRc is not True:
2531 sLastScreenshotPath = None
2532
2533 #
2534 # Terminate the VM
2535 #
2536
2537 # Cancel the progress object if specified.
2538 if oProgress is not None:
2539 if not oProgress.isCompleted() and oProgress.isCancelable():
2540 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
2541 try:
2542 oProgress.o.cancel();
2543 except:
2544 reporter.logXcpt();
2545 else:
2546 oProgress.wait();
2547 self.removeTask(oProgress);
2548
2549 # Check if the VM has terminated by it self before powering it off.
2550 fClose = True;
2551 fRc = oSession.pollTask();
2552 if fRc is not True:
2553 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
2554 fRc = oSession.powerOff(fFudgeOnFailure = False);
2555 if fRc is not True:
2556 # power off failed, try terminate it in a nice manner.
2557 fRc = False;
2558 uPid = oSession.getPid();
2559 if uPid is not None:
2560 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
2561 fClose = base.processTerminate(uPid);
2562 if fClose is True:
2563 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2564 fClose = oSession.waitForTask(1000);
2565
2566 if fClose is not True:
2567 # Being nice failed...
2568 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
2569 % (uPid, oSession.sName));
2570 fClose = base.processKill(uPid);
2571 if fClose is True:
2572 self.waitOnDirectSessionClose(oSession.oVM, 5000);
2573 fClose = oSession.waitForTask(1000);
2574 if fClose is not True:
2575 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
2576
2577 # The final steps.
2578 if fClose is True:
2579 oSession.close();
2580 self.waitOnDirectSessionClose(oSession.oVM, 10000);
2581 try:
2582 eState = oSession.oVM.state;
2583 except:
2584 reporter.logXcpt();
2585 else:
2586 if eState == vboxcon.MachineState_Aborted:
2587 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
2588 self.removeTask(oSession);
2589
2590 #
2591 # Add the release log, debug log and a screenshot of the VM to the test report.
2592 #
2593 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
2594 oSession.addLogsToReport();
2595
2596 # Add a screenshot if it has been requested and taken successfully.
2597 if sLastScreenshotPath is not None:
2598 if reporter.testErrorCount() > 0:
2599 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
2600 else:
2601 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
2602
2603 return fRc;
2604
2605
2606 #
2607 # Some information query functions (mix).
2608 #
2609 # Methods require the VBox API. If the information is provided by both
2610 # the testboxscript as well as VBox API, we'll check if it matches.
2611 #
2612
2613 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
2614 """
2615 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
2616
2617 Returns True / False.
2618 Raises exception on environment / host mismatch.
2619 """
2620 fEnv = os.environ.get(sEnvVar, None);
2621 if fEnv is not None:
2622 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
2623
2624 fVBox = None;
2625 self.importVBoxApi();
2626 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
2627 try:
2628 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
2629 except:
2630 if not fQuiet:
2631 reporter.logXcpt();
2632
2633 if fVBox is not None:
2634 if fEnv is not None:
2635 if fEnv != fVBox and not fQuiet:
2636 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
2637 % (fVBox, sEnum, fEnv, sEnvVar));
2638 return fEnv;
2639 return fVBox;
2640 if fEnv is not None:
2641 return fEnv;
2642 return False;
2643
2644 def hasHostHwVirt(self, fQuiet = False):
2645 """
2646 Checks if hardware assisted virtualization is supported by the host.
2647
2648 Returns True / False.
2649 Raises exception on environment / host mismatch.
2650 """
2651 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
2652
2653 def hasHostNestedPaging(self, fQuiet = False):
2654 """
2655 Checks if nested paging is supported by the host.
2656
2657 Returns True / False.
2658 Raises exception on environment / host mismatch.
2659 """
2660 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
2661 and self.hasHostHwVirt(fQuiet);
2662
2663 def hasHostLongMode(self, fQuiet = False):
2664 """
2665 Checks if the host supports 64-bit guests.
2666
2667 Returns True / False.
2668 Raises exception on environment / host mismatch.
2669 """
2670 # Note that the testboxscript doesn't export this variable atm.
2671 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
2672
2673 def getHostCpuCount(self, fQuiet = False):
2674 """
2675 Returns the number of CPUs on the host.
2676
2677 Returns True / False.
2678 Raises exception on environment / host mismatch.
2679 """
2680 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
2681 if cEnv is not None:
2682 cEnv = int(cEnv);
2683
2684 try:
2685 cVBox = self.oVBox.host.processorOnlineCount;
2686 except:
2687 if not fQuiet:
2688 reporter.logXcpt();
2689 cVBox = None;
2690
2691 if cVBox is not None:
2692 if cEnv is not None:
2693 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
2694 return cVBox;
2695 if cEnv is not None:
2696 return cEnv;
2697 return 1;
2698
2699 def _getHostCpuDesc(self, fQuiet = False):
2700 """
2701 Internal method used for getting the host CPU description from VBoxSVC.
2702 Returns description string, on failure an empty string is returned.
2703 """
2704 try:
2705 return self.oVBox.host.getProcessorDescription(0);
2706 except:
2707 if not fQuiet:
2708 reporter.logXcpt();
2709 return '';
2710
2711 def isHostCpuAmd(self, fQuiet = False):
2712 """
2713 Checks if the host CPU vendor is AMD.
2714
2715 Returns True / False.
2716 """
2717 sCpuDesc = self._getHostCpuDesc(fQuiet);
2718 return sCpuDesc.startswith("AMD") or sCpuDesc == 'AuthenticAMD';
2719
2720 def isHostCpuIntel(self, fQuiet = False):
2721 """
2722 Checks if the host CPU vendor is Intel.
2723
2724 Returns True / False.
2725 """
2726 sCpuDesc = self._getHostCpuDesc(fQuiet);
2727 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
2728
2729 def isHostCpuVia(self, fQuiet = False):
2730 """
2731 Checks if the host CPU vendor is VIA (or Centaur).
2732
2733 Returns True / False.
2734 """
2735 sCpuDesc = self._getHostCpuDesc(fQuiet);
2736 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
2737
2738 def hasRawModeSupport(self, fQuiet = False):
2739 """
2740 Checks if raw-mode is supported by VirtualBox that the testbox is
2741 configured for it.
2742
2743 Returns True / False.
2744 Raises no exceptions.
2745
2746 Note! Differs from the rest in that we don't require the
2747 TESTBOX_WITH_RAW_MODE value to match the API. It is
2748 sometimes helpful to disable raw-mode on individual
2749 test boxes. (This probably goes for
2750 """
2751 # The environment variable can be used to disable raw-mode.
2752 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
2753 if fEnv is not None:
2754 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
2755 if fEnv is False:
2756 return False;
2757
2758 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
2759 # with raw-mode support or not.
2760 self.importVBoxApi();
2761 if self.fpApiVer >= 5.0:
2762 try:
2763 fVBox = self.oVBox.systemProperties.rawModeSupported;
2764 except:
2765 if not fQuiet:
2766 reporter.logXcpt();
2767 fVBox = True;
2768 if fVBox is False:
2769 return False;
2770
2771 return True;
2772
2773 #
2774 # Testdriver execution methods.
2775 #
2776
2777 def handleTask(self, oTask, sMethod):
2778 """
2779 Callback method for handling unknown tasks in the various run loops.
2780
2781 The testdriver should override this if it already tasks running when
2782 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
2783 Call super to handle unknown tasks.
2784
2785 Returns True if handled, False if not.
2786 """
2787 reporter.error('%s: unknown task %s' % (sMethod, oTask));
2788 return False;
2789
2790 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
2791 """
2792 Generic TXS task wrapper which waits both on the TXS and the session tasks.
2793
2794 Returns False on error, logged.
2795
2796 Returns task result on success.
2797 """
2798 # All async methods ends with the following to args.
2799 cMsTimeout = aArgs[-2];
2800 fIgnoreErrors = aArgs[-1];
2801
2802 fRemoveVm = self.addTask(oSession);
2803 fRemoveTxs = self.addTask(oTxsSession);
2804
2805 rc = fnAsync(*aArgs); # pylint: disable=W0142
2806 if rc is True:
2807 rc = False;
2808 oTask = self.waitForTasks(cMsTimeout + 1);
2809 if oTask is oTxsSession:
2810 if oTxsSession.isSuccess():
2811 rc = oTxsSession.getResult();
2812 elif fIgnoreErrors is True:
2813 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
2814 else:
2815 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
2816 else:
2817 oTxsSession.cancelTask();
2818 if oTask is None:
2819 if fIgnoreErrors is True:
2820 reporter.log( 'txsDoTask: The task timed out.');
2821 else:
2822 reporter.errorTimeout('txsDoTask: The task timed out.');
2823 elif oTask is oSession:
2824 reporter.error('txsDoTask: The VM terminated unexpectedly');
2825 else:
2826 if fIgnoreErrors is True:
2827 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
2828 else:
2829 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
2830 else:
2831 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
2832
2833 if fRemoveTxs:
2834 self.removeTask(oTxsSession);
2835 if fRemoveVm:
2836 self.removeTask(oSession);
2837 return rc;
2838
2839 # pylint: disable=C0111
2840
2841 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
2842 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
2843 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2844
2845 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
2846 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
2847 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2848
2849 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0700, cMsTimeout = 30000, fIgnoreErrors = False):
2850 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
2851 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2852
2853 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0700, cMsTimeout = 30000, fIgnoreErrors = False):
2854 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
2855 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2856
2857 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
2858 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
2859 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2860
2861 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
2862 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
2863 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2864
2865 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2866 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
2867 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2868
2869 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
2870 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
2871 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2872
2873 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
2874 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
2875 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2876
2877 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
2878 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
2879 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2880
2881 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2882 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
2883 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2884
2885 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
2886 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
2887 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2888
2889 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2890 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
2891 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2892
2893 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2894 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
2895 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2896
2897 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
2898 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
2899 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2900
2901 def txsDownloadFiles(self, oSession, oTxsSession, asFiles, fIgnoreErrors = False):
2902 """
2903 Convenience function to get files from the guest and stores it
2904 into the scratch directory for later (manual) review.
2905
2906 Returns True on success.
2907
2908 Returns False on failure, logged.
2909 """
2910 fRc = True;
2911 for sGstFile in asFiles:
2912 ## @todo Check for already existing files on the host and create a new
2913 # name for the current file to download.
2914 sTmpFile = os.path.join(self.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
2915 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
2916 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sTmpFile, 30 * 1000, fIgnoreErrors);
2917 try: os.unlink(sTmpFile);
2918 except: pass;
2919 if fRc:
2920 reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
2921 else:
2922 if fIgnoreErrors is not True:
2923 reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
2924 return fRc;
2925 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
2926 return True;
2927
2928 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
2929 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
2930 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2931
2932 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
2933 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
2934 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
2935
2936 # pylint: enable=C0111
2937
2938 def txsCdWait(self, oSession, oTxsSession, cMsTimeout = 30000, sFileCdWait = 'vboxtxs-readme.txt'):
2939 """
2940 Mostly an internal helper for txsRebootAndReconnectViaTcp and
2941 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
2942 ready. It does this by polling for a file it knows to exist on the CD.
2943
2944 Returns True on success.
2945
2946 Returns False on failure, logged.
2947 """
2948
2949 fRemoveVm = self.addTask(oSession);
2950 fRemoveTxs = self.addTask(oTxsSession);
2951 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
2952 msStart = base.timestampMilli();
2953 cMsTimeout2 = cMsTimeout;
2954 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFileCdWait), cMsTimeout2);
2955 if fRc is True:
2956 while True:
2957 # wait for it to complete.
2958 oTask = self.waitForTasks(cMsTimeout2 + 1);
2959 if oTask is not oTxsSession:
2960 oTxsSession.cancelTask();
2961 if oTask is None:
2962 reporter.errorTimeout('txsToCdWait: The task timed out (after %s ms).'
2963 % (base.timestampMilli() - msStart,));
2964 elif oTask is oSession:
2965 reporter.error('txsToCdWait: The VM terminated unexpectedly');
2966 else:
2967 reporter.error('txsToCdWait: An unknown task %s was returned' % (oTask,));
2968 fRc = False;
2969 break;
2970 if oTxsSession.isSuccess():
2971 break;
2972
2973 # Check for timeout.
2974 cMsElapsed = base.timestampMilli() - msStart;
2975 if cMsElapsed >= cMsTimeout:
2976 reporter.error('txsToCdWait: timed out');
2977 fRc = False;
2978 break;
2979
2980 # delay.
2981 self.sleep(1);
2982
2983 # resubmitt the task.
2984 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
2985 if cMsTimeout2 < 500:
2986 cMsTimeout2 = 500;
2987 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFileCdWait), cMsTimeout2);
2988 if fRc is not True:
2989 reporter.error('txsToCdWait: asyncIsFile failed');
2990 break;
2991 else:
2992 reporter.error('txsToCdWait: asyncIsFile failed');
2993
2994 if fRemoveTxs:
2995 self.removeTask(oTxsSession);
2996 if fRemoveVm:
2997 self.removeTask(oSession);
2998 return fRc;
2999
3000 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
3001 """
3002 Mostly an internal worker for connecting to TXS via TCP used by the
3003 *ViaTcp methods.
3004
3005 Returns a tuplet with True/False and TxsSession/None depending on the
3006 result. Errors are logged.
3007 """
3008
3009 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
3010 % (oSession, cMsTimeout, fNatForwardingForTxs));
3011
3012 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3013 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
3014 if oTxsConnect is not None:
3015 self.addTask(oTxsConnect);
3016 fRemoveVm = self.addTask(oSession);
3017 oTask = self.waitForTasks(cMsTimeout + 1);
3018 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
3019 self.removeTask(oTxsConnect);
3020 if oTask is oTxsConnect:
3021 oTxsSession = oTxsConnect.getResult();
3022 if oTxsSession is not None:
3023 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
3024 return (True, oTxsSession);
3025
3026 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
3027 else:
3028 oTxsConnect.cancelTask();
3029 if oTask is None:
3030 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
3031 elif oTask is oSession:
3032 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
3033 else:
3034 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
3035 if fRemoveVm:
3036 self.removeTask(oSession);
3037 else:
3038 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
3039 return (False, None);
3040
3041 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
3042 cMsCdWait = 30000, sFileCdWait = 'vboxtxs-readme.txt', \
3043 fNatForwardingForTxs = False):
3044 """
3045 Starts the specified VM and tries to connect to its TXS via TCP.
3046 The VM will be powered off if TXS doesn't respond before the specified
3047 time has elapsed.
3048
3049 Returns a the VM and TXS sessions (a two tuple) on success. The VM
3050 session is in the task list, the TXS session is not.
3051 Returns (None, None) on failure, fully logged.
3052 """
3053
3054 # Zap the guest IP to make sure we're not getting a stale entry
3055 # (unless we're restoring the VM of course).
3056 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
3057 if oTestVM is None \
3058 or oTestVM.fSnapshotRestoreCurrent is False:
3059 try:
3060 oSession1 = self.openSession(self.getVmByName(sVmName));
3061 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
3062 oSession1.saveSettings(True);
3063 del oSession1;
3064 except:
3065 reporter.logXcpt();
3066
3067 # Start the VM.
3068 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
3069 oSession = self.startVmByName(sVmName);
3070 if oSession is not None:
3071 # Connect to TXS.
3072 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
3073 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
3074 if fRc is True:
3075 if fCdWait:
3076 # Wait for CD?
3077 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3078 if fRc is not True:
3079 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
3080 if fRc is True:
3081 # Success!
3082 return (oSession, oTxsSession);
3083 else:
3084 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
3085 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
3086 self.terminateVmBySession(oSession);
3087 return (None, None);
3088
3089 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
3090 cMsCdWait = 30000, sFileCdWait = 'vboxtxs-readme.txt', fNatForwardingForTxs = False):
3091 """
3092 Executes the TXS reboot command
3093
3094 Returns A tuple of True and the new TXS session on success.
3095
3096 Returns A tuple of False and either the old TXS session or None on failure.
3097 """
3098 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
3099
3100 #
3101 # This stuff is a bit complicated because of rebooting being kind of
3102 # disruptive to the TXS and such... The protocol is that TXS will:
3103 # - ACK the reboot command.
3104 # - Shutdown the transport layer, implicitly disconnecting us.
3105 # - Execute the reboot operation.
3106 # - On failure, it will be re-init the transport layer and be
3107 # available pretty much immediately. UUID unchanged.
3108 # - On success, it will be respawed after the reboot (hopefully),
3109 # with a different UUID.
3110 #
3111 fRc = False;
3112 iStart = base.timestampMilli();
3113
3114 # Get UUID.
3115 cMsTimeout2 = min(60000, cMsTimeout);
3116 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
3117 if sUuidBefore is not False:
3118 # Reboot.
3119 cMsElapsed = base.timestampMilli() - iStart;
3120 cMsTimeout2 = cMsTimeout - cMsElapsed;
3121 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
3122 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3123 if fRc is True:
3124 # Reconnect.
3125 if fNatForwardingForTxs is True:
3126 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
3127 cMsElapsed = base.timestampMilli() - iStart;
3128 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
3129 if fRc is True:
3130 # Check the UUID.
3131 cMsElapsed = base.timestampMilli() - iStart;
3132 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
3133 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3134 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3135 if sUuidBefore is not False:
3136 if sUuidAfter != sUuidBefore:
3137 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
3138
3139 # Do CD wait if specified.
3140 if fCdWait:
3141 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3142 if fRc is not True:
3143 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
3144 else:
3145 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
3146 else:
3147 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
3148 else:
3149 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
3150 else:
3151 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
3152 else:
3153 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
3154 return (fRc, oTxsSession);
3155
3156 # pylint: disable=R0914,R0913
3157
3158 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = ""):
3159 """
3160 Executes the specified test task, waiting till it completes or times out.
3161
3162 The VM session (if any) must be in the task list.
3163
3164 Returns True if we executed the task and nothing abnormal happend.
3165 Query the process status from the TXS session.
3166
3167 Returns False if some unexpected task was signalled or we failed to
3168 submit the job.
3169 """
3170 reporter.testStart(sTestName);
3171 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3172
3173 # Submit the job.
3174 fRc = False;
3175 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3176 self.addTask(oTxsSession);
3177
3178 # Wait for the job to complete.
3179 while True:
3180 oTask = self.waitForTasks(cMsTimeout + 1);
3181 if oTask is None:
3182 reporter.log('txsRunTest: waitForTasks timed out');
3183 break;
3184 if oTask is oTxsSession:
3185 fRc = True;
3186 reporter.log('txsRunTest: isSuccess=%s getResult=%s' % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3187 break;
3188 if not self.handleTask(oTask, 'txsRunTest'):
3189 break;
3190
3191 self.removeTask(oTxsSession);
3192 if not oTxsSession.pollTask():
3193 oTxsSession.cancelTask();
3194 else:
3195 reporter.error('txsRunTest: asyncExec failed');
3196
3197 reporter.testDone();
3198 return fRc;
3199
3200 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
3201 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
3202 """
3203 Executes the specified test task, waiting till it completes or times out,
3204 redirecting stdin, stdout and stderr to the given objects.
3205
3206 The VM session (if any) must be in the task list.
3207
3208 Returns True if we executed the task and nothing abnormal happend.
3209 Query the process status from the TXS session.
3210
3211 Returns False if some unexpected task was signalled or we failed to
3212 submit the job.
3213 """
3214 reporter.testStart(sTestName);
3215 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3216
3217 # Submit the job.
3218 fRc = False;
3219 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
3220 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3221 self.addTask(oTxsSession);
3222
3223 # Wait for the job to complete.
3224 while True:
3225 oTask = self.waitForTasks(cMsTimeout + 1);
3226 if oTask is None:
3227 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
3228 break;
3229 if oTask is oTxsSession:
3230 fRc = True;
3231 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
3232 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3233 break;
3234 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
3235 break;
3236
3237 self.removeTask(oTxsSession);
3238 if not oTxsSession.pollTask():
3239 oTxsSession.cancelTask();
3240 else:
3241 reporter.error('txsRunTestRedirectStd: asyncExec failed');
3242
3243 reporter.testDone();
3244 return fRc;
3245
3246 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
3247 sExecName1, asArgs1,
3248 sExecName2, asArgs2,
3249 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
3250 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
3251 """
3252 Executes the specified test tasks, waiting till they complete or
3253 times out. The 1st task is started after the 2nd one.
3254
3255 The VM session (if any) must be in the task list.
3256
3257 Returns True if we executed the task and nothing abnormal happend.
3258 Query the process status from the TXS sessions.
3259
3260 Returns False if some unexpected task was signalled or we failed to
3261 submit the job.
3262 """
3263 reporter.testStart(sTestName);
3264
3265 # Submit the jobs.
3266 fRc = False;
3267 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
3268 self.adjustTimeoutMs(cMsTimeout)):
3269 self.addTask(oTxsSession1);
3270
3271 self.sleep(2); # fudge! grr
3272
3273 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
3274 self.adjustTimeoutMs(cMsTimeout)):
3275 self.addTask(oTxsSession2);
3276
3277 # Wait for the jobs to complete.
3278 cPendingJobs = 2;
3279 while True:
3280 oTask = self.waitForTasks(cMsTimeout + 1);
3281 if oTask is None:
3282 reporter.log('txsRunTest2: waitForTasks timed out');
3283 break;
3284
3285 if oTask is oTxsSession1 or oTask is oTxsSession2:
3286 if oTask is oTxsSession1: iTask = 1;
3287 else: iTask = 2;
3288 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
3289 % (iTask, oTask.isSuccess(), oTask.getResult()));
3290 self.removeTask(oTask);
3291 cPendingJobs -= 1;
3292 if cPendingJobs <= 0:
3293 fRc = True;
3294 break;
3295
3296 elif not self.handleTask(oTask, 'txsRunTest'):
3297 break;
3298
3299 self.removeTask(oTxsSession2);
3300 if not oTxsSession2.pollTask():
3301 oTxsSession2.cancelTask();
3302 else:
3303 reporter.error('txsRunTest2: asyncExec #2 failed');
3304
3305 self.removeTask(oTxsSession1);
3306 if not oTxsSession1.pollTask():
3307 oTxsSession1.cancelTask();
3308 else:
3309 reporter.error('txsRunTest2: asyncExec #1 failed');
3310
3311 reporter.testDone();
3312 return fRc;
3313
3314 # pylint: enable=R0914,R0913
3315
Note: See TracBrowser for help on using the repository browser.

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