VirtualBox

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

Last change on this file since 90802 was 90244, checked in by vboxsync, 3 years ago

ValidationKit: Add support for testing IOMMU. Ubuntu 20.04 smoketest is prepared to test both Intel and AMD IOMMUs.

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