VirtualBox

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

Last change on this file since 86446 was 86440, checked in by vboxsync, 5 years ago

testdriver/vbox.py: Undo libasan.so.X preloading and memory leak detection disabling from vboxinstaller.py. Also, try use VBoxManage --dump-build-type to figure out the build type instead of assuming everything is a release build. bugref:9841

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

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