VirtualBox

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

Last change on this file since 86672 was 86627, checked in by vboxsync, 4 years ago

ValKit/vbox.py: Quitet down the build type detection a little. bugref:9841

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