VirtualBox

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

Last change on this file since 106317 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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

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