VirtualBox

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

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

Copyright year updates by scm.

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

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