VirtualBox

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

Last change on this file since 108641 was 108641, checked in by vboxsync, 7 weeks ago

Removed 2D video acceleration (aka VHWA / VBOX_WITH_VIDEOHWACCEL). bugref:10756

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