VirtualBox

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

Last change on this file since 104883 was 103520, checked in by vboxsync, 9 months ago

Validation Kit/testdriver: Also log the audio enabled in/out status in _logVmInfoUnsafe().

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