VirtualBox

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

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

Validation Kit/vbox.py: Got rid of recording upload BUGBUG -- see r153411 + r153415.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 203.8 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 96548 2022-08-29 17:58:57Z 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: 96548 $"
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,too-many-branches
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-%s-screen-%d.webm" % (sName, oScreen.id));
2564 oScreen.filename = sRecFile;
2565 sRecFile = oScreen.filename; # Get back the file from Main, in case it was modified somehow.
2566 oRecFile = { "id" : oScreen.id, "file" : sRecFile };
2567 self.aRecordingFiles.append(oRecFile);
2568 if self.fpApiVer >= 7.0:
2569 aFeatures = [ vboxcon.RecordingFeature_Video ];
2570 if self.fRecordingAudio:
2571 aFeatures.append(vboxcon.RecordingFeature_Audio);
2572 oScreen.setFeatures(aFeatures);
2573 else: # <= VBox 6.1 the feature were kept as a ULONG.
2574 uFeatures = vboxcon.RecordingFeature_Video;
2575 if self.fRecordingAudio:
2576 uFeatures = uFeatures | vboxcon.RecordingFeature_Audio;
2577 oScreen.setFeatures(uFeatures);
2578 reporter.log2('Recording screen %d to "%s"' % (oRecFile['id'], oRecFile['file']));
2579 oScreen.maxTime = self.cSecsRecordingMax;
2580 oScreen.maxFileSize = self.cMbRecordingMax;
2581 except:
2582 reporter.errorXcpt('failed to configure recording for "%s" (screen %d)' % (sName, oScreen.id));
2583 else:
2584 # Not fatal.
2585 reporter.log('Recording only available for VBox >= 6.1, sorry!')
2586 except:
2587 reporter.errorXcpt('failed to configure recording for "%s"' % (sName));
2588 if fRc and sChipsetType == 'piix3':
2589 fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
2590 elif fRc and sChipsetType == 'ich9':
2591 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
2592 if fRc and sCom1RawFile:
2593 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2594 if fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_AMD') and sIommuType == 'amd':
2595 fRc = oSession.setIommuType(vboxcon.IommuType_AMD);
2596 elif fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_Intel') and sIommuType == 'intel':
2597 fRc = oSession.setIommuType(vboxcon.IommuType_Intel);
2598
2599 if fRc: fRc = oSession.saveSettings();
2600 if not fRc: oSession.discardSettings(True);
2601 oSession.close();
2602 if not fRc:
2603 if self.fpApiVer >= 4.0:
2604 try: oVM.unregister(vboxcon.CleanupMode_Full);
2605 except: reporter.logXcpt();
2606 try:
2607 if self.fpApiVer >= 4.3:
2608 oProgress = oVM.deleteConfig([]);
2609 else:
2610 oProgress = oVM.delete([]);
2611 self.waitOnProgress(oProgress);
2612 except:
2613 reporter.logXcpt();
2614 else:
2615 try: self.oVBox.unregisterMachine(oVM.id);
2616 except: reporter.logXcpt();
2617 try: oVM.deleteSettings();
2618 except: reporter.logXcpt();
2619 return None;
2620
2621 # success.
2622 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2623 self.aoVMs.append(oVM);
2624 self.logVmInfo(oVM); # testing...
2625 return oVM;
2626 # pylint: enable=too-many-arguments,too-many-locals,too-many-statements
2627
2628 def createTestVmWithDefaults(self, # pylint: disable=too-many-arguments
2629 sName,
2630 iGroup,
2631 sKind,
2632 sDvdImage = None,
2633 fFastBootLogo = True,
2634 eNic0AttachType = None,
2635 sNic0NetName = 'default',
2636 sNic0MacAddr = 'grouped',
2637 fVmmDevTestingPart = None,
2638 fVmmDevTestingMmio = False,
2639 sCom1RawFile = None):
2640 """
2641 Creates a test VM with all defaults and no HDs.
2642 """
2643 # create + register the VM
2644 oVM = self.createTestVMOnly(sName, sKind);
2645 if oVM is not None:
2646 # Configure the VM with defaults according to sKind.
2647 fRc = True;
2648 oSession = self.openSession(oVM);
2649 if oSession is not None:
2650 if self.fpApiVer >= 6.0:
2651 try:
2652 oSession.o.machine.applyDefaults('');
2653 except:
2654 reporter.errorXcpt('failed to apply defaults to vm "%s"' % (sName,));
2655 fRc = False;
2656 else:
2657 reporter.error("Implement applyDefaults for vbox version %s" % (self.fpApiVer,));
2658 #fRc = oSession.setupPreferredConfig();
2659 fRc = False;
2660
2661 # Apply the specified configuration:
2662 if fRc and sDvdImage is not None:
2663 #fRc = oSession.insertDvd(sDvdImage); # attachDvd
2664 reporter.error('Implement: oSession.insertDvd(%s)' % (sDvdImage,));
2665 fRc = False;
2666
2667 if fRc and fFastBootLogo is not None:
2668 fRc = oSession.setupBootLogo(fFastBootLogo);
2669
2670 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2671 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2672 if fRc and sNic0MacAddr is not None:
2673 if sNic0MacAddr == 'grouped':
2674 sNic0MacAddr = '%02X' % (iGroup,);
2675 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2676 # Needed to reach the host (localhost) from the guest. See xTracker #9896.
2677 if fRc and self.fpApiVer >= 7.0:
2678 fRc = oSession.setNicLocalhostReachable(True, 0);
2679
2680 if fRc and self.fEnableVrdp:
2681 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2682
2683 if fRc and fVmmDevTestingPart is not None:
2684 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2685
2686 if fRc and sCom1RawFile:
2687 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2688
2689 # Save the settings if we were successfull, otherwise discard them.
2690 if fRc:
2691 fRc = oSession.saveSettings();
2692 if not fRc:
2693 oSession.discardSettings(True);
2694 oSession.close();
2695
2696 if fRc is True:
2697 # If we've been successful, add the VM to the list and return it.
2698 # success.
2699 reporter.log('created "%s" with name "%s"' % (oVM.id, sName, ));
2700 self.aoVMs.append(oVM);
2701 self.logVmInfo(oVM); # testing...
2702 return oVM;
2703
2704 # Failed. Unregister the machine and delete it.
2705 if self.fpApiVer >= 4.0:
2706 try: oVM.unregister(vboxcon.CleanupMode_Full);
2707 except: reporter.logXcpt();
2708 try:
2709 if self.fpApiVer >= 4.3:
2710 oProgress = oVM.deleteConfig([]);
2711 else:
2712 oProgress = oVM.delete([]);
2713 self.waitOnProgress(oProgress);
2714 except:
2715 reporter.logXcpt();
2716 else:
2717 try: self.oVBox.unregisterMachine(oVM.id);
2718 except: reporter.logXcpt();
2719 try: oVM.deleteSettings();
2720 except: reporter.logXcpt();
2721 return None;
2722
2723 def addTestMachine(self, sNameOrId, fQuiet = False):
2724 """
2725 Adds an already existing (that is, configured) test VM to the
2726 test VM list.
2727
2728 Returns the VM object on success, None if failed.
2729 """
2730 # find + add the VM to the list.
2731 oVM = None;
2732 try:
2733 if self.fpApiVer >= 4.0:
2734 oVM = self.oVBox.findMachine(sNameOrId);
2735 else:
2736 reporter.error('fpApiVer=%s - did you remember to initialize the API' % (self.fpApiVer,));
2737 except:
2738 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2739
2740 if oVM:
2741 self.aoVMs.append(oVM);
2742 if not fQuiet:
2743 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2744 self.logVmInfo(oVM);
2745 return oVM;
2746
2747 def forgetTestMachine(self, oVM, fQuiet = False):
2748 """
2749 Forget about an already known test VM in the test VM list.
2750
2751 Returns True on success, False if failed.
2752 """
2753 try:
2754 sUuid = oVM.id;
2755 sName = oVM.name;
2756 except:
2757 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2758 return False;
2759 try:
2760 self.aoVMs.remove(oVM);
2761 if not fQuiet:
2762 reporter.log('Removed "%s" with name "%s"' % (sUuid, sName));
2763 except:
2764 reporter.errorXcpt('could not find vm "%s"' % (sName,));
2765 return False;
2766 return True;
2767
2768 def openSession(self, oVM):
2769 """
2770 Opens a session for the VM. Returns the a Session wrapper object that
2771 will automatically close the session when the wrapper goes out of scope.
2772
2773 On failure None is returned and an error is logged.
2774 """
2775 try:
2776 sUuid = oVM.id;
2777 except:
2778 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2779 return None;
2780
2781 # This loop is a kludge to deal with us racing the closing of the
2782 # direct session of a previous VM run. See waitOnDirectSessionClose.
2783 for i in range(10):
2784 try:
2785 if self.fpApiVer <= 3.2:
2786 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2787 else:
2788 oSession = self.oVBoxMgr.openMachineSession(oVM);
2789 break;
2790 except:
2791 if i == 9:
2792 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2793 return None;
2794 if i > 0:
2795 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2796 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2797 from testdriver.vboxwrappers import SessionWrapper;
2798 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2799
2800 #
2801 # Guest locations.
2802 #
2803
2804 @staticmethod
2805 def getGuestTempDir(oTestVm):
2806 """
2807 Helper for finding a temporary directory in the test VM.
2808
2809 Note! It may be necessary to create it!
2810 """
2811 if oTestVm.isWindows():
2812 return "C:\\Temp";
2813 if oTestVm.isOS2():
2814 return "C:\\Temp";
2815 return '/var/tmp';
2816
2817 @staticmethod
2818 def getGuestSystemDir(oTestVm, sPathPrefix = ''):
2819 """
2820 Helper for finding a system directory in the test VM that we can play around with.
2821 sPathPrefix can be used to specify other directories, such as /usr/local/bin/ or /usr/bin, for instance.
2822
2823 On Windows this is always the System32 directory, so this function can be used as
2824 basis for locating other files in or under that directory.
2825 """
2826 if oTestVm.isWindows():
2827 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
2828 if oTestVm.isOS2():
2829 return 'C:\\OS2\\DLL';
2830
2831 # OL / RHEL symlinks "/bin"/ to "/usr/bin". To avoid (unexpectedly) following symlinks, use "/usr/bin" then instead.
2832 if not sPathPrefix \
2833 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
2834 return "/usr/bin";
2835
2836 return sPathPrefix + "/bin";
2837
2838 @staticmethod
2839 def getGuestSystemAdminDir(oTestVm, sPathPrefix = ''):
2840 """
2841 Helper for finding a system admin directory ("sbin") in the test VM that we can play around with.
2842 sPathPrefix can be used to specify other directories, such as /usr/local/sbin/ or /usr/sbin, for instance.
2843
2844 On Windows this is always the System32 directory, so this function can be used as
2845 basis for locating other files in or under that directory.
2846 On UNIX-y systems this always is the "sh" shell to guarantee a common shell syntax.
2847 """
2848 if oTestVm.isWindows():
2849 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
2850 if oTestVm.isOS2():
2851 return 'C:\\OS2\\DLL'; ## @todo r=andy Not sure here.
2852
2853 # OL / RHEL symlinks "/sbin"/ to "/usr/sbin". To avoid (unexpectedly) following symlinks, use "/usr/sbin" then instead.
2854 if not sPathPrefix \
2855 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
2856 return "/usr/sbin";
2857
2858 return sPathPrefix + "/sbin";
2859
2860 @staticmethod
2861 def getGuestWinDir(oTestVm):
2862 """
2863 Helper for finding the Windows directory in the test VM that we can play around with.
2864 ASSUMES that we always install Windows on drive C.
2865
2866 Returns the Windows directory, or an empty string when executed on a non-Windows guest (asserts).
2867 """
2868 sWinDir = '';
2869 if oTestVm.isWindows():
2870 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x',]:
2871 sWinDir = 'C:\\WinNT\\';
2872 else:
2873 sWinDir = 'C:\\Windows\\';
2874 assert sWinDir != '', 'Retrieving Windows directory for non-Windows OS';
2875 return sWinDir;
2876
2877 @staticmethod
2878 def getGuestSystemShell(oTestVm):
2879 """
2880 Helper for finding the default system shell in the test VM.
2881 """
2882 if oTestVm.isWindows():
2883 return TestDriver.getGuestSystemDir(oTestVm) + '\\cmd.exe';
2884 if oTestVm.isOS2():
2885 return TestDriver.getGuestSystemDir(oTestVm) + '\\..\\CMD.EXE';
2886 return "/bin/sh";
2887
2888 @staticmethod
2889 def getGuestSystemFileForReading(oTestVm):
2890 """
2891 Helper for finding a file in the test VM that we can read.
2892 """
2893 if oTestVm.isWindows():
2894 return TestDriver.getGuestSystemDir(oTestVm) + '\\ntdll.dll';
2895 if oTestVm.isOS2():
2896 return TestDriver.getGuestSystemDir(oTestVm) + '\\DOSCALL1.DLL';
2897 return "/bin/sh";
2898
2899 def getVmByName(self, sName):
2900 """
2901 Get a test VM by name. Returns None if not found, logged.
2902 """
2903 # Look it up in our 'cache'.
2904 for oVM in self.aoVMs:
2905 try:
2906 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2907 if oVM.name == sName:
2908 return oVM;
2909 except:
2910 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2911
2912 # Look it up the standard way.
2913 return self.addTestMachine(sName, fQuiet = True);
2914
2915 def getVmByUuid(self, sUuid):
2916 """
2917 Get a test VM by uuid. Returns None if not found, logged.
2918 """
2919 # Look it up in our 'cache'.
2920 for oVM in self.aoVMs:
2921 try:
2922 if oVM.id == sUuid:
2923 return oVM;
2924 except:
2925 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2926
2927 # Look it up the standard way.
2928 return self.addTestMachine(sUuid, fQuiet = True);
2929
2930 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2931 """
2932 Waits for a progress object to complete. Returns the status code.
2933 """
2934 # Wait for progress no longer than cMsTimeout time period.
2935 tsStart = datetime.datetime.now()
2936 while True:
2937 self.processPendingEvents();
2938 try:
2939 if oProgress.completed:
2940 break;
2941 except:
2942 return -1;
2943 self.processPendingEvents();
2944
2945 tsNow = datetime.datetime.now()
2946 tsDelta = tsNow - tsStart
2947 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
2948 if fErrorOnTimeout:
2949 reporter.errorTimeout('Timeout while waiting for progress.')
2950 return -1
2951
2952 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
2953 try: oProgress.waitForCompletion(cMsInterval);
2954 except: return -2;
2955
2956 try: rc = oProgress.resultCode;
2957 except: rc = -2;
2958 self.processPendingEvents();
2959 return rc;
2960
2961 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2962 """
2963 Waits for the VM process to close it's current direct session.
2964
2965 Returns None.
2966 """
2967 # Get the original values so we're not subject to
2968 try:
2969 eCurState = oVM.sessionState;
2970 if self.fpApiVer >= 5.0:
2971 sCurName = sOrgName = oVM.sessionName;
2972 else:
2973 sCurName = sOrgName = oVM.sessionType;
2974 if self.fpApiVer >= 4.2:
2975 iCurPid = iOrgPid = oVM.sessionPID;
2976 else:
2977 iCurPid = iOrgPid = oVM.sessionPid;
2978 except Exception as oXcpt:
2979 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2980 reporter.logXcpt();
2981 self.processPendingEvents();
2982 return None;
2983 self.processPendingEvents();
2984
2985 msStart = base.timestampMilli();
2986 while iCurPid == iOrgPid \
2987 and sCurName == sOrgName \
2988 and sCurName != '' \
2989 and base.timestampMilli() - msStart < cMsTimeout \
2990 and eCurState in (vboxcon.SessionState_Unlocking, vboxcon.SessionState_Spawning, vboxcon.SessionState_Locked,):
2991 self.processEvents(1000);
2992 try:
2993 eCurState = oVM.sessionState;
2994 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2995 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2996 except Exception as oXcpt:
2997 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2998 reporter.logXcpt();
2999 break;
3000 self.processPendingEvents();
3001 self.processPendingEvents();
3002 return None;
3003
3004 def uploadStartupLogFile(self, oVM, sVmName):
3005 """
3006 Uploads the VBoxStartup.log when present.
3007 """
3008 fRc = True;
3009 try:
3010 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
3011 except:
3012 reporter.logXcpt();
3013 fRc = False;
3014 else:
3015 if os.path.isfile(sLogFile):
3016 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
3017 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
3018 return fRc;
3019
3020 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
3021 """
3022 Annotates the given VM process report and uploads it if successfull.
3023 """
3024 fRc = False;
3025 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
3026 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
3027 self.getBuildOs(), self.getBuildArch(),
3028 fnLog = reporter.log);
3029 fRcTmp = oResolver.prepareEnv();
3030 if fRcTmp:
3031 reporter.log('Successfully prepared environment');
3032 sReportDbgSym = oResolver.annotateReport(sProcessReport);
3033 if sReportDbgSym and len(sReportDbgSym) > 8:
3034 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
3035 fRc = True;
3036 else:
3037 reporter.log('Annotating report failed');
3038 oResolver.cleanupEnv();
3039 return fRc;
3040
3041 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=too-many-locals,too-many-statements
3042 """
3043 Start the VM, returning the VM session and progress object on success.
3044 The session is also added to the task list and to the aoRemoteSessions set.
3045
3046 asEnv is a list of string on the putenv() form.
3047
3048 On failure (None, None) is returned and an error is logged.
3049 """
3050 # Massage and check the input.
3051 if sType is None:
3052 sType = self.sSessionType;
3053 if sName is None:
3054 try: sName = oVM.name;
3055 except: sName = 'bad-vm-handle';
3056 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
3057 if oVM is None:
3058 return (None, None);
3059
3060 ## @todo Do this elsewhere.
3061 # Hack alert. Disables all annoying GUI popups.
3062 if sType == 'gui' and not self.aoRemoteSessions:
3063 try:
3064 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
3065 if self.fpApiVer >= 3.2:
3066 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
3067 else:
3068 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
3069 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
3070 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
3071 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
3072 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
3073 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
3074 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
3075 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
3076 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
3077 except:
3078 reporter.logXcpt();
3079
3080 # The UUID for the name.
3081 try:
3082 sUuid = oVM.id;
3083 except:
3084 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
3085 return (None, None);
3086 self.processPendingEvents();
3087
3088 # Construct the environment.
3089 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
3090 try: os.remove(sLogFile);
3091 except: pass;
3092 if self.sLogSessionDest:
3093 sLogDest = self.sLogSessionDest;
3094 else:
3095 sLogDest = 'file=%s' % (sLogFile,);
3096 asEnvFinal = [
3097 'VBOX_LOG=%s' % (self.sLogSessionGroups,),
3098 'VBOX_LOG_FLAGS=%s' % (self.sLogSessionFlags,),
3099 'VBOX_LOG_DEST=nodeny %s' % (sLogDest,),
3100 'VBOX_RELEASE_LOG_FLAGS=append time',
3101 ];
3102 if sType == 'gui':
3103 asEnvFinal.append('VBOX_GUI_DBG_ENABLED=1');
3104 if asEnv is not None and asEnv:
3105 asEnvFinal += asEnv;
3106
3107 # Shortcuts for local testing.
3108 oProgress = oWrapped = None;
3109 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
3110 try:
3111 if oTestVM is not None \
3112 and oTestVM.fSnapshotRestoreCurrent is True:
3113 if oVM.state is vboxcon.MachineState_Running:
3114 reporter.log2('Machine "%s" already running.' % (sName,));
3115 oProgress = None;
3116 oWrapped = self.openSession(oVM);
3117 else:
3118 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
3119 oSessionWrapperRestore = self.openSession(oVM);
3120 if oSessionWrapperRestore is not None:
3121 oSnapshotCur = oVM.currentSnapshot;
3122 if oSnapshotCur is not None:
3123 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
3124 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
3125 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
3126 else:
3127 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
3128 oSessionWrapperRestore.close();
3129 except:
3130 reporter.errorXcpt();
3131 return (None, None);
3132
3133 oSession = None; # Must be initialized, otherwise the log statement at the end of the function can fail.
3134
3135 # Open a remote session, wait for this operation to complete.
3136 # (The loop is a kludge to deal with us racing the closing of the
3137 # direct session of a previous VM run. See waitOnDirectSessionClose.)
3138 if oWrapped is None:
3139 for i in range(10):
3140 try:
3141 if self.fpApiVer < 4.3 \
3142 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
3143 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3144 elif self.fpApiVer < 5.2 \
3145 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
3146 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3147 else:
3148 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=no-member,no-value-for-parameter
3149 if self.fpApiVer < 3.3:
3150 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, '\n'.join(asEnvFinal));
3151 else:
3152 if self.uApiRevision >= self.makeApiRevision(6, 1, 0, 1):
3153 oProgress = oVM.launchVMProcess(oSession, sType, asEnvFinal);
3154 else:
3155 oProgress = oVM.launchVMProcess(oSession, sType, '\n'.join(asEnvFinal));
3156 break;
3157 except:
3158 if i == 9:
3159 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
3160 return (None, None);
3161 oSession = None;
3162 if i >= 0:
3163 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=line-too-long
3164 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
3165 if fWait and oProgress is not None:
3166 rc = self.waitOnProgress(oProgress);
3167 if rc < 0:
3168 self.waitOnDirectSessionClose(oVM, 5000);
3169
3170 # VM failed to power up, still collect VBox.log, need to wrap the session object
3171 # in order to use the helper for adding the log files to the report.
3172 from testdriver.vboxwrappers import SessionWrapper;
3173 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
3174 oTmp.addLogsToReport();
3175
3176 # Try to collect a stack trace of the process for further investigation of any startup hangs.
3177 uPid = oTmp.getPid();
3178 if uPid is not None:
3179 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3180 if sHostProcessInfoHung is not None:
3181 reporter.log('Trying to annotate the hung VM startup process report, please stand by...');
3182 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-startup-hung.log',
3183 'process/report/vm', 'Annotated hung VM process state during startup'); # pylint: disable=line-too-long
3184 # Upload the raw log for manual annotation in case resolving failed.
3185 if not fRcTmp:
3186 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3187 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-startup-hung.log', 'process/report/vm',
3188 'Hung VM process state during startup');
3189
3190 try:
3191 if oSession is not None:
3192 oSession.close();
3193 except: pass;
3194 reportError(oProgress, 'failed to open session for "%s"' % (sName));
3195 self.uploadStartupLogFile(oVM, sName);
3196 return (None, None);
3197 reporter.log2('waitOnProgress -> %s' % (rc,));
3198
3199 # Wrap up the session object and push on to the list before returning it.
3200 if oWrapped is None:
3201 from testdriver.vboxwrappers import SessionWrapper;
3202 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
3203
3204 oWrapped.registerEventHandlerForTask();
3205 self.aoRemoteSessions.append(oWrapped);
3206 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
3207 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
3208 % (oWrapped, len(self.aoRemoteSessions) - 1,
3209 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
3210 self.addTask(oWrapped);
3211
3212 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
3213
3214 from testdriver.vboxwrappers import ProgressWrapper;
3215 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
3216 'starting %s' % (sName,)) if oProgress else None);
3217
3218 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
3219 """ Simplified version of startVmEx. """
3220 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
3221 return oSession;
3222
3223 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
3224 """
3225 Start the VM, returning the VM session and progress object on success.
3226 The session is also added to the task list and to the aoRemoteSessions set.
3227
3228 On failure (None, None) is returned and an error is logged.
3229 """
3230 oVM = self.getVmByName(sName);
3231 if oVM is None:
3232 return (None, None);
3233 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
3234
3235 def startVmByName(self, sName, sType=None, asEnv = None):
3236 """
3237 Start the VM, returning the VM session on success. The session is
3238 also added to the task list and to the aoRemoteSessions set.
3239
3240 On failure None is returned and an error is logged.
3241 """
3242 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
3243 return oSession;
3244
3245 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=too-many-statements
3246 """
3247 Terminates the VM specified by oSession and adds the release logs to
3248 the test report.
3249
3250 This will try achieve this by using powerOff, but will resort to
3251 tougher methods if that fails.
3252
3253 The session will always be removed from the task list.
3254 The session will be closed unless we fail to kill the process.
3255 The session will be removed from the remote session list if closed.
3256
3257 The progress object (a wrapper!) is for teleportation and similar VM
3258 operations, it will be attempted canceled before powering off the VM.
3259 Failures are logged but ignored.
3260 The progress object will always be removed from the task list.
3261
3262 Returns True if powerOff and session close both succeed.
3263 Returns False if on failure (logged), including when we successfully
3264 kill the VM process.
3265 """
3266 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
3267
3268 # Call getPid first to make sure the PID is cached in the wrapper.
3269 oSession.getPid();
3270
3271 #
3272 # If the host is out of memory, just skip all the info collection as it
3273 # requires memory too and seems to wedge.
3274 #
3275 sHostProcessInfo = None;
3276 sHostProcessInfoHung = None;
3277 sLastScreenshotPath = None;
3278 sOsKernelLog = None;
3279 sVgaText = None;
3280 asMiscInfos = [];
3281
3282 if not oSession.fHostMemoryLow:
3283 # Try to fetch the VM process info before meddling with its state.
3284 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3285 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
3286
3287 #
3288 # Pause the VM if we're going to take any screenshots or dig into the
3289 # guest. Failures are quitely ignored.
3290 #
3291 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3292 try:
3293 if oSession.oVM.state in [ vboxcon.MachineState_Running,
3294 vboxcon.MachineState_LiveSnapshotting,
3295 vboxcon.MachineState_Teleporting ]:
3296 oSession.o.console.pause();
3297 except:
3298 reporter.logXcpt();
3299
3300 #
3301 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
3302 #
3303 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
3304 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
3305 fRc = oSession.takeScreenshot(sLastScreenshotPath);
3306 if fRc is not True:
3307 sLastScreenshotPath = None;
3308
3309 # Query the OS kernel log from the debugger if appropriate/requested.
3310 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3311 sOsKernelLog = oSession.queryOsKernelLog();
3312
3313 # Do "info vgatext all" separately.
3314 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3315 sVgaText = oSession.queryDbgInfoVgaText();
3316
3317 # Various infos (do after kernel because of symbols).
3318 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3319 # Dump the guest stack for all CPUs.
3320 cCpus = oSession.getCpuCount();
3321 if cCpus > 0:
3322 for iCpu in xrange(0, cCpus):
3323 sThis = oSession.queryDbgGuestStack(iCpu);
3324 if sThis:
3325 asMiscInfos += [
3326 '================ start guest stack VCPU %s ================\n' % (iCpu,),
3327 sThis,
3328 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
3329 ];
3330
3331 for sInfo, sArg in [ ('mode', 'all'),
3332 ('fflags', ''),
3333 ('cpumguest', 'verbose all'),
3334 ('cpumguestinstr', 'symbol all'),
3335 ('exits', ''),
3336 ('pic', ''),
3337 ('apic', ''),
3338 ('apiclvt', ''),
3339 ('apictimer', ''),
3340 ('ioapic', ''),
3341 ('pit', ''),
3342 ('phys', ''),
3343 ('clocks', ''),
3344 ('timers', ''),
3345 ('gdt', ''),
3346 ('ldt', ''),
3347 ]:
3348 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
3349 continue;
3350 sThis = oSession.queryDbgInfo(sInfo, sArg);
3351 if sThis:
3352 if sThis[-1] != '\n':
3353 sThis += '\n';
3354 asMiscInfos += [
3355 '================ start %s %s ================\n' % (sInfo, sArg),
3356 sThis,
3357 '================ end %s %s ==================\n' % (sInfo, sArg),
3358 ];
3359
3360 #
3361 # Terminate the VM
3362 #
3363
3364 # Cancel the progress object if specified.
3365 if oProgress is not None:
3366 if not oProgress.isCompleted() and oProgress.isCancelable():
3367 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
3368 try:
3369 oProgress.o.cancel();
3370 except:
3371 reporter.logXcpt();
3372 else:
3373 oProgress.wait();
3374 self.removeTask(oProgress);
3375
3376 # Check if the VM has terminated by itself before powering it off.
3377 fClose = True;
3378 fRc = True;
3379 if oSession.needsPoweringOff():
3380 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
3381 fRc = oSession.powerOff(fFudgeOnFailure = False);
3382 if fRc is not True:
3383 # power off failed, try terminate it in a nice manner.
3384 fRc = False;
3385 uPid = oSession.getPid();
3386 if uPid is not None:
3387 #
3388 # Collect some information about the VM process first to have
3389 # some state information for further investigation why powering off failed.
3390 #
3391 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3392
3393 # Exterminate...
3394 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
3395 fClose = base.processTerminate(uPid);
3396 if fClose is True:
3397 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3398 fClose = oSession.waitForTask(1000);
3399
3400 if fClose is not True:
3401 # Being nice failed...
3402 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
3403 % (uPid, oSession.sName));
3404 fClose = base.processKill(uPid);
3405 if fClose is True:
3406 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3407 fClose = oSession.waitForTask(1000);
3408 if fClose is not True:
3409 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
3410
3411 # The final steps.
3412 if fClose is True:
3413 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
3414 oSession.close();
3415 self.waitOnDirectSessionClose(oSession.oVM, 10000);
3416 try:
3417 eState = oSession.oVM.state;
3418 except:
3419 reporter.logXcpt();
3420 else:
3421 if eState == vboxcon.MachineState_Aborted:
3422 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
3423 self.removeTask(oSession);
3424
3425 #
3426 # Add the release log, debug log and a screenshot of the VM to the test report.
3427 #
3428 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3429 oSession.addLogsToReport();
3430
3431 # Add a screenshot if it has been requested and taken successfully.
3432 if sLastScreenshotPath is not None:
3433 if reporter.testErrorCount() > 0:
3434 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
3435 else:
3436 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
3437
3438 # Add produced recording files (if any) to the log files to be uploaded.
3439 if reporter.testErrorCount() > 0 \
3440 or self.fRecordingForceUpload: # By default we only upload WebM file on failures, to save some space.
3441 for oRecFile in self.aRecordingFiles:
3442 reporter.addLogFile(oRecFile['file'], 'video/webm', 'Recording of screen #%d', oRecFile['id']);
3443
3444 # Add the guest OS log if it has been requested and taken successfully.
3445 if sOsKernelLog is not None:
3446 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
3447
3448 # Add "info vgatext all" if we've got it.
3449 if sVgaText is not None:
3450 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
3451
3452 # Add the "info xxxx" items if we've got any.
3453 if asMiscInfos:
3454 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
3455
3456 # Add the host process info if we were able to retrieve it.
3457 if sHostProcessInfo is not None:
3458 reporter.log('Trying to annotate the VM process report, please stand by...');
3459 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
3460 'process/report/vm', 'Annotated VM process state');
3461 # Upload the raw log for manual annotation in case resolving failed.
3462 if not fRcTmp:
3463 reporter.log('Failed to annotate VM process report, uploading raw report');
3464 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
3465
3466 # Add the host process info for failed power off attempts if we were able to retrieve it.
3467 if sHostProcessInfoHung is not None:
3468 reporter.log('Trying to annotate the hung VM process report, please stand by...');
3469 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
3470 'process/report/vm', 'Annotated hung VM process state');
3471 # Upload the raw log for manual annotation in case resolving failed.
3472 if not fRcTmp:
3473 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3474 fRcTmp = reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
3475 'Hung VM process state');
3476 if not fRcTmp:
3477 try: reporter.log('******* START vmprocess-hung.log *******\n%s\n******* END vmprocess-hung.log *******\n'
3478 % (sHostProcessInfoHung,));
3479 except: pass; # paranoia
3480
3481
3482 return fRc;
3483
3484
3485 #
3486 # Some information query functions (mix).
3487 #
3488 # Methods require the VBox API. If the information is provided by both
3489 # the testboxscript as well as VBox API, we'll check if it matches.
3490 #
3491
3492 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
3493 """
3494 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
3495
3496 Returns True / False.
3497 Raises exception on environment / host mismatch.
3498 """
3499 fEnv = os.environ.get(sEnvVar, None);
3500 if fEnv is not None:
3501 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3502
3503 fVBox = None;
3504 self.importVBoxApi();
3505 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
3506 try:
3507 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
3508 except:
3509 if not fQuiet:
3510 reporter.logXcpt();
3511
3512 if fVBox is not None:
3513 if fEnv is not None:
3514 if fEnv != fVBox and not fQuiet:
3515 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
3516 % (fVBox, sEnum, fEnv, sEnvVar));
3517 return fEnv;
3518 return fVBox;
3519 if fEnv is not None:
3520 return fEnv;
3521 return False;
3522
3523 def hasHostHwVirt(self, fQuiet = False):
3524 """
3525 Checks if hardware assisted virtualization is supported by the host.
3526
3527 Returns True / False.
3528 Raises exception on environment / host mismatch.
3529 """
3530 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
3531
3532 def hasHostNestedPaging(self, fQuiet = False):
3533 """
3534 Checks if nested paging is supported by the host.
3535
3536 Returns True / False.
3537 Raises exception on environment / host mismatch.
3538 """
3539 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
3540 and self.hasHostHwVirt(fQuiet);
3541
3542 def hasHostNestedHwVirt(self, fQuiet = False):
3543 """
3544 Checks if nested hardware-assisted virtualization is supported by the host.
3545
3546 Returns True / False.
3547 Raises exception on environment / host mismatch.
3548 """
3549 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_HWVIRT', 'ProcessorFeature_NestedHWVirt', 6.0, fQuiet) \
3550 and self.hasHostHwVirt(fQuiet);
3551
3552 def hasHostLongMode(self, fQuiet = False):
3553 """
3554 Checks if the host supports 64-bit guests.
3555
3556 Returns True / False.
3557 Raises exception on environment / host mismatch.
3558 """
3559 # Note that the testboxscript doesn't export this variable atm.
3560 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
3561
3562 def getHostCpuCount(self, fQuiet = False):
3563 """
3564 Returns the number of CPUs on the host.
3565
3566 Returns True / False.
3567 Raises exception on environment / host mismatch.
3568 """
3569 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
3570 if cEnv is not None:
3571 cEnv = int(cEnv);
3572
3573 try:
3574 cVBox = self.oVBox.host.processorOnlineCount;
3575 except:
3576 if not fQuiet:
3577 reporter.logXcpt();
3578 cVBox = None;
3579
3580 if cVBox is not None:
3581 if cEnv is not None:
3582 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
3583 return cVBox;
3584 if cEnv is not None:
3585 return cEnv;
3586 return 1;
3587
3588 def _getHostCpuDesc(self, fQuiet = False):
3589 """
3590 Internal method used for getting the host CPU description from VBoxSVC.
3591 Returns description string, on failure an empty string is returned.
3592 """
3593 try:
3594 return self.oVBox.host.getProcessorDescription(0);
3595 except:
3596 if not fQuiet:
3597 reporter.logXcpt();
3598 return '';
3599
3600 def isHostCpuAmd(self, fQuiet = False):
3601 """
3602 Checks if the host CPU vendor is AMD.
3603
3604 Returns True / False.
3605 """
3606 sCpuDesc = self._getHostCpuDesc(fQuiet);
3607 return 'AMD' in sCpuDesc or sCpuDesc == 'AuthenticAMD';
3608
3609 def isHostCpuIntel(self, fQuiet = False):
3610 """
3611 Checks if the host CPU vendor is Intel.
3612
3613 Returns True / False.
3614 """
3615 sCpuDesc = self._getHostCpuDesc(fQuiet);
3616 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
3617
3618 def isHostCpuVia(self, fQuiet = False):
3619 """
3620 Checks if the host CPU vendor is VIA (or Centaur).
3621
3622 Returns True / False.
3623 """
3624 sCpuDesc = self._getHostCpuDesc(fQuiet);
3625 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
3626
3627 def isHostCpuShanghai(self, fQuiet = False):
3628 """
3629 Checks if the host CPU vendor is Shanghai (or Zhaoxin).
3630
3631 Returns True / False.
3632 """
3633 sCpuDesc = self._getHostCpuDesc(fQuiet);
3634 return sCpuDesc.startswith("ZHAOXIN") or sCpuDesc.strip(' ') == 'Shanghai';
3635
3636 def isHostCpuP4(self, fQuiet = False):
3637 """
3638 Checks if the host CPU is a Pentium 4 / Pentium D.
3639
3640 Returns True / False.
3641 """
3642 if not self.isHostCpuIntel(fQuiet):
3643 return False;
3644
3645 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
3646 return ((uFamilyModel >> 8) & 0xf) == 0xf;
3647
3648 def hasRawModeSupport(self, fQuiet = False):
3649 """
3650 Checks if raw-mode is supported by VirtualBox that the testbox is
3651 configured for it.
3652
3653 Returns True / False.
3654 Raises no exceptions.
3655
3656 Note! Differs from the rest in that we don't require the
3657 TESTBOX_WITH_RAW_MODE value to match the API. It is
3658 sometimes helpful to disable raw-mode on individual
3659 test boxes. (This probably goes for
3660 """
3661 # The environment variable can be used to disable raw-mode.
3662 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
3663 if fEnv is not None:
3664 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3665 if fEnv is False:
3666 return False;
3667
3668 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
3669 # with raw-mode support or not.
3670 self.importVBoxApi();
3671 if self.fpApiVer >= 5.0:
3672 try:
3673 fVBox = self.oVBox.systemProperties.rawModeSupported;
3674 except:
3675 if not fQuiet:
3676 reporter.logXcpt();
3677 fVBox = True;
3678 if fVBox is False:
3679 return False;
3680
3681 return True;
3682
3683 #
3684 # Testdriver execution methods.
3685 #
3686
3687 def handleTask(self, oTask, sMethod):
3688 """
3689 Callback method for handling unknown tasks in the various run loops.
3690
3691 The testdriver should override this if it already tasks running when
3692 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3693 Call super to handle unknown tasks.
3694
3695 Returns True if handled, False if not.
3696 """
3697 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3698 return False;
3699
3700 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3701 """
3702 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3703
3704 Returns False on error, logged.
3705 Returns task result on success.
3706 """
3707 # All async methods ends with the following two args.
3708 cMsTimeout = aArgs[-2];
3709 fIgnoreErrors = aArgs[-1];
3710
3711 fRemoveVm = self.addTask(oSession);
3712 fRemoveTxs = self.addTask(oTxsSession);
3713
3714 rc = fnAsync(*aArgs); # pylint: disable=star-args
3715 if rc is True:
3716 rc = False;
3717 oTask = self.waitForTasks(cMsTimeout + 1);
3718 if oTask is oTxsSession:
3719 if oTxsSession.isSuccess():
3720 rc = oTxsSession.getResult();
3721 elif fIgnoreErrors is True:
3722 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3723 else:
3724 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3725 else:
3726 oTxsSession.cancelTask();
3727 if oTask is None:
3728 if fIgnoreErrors is True:
3729 reporter.log( 'txsDoTask: The task timed out.');
3730 else:
3731 reporter.errorTimeout('txsDoTask: The task timed out.');
3732 elif oTask is oSession:
3733 reporter.error('txsDoTask: The VM terminated unexpectedly');
3734 else:
3735 if fIgnoreErrors is True:
3736 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
3737 else:
3738 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
3739 else:
3740 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
3741
3742 if fRemoveTxs:
3743 self.removeTask(oTxsSession);
3744 if fRemoveVm:
3745 self.removeTask(oSession);
3746 return rc;
3747
3748 # pylint: disable=missing-docstring
3749
3750 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3751 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3752 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3753
3754 def txsVer(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3755 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncVer,
3756 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3757
3758 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3759 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3760 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3761
3762 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3763 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3764 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3765
3766 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3767 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3768 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3769
3770 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3771 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3772 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3773
3774 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3775 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3776 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3777
3778 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3779 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3780 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3781
3782 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3783 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3784 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3785
3786 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
3787 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
3788 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3789
3790 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3791 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
3792 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3793
3794 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3795 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
3796 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3797
3798 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3799 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
3800 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3801
3802 def txsCopyFile(self, oSession, oTxsSession, sSrcFile, sDstFile, fMode = 0, cMsTimeout = 30000, fIgnoreErrors = False):
3803 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncCopyFile, \
3804 (sSrcFile, sDstFile, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3805
3806 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3807 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
3808 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3809
3810 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3811 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
3812 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3813
3814 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
3815 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
3816 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3817
3818 def txsDownloadFiles(self, oSession, oTxsSession, aasFiles, fAddToLog = True, fIgnoreErrors = False):
3819 """
3820 Convenience function to get files from the guest, storing them in the
3821 scratch and adding them to the test result set (optional, but default).
3822
3823 The aasFiles parameter contains an array of with guest-path + host-path
3824 pairs, optionally a file 'kind', description and an alternative upload
3825 filename can also be specified.
3826
3827 Host paths are relative to the scratch directory or they must be given
3828 in absolute form. The guest path should be using guest path style.
3829
3830 Returns True on success.
3831 Returns False on failure (unless fIgnoreErrors is set), logged.
3832 """
3833 for asEntry in aasFiles:
3834 # Unpack:
3835 sGstFile = asEntry[0];
3836 sHstFile = asEntry[1];
3837 sKind = asEntry[2] if len(asEntry) > 2 and asEntry[2] else 'misc/other';
3838 sDescription = asEntry[3] if len(asEntry) > 3 and asEntry[3] else '';
3839 sAltName = asEntry[4] if len(asEntry) > 4 and asEntry[4] else None;
3840 assert len(asEntry) <= 5 and sGstFile and sHstFile;
3841 if not os.path.isabs(sHstFile):
3842 sHstFile = os.path.join(self.sScratchPath, sHstFile);
3843
3844 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sHstFile,));
3845
3846 try: os.unlink(sHstFile); ## @todo txsDownloadFile doesn't truncate the output file.
3847 except: pass;
3848
3849 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sHstFile, 30 * 1000, fIgnoreErrors);
3850 if fRc:
3851 if fAddToLog:
3852 reporter.addLogFile(sHstFile, sKind, sDescription, sAltName);
3853 else:
3854 if fIgnoreErrors is not True:
3855 return reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sHstFile));
3856 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
3857 return True;
3858
3859 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
3860 cMsTimeout = 30000, fIgnoreErrors = False):
3861 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
3862 (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3863
3864 def txsPackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteSource, cMsTimeout = 30000, fIgnoreErrors = False):
3865 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncPackFile, \
3866 (sRemoteFile, sRemoteSource, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3867
3868 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3869 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
3870 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3871
3872 def txsExpandString(self, oSession, oTxsSession, sString, cMsTimeout = 30000, fIgnoreErrors = False):
3873 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncExpandString, \
3874 (sString, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3875
3876 # pylint: enable=missing-docstring
3877
3878 def txsCdWait(self,
3879 oSession, # type: vboxwrappers.SessionWrapper
3880 oTxsSession, # type: txsclient.Session
3881 cMsTimeout = 30000, # type: int
3882 sFile = None # type: String
3883 ): # -> bool
3884 """
3885 Mostly an internal helper for txsRebootAndReconnectViaTcp and
3886 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
3887 ready. It does this by polling for a file it knows to exist on the CD.
3888
3889 Returns True on success.
3890
3891 Returns False on failure, logged.
3892 """
3893
3894 if sFile is None:
3895 sFile = 'valkit.txt';
3896
3897 reporter.log('txsCdWait: Waiting for file "%s" to become available ...' % (sFile,));
3898
3899 fRemoveVm = self.addTask(oSession);
3900 fRemoveTxs = self.addTask(oTxsSession);
3901 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3902 msStart = base.timestampMilli();
3903 cMsTimeout2 = cMsTimeout;
3904 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3905 if fRc is True:
3906 while True:
3907 # wait for it to complete.
3908 oTask = self.waitForTasks(cMsTimeout2 + 1);
3909 if oTask is not oTxsSession:
3910 oTxsSession.cancelTask();
3911 if oTask is None:
3912 reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
3913 % (base.timestampMilli() - msStart,));
3914 elif oTask is oSession:
3915 reporter.error('txsCdWait: The VM terminated unexpectedly');
3916 else:
3917 reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
3918 fRc = False;
3919 break;
3920 if oTxsSession.isSuccess():
3921 break;
3922
3923 # Check for timeout.
3924 cMsElapsed = base.timestampMilli() - msStart;
3925 if cMsElapsed >= cMsTimeout:
3926 reporter.error('txsCdWait: timed out');
3927 fRc = False;
3928 break;
3929 # delay.
3930 self.sleep(1);
3931
3932 # resubmit the task.
3933 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
3934 cMsTimeout2 = max(cMsTimeout2, 500);
3935 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3936 if fRc is not True:
3937 reporter.error('txsCdWait: asyncIsFile failed');
3938 break;
3939 else:
3940 reporter.error('txsCdWait: asyncIsFile failed');
3941
3942 if not fRc:
3943 # Do some diagnosis to find out why this failed.
3944 ## @todo Identify guest OS type and only run one of the following commands.
3945 fIsNotWindows = True;
3946 reporter.log('txsCdWait: Listing root contents of ${CDROM}:');
3947 if fIsNotWindows:
3948 reporter.log('txsCdWait: Tiggering udevadm ...');
3949 oTxsSession.syncExec("/sbin/udevadm", ("/sbin/udevadm", "trigger", "--verbose"), fIgnoreErrors = True);
3950 time.sleep(15);
3951 oTxsSession.syncExec("/bin/ls", ("/bin/ls", "-al", "${CDROM}"), fIgnoreErrors = True);
3952 reporter.log('txsCdWait: Listing media directory:');
3953 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3954 reporter.log('txsCdWait: Listing mount points / drives:');
3955 oTxsSession.syncExec('/bin/mount', ('/bin/mount',), fIgnoreErrors = True);
3956 oTxsSession.syncExec('/bin/cat', ('/bin/cat', '/etc/fstab'), fIgnoreErrors = True);
3957 oTxsSession.syncExec('/bin/dmesg', ('/bin/dmesg',), fIgnoreErrors = True);
3958 oTxsSession.syncExec('/usr/bin/lshw', ('/usr/bin/lshw', '-c', 'disk'), fIgnoreErrors = True);
3959 oTxsSession.syncExec('/bin/journalctl',
3960 ('/bin/journalctl', '-x', '-b'), fIgnoreErrors = True);
3961 oTxsSession.syncExec('/bin/journalctl',
3962 ('/bin/journalctl', '-x', '-b', '/usr/lib/udisks2/udisksd'), fIgnoreErrors = True);
3963 oTxsSession.syncExec('/usr/bin/udisksctl',
3964 ('/usr/bin/udisksctl', 'info', '-b', '/dev/sr0'), fIgnoreErrors = True);
3965 oTxsSession.syncExec('/bin/systemctl',
3966 ('/bin/systemctl', 'status', 'udisks2'), fIgnoreErrors = True);
3967 oTxsSession.syncExec('/bin/ps',
3968 ('/bin/ps', '-a', '-u', '-x'), fIgnoreErrors = True);
3969 reporter.log('txsCdWait: Mounting manually ...');
3970 for _ in range(3):
3971 oTxsSession.syncExec('/bin/mount', ('/bin/mount', '/dev/sr0', '${CDROM}'), fIgnoreErrors = True);
3972 time.sleep(5);
3973 reporter.log('txsCdWait: Re-Listing media directory:');
3974 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3975 else:
3976 # ASSUMES that we always install Windows on drive C right now.
3977 sWinDir = "C:\\Windows\\System32\\";
3978 # Should work since WinXP Pro.
3979 oTxsSession.syncExec(sWinDir + "wbem\\WMIC.exe",
3980 ("WMIC.exe", "logicaldisk", "get",
3981 "deviceid, volumename, description"),
3982 fIgnoreErrors = True);
3983 oTxsSession.syncExec(sWinDir + " cmd.exe",
3984 ('cmd.exe', '/C', 'dir', '${CDROM}'),
3985 fIgnoreErrors = True);
3986
3987 if fRemoveTxs:
3988 self.removeTask(oTxsSession);
3989 if fRemoveVm:
3990 self.removeTask(oSession);
3991 return fRc;
3992
3993 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
3994 """
3995 Mostly an internal worker for connecting to TXS via TCP used by the
3996 *ViaTcp methods.
3997
3998 Returns a tuplet with True/False and TxsSession/None depending on the
3999 result. Errors are logged.
4000 """
4001
4002 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
4003 % (oSession, cMsTimeout, fNatForwardingForTxs));
4004
4005 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4006 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
4007 if oTxsConnect is not None:
4008 self.addTask(oTxsConnect);
4009 fRemoveVm = self.addTask(oSession);
4010 oTask = self.waitForTasks(cMsTimeout + 1);
4011 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
4012 self.removeTask(oTxsConnect);
4013 if oTask is oTxsConnect:
4014 oTxsSession = oTxsConnect.getResult();
4015 if oTxsSession is not None:
4016 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
4017 return (True, oTxsSession);
4018
4019 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
4020 else:
4021 oTxsConnect.cancelTask();
4022 if oTask is None:
4023 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
4024 elif oTask is oSession:
4025 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
4026 else:
4027 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
4028 if fRemoveVm:
4029 self.removeTask(oSession);
4030 else:
4031 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
4032 return (False, None);
4033
4034 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
4035 cMsCdWait = 30000, sFileCdWait = None, \
4036 fNatForwardingForTxs = False):
4037 """
4038 Starts the specified VM and tries to connect to its TXS via TCP.
4039 The VM will be powered off if TXS doesn't respond before the specified
4040 time has elapsed.
4041
4042 Returns a the VM and TXS sessions (a two tuple) on success. The VM
4043 session is in the task list, the TXS session is not.
4044 Returns (None, None) on failure, fully logged.
4045 """
4046
4047 # Zap the guest IP to make sure we're not getting a stale entry
4048 # (unless we're restoring the VM of course).
4049 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
4050 if oTestVM is None \
4051 or oTestVM.fSnapshotRestoreCurrent is False:
4052 try:
4053 oSession1 = self.openSession(self.getVmByName(sVmName));
4054 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
4055 oSession1.saveSettings(True);
4056 del oSession1;
4057 except:
4058 reporter.logXcpt();
4059
4060 # Start the VM.
4061 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4062 reporter.flushall();
4063 oSession = self.startVmByName(sVmName);
4064 if oSession is not None:
4065 # Connect to TXS.
4066 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
4067 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
4068 if fRc is True:
4069 if fCdWait:
4070 # Wait for CD?
4071 reporter.log2('startVmAndConnectToTxsViaTcp: Waiting for file "%s" to become available ...' % (sFileCdWait,));
4072 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4073 if fRc is not True:
4074 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
4075
4076 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4077 if sVer is not False:
4078 reporter.log('startVmAndConnectToTxsViaTcp: TestExecService version %s' % (sVer,));
4079 else:
4080 reporter.log('startVmAndConnectToTxsViaTcp: Unable to retrieve TestExecService version');
4081
4082 if fRc is True:
4083 # Success!
4084 return (oSession, oTxsSession);
4085 else:
4086 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
4087 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
4088 self.terminateVmBySession(oSession);
4089 return (None, None);
4090
4091 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
4092 cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
4093 """
4094 Executes the TXS reboot command
4095
4096 Returns A tuple of True and the new TXS session on success.
4097
4098 Returns A tuple of False and either the old TXS session or None on failure.
4099 """
4100 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
4101
4102 #
4103 # This stuff is a bit complicated because of rebooting being kind of
4104 # disruptive to the TXS and such... The protocol is that TXS will:
4105 # - ACK the reboot command.
4106 # - Shutdown the transport layer, implicitly disconnecting us.
4107 # - Execute the reboot operation.
4108 # - On failure, it will be re-init the transport layer and be
4109 # available pretty much immediately. UUID unchanged.
4110 # - On success, it will be respawed after the reboot (hopefully),
4111 # with a different UUID.
4112 #
4113 fRc = False;
4114 iStart = base.timestampMilli();
4115
4116 # Get UUID.
4117 cMsTimeout2 = min(60000, cMsTimeout);
4118 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
4119 if sUuidBefore is not False:
4120 # Reboot.
4121 cMsElapsed = base.timestampMilli() - iStart;
4122 cMsTimeout2 = cMsTimeout - cMsElapsed;
4123 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
4124 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4125 if fRc is True:
4126 # Reconnect.
4127 if fNatForwardingForTxs is True:
4128 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
4129 cMsElapsed = base.timestampMilli() - iStart;
4130 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
4131 if fRc is True:
4132 # Check the UUID.
4133 cMsElapsed = base.timestampMilli() - iStart;
4134 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
4135 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
4136 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4137 if sUuidBefore is not False:
4138 if sUuidAfter != sUuidBefore:
4139 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
4140
4141 # Do CD wait if specified.
4142 if fCdWait:
4143 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4144 if fRc is not True:
4145 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
4146
4147 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4148 if sVer is not False:
4149 reporter.log('txsRebootAndReconnectViaTcp: TestExecService version %s' % (sVer,));
4150 else:
4151 reporter.log('txsRebootAndReconnectViaTcp: Unable to retrieve TestExecService version');
4152 else:
4153 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
4154 else:
4155 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
4156 else:
4157 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
4158 else:
4159 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
4160 else:
4161 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
4162 return (fRc, oTxsSession);
4163
4164 # pylint: disable=too-many-locals,too-many-arguments
4165
4166 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4167 fCheckSessionStatus = False):
4168 """
4169 Executes the specified test task, waiting till it completes or times out.
4170
4171 The VM session (if any) must be in the task list.
4172
4173 Returns True if we executed the task and nothing abnormal happend.
4174 Query the process status from the TXS session.
4175
4176 Returns False if some unexpected task was signalled or we failed to
4177 submit the job.
4178
4179 If fCheckSessionStatus is set to True, the overall session status will be
4180 taken into account and logged as an error on failure.
4181 """
4182 reporter.testStart(sTestName);
4183 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4184
4185 # Submit the job.
4186 fRc = False;
4187 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4188 self.addTask(oTxsSession);
4189
4190 # Wait for the job to complete.
4191 while True:
4192 oTask = self.waitForTasks(cMsTimeout + 1);
4193 if oTask is None:
4194 if fCheckSessionStatus:
4195 reporter.error('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4196 else:
4197 reporter.log('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4198 break;
4199 if oTask is oTxsSession:
4200 if fCheckSessionStatus \
4201 and not oTxsSession.isSuccess():
4202 reporter.error('txsRunTest: Test "%s" failed' % (sTestName,));
4203 else:
4204 fRc = True;
4205 reporter.log('txsRunTest: isSuccess=%s getResult=%s' \
4206 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4207 break;
4208 if not self.handleTask(oTask, 'txsRunTest'):
4209 break;
4210
4211 self.removeTask(oTxsSession);
4212 if not oTxsSession.pollTask():
4213 oTxsSession.cancelTask();
4214 else:
4215 reporter.error('txsRunTest: asyncExec failed');
4216
4217 reporter.testDone();
4218 return fRc;
4219
4220 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4221 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
4222 """
4223 Executes the specified test task, waiting till it completes or times out,
4224 redirecting stdin, stdout and stderr to the given objects.
4225
4226 The VM session (if any) must be in the task list.
4227
4228 Returns True if we executed the task and nothing abnormal happend.
4229 Query the process status from the TXS session.
4230
4231 Returns False if some unexpected task was signalled or we failed to
4232 submit the job.
4233 """
4234 reporter.testStart(sTestName);
4235 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4236
4237 # Submit the job.
4238 fRc = False;
4239 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
4240 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4241 self.addTask(oTxsSession);
4242
4243 # Wait for the job to complete.
4244 while True:
4245 oTask = self.waitForTasks(cMsTimeout + 1);
4246 if oTask is None:
4247 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
4248 break;
4249 if oTask is oTxsSession:
4250 fRc = True;
4251 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
4252 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4253 break;
4254 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
4255 break;
4256
4257 self.removeTask(oTxsSession);
4258 if not oTxsSession.pollTask():
4259 oTxsSession.cancelTask();
4260 else:
4261 reporter.error('txsRunTestRedirectStd: asyncExec failed');
4262
4263 reporter.testDone();
4264 return fRc;
4265
4266 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
4267 sExecName1, asArgs1,
4268 sExecName2, asArgs2,
4269 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
4270 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
4271 """
4272 Executes the specified test tasks, waiting till they complete or
4273 times out. The 1st task is started after the 2nd one.
4274
4275 The VM session (if any) must be in the task list.
4276
4277 Returns True if we executed the task and nothing abnormal happend.
4278 Query the process status from the TXS sessions.
4279
4280 Returns False if some unexpected task was signalled or we failed to
4281 submit the job.
4282 """
4283 reporter.testStart(sTestName);
4284
4285 # Submit the jobs.
4286 fRc = False;
4287 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
4288 self.adjustTimeoutMs(cMsTimeout)):
4289 self.addTask(oTxsSession1);
4290
4291 self.sleep(2); # fudge! grr
4292
4293 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
4294 self.adjustTimeoutMs(cMsTimeout)):
4295 self.addTask(oTxsSession2);
4296
4297 # Wait for the jobs to complete.
4298 cPendingJobs = 2;
4299 while True:
4300 oTask = self.waitForTasks(cMsTimeout + 1);
4301 if oTask is None:
4302 reporter.log('txsRunTest2: waitForTasks timed out');
4303 break;
4304
4305 if oTask is oTxsSession1 or oTask is oTxsSession2:
4306 if oTask is oTxsSession1: iTask = 1;
4307 else: iTask = 2;
4308 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
4309 % (iTask, oTask.isSuccess(), oTask.getResult()));
4310 self.removeTask(oTask);
4311 cPendingJobs -= 1;
4312 if cPendingJobs <= 0:
4313 fRc = True;
4314 break;
4315
4316 elif not self.handleTask(oTask, 'txsRunTest'):
4317 break;
4318
4319 self.removeTask(oTxsSession2);
4320 if not oTxsSession2.pollTask():
4321 oTxsSession2.cancelTask();
4322 else:
4323 reporter.error('txsRunTest2: asyncExec #2 failed');
4324
4325 self.removeTask(oTxsSession1);
4326 if not oTxsSession1.pollTask():
4327 oTxsSession1.cancelTask();
4328 else:
4329 reporter.error('txsRunTest2: asyncExec #1 failed');
4330
4331 reporter.testDone();
4332 return fRc;
4333
4334 # pylint: enable=too-many-locals,too-many-arguments
4335
4336
4337 #
4338 # Working with test results via serial port.
4339 #
4340
4341 class TxsMonitorComFile(base.TdTaskBase):
4342 """
4343 Class that monitors a COM output file.
4344 """
4345
4346 def __init__(self, sComRawFile, asStopWords = None):
4347 base.TdTaskBase.__init__(self, utils.getCallerName());
4348 self.sComRawFile = sComRawFile;
4349 self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
4350 self.sResult = None; ##< The result.
4351 self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
4352
4353 def toString(self):
4354 return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
4355 % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
4356
4357 def pollTask(self, fLocked = False):
4358 """
4359 Overrides TdTaskBase.pollTask() for the purpose of polling the file.
4360 """
4361 if not fLocked:
4362 self.lockTask();
4363
4364 sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
4365 if len(sFile) > self.cchDisplayed:
4366 sNew = sFile[self.cchDisplayed:];
4367 oMatch = self.oStopRegExp.search(sNew);
4368 if oMatch:
4369 # Done! Get result, flush all the output and signal the task.
4370 self.sResult = oMatch.group(1);
4371 for sLine in sNew.split('\n'):
4372 reporter.log('COM OUTPUT: %s' % (sLine,));
4373 self.cchDisplayed = len(sFile);
4374 self.signalTaskLocked();
4375 else:
4376 # Output whole lines only.
4377 offNewline = sFile.find('\n', self.cchDisplayed);
4378 while offNewline >= 0:
4379 reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
4380 self.cchDisplayed = offNewline + 1;
4381 offNewline = sFile.find('\n', self.cchDisplayed);
4382
4383 fRet = self.fSignalled;
4384 if not fLocked:
4385 self.unlockTask();
4386 return fRet;
4387
4388 # Our stuff.
4389 def getResult(self):
4390 """
4391 Returns the connected TXS session object on success.
4392 Returns None on failure or if the task has not yet completed.
4393 """
4394 self.oCv.acquire();
4395 sResult = self.sResult;
4396 self.oCv.release();
4397 return sResult;
4398
4399 def cancelTask(self):
4400 """ Cancels the task. """
4401 self.signalTask();
4402 return True;
4403
4404
4405 def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4406 """
4407 Monitors the COM output file for stop words (PASSED and FAILED by default).
4408
4409 Returns the stop word.
4410 Returns None on VM error and timeout.
4411 """
4412
4413 reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
4414
4415 oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
4416 self.addTask(oMonitorTask);
4417
4418 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4419 oTask = self.waitForTasks(cMsTimeout + 1);
4420 reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
4421
4422 if oTask is not oMonitorTask:
4423 oMonitorTask.cancelTask();
4424 self.removeTask(oMonitorTask);
4425
4426 oMonitorTask.pollTask();
4427 return oMonitorTask.getResult();
4428
4429
4430 def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4431 """
4432 Runs the specified VM and monitors the given COM output file for stop
4433 words (PASSED and FAILED by default).
4434
4435 The caller is assumed to have configured the VM to use the given
4436 file. The method will take no action to verify this.
4437
4438 Returns the stop word.
4439 Returns None on VM error and timeout.
4440 """
4441
4442 # Start the VM.
4443 reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4444 reporter.flushall();
4445 oSession = self.startVmByName(sVmName);
4446 if oSession is not None:
4447 # Let it run and then terminate it.
4448 sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
4449 self.terminateVmBySession(oSession);
4450 else:
4451 sRet = None;
4452 return sRet;
4453
4454 #
4455 # Other stuff
4456 #
4457
4458 def waitForGAs(self,
4459 oSession, # type: vboxwrappers.SessionWrapper
4460 cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
4461 """
4462 Waits for the guest additions to enter a certain state.
4463
4464 aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
4465 aenmWaitForActive - List facilities (type values) that must be active.
4466 aenmWaitForInactive - List facilities (type values) that must be inactive.
4467
4468 Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
4469
4470 Returns True on success, False w/ error logging on timeout or failure.
4471 """
4472 reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
4473
4474 #
4475 # Get IGuest:
4476 #
4477 try:
4478 oIGuest = oSession.o.console.guest;
4479 except:
4480 return reporter.errorXcpt();
4481
4482 #
4483 # Create a wait task:
4484 #
4485 from testdriver.vboxwrappers import AdditionsStatusTask;
4486 try:
4487 oGaStatusTask = AdditionsStatusTask(oSession = oSession,
4488 oIGuest = oIGuest,
4489 cMsTimeout = cMsTimeout,
4490 aenmWaitForRunLevels = aenmWaitForRunLevels,
4491 aenmWaitForActive = aenmWaitForActive,
4492 aenmWaitForInactive = aenmWaitForInactive);
4493 except:
4494 return reporter.errorXcpt();
4495
4496 #
4497 # Add the task and make sure the VM session is also present.
4498 #
4499 self.addTask(oGaStatusTask);
4500 fRemoveSession = self.addTask(oSession);
4501 oTask = self.waitForTasks(cMsTimeout + 1);
4502 reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
4503 self.removeTask(oGaStatusTask);
4504 if fRemoveSession:
4505 self.removeTask(oSession);
4506
4507 #
4508 # Digest the result.
4509 #
4510 if oTask is oGaStatusTask:
4511 fSucceeded = oGaStatusTask.getResult();
4512 if fSucceeded is True:
4513 reporter.log('waitForGAs: Succeeded.');
4514 else:
4515 reporter.error('waitForGAs: Failed.');
4516 else:
4517 oGaStatusTask.cancelTask();
4518 if oTask is None:
4519 reporter.error('waitForGAs: Timed out.');
4520 elif oTask is oSession:
4521 oSession.reportPrematureTermination('waitForGAs: ');
4522 else:
4523 reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
4524 fSucceeded = False;
4525 return fSucceeded;
4526
4527 @staticmethod
4528 def controllerTypeToName(eControllerType):
4529 """
4530 Translate a controller type to a standard controller name.
4531 """
4532 if eControllerType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
4533 sName = "IDE Controller";
4534 elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
4535 sName = "SATA Controller";
4536 elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
4537 sName = "SAS Controller";
4538 elif eControllerType in (vboxcon.StorageControllerType_LsiLogic, vboxcon.StorageControllerType_BusLogic,):
4539 sName = "SCSI Controller";
4540 elif eControllerType == vboxcon.StorageControllerType_NVMe:
4541 sName = "NVMe Controller";
4542 elif eControllerType == vboxcon.StorageControllerType_VirtioSCSI:
4543 sName = "VirtIO SCSI Controller";
4544 else:
4545 sName = "Storage Controller";
4546 return sName;
Note: See TracBrowser for help on using the repository browser.

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