VirtualBox

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

Last change on this file since 109300 was 109300, checked in by vboxsync, 8 days ago

ValKit/testdriver/vbox.py: Use utils.versionCompare to implement isVersionEqualOrBigger; removed versionToTuple and compareVersion.

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

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