VirtualBox

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

Last change on this file since 103068 was 103045, checked in by vboxsync, 16 months ago

ValidationKit/vboxtestvms.py: Add an OL 9.2 amd64 and arm64 smoketest VM for comparison

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

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