VirtualBox

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

Last change on this file since 86208 was 85532, checked in by vboxsync, 4 years ago

ValKit/vbox.py/terminateVmBySession: Just dump the vmprocess-hung.log into the test log if we fail to upload it.

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