VirtualBox

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

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

Validation Kit/testdriver: Hack alert: Added sanitizeLogFlagsString() to support more than one log flag as command line argument (--vbox-XXX-log-flags).

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