VirtualBox

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

Last change on this file since 70508 was 70508, checked in by vboxsync, 7 years ago

testdriver: python 3 updates.

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

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