VirtualBox

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

Last change on this file since 98709 was 98655, checked in by vboxsync, 2 years ago

ValKit: Pylint 2.16.2 adjustments.

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