VirtualBox

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

Last change on this file since 96531 was 96531, checked in by vboxsync, 3 years ago

ValKit/testdriver/vbox.py: Shut up pylint warning about too many branches in parseOption; corrected member variable names related to recording; added missing integer conversion of command line argument values; missing negative upload option; help text.

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

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