VirtualBox

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

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

Devices/DevVGA, Main: Get rid of obsolete code enclosed by VBOX_WITH_CRHGSMI, bugref:9529

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