VirtualBox

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

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

Validation Kit/vbox.py: Print environment variables in verbose mode (nits).

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

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