VirtualBox

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

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

Validation Kit/testdriver: Now using the localhost reachable flag for the NAT attachment type via API. ​bugref:9896

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 196.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 92199 2021-11-03 19:27:28Z 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: 92199 $"
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 stable within 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 [6.1, 5.2, 4.3, 3.2,]:
1421 self.fpApiVer += 0.1;
1422 else:
1423 self.fpApiVer = int(self.fpApiVer) + 1.0;
1424 # fudge value to be always bigger than the nominal value (0.1 gets rounded down)
1425 if round(self.fpApiVer, 1) > self.fpApiVer:
1426 self.fpApiVer += sys.float_info.epsilon * self.fpApiVer / 2.0;
1427
1428 try:
1429 self.uRevision = oVBox.revision;
1430 except:
1431 reporter.logXcpt('Failed to get VirtualBox revision, assuming 0');
1432 self.uRevision = 0;
1433 reporter.log("IVirtualBox.revision=%u" % (self.uRevision,));
1434
1435 try:
1436 self.uApiRevision = oVBox.APIRevision;
1437 except:
1438 reporter.logXcpt('Failed to get VirtualBox APIRevision, faking it.');
1439 self.uApiRevision = self.makeApiRevision(aiVerComponents[0], aiVerComponents[1], aiVerComponents[2], 0);
1440 reporter.log("IVirtualBox.APIRevision=%#x" % (self.uApiRevision,));
1441
1442 # Patch VBox manage to gloss over portability issues (error constants, etc).
1443 self._patchVBoxMgr();
1444
1445 # Wrap oVBox.
1446 from testdriver.vboxwrappers import VirtualBoxWrapper;
1447 self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
1448
1449 # Install the constant wrapping hack.
1450 vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
1451 vboxcon.fpApiVer = self.fpApiVer;
1452 reporter.setComXcptFormatter(formatComOrXpComException);
1453
1454 except:
1455 self.oVBoxMgr = None;
1456 self.oVBox = None;
1457 reporter.logXcpt("getVirtualBox / API version exception");
1458 return False;
1459
1460 # Done
1461 self.fImportedVBoxApi = True;
1462 reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
1463 return True;
1464
1465 def _patchVBoxMgr(self):
1466 """
1467 Glosses over missing self.oVBoxMgr methods on older VBox versions.
1468 """
1469
1470 def _xcptGetResult(oSelf, oXcpt = None):
1471 """ See vboxapi. """
1472 _ = oSelf;
1473 if oXcpt is None: oXcpt = sys.exc_info()[1];
1474 if sys.platform == 'win32':
1475 import winerror; # pylint: disable=import-error
1476 hrXcpt = oXcpt.hresult;
1477 if hrXcpt == winerror.DISP_E_EXCEPTION:
1478 hrXcpt = oXcpt.excepinfo[5];
1479 else:
1480 hrXcpt = oXcpt.error;
1481 return hrXcpt;
1482
1483 def _xcptIsDeadInterface(oSelf, oXcpt = None):
1484 """ See vboxapi. """
1485 return oSelf.xcptGetStatus(oXcpt) in [
1486 0x80004004, -2147467260, # NS_ERROR_ABORT
1487 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
1488 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
1489 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
1490 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
1491 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
1492 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
1493 ];
1494
1495 def _xcptIsOurXcptKind(oSelf, oXcpt = None):
1496 """ See vboxapi. """
1497 _ = oSelf;
1498 if oXcpt is None: oXcpt = sys.exc_info()[1];
1499 if sys.platform == 'win32':
1500 from pythoncom import com_error as NativeComExceptionClass # pylint: disable=import-error,no-name-in-module
1501 else:
1502 from xpcom import Exception as NativeComExceptionClass # pylint: disable=import-error
1503 return isinstance(oXcpt, NativeComExceptionClass);
1504
1505 def _xcptIsEqual(oSelf, oXcpt, hrStatus):
1506 """ See vboxapi. """
1507 hrXcpt = oSelf.xcptGetResult(oXcpt);
1508 return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000; # pylint: disable=consider-using-in
1509
1510 def _xcptToString(oSelf, oXcpt):
1511 """ See vboxapi. """
1512 _ = oSelf;
1513 if oXcpt is None: oXcpt = sys.exc_info()[1];
1514 return str(oXcpt);
1515
1516 def _getEnumValueName(oSelf, sEnumTypeNm, oEnumValue, fTypePrefix = False):
1517 """ See vboxapi. """
1518 _ = oSelf; _ = fTypePrefix;
1519 return '%s::%s' % (sEnumTypeNm, oEnumValue);
1520
1521 # Add utilities found in newer vboxapi revision.
1522 if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
1523 import types;
1524 self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
1525 self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
1526 self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
1527 self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
1528 self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
1529 if not hasattr(self.oVBoxMgr, 'getEnumValueName'):
1530 import types;
1531 self.oVBoxMgr.getEnumValueName = types.MethodType(_getEnumValueName, self.oVBoxMgr);
1532
1533
1534 def _teardownVBoxApi(self): # pylint: disable=too-many-statements
1535 """
1536 Drop all VBox object references and shutdown com/xpcom.
1537 """
1538 if not self.fImportedVBoxApi:
1539 return True;
1540 import gc;
1541
1542 # Drop all references we've have to COM objects.
1543 self.aoRemoteSessions = [];
1544 self.aoVMs = [];
1545 self.oVBoxMgr = None;
1546 self.oVBox = None;
1547 vboxcon.goHackModuleClass.oVBoxMgr = None; # VBoxConstantWrappingHack.
1548 reporter.setComXcptFormatter(None);
1549
1550 # Do garbage collection to try get rid of those objects.
1551 try:
1552 gc.collect();
1553 except:
1554 reporter.logXcpt();
1555 self.fImportedVBoxApi = False;
1556
1557 # Check whether the python is still having any COM objects/interfaces around.
1558 cVBoxMgrs = 0;
1559 aoObjsLeftBehind = [];
1560 if self.sHost == 'win':
1561 import pythoncom; # pylint: disable=import-error
1562 try:
1563 cIfs = pythoncom._GetInterfaceCount(); # pylint: disable=no-member,protected-access
1564 cObjs = pythoncom._GetGatewayCount(); # pylint: disable=no-member,protected-access
1565 if cObjs == 0 and cIfs == 0:
1566 reporter.log('_teardownVBoxApi: no interfaces or objects left behind.');
1567 else:
1568 reporter.log('_teardownVBoxApi: Python COM still has %s objects and %s interfaces...' % ( cObjs, cIfs));
1569
1570 from win32com.client import DispatchBaseClass; # pylint: disable=import-error
1571 for oObj in gc.get_objects():
1572 if isinstance(oObj, DispatchBaseClass):
1573 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1574 aoObjsLeftBehind.append(oObj);
1575 elif utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1576 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1577 cVBoxMgrs += 1;
1578 aoObjsLeftBehind.append(oObj);
1579 oObj = None;
1580 except:
1581 reporter.logXcpt();
1582
1583 # If not being used, we can safely uninitialize COM.
1584 if cIfs == 0 and cObjs == 0 and cVBoxMgrs == 0 and not aoObjsLeftBehind:
1585 reporter.log('_teardownVBoxApi: Calling CoUninitialize...');
1586 try: pythoncom.CoUninitialize(); # pylint: disable=no-member
1587 except: reporter.logXcpt();
1588 else:
1589 reporter.log('_teardownVBoxApi: Returned from CoUninitialize.');
1590 else:
1591 try:
1592 # XPCOM doesn't crash and burn like COM if you shut it down with interfaces and objects around.
1593 # Also, it keeps a number of internal objects and interfaces around to do its job, so shutting
1594 # it down before we go looking for dangling interfaces is more or less required.
1595 from xpcom import _xpcom as _xpcom; # pylint: disable=import-error,useless-import-alias
1596 hrc = _xpcom.DeinitCOM();
1597 cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=protected-access
1598 cObjs = _xpcom._GetGatewayCount(); # pylint: disable=protected-access
1599
1600 if cObjs == 0 and cIfs == 0:
1601 reporter.log('_teardownVBoxApi: No XPCOM interfaces or objects active. (hrc=%#x)' % (hrc,));
1602 else:
1603 reporter.log('_teardownVBoxApi: %s XPCOM objects and %s interfaces still around! (hrc=%#x)'
1604 % (cObjs, cIfs, hrc));
1605 if hasattr(_xpcom, '_DumpInterfaces'):
1606 try: _xpcom._DumpInterfaces(); # pylint: disable=protected-access
1607 except: reporter.logXcpt('_teardownVBoxApi: _DumpInterfaces failed');
1608
1609 from xpcom.client import Component; # pylint: disable=import-error
1610 for oObj in gc.get_objects():
1611 if isinstance(oObj, Component):
1612 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1613 aoObjsLeftBehind.append(oObj);
1614 if utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
1615 reporter.log('_teardownVBoxApi: %s' % (oObj,));
1616 cVBoxMgrs += 1;
1617 aoObjsLeftBehind.append(oObj);
1618 oObj = None;
1619 except:
1620 reporter.logXcpt();
1621
1622 # Try get the referrers to (XP)COM interfaces and objects that was left behind.
1623 for iObj in range(len(aoObjsLeftBehind)): # pylint: disable=consider-using-enumerate
1624 try:
1625 aoReferrers = gc.get_referrers(aoObjsLeftBehind[iObj]);
1626 reporter.log('_teardownVBoxApi: Found %u referrers to %s:' % (len(aoReferrers), aoObjsLeftBehind[iObj],));
1627 for oReferrer in aoReferrers:
1628 oMyFrame = sys._getframe(0); # pylint: disable=protected-access
1629 if oReferrer is oMyFrame:
1630 reporter.log('_teardownVBoxApi: - frame of this function');
1631 elif oReferrer is aoObjsLeftBehind:
1632 reporter.log('_teardownVBoxApi: - aoObjsLeftBehind');
1633 else:
1634 fPrinted = False;
1635 if isinstance(oReferrer, (dict, list, tuple)):
1636 try:
1637 aoSubReferreres = gc.get_referrers(oReferrer);
1638 for oSubRef in aoSubReferreres:
1639 if not isinstance(oSubRef, list) \
1640 and not isinstance(oSubRef, dict) \
1641 and oSubRef is not oMyFrame \
1642 and oSubRef is not aoSubReferreres:
1643 reporter.log('_teardownVBoxApi: - %s :: %s:'
1644 % (utils.getObjectTypeName(oSubRef), utils.getObjectTypeName(oReferrer)));
1645 fPrinted = True;
1646 break;
1647 del aoSubReferreres;
1648 except:
1649 reporter.logXcpt('subref');
1650 if not fPrinted:
1651 reporter.log('_teardownVBoxApi: - %s:' % (utils.getObjectTypeName(oReferrer),));
1652 try:
1653 import pprint;
1654 for sLine in pprint.pformat(oReferrer, width = 130).split('\n'):
1655 reporter.log('_teardownVBoxApi: %s' % (sLine,));
1656 except:
1657 reporter.log('_teardownVBoxApi: %s' % (oReferrer,));
1658 except:
1659 reporter.logXcpt();
1660 del aoObjsLeftBehind;
1661
1662 # Force garbage collection again, just for good measure.
1663 try:
1664 gc.collect();
1665 time.sleep(0.5); # fudge factor
1666 except:
1667 reporter.logXcpt();
1668 return True;
1669
1670 def _powerOffAllVms(self):
1671 """
1672 Tries to power off all running VMs.
1673 """
1674 for oSession in self.aoRemoteSessions:
1675 uPid = oSession.getPid();
1676 if uPid is not None:
1677 reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
1678 base.processKill(uPid);
1679 else:
1680 reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
1681 oSession.close();
1682 return None;
1683
1684
1685
1686 #
1687 # Build type, OS and arch getters.
1688 #
1689
1690 def getBuildType(self):
1691 """
1692 Get the build type.
1693 """
1694 if not self._detectBuild():
1695 return 'release';
1696 return self.oBuild.sType;
1697
1698 def getBuildOs(self):
1699 """
1700 Get the build OS.
1701 """
1702 if not self._detectBuild():
1703 return self.sHost;
1704 return self.oBuild.sOs;
1705
1706 def getBuildArch(self):
1707 """
1708 Get the build arch.
1709 """
1710 if not self._detectBuild():
1711 return self.sHostArch;
1712 return self.oBuild.sArch;
1713
1714 def getGuestAdditionsIso(self):
1715 """
1716 Get the path to the guest addition iso.
1717 """
1718 if not self._detectBuild():
1719 return None;
1720 return self.oBuild.sGuestAdditionsIso;
1721
1722 #
1723 # Override everything from the base class so the testdrivers don't have to
1724 # check whether we have overridden a method or not.
1725 #
1726
1727 def showUsage(self):
1728 rc = base.TestDriver.showUsage(self);
1729 reporter.log('');
1730 reporter.log('Generic VirtualBox Options:');
1731 reporter.log(' --vbox-session-type <type>');
1732 reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
1733 reporter.log(' Default: %s' % (self.sSessionTypeDef));
1734 reporter.log(' --vrdp, --no-vrdp');
1735 reporter.log(' Enables VRDP, ports starting at 6000');
1736 reporter.log(' Default: --vrdp');
1737 reporter.log(' --vrdp-base-port <port>');
1738 reporter.log(' Sets the base for VRDP port assignments.');
1739 reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
1740 reporter.log(' --vbox-default-bridged-nic <interface>');
1741 reporter.log(' Sets the default interface for bridged networking.');
1742 reporter.log(' Default: autodetect');
1743 reporter.log(' --vbox-use-svc-defaults');
1744 reporter.log(' Use default locations and files for VBoxSVC. This is useful');
1745 reporter.log(' for automatically configuring the test VMs for debugging.');
1746 reporter.log(' --vbox-log');
1747 reporter.log(' The VBox logger group settings for everyone.');
1748 reporter.log(' --vbox-log-flags');
1749 reporter.log(' The VBox logger flags settings for everyone.');
1750 reporter.log(' --vbox-log-dest');
1751 reporter.log(' The VBox logger destination settings for everyone.');
1752 reporter.log(' --vbox-self-log');
1753 reporter.log(' The VBox logger group settings for the testdriver.');
1754 reporter.log(' --vbox-self-log-flags');
1755 reporter.log(' The VBox logger flags settings for the testdriver.');
1756 reporter.log(' --vbox-self-log-dest');
1757 reporter.log(' The VBox logger destination settings for the testdriver.');
1758 reporter.log(' --vbox-session-log');
1759 reporter.log(' The VM session logger group settings.');
1760 reporter.log(' --vbox-session-log-flags');
1761 reporter.log(' The VM session logger flags.');
1762 reporter.log(' --vbox-session-log-dest');
1763 reporter.log(' The VM session logger destination settings.');
1764 reporter.log(' --vbox-svc-log');
1765 reporter.log(' The VBoxSVC logger group settings.');
1766 reporter.log(' --vbox-svc-log-flags');
1767 reporter.log(' The VBoxSVC logger flag settings.');
1768 reporter.log(' --vbox-svc-log-dest');
1769 reporter.log(' The VBoxSVC logger destination settings.');
1770 reporter.log(' --vbox-svc-debug');
1771 reporter.log(' Start VBoxSVC in a debugger.');
1772 reporter.log(' --vbox-svc-wait-debug');
1773 reporter.log(' Start VBoxSVC and wait for debugger to attach to it.');
1774 reporter.log(' --vbox-always-upload-logs');
1775 reporter.log(' Whether to always upload log files, or only do so on failure.');
1776 reporter.log(' --vbox-always-upload-screenshots');
1777 reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
1778 reporter.log(' --vbox-debugger, --no-vbox-debugger');
1779 reporter.log(' Enables the VBox debugger, port at 5000');
1780 reporter.log(' Default: --vbox-debugger');
1781 if self.oTestVmSet is not None:
1782 self.oTestVmSet.showUsage();
1783 return rc;
1784
1785 def parseOption(self, asArgs, iArg): # pylint: disable=too-many-statements
1786 if asArgs[iArg] == '--vbox-session-type':
1787 iArg += 1;
1788 if iArg >= len(asArgs):
1789 raise base.InvalidOption('The "--vbox-session-type" takes an argument');
1790 self.sSessionType = asArgs[iArg];
1791 elif asArgs[iArg] == '--vrdp':
1792 self.fEnableVrdp = True;
1793 elif asArgs[iArg] == '--no-vrdp':
1794 self.fEnableVrdp = False;
1795 elif asArgs[iArg] == '--vrdp-base-port':
1796 iArg += 1;
1797 if iArg >= len(asArgs):
1798 raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
1799 try: self.uVrdpBasePort = int(asArgs[iArg]);
1800 except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer' % (asArgs[iArg],));
1801 if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
1802 raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)'
1803 % (asArgs[iArg],));
1804 elif asArgs[iArg] == '--vbox-default-bridged-nic':
1805 iArg += 1;
1806 if iArg >= len(asArgs):
1807 raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
1808 self.sDefBridgedNic = asArgs[iArg];
1809 elif asArgs[iArg] == '--vbox-use-svc-defaults':
1810 self.fUseDefaultSvc = True;
1811 elif asArgs[iArg] == '--vbox-self-log':
1812 iArg += 1;
1813 if iArg >= len(asArgs):
1814 raise base.InvalidOption('The "--vbox-self-log" takes an argument');
1815 self.sLogSelfGroups = asArgs[iArg];
1816 elif asArgs[iArg] == '--vbox-self-log-flags':
1817 iArg += 1;
1818 if iArg >= len(asArgs):
1819 raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
1820 self.sLogSelfFlags = asArgs[iArg];
1821 elif asArgs[iArg] == '--vbox-self-log-dest':
1822 iArg += 1;
1823 if iArg >= len(asArgs):
1824 raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
1825 self.sLogSelfDest = asArgs[iArg];
1826 elif asArgs[iArg] == '--vbox-session-log':
1827 iArg += 1;
1828 if iArg >= len(asArgs):
1829 raise base.InvalidOption('The "--vbox-session-log" takes an argument');
1830 self.sLogSessionGroups = asArgs[iArg];
1831 elif asArgs[iArg] == '--vbox-session-log-flags':
1832 iArg += 1;
1833 if iArg >= len(asArgs):
1834 raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
1835 self.sLogSessionFlags = asArgs[iArg];
1836 elif asArgs[iArg] == '--vbox-session-log-dest':
1837 iArg += 1;
1838 if iArg >= len(asArgs):
1839 raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
1840 self.sLogSessionDest = asArgs[iArg];
1841 elif asArgs[iArg] == '--vbox-svc-log':
1842 iArg += 1;
1843 if iArg >= len(asArgs):
1844 raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
1845 self.sLogSvcGroups = asArgs[iArg];
1846 elif asArgs[iArg] == '--vbox-svc-log-flags':
1847 iArg += 1;
1848 if iArg >= len(asArgs):
1849 raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
1850 self.sLogSvcFlags = asArgs[iArg];
1851 elif asArgs[iArg] == '--vbox-svc-log-dest':
1852 iArg += 1;
1853 if iArg >= len(asArgs):
1854 raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
1855 self.sLogSvcDest = asArgs[iArg];
1856 elif asArgs[iArg] == '--vbox-log':
1857 iArg += 1;
1858 if iArg >= len(asArgs):
1859 raise base.InvalidOption('The "--vbox-log" takes an argument');
1860 self.sLogSelfGroups = asArgs[iArg];
1861 self.sLogSessionGroups = asArgs[iArg];
1862 self.sLogSvcGroups = asArgs[iArg];
1863 elif asArgs[iArg] == '--vbox-log-flags':
1864 iArg += 1;
1865 if iArg >= len(asArgs):
1866 raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
1867 self.sLogSelfFlags = asArgs[iArg];
1868 self.sLogSessionFlags = asArgs[iArg];
1869 self.sLogSvcFlags = asArgs[iArg];
1870 elif asArgs[iArg] == '--vbox-log-dest':
1871 iArg += 1;
1872 if iArg >= len(asArgs):
1873 raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
1874 self.sLogSelfDest = asArgs[iArg];
1875 self.sLogSessionDest = asArgs[iArg];
1876 self.sLogSvcDest = asArgs[iArg];
1877 elif asArgs[iArg] == '--vbox-svc-debug':
1878 self.fVBoxSvcInDebugger = True;
1879 elif asArgs[iArg] == '--vbox-svc-wait-debug':
1880 self.fVBoxSvcWaitForDebugger = True;
1881 elif asArgs[iArg] == '--vbox-always-upload-logs':
1882 self.fAlwaysUploadLogs = True;
1883 elif asArgs[iArg] == '--vbox-always-upload-screenshots':
1884 self.fAlwaysUploadScreenshots = True;
1885 elif asArgs[iArg] == '--vbox-debugger':
1886 self.fEnableDebugger = True;
1887 elif asArgs[iArg] == '--no-vbox-debugger':
1888 self.fEnableDebugger = False;
1889 else:
1890 # Relevant for selecting VMs to test?
1891 if self.oTestVmSet is not None:
1892 iRc = self.oTestVmSet.parseOption(asArgs, iArg);
1893 if iRc != iArg:
1894 return iRc;
1895
1896 # Hand it to the base class.
1897 return base.TestDriver.parseOption(self, asArgs, iArg);
1898 return iArg + 1;
1899
1900 def completeOptions(self):
1901 return base.TestDriver.completeOptions(self);
1902
1903 def getNetworkAdapterNameFromType(self, oNic):
1904 """
1905 Returns the network adapter name from a given adapter type.
1906
1907 Returns an empty string if not found / invalid.
1908 """
1909 sAdpName = '';
1910 if oNic.adapterType == vboxcon.NetworkAdapterType_Am79C970A \
1911 or oNic.adapterType == vboxcon.NetworkAdapterType_Am79C973 \
1912 or oNic.adapterType == vboxcon.NetworkAdapterType_Am79C960:
1913 sAdpName = 'pcnet';
1914 elif oNic.adapterType == vboxcon.NetworkAdapterType_I82540EM \
1915 or oNic.adapterType == vboxcon.NetworkAdapterType_I82543GC \
1916 or oNic.adapterType == vboxcon.NetworkAdapterType_I82545EM:
1917 sAdpName = 'e1000';
1918 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio:
1919 sAdpName = 'virtio-net';
1920 elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio_1_0:
1921 sAdpName = 'virtio-net-1-dot-0';
1922 return sAdpName;
1923
1924 def getResourceSet(self):
1925 asRsrcs = [];
1926 if self.oTestVmSet is not None:
1927 asRsrcs.extend(self.oTestVmSet.getResourceSet());
1928 asRsrcs.extend(base.TestDriver.getResourceSet(self));
1929 return asRsrcs;
1930
1931 def actionExtract(self):
1932 return base.TestDriver.actionExtract(self);
1933
1934 def actionVerify(self):
1935 return base.TestDriver.actionVerify(self);
1936
1937 def actionConfig(self):
1938 return base.TestDriver.actionConfig(self);
1939
1940 def actionExecute(self):
1941 return base.TestDriver.actionExecute(self);
1942
1943 def actionCleanupBefore(self):
1944 """
1945 Kill any VBoxSVC left behind by a previous test run.
1946 """
1947 self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
1948 return base.TestDriver.actionCleanupBefore(self);
1949
1950 def actionCleanupAfter(self):
1951 """
1952 Clean up the VBox bits and then call the base driver.
1953
1954 If your test driver overrides this, it should normally call us at the
1955 end of the job.
1956 """
1957 cErrorsEntry = reporter.getErrorCount();
1958
1959 # Kill any left over VM processes.
1960 self._powerOffAllVms();
1961
1962 # Drop all VBox object references and shutdown xpcom then
1963 # terminating VBoxSVC, with extreme prejudice if need be.
1964 self._teardownVBoxApi();
1965 self._stopVBoxSVC();
1966
1967 # Add the VBoxSVC and testdriver debug+release log files.
1968 if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
1969 if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
1970 reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
1971 self.sVBoxSvcLogFile = None;
1972
1973 if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
1974 reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
1975 self.sSelfLogFile = None;
1976
1977 sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
1978 if os.path.isfile(sVBoxSvcRelLog):
1979 reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
1980 for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
1981 if os.path.isfile(sVBoxSvcRelLog + sSuff):
1982 reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
1983 # Testbox debugging - START - TEMPORARY, REMOVE ASAP.
1984 if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
1985 try:
1986 reporter.log('> ls -R -la %s' % (self.sScratchPath,));
1987 utils.processCall(['ls', '-R', '-la', self.sScratchPath]);
1988 except: pass;
1989 # Testbox debugging - END - TEMPORARY, REMOVE ASAP.
1990
1991 # Finally, call the base driver to wipe the scratch space.
1992 fRc = base.TestDriver.actionCleanupAfter(self);
1993
1994 # Flag failure if the error count increased.
1995 if reporter.getErrorCount() > cErrorsEntry:
1996 fRc = False;
1997 return fRc;
1998
1999
2000 def actionAbort(self):
2001 """
2002 Terminate VBoxSVC if we've got a pid file.
2003 """
2004 #
2005 # Take default action first, then kill VBoxSVC. The other way around
2006 # is problematic since the testscript would continue running and possibly
2007 # trigger a new VBoxSVC to start.
2008 #
2009 fRc1 = base.TestDriver.actionAbort(self);
2010 fRc2 = self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
2011 return fRc1 is True and fRc2 is True;
2012
2013 def onExit(self, iRc):
2014 """
2015 Stop VBoxSVC if we've started it.
2016 """
2017 if self.oVBoxSvcProcess is not None:
2018 reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
2019 self._powerOffAllVms();
2020 self._teardownVBoxApi();
2021 self._stopVBoxSVC();
2022 reporter.log('*** VBox API shutdown done.');
2023 return base.TestDriver.onExit(self, iRc);
2024
2025
2026 #
2027 # Task wait method override.
2028 #
2029
2030 def notifyAboutReadyTask(self, oTask):
2031 """
2032 Overriding base.TestDriver.notifyAboutReadyTask.
2033 """
2034 try:
2035 self.oVBoxMgr.interruptWaitEvents();
2036 reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
2037 except:
2038 reporter.logXcpt('vbox.notifyAboutReadyTask');
2039 return base.TestDriver.notifyAboutReadyTask(self, oTask);
2040
2041 def waitForTasksSleepWorker(self, cMsTimeout):
2042 """
2043 Overriding base.TestDriver.waitForTasksSleepWorker.
2044 """
2045 try:
2046 rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
2047 _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
2048 reporter.doPollWork('vbox.TestDriver.waitForTasksSleepWorker');
2049 return True;
2050 except KeyboardInterrupt:
2051 raise;
2052 except:
2053 reporter.logXcpt('vbox.waitForTasksSleepWorker');
2054 return False;
2055
2056 #
2057 # Utility methods.
2058 #
2059
2060 def processEvents(self, cMsTimeout = 0):
2061 """
2062 Processes events, returning after the first batch has been processed
2063 or the time limit has been reached.
2064
2065 Only Ctrl-C exception, no return.
2066 """
2067 try:
2068 self.oVBoxMgr.waitForEvents(cMsTimeout);
2069 except KeyboardInterrupt:
2070 raise;
2071 except:
2072 pass;
2073 return None;
2074
2075 def processPendingEvents(self):
2076 """ processEvents(0) - no waiting. """
2077 return self.processEvents(0);
2078
2079 def sleep(self, cSecs):
2080 """
2081 Sleep for a specified amount of time, processing XPCOM events all the while.
2082 """
2083 cMsTimeout = long(cSecs * 1000);
2084 msStart = base.timestampMilli();
2085 self.processEvents(0);
2086 while True:
2087 cMsElapsed = base.timestampMilli() - msStart;
2088 if cMsElapsed > cMsTimeout:
2089 break;
2090 #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
2091 self.processEvents(cMsTimeout - cMsElapsed);
2092 return None;
2093
2094 def _logVmInfoUnsafe(self, oVM): # pylint: disable=too-many-statements,too-many-branches
2095 """
2096 Internal worker for logVmInfo that is wrapped in try/except.
2097 """
2098 reporter.log(" Name: %s" % (oVM.name,));
2099 reporter.log(" ID: %s" % (oVM.id,));
2100 oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId);
2101 reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description,));
2102 reporter.log(" Machine state: %s" % (oVM.state,));
2103 reporter.log(" Session state: %s" % (oVM.sessionState,));
2104 if self.fpApiVer >= 4.2:
2105 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID,));
2106 else:
2107 reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid,));
2108 if self.fpApiVer >= 5.0:
2109 reporter.log(" Session Name: %s" % (oVM.sessionName,));
2110 else:
2111 reporter.log(" Session Name: %s" % (oVM.sessionType,));
2112 reporter.log(" CPUs: %s" % (oVM.CPUCount,));
2113 reporter.log(" RAM: %sMB" % (oVM.memorySize,));
2114 if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
2115 reporter.log(" VRAM: %sMB" % (oVM.graphicsAdapter.VRAMSize,));
2116 reporter.log(" Monitors: %s" % (oVM.graphicsAdapter.monitorCount,));
2117 reporter.log(" GraphicsController: %s"
2118 % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', # pylint: disable=not-callable
2119 oVM.graphicsAdapter.graphicsControllerType),));
2120 else:
2121 reporter.log(" VRAM: %sMB" % (oVM.VRAMSize,));
2122 reporter.log(" Monitors: %s" % (oVM.monitorCount,));
2123 reporter.log(" GraphicsController: %s"
2124 % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', oVM.graphicsControllerType),)); # pylint: disable=not-callable
2125 reporter.log(" Chipset: %s" % (self.oVBoxMgr.getEnumValueName('ChipsetType', oVM.chipsetType),)); # pylint: disable=not-callable
2126 if self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_None'):
2127 reporter.log(" IOMMU: %s" % (self.oVBoxMgr.getEnumValueName('IommuType', oVM.iommuType),)); # pylint: disable=not-callable
2128 reporter.log(" Firmware: %s" % (self.oVBoxMgr.getEnumValueName('FirmwareType', oVM.firmwareType),)); # pylint: disable=not-callable
2129 reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled),));
2130 reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID),));
2131 reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging),));
2132 atTypes = [
2133 ( 'CPUPropertyType_PAE', 'PAE: '),
2134 ( 'CPUPropertyType_LongMode', 'Long-mode: '),
2135 ( 'CPUPropertyType_HWVirt', 'Nested VT-x/AMD-V: '),
2136 ( 'CPUPropertyType_APIC', 'APIC: '),
2137 ( 'CPUPropertyType_X2APIC', 'X2APIC: '),
2138 ( 'CPUPropertyType_TripleFaultReset', 'TripleFaultReset: '),
2139 ( 'CPUPropertyType_IBPBOnVMExit', 'IBPBOnVMExit: '),
2140 ( 'CPUPropertyType_SpecCtrl', 'SpecCtrl: '),
2141 ( 'CPUPropertyType_SpecCtrlByHost', 'SpecCtrlByHost: '),
2142 ];
2143 for sEnumValue, sDesc in atTypes:
2144 if hasattr(vboxcon, sEnumValue):
2145 reporter.log(" %s%s" % (sDesc, oVM.getCPUProperty(getattr(vboxcon, sEnumValue)),));
2146 reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled,));
2147 reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled,));
2148 if self.fpApiVer >= 3.2:
2149 if self.fpApiVer >= 4.2:
2150 reporter.log(" HPET: %s" % (oVM.HPETEnabled,));
2151 else:
2152 reporter.log(" HPET: %s" % (oVM.hpetEnabled,));
2153 if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
2154 reporter.log(" 3D acceleration: %s" % (oVM.graphicsAdapter.accelerate3DEnabled,));
2155 reporter.log(" 2D acceleration: %s" % (oVM.graphicsAdapter.accelerate2DVideoEnabled,));
2156 else:
2157 reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled,));
2158 reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled,));
2159 reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled,));
2160 reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort,));
2161 reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress,));
2162 reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword,));
2163 reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode,));
2164 if self.fpApiVer >= 5.0:
2165 reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode,));
2166 elif self.fpApiVer >= 4.3:
2167 reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode,));
2168 if self.fpApiVer >= 4.0:
2169 reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled,));
2170 try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
2171 except: sPorts = "";
2172 reporter.log(" VRDP server ports: %s" % (sPorts,));
2173 reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary,));
2174 else:
2175 reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled,));
2176 reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports,));
2177 reporter.log(" Last changed: %s" % (oVM.lastStateChange,));
2178
2179 aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
2180 if aoControllers:
2181 reporter.log(" Controllers:");
2182 for oCtrl in aoControllers:
2183 reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType,));
2184 reporter.log(" AudioController: %s"
2185 % (self.oVBoxMgr.getEnumValueName('AudioControllerType', oVM.audioAdapter.audioController),)); # pylint: disable=not-callable
2186 reporter.log(" AudioEnabled: %s" % (oVM.audioAdapter.enabled,));
2187 reporter.log(" Host AudioDriver: %s"
2188 % (self.oVBoxMgr.getEnumValueName('AudioDriverType', oVM.audioAdapter.audioDriver),)); # pylint: disable=not-callable
2189
2190 self.processPendingEvents();
2191 aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
2192 if aoAttachments:
2193 reporter.log(" Attachments:");
2194 for oAtt in aoAttachments:
2195 sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
2196 oMedium = oAtt.medium
2197 if oAtt.type == vboxcon.DeviceType_HardDisk:
2198 reporter.log(" %s: HDD" % sCtrl);
2199 reporter.log(" Id: %s" % (oMedium.id,));
2200 reporter.log(" Name: %s" % (oMedium.name,));
2201 reporter.log(" Format: %s" % (oMedium.format,));
2202 reporter.log(" Location: %s" % (oMedium.location,));
2203
2204 if oAtt.type == vboxcon.DeviceType_DVD:
2205 reporter.log(" %s: DVD" % sCtrl);
2206 if oMedium:
2207 reporter.log(" Id: %s" % (oMedium.id,));
2208 reporter.log(" Name: %s" % (oMedium.name,));
2209 if oMedium.hostDrive:
2210 reporter.log(" Host DVD %s" % (oMedium.location,));
2211 if oAtt.passthrough:
2212 reporter.log(" [passthrough mode]");
2213 else:
2214 reporter.log(" Virtual image: %s" % (oMedium.location,));
2215 reporter.log(" Size: %s" % (oMedium.size,));
2216 else:
2217 reporter.log(" empty");
2218
2219 if oAtt.type == vboxcon.DeviceType_Floppy:
2220 reporter.log(" %s: Floppy" % sCtrl);
2221 if oMedium:
2222 reporter.log(" Id: %s" % (oMedium.id,));
2223 reporter.log(" Name: %s" % (oMedium.name,));
2224 if oMedium.hostDrive:
2225 reporter.log(" Host floppy: %s" % (oMedium.location,));
2226 else:
2227 reporter.log(" Virtual image: %s" % (oMedium.location,));
2228 reporter.log(" Size: %s" % (oMedium.size,));
2229 else:
2230 reporter.log(" empty");
2231 self.processPendingEvents();
2232
2233 reporter.log(" Network Adapter:");
2234 for iSlot in range(0, 32):
2235 try: oNic = oVM.getNetworkAdapter(iSlot)
2236 except: break;
2237 if not oNic.enabled:
2238 reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
2239 continue;
2240 reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s"
2241 % (iSlot, self.oVBoxMgr.getEnumValueName('NetworkAdapterType', oNic.adapterType), # pylint: disable=not-callable
2242 oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
2243
2244 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
2245 reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType,));
2246 if self.fpApiVer >= 4.1:
2247 reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
2248 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
2249 reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType,));
2250 if self.fpApiVer >= 4.1:
2251 reporter.log(" hostInterface: %s" % (oNic.bridgedInterface,));
2252 else:
2253 reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
2254 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
2255 reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType,));
2256 reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
2257 elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
2258 reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType,));
2259 if self.fpApiVer >= 4.1:
2260 reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface,));
2261 else:
2262 reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
2263 else:
2264 if self.fpApiVer >= 7.0:
2265 if oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
2266 reporter.log(" attachmentType: HostOnlyNetwork (%s)" % (oNic.attachmentType,));
2267 reporter.log(" hostonly-net: %s" % (oNic.hostOnlyNetwork,));
2268 elif self.fpApiVer >= 4.1:
2269 if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
2270 reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType,));
2271 reporter.log(" generic-driver: %s" % (oNic.GenericDriver,));
2272 else:
2273 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
2274 else:
2275 reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
2276 if oNic.traceEnabled:
2277 reporter.log(" traceFile: %s" % (oNic.traceFile,));
2278 self.processPendingEvents();
2279
2280 reporter.log(" Serial ports:");
2281 for iSlot in range(0, 8):
2282 try: oPort = oVM.getSerialPort(iSlot)
2283 except: break;
2284 if oPort is not None and oPort.enabled:
2285 enmHostMode = oPort.hostMode;
2286 reporter.log(" slot #%d: hostMode: %s (%s) I/O port: %s IRQ: %s server: %s path: %s" %
2287 (iSlot, self.oVBoxMgr.getEnumValueName('PortMode', enmHostMode), # pylint: disable=not-callable
2288 enmHostMode, oPort.IOBase, oPort.IRQ, oPort.server, oPort.path,) );
2289 self.processPendingEvents();
2290
2291 return True;
2292
2293 def logVmInfo(self, oVM): # pylint: disable=too-many-statements,too-many-branches
2294 """
2295 Logs VM configuration details.
2296
2297 This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
2298 """
2299 try:
2300 fRc = self._logVmInfoUnsafe(oVM);
2301 except:
2302 reporter.logXcpt();
2303 fRc = False;
2304 return fRc;
2305
2306 def logVmInfoByName(self, sName):
2307 """
2308 logVmInfo + getVmByName.
2309 """
2310 return self.logVmInfo(self.getVmByName(sName));
2311
2312 def tryFindGuestOsId(self, sIdOrDesc):
2313 """
2314 Takes a guest OS ID or Description and returns the ID.
2315 If nothing matching it is found, the input is returned unmodified.
2316 """
2317
2318 if self.fpApiVer >= 4.0:
2319 if sIdOrDesc == 'Solaris (64 bit)':
2320 sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
2321
2322 try:
2323 aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
2324 except:
2325 reporter.logXcpt();
2326 else:
2327 for oGuestOS in aoGuestTypes:
2328 try:
2329 sId = oGuestOS.id;
2330 sDesc = oGuestOS.description;
2331 except:
2332 reporter.logXcpt();
2333 else:
2334 if sIdOrDesc in (sId, sDesc,):
2335 sIdOrDesc = sId;
2336 break;
2337 self.processPendingEvents();
2338 return sIdOrDesc
2339
2340 def resourceFindVmHd(self, sVmName, sFlavor):
2341 """
2342 Search the test resources for the most recent VM HD.
2343
2344 Returns path relative to the test resource root.
2345 """
2346 ## @todo implement a proper search algo here.
2347 return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
2348
2349
2350 #
2351 # VM Api wrappers that logs errors, hides exceptions and other details.
2352 #
2353
2354 def createTestVMOnly(self, sName, sKind):
2355 """
2356 Creates and register a test VM without doing any kind of configuration.
2357
2358 Returns VM object (IMachine) on success, None on failure.
2359 """
2360 if not self.importVBoxApi():
2361 return None;
2362
2363 # create + register the VM
2364 try:
2365 if self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
2366 oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
2367 elif self.fpApiVer >= 4.0:
2368 oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
2369 elif self.fpApiVer >= 3.2:
2370 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
2371 else:
2372 oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
2373 try:
2374 oVM.saveSettings();
2375 try:
2376 self.oVBox.registerMachine(oVM);
2377 return oVM;
2378 except:
2379 reporter.logXcpt();
2380 raise;
2381 except:
2382 reporter.logXcpt();
2383 if self.fpApiVer >= 4.0:
2384 try:
2385 if self.fpApiVer >= 4.3:
2386 oProgress = oVM.deleteConfig([]);
2387 else:
2388 oProgress = oVM.delete(None);
2389 self.waitOnProgress(oProgress);
2390 except:
2391 reporter.logXcpt();
2392 else:
2393 try: oVM.deleteSettings();
2394 except: reporter.logXcpt();
2395 raise;
2396 except:
2397 reporter.errorXcpt('failed to create vm "%s"' % (sName));
2398 return None;
2399
2400 # pylint: disable=too-many-arguments,too-many-locals,too-many-statements
2401 def createTestVM(self,
2402 sName,
2403 iGroup,
2404 sHd = None,
2405 cMbRam = None,
2406 cCpus = 1,
2407 fVirtEx = None,
2408 fNestedPaging = None,
2409 sDvdImage = None,
2410 sKind = "Other",
2411 fIoApic = None,
2412 fNstHwVirt = None,
2413 fPae = None,
2414 fFastBootLogo = True,
2415 eNic0Type = None,
2416 eNic0AttachType = None,
2417 sNic0NetName = 'default',
2418 sNic0MacAddr = 'grouped',
2419 sFloppy = None,
2420 fNatForwardingForTxs = None,
2421 sHddControllerType = 'IDE Controller',
2422 fVmmDevTestingPart = None,
2423 fVmmDevTestingMmio = False,
2424 sFirmwareType = 'bios',
2425 sChipsetType = 'piix3',
2426 sIommuType = 'none',
2427 sDvdControllerType = 'IDE Controller',
2428 sCom1RawFile = None):
2429 """
2430 Creates a test VM with a immutable HD from the test resources.
2431 """
2432 # create + register the VM
2433 oVM = self.createTestVMOnly(sName, sKind);
2434 if not oVM:
2435 return None;
2436
2437 # Configure the VM.
2438 fRc = True;
2439 oSession = self.openSession(oVM);
2440 if oSession is not None:
2441 fRc = oSession.setupPreferredConfig();
2442
2443 if fRc and cMbRam is not None :
2444 fRc = oSession.setRamSize(cMbRam);
2445 if fRc and cCpus is not None:
2446 fRc = oSession.setCpuCount(cCpus);
2447 if fRc and fVirtEx is not None:
2448 fRc = oSession.enableVirtEx(fVirtEx);
2449 if fRc and fNestedPaging is not None:
2450 fRc = oSession.enableNestedPaging(fNestedPaging);
2451 if fRc and fIoApic is not None:
2452 fRc = oSession.enableIoApic(fIoApic);
2453 if fRc and fNstHwVirt is not None:
2454 fRc = oSession.enableNestedHwVirt(fNstHwVirt);
2455 if fRc and fPae is not None:
2456 fRc = oSession.enablePae(fPae);
2457 if fRc and sDvdImage is not None:
2458 fRc = oSession.attachDvd(sDvdImage, sDvdControllerType);
2459 if fRc and sHd is not None:
2460 fRc = oSession.attachHd(sHd, sHddControllerType);
2461 if fRc and sFloppy is not None:
2462 fRc = oSession.attachFloppy(sFloppy);
2463 if fRc and eNic0Type is not None:
2464 fRc = oSession.setNicType(eNic0Type, 0);
2465 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2466 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2467 if fRc and sNic0MacAddr is not None:
2468 if sNic0MacAddr == 'grouped':
2469 sNic0MacAddr = '%02X' % (iGroup);
2470 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2471 if fRc and fNatForwardingForTxs is True:
2472 fRc = oSession.setupNatForwardingForTxs();
2473 if fRc and fFastBootLogo is not None:
2474 fRc = oSession.setupBootLogo(fFastBootLogo);
2475 if fRc and self.fEnableVrdp:
2476 fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
2477 if fRc and fVmmDevTestingPart is not None:
2478 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2479 if fRc and sFirmwareType == 'bios':
2480 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
2481 elif sFirmwareType == 'efi':
2482 fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
2483 if fRc and self.fEnableDebugger:
2484 fRc = oSession.setExtraData('VBoxInternal/DBGC/Enabled', '1');
2485 if fRc and sChipsetType == 'piix3':
2486 fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
2487 elif sChipsetType == 'ich9':
2488 fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
2489 if fRc and sCom1RawFile:
2490 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2491 if fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_AMD') and sIommuType == 'amd':
2492 fRc = oSession.setIommuType(vboxcon.IommuType_AMD);
2493 elif fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_Intel') and sIommuType == 'intel':
2494 fRc = oSession.setIommuType(vboxcon.IommuType_Intel);
2495
2496 if fRc: fRc = oSession.saveSettings();
2497 if not fRc: oSession.discardSettings(True);
2498 oSession.close();
2499 if not fRc:
2500 try: self.oVBox.unregisterMachine(oVM.id);
2501 except: pass;
2502 if self.fpApiVer >= 4.0:
2503 try:
2504 if self.fpApiVer >= 4.3:
2505 oProgress = oVM.deleteConfig([]);
2506 else:
2507 oProgress = oVM.delete(None);
2508 self.waitOnProgress(oProgress);
2509 except:
2510 reporter.logXcpt();
2511 else:
2512 try: oVM.deleteSettings();
2513 except: reporter.logXcpt();
2514 return None;
2515
2516 # success.
2517 reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
2518 self.aoVMs.append(oVM);
2519 self.logVmInfo(oVM); # testing...
2520 return oVM;
2521 # pylint: enable=too-many-arguments,too-many-locals,too-many-statements
2522
2523 def createTestVmWithDefaults(self, # pylint: disable=too-many-arguments
2524 sName,
2525 iGroup,
2526 sKind,
2527 sDvdImage = None,
2528 fFastBootLogo = True,
2529 eNic0AttachType = None,
2530 sNic0NetName = 'default',
2531 sNic0MacAddr = 'grouped',
2532 fVmmDevTestingPart = None,
2533 fVmmDevTestingMmio = False,
2534 sCom1RawFile = None):
2535 """
2536 Creates a test VM with all defaults and no HDs.
2537 """
2538 # create + register the VM
2539 oVM = self.createTestVMOnly(sName, sKind);
2540 if oVM is not None:
2541 # Configure the VM with defaults according to sKind.
2542 fRc = True;
2543 oSession = self.openSession(oVM);
2544 if oSession is not None:
2545 if self.fpApiVer >= 6.0:
2546 try:
2547 oSession.o.machine.applyDefaults('');
2548 except:
2549 reporter.errorXcpt('failed to apply defaults to vm "%s"' % (sName,));
2550 fRc = False;
2551 else:
2552 reporter.error("Implement applyDefaults for vbox version %s" % (self.fpApiVer,));
2553 #fRc = oSession.setupPreferredConfig();
2554 fRc = False;
2555
2556 # Apply the specified configuration:
2557 if fRc and sDvdImage is not None:
2558 #fRc = oSession.insertDvd(sDvdImage); # attachDvd
2559 reporter.error('Implement: oSession.insertDvd(%s)' % (sDvdImage,));
2560 fRc = False;
2561
2562 if fRc and fFastBootLogo is not None:
2563 fRc = oSession.setupBootLogo(fFastBootLogo);
2564
2565 if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
2566 fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
2567 if fRc and sNic0MacAddr is not None:
2568 if sNic0MacAddr == 'grouped':
2569 sNic0MacAddr = '%02X' % (iGroup,);
2570 fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
2571
2572 if fRc and fVmmDevTestingPart is not None:
2573 fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
2574
2575 if fRc and sCom1RawFile:
2576 fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
2577
2578 # Save the settings if we were successfull, otherwise discard them.
2579 if fRc:
2580 fRc = oSession.saveSettings();
2581 if not fRc:
2582 oSession.discardSettings(True);
2583 oSession.close();
2584
2585 if fRc is True:
2586 # If we've been successful, add the VM to the list and return it.
2587 # success.
2588 reporter.log('created "%s" with name "%s"' % (oVM.id, sName, ));
2589 self.aoVMs.append(oVM);
2590 self.logVmInfo(oVM); # testing...
2591 return oVM;
2592
2593 # Failed. Unregister the machine and delete it.
2594 try: self.oVBox.unregisterMachine(oVM.id);
2595 except: pass;
2596
2597 if self.fpApiVer >= 4.0:
2598 try:
2599 if self.fpApiVer >= 4.3:
2600 oProgress = oVM.deleteConfig([]);
2601 else:
2602 oProgress = oVM.delete(None);
2603 self.waitOnProgress(oProgress);
2604 except:
2605 reporter.logXcpt();
2606 else:
2607 try: oVM.deleteSettings();
2608 except: reporter.logXcpt();
2609 return None;
2610
2611 def addTestMachine(self, sNameOrId, fQuiet = False):
2612 """
2613 Adds an already existing (that is, configured) test VM to the
2614 test VM list.
2615 """
2616 # find + add the VM to the list.
2617 try:
2618 if self.fpApiVer >= 4.0:
2619 oVM = self.oVBox.findMachine(sNameOrId);
2620 else:
2621 reporter.error('fpApiVer=%s - did you remember to initialize the API' % (self.fpApiVer,));
2622 except:
2623 reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
2624 return None;
2625
2626 self.aoVMs.append(oVM);
2627 if not fQuiet:
2628 reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
2629 self.logVmInfo(oVM);
2630 return oVM;
2631
2632 def openSession(self, oVM):
2633 """
2634 Opens a session for the VM. Returns the a Session wrapper object that
2635 will automatically close the session when the wrapper goes out of scope.
2636
2637 On failure None is returned and an error is logged.
2638 """
2639 try:
2640 sUuid = oVM.id;
2641 except:
2642 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
2643 return None;
2644
2645 # This loop is a kludge to deal with us racing the closing of the
2646 # direct session of a previous VM run. See waitOnDirectSessionClose.
2647 for i in range(10):
2648 try:
2649 if self.fpApiVer <= 3.2:
2650 oSession = self.oVBoxMgr.openMachineSession(sUuid);
2651 else:
2652 oSession = self.oVBoxMgr.openMachineSession(oVM);
2653 break;
2654 except:
2655 if i == 9:
2656 reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
2657 return None;
2658 if i > 0:
2659 reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
2660 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2661 from testdriver.vboxwrappers import SessionWrapper;
2662 return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
2663
2664 #
2665 # Guest locations.
2666 #
2667
2668 @staticmethod
2669 def getGuestTempDir(oTestVm):
2670 """
2671 Helper for finding a temporary directory in the test VM.
2672
2673 Note! It may be necessary to create it!
2674 """
2675 if oTestVm.isWindows():
2676 return "C:\\Temp";
2677 if oTestVm.isOS2():
2678 return "C:\\Temp";
2679 return '/var/tmp';
2680
2681 @staticmethod
2682 def getGuestSystemDir(oTestVm, sPathPrefix = ''):
2683 """
2684 Helper for finding a system directory in the test VM that we can play around with.
2685 sPathPrefix can be used to specify other directories, such as /usr/local/bin/ or /usr/bin, for instance.
2686
2687 On Windows this is always the System32 directory, so this function can be used as
2688 basis for locating other files in or under that directory.
2689 """
2690 if oTestVm.isWindows():
2691 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
2692 if oTestVm.isOS2():
2693 return 'C:\\OS2\\DLL';
2694
2695 # OL / RHEL symlinks "/bin"/ to "/usr/bin". To avoid (unexpectedly) following symlinks, use "/usr/bin" then instead.
2696 if not sPathPrefix \
2697 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
2698 return "/usr/bin";
2699
2700 return sPathPrefix + "/bin";
2701
2702 @staticmethod
2703 def getGuestSystemAdminDir(oTestVm, sPathPrefix = ''):
2704 """
2705 Helper for finding a system admin directory ("sbin") in the test VM that we can play around with.
2706 sPathPrefix can be used to specify other directories, such as /usr/local/sbin/ or /usr/sbin, for instance.
2707
2708 On Windows this is always the System32 directory, so this function can be used as
2709 basis for locating other files in or under that directory.
2710 On UNIX-y systems this always is the "sh" shell to guarantee a common shell syntax.
2711 """
2712 if oTestVm.isWindows():
2713 return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
2714 if oTestVm.isOS2():
2715 return 'C:\\OS2\\DLL'; ## @todo r=andy Not sure here.
2716
2717 # OL / RHEL symlinks "/sbin"/ to "/usr/sbin". To avoid (unexpectedly) following symlinks, use "/usr/sbin" then instead.
2718 if not sPathPrefix \
2719 and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
2720 return "/usr/sbin";
2721
2722 return sPathPrefix + "/sbin";
2723
2724 @staticmethod
2725 def getGuestWinDir(oTestVm):
2726 """
2727 Helper for finding the Windows directory in the test VM that we can play around with.
2728 ASSUMES that we always install Windows on drive C.
2729
2730 Returns the Windows directory, or an empty string when executed on a non-Windows guest (asserts).
2731 """
2732 sWinDir = '';
2733 if oTestVm.isWindows():
2734 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x',]:
2735 sWinDir = 'C:\\WinNT\\';
2736 else:
2737 sWinDir = 'C:\\Windows\\';
2738 assert sWinDir != '', 'Retrieving Windows directory for non-Windows OS';
2739 return sWinDir;
2740
2741 @staticmethod
2742 def getGuestSystemShell(oTestVm):
2743 """
2744 Helper for finding the default system shell in the test VM.
2745 """
2746 if oTestVm.isWindows():
2747 return TestDriver.getGuestSystemDir(oTestVm) + '\\cmd.exe';
2748 if oTestVm.isOS2():
2749 return TestDriver.getGuestSystemDir(oTestVm) + '\\..\\CMD.EXE';
2750 return "/bin/sh";
2751
2752 @staticmethod
2753 def getGuestSystemFileForReading(oTestVm):
2754 """
2755 Helper for finding a file in the test VM that we can read.
2756 """
2757 if oTestVm.isWindows():
2758 return TestDriver.getGuestSystemDir(oTestVm) + '\\ntdll.dll';
2759 if oTestVm.isOS2():
2760 return TestDriver.getGuestSystemDir(oTestVm) + '\\DOSCALL1.DLL';
2761 return "/bin/sh";
2762
2763 def getVmByName(self, sName):
2764 """
2765 Get a test VM by name. Returns None if not found, logged.
2766 """
2767 # Look it up in our 'cache'.
2768 for oVM in self.aoVMs:
2769 try:
2770 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2771 if oVM.name == sName:
2772 return oVM;
2773 except:
2774 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2775
2776 # Look it up the standard way.
2777 return self.addTestMachine(sName, fQuiet = True);
2778
2779 def getVmByUuid(self, sUuid):
2780 """
2781 Get a test VM by uuid. Returns None if not found, logged.
2782 """
2783 # Look it up in our 'cache'.
2784 for oVM in self.aoVMs:
2785 try:
2786 if oVM.id == sUuid:
2787 return oVM;
2788 except:
2789 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2790
2791 # Look it up the standard way.
2792 return self.addTestMachine(sUuid, fQuiet = True);
2793
2794 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2795 """
2796 Waits for a progress object to complete. Returns the status code.
2797 """
2798 # Wait for progress no longer than cMsTimeout time period.
2799 tsStart = datetime.datetime.now()
2800 while True:
2801 self.processPendingEvents();
2802 try:
2803 if oProgress.completed:
2804 break;
2805 except:
2806 return -1;
2807 self.processPendingEvents();
2808
2809 tsNow = datetime.datetime.now()
2810 tsDelta = tsNow - tsStart
2811 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
2812 if fErrorOnTimeout:
2813 reporter.errorTimeout('Timeout while waiting for progress.')
2814 return -1
2815
2816 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
2817 try: oProgress.waitForCompletion(cMsInterval);
2818 except: return -2;
2819
2820 try: rc = oProgress.resultCode;
2821 except: rc = -2;
2822 self.processPendingEvents();
2823 return rc;
2824
2825 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2826 """
2827 Waits for the VM process to close it's current direct session.
2828
2829 Returns None.
2830 """
2831 # Get the original values so we're not subject to
2832 try:
2833 eCurState = oVM.sessionState;
2834 if self.fpApiVer >= 5.0:
2835 sCurName = sOrgName = oVM.sessionName;
2836 else:
2837 sCurName = sOrgName = oVM.sessionType;
2838 if self.fpApiVer >= 4.2:
2839 iCurPid = iOrgPid = oVM.sessionPID;
2840 else:
2841 iCurPid = iOrgPid = oVM.sessionPid;
2842 except Exception as oXcpt:
2843 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2844 reporter.logXcpt();
2845 self.processPendingEvents();
2846 return None;
2847 self.processPendingEvents();
2848
2849 msStart = base.timestampMilli();
2850 while iCurPid == iOrgPid \
2851 and sCurName == sOrgName \
2852 and sCurName != '' \
2853 and base.timestampMilli() - msStart < cMsTimeout \
2854 and eCurState in (vboxcon.SessionState_Unlocking, vboxcon.SessionState_Spawning, vboxcon.SessionState_Locked,):
2855 self.processEvents(1000);
2856 try:
2857 eCurState = oVM.sessionState;
2858 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2859 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2860 except Exception as oXcpt:
2861 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2862 reporter.logXcpt();
2863 break;
2864 self.processPendingEvents();
2865 self.processPendingEvents();
2866 return None;
2867
2868 def uploadStartupLogFile(self, oVM, sVmName):
2869 """
2870 Uploads the VBoxStartup.log when present.
2871 """
2872 fRc = True;
2873 try:
2874 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
2875 except:
2876 reporter.logXcpt();
2877 fRc = False;
2878 else:
2879 if os.path.isfile(sLogFile):
2880 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
2881 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
2882 return fRc;
2883
2884 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
2885 """
2886 Annotates the given VM process report and uploads it if successfull.
2887 """
2888 fRc = False;
2889 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
2890 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
2891 self.getBuildOs(), self.getBuildArch(),
2892 fnLog = reporter.log);
2893 fRcTmp = oResolver.prepareEnv();
2894 if fRcTmp:
2895 reporter.log('Successfully prepared environment');
2896 sReportDbgSym = oResolver.annotateReport(sProcessReport);
2897 if sReportDbgSym is not None:
2898 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
2899 fRc = True;
2900 else:
2901 reporter.log('Annotating report failed');
2902 oResolver.cleanupEnv();
2903 return fRc;
2904
2905 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=too-many-locals,too-many-statements
2906 """
2907 Start the VM, returning the VM session and progress object on success.
2908 The session is also added to the task list and to the aoRemoteSessions set.
2909
2910 asEnv is a list of string on the putenv() form.
2911
2912 On failure (None, None) is returned and an error is logged.
2913 """
2914 # Massage and check the input.
2915 if sType is None:
2916 sType = self.sSessionType;
2917 if sName is None:
2918 try: sName = oVM.name;
2919 except: sName = 'bad-vm-handle';
2920 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
2921 if oVM is None:
2922 return (None, None);
2923
2924 ## @todo Do this elsewhere.
2925 # Hack alert. Disables all annoying GUI popups.
2926 if sType == 'gui' and not self.aoRemoteSessions:
2927 try:
2928 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
2929 if self.fpApiVer >= 3.2:
2930 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
2931 else:
2932 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
2933 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
2934 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
2935 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
2936 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
2937 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
2938 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
2939 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
2940 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
2941 except:
2942 reporter.logXcpt();
2943
2944 if self.fpApiVer >= 7.0:
2945 # Needed to reach the host (localhost) from the guest. See xTracker #9896.
2946 for iSlot in range(0, 32):
2947 try:
2948 oNic = oVM.getNetworkAdapter(iSlot);
2949 if not oNic.enabled:
2950 continue;
2951 sAdpName = self.getNetworkAdapterNameFromType(oNic);
2952 if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
2953 reporter.log2('Enabling "LocalhostReachable" (NAT) for network adapter "%s" in slot %d' % \
2954 (sAdpName, iSlot));
2955 oNatEngine = oNic.NATEngine;
2956 oNatEngine.LocalhostReachable = True;
2957 else:
2958 # Other attachments will fail if 'LocalhostReachable' extra data override is present
2959 ## @todo r=andy Is this still needed, as we now have the API (see above) in place?
2960 sKey = 'VBoxInternal/Devices/%s/%d/LUN#0/Config/LocalhostReachable' % (sAdpName, iSlot);
2961 reporter.log2('Disabling "LocalhostReachable" (NAT) for network adapter "%s" in slot %d (key: %s)' % \
2962 (sAdpName, iSlot, sKey));
2963 self.oVBox.setExtraData(sKey, '');
2964 except:
2965 pass;
2966
2967 # The UUID for the name.
2968 try:
2969 sUuid = oVM.id;
2970 except:
2971 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
2972 return (None, None);
2973 self.processPendingEvents();
2974
2975 # Construct the environment.
2976 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
2977 try: os.remove(sLogFile);
2978 except: pass;
2979 if self.sLogSessionDest:
2980 sLogDest = self.sLogSessionDest;
2981 else:
2982 sLogDest = 'file=%s' % (sLogFile,);
2983 asEnvFinal = [
2984 'VBOX_LOG=%s' % (self.sLogSessionGroups,),
2985 'VBOX_LOG_FLAGS=%s' % (self.sLogSessionFlags,),
2986 'VBOX_LOG_DEST=nodeny %s' % (sLogDest,),
2987 'VBOX_RELEASE_LOG_FLAGS=append time',
2988 ];
2989 if sType == 'gui':
2990 asEnvFinal.append('VBOX_GUI_DBG_ENABLED=1');
2991 if asEnv is not None and asEnv:
2992 asEnvFinal += asEnv;
2993
2994 # Shortcuts for local testing.
2995 oProgress = oWrapped = None;
2996 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
2997 try:
2998 if oTestVM is not None \
2999 and oTestVM.fSnapshotRestoreCurrent is True:
3000 if oVM.state is vboxcon.MachineState_Running:
3001 reporter.log2('Machine "%s" already running.' % (sName,));
3002 oProgress = None;
3003 oWrapped = self.openSession(oVM);
3004 else:
3005 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
3006 oSessionWrapperRestore = self.openSession(oVM);
3007 if oSessionWrapperRestore is not None:
3008 oSnapshotCur = oVM.currentSnapshot;
3009 if oSnapshotCur is not None:
3010 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
3011 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
3012 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
3013 else:
3014 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
3015 oSessionWrapperRestore.close();
3016 except:
3017 reporter.errorXcpt();
3018 return (None, None);
3019
3020 # Open a remote session, wait for this operation to complete.
3021 # (The loop is a kludge to deal with us racing the closing of the
3022 # direct session of a previous VM run. See waitOnDirectSessionClose.)
3023 if oWrapped is None:
3024 for i in range(10):
3025 try:
3026 if self.fpApiVer < 4.3 \
3027 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
3028 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3029 elif self.fpApiVer < 5.2 \
3030 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
3031 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=no-member
3032 else:
3033 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=no-member,no-value-for-parameter
3034 if self.fpApiVer < 3.3:
3035 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, '\n'.join(asEnvFinal));
3036 else:
3037 if self.uApiRevision >= self.makeApiRevision(6, 1, 0, 1):
3038 oProgress = oVM.launchVMProcess(oSession, sType, asEnvFinal);
3039 else:
3040 oProgress = oVM.launchVMProcess(oSession, sType, '\n'.join(asEnvFinal));
3041 break;
3042 except:
3043 if i == 9:
3044 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
3045 return (None, None);
3046 oSession = None;
3047 if i >= 0:
3048 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=line-too-long
3049 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
3050 if fWait and oProgress is not None:
3051 rc = self.waitOnProgress(oProgress);
3052 if rc < 0:
3053 self.waitOnDirectSessionClose(oVM, 5000);
3054
3055 # VM failed to power up, still collect VBox.log, need to wrap the session object
3056 # in order to use the helper for adding the log files to the report.
3057 from testdriver.vboxwrappers import SessionWrapper;
3058 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
3059 oTmp.addLogsToReport();
3060
3061 # Try to collect a stack trace of the process for fruther investigation of any startup hangs.
3062 uPid = oTmp.getPid();
3063 if uPid is not None:
3064 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3065 if sHostProcessInfoHung is not None:
3066 reporter.log('Trying to annotate the hung VM startup process report, please stand by...');
3067 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-startup-hung.log',
3068 'process/report/vm', 'Annotated hung VM process state during startup'); # pylint: disable=line-too-long
3069 # Upload the raw log for manual annotation in case resolving failed.
3070 if not fRcTmp:
3071 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3072 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-startup-hung.log', 'process/report/vm',
3073 'Hung VM process state during startup');
3074
3075 try:
3076 if oSession is not None:
3077 oSession.close();
3078 except: pass;
3079 reportError(oProgress, 'failed to open session for "%s"' % (sName));
3080 self.uploadStartupLogFile(oVM, sName);
3081 return (None, None);
3082 reporter.log2('waitOnProgress -> %s' % (rc,));
3083
3084 # Wrap up the session object and push on to the list before returning it.
3085 if oWrapped is None:
3086 from testdriver.vboxwrappers import SessionWrapper;
3087 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
3088
3089 oWrapped.registerEventHandlerForTask();
3090 self.aoRemoteSessions.append(oWrapped);
3091 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
3092 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
3093 % (oWrapped, len(self.aoRemoteSessions) - 1,
3094 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
3095 self.addTask(oWrapped);
3096
3097 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
3098
3099 from testdriver.vboxwrappers import ProgressWrapper;
3100 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
3101 'starting %s' % (sName,)) if oProgress else None);
3102
3103 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
3104 """ Simplified version of startVmEx. """
3105 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
3106 return oSession;
3107
3108 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
3109 """
3110 Start the VM, returning the VM session and progress object on success.
3111 The session is also added to the task list and to the aoRemoteSessions set.
3112
3113 On failure (None, None) is returned and an error is logged.
3114 """
3115 oVM = self.getVmByName(sName);
3116 if oVM is None:
3117 return (None, None);
3118 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
3119
3120 def startVmByName(self, sName, sType=None, asEnv = None):
3121 """
3122 Start the VM, returning the VM session on success. The session is
3123 also added to the task list and to the aoRemoteSessions set.
3124
3125 On failure None is returned and an error is logged.
3126 """
3127 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
3128 return oSession;
3129
3130 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=too-many-statements
3131 """
3132 Terminates the VM specified by oSession and adds the release logs to
3133 the test report.
3134
3135 This will try achieve this by using powerOff, but will resort to
3136 tougher methods if that fails.
3137
3138 The session will always be removed from the task list.
3139 The session will be closed unless we fail to kill the process.
3140 The session will be removed from the remote session list if closed.
3141
3142 The progress object (a wrapper!) is for teleportation and similar VM
3143 operations, it will be attempted canceled before powering off the VM.
3144 Failures are logged but ignored.
3145 The progress object will always be removed from the task list.
3146
3147 Returns True if powerOff and session close both succeed.
3148 Returns False if on failure (logged), including when we successfully
3149 kill the VM process.
3150 """
3151 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
3152
3153 # Call getPid first to make sure the PID is cached in the wrapper.
3154 oSession.getPid();
3155
3156 #
3157 # If the host is out of memory, just skip all the info collection as it
3158 # requires memory too and seems to wedge.
3159 #
3160 sHostProcessInfo = None;
3161 sHostProcessInfoHung = None;
3162 sLastScreenshotPath = None;
3163 sOsKernelLog = None;
3164 sVgaText = None;
3165 asMiscInfos = [];
3166
3167 if not oSession.fHostMemoryLow:
3168 # Try to fetch the VM process info before meddling with its state.
3169 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3170 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
3171
3172 #
3173 # Pause the VM if we're going to take any screenshots or dig into the
3174 # guest. Failures are quitely ignored.
3175 #
3176 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3177 try:
3178 if oSession.oVM.state in [ vboxcon.MachineState_Running,
3179 vboxcon.MachineState_LiveSnapshotting,
3180 vboxcon.MachineState_Teleporting ]:
3181 oSession.o.console.pause();
3182 except:
3183 reporter.logXcpt();
3184
3185 #
3186 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
3187 #
3188 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
3189 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
3190 fRc = oSession.takeScreenshot(sLastScreenshotPath);
3191 if fRc is not True:
3192 sLastScreenshotPath = None;
3193
3194 # Query the OS kernel log from the debugger if appropriate/requested.
3195 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3196 sOsKernelLog = oSession.queryOsKernelLog();
3197
3198 # Do "info vgatext all" separately.
3199 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3200 sVgaText = oSession.queryDbgInfoVgaText();
3201
3202 # Various infos (do after kernel because of symbols).
3203 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3204 # Dump the guest stack for all CPUs.
3205 cCpus = oSession.getCpuCount();
3206 if cCpus > 0:
3207 for iCpu in xrange(0, cCpus):
3208 sThis = oSession.queryDbgGuestStack(iCpu);
3209 if sThis:
3210 asMiscInfos += [
3211 '================ start guest stack VCPU %s ================\n' % (iCpu,),
3212 sThis,
3213 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
3214 ];
3215
3216 for sInfo, sArg in [ ('mode', 'all'),
3217 ('fflags', ''),
3218 ('cpumguest', 'verbose all'),
3219 ('cpumguestinstr', 'symbol all'),
3220 ('exits', ''),
3221 ('pic', ''),
3222 ('apic', ''),
3223 ('apiclvt', ''),
3224 ('apictimer', ''),
3225 ('ioapic', ''),
3226 ('pit', ''),
3227 ('phys', ''),
3228 ('clocks', ''),
3229 ('timers', ''),
3230 ('gdt', ''),
3231 ('ldt', ''),
3232 ]:
3233 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
3234 continue;
3235 sThis = oSession.queryDbgInfo(sInfo, sArg);
3236 if sThis:
3237 if sThis[-1] != '\n':
3238 sThis += '\n';
3239 asMiscInfos += [
3240 '================ start %s %s ================\n' % (sInfo, sArg),
3241 sThis,
3242 '================ end %s %s ==================\n' % (sInfo, sArg),
3243 ];
3244
3245 #
3246 # Terminate the VM
3247 #
3248
3249 # Cancel the progress object if specified.
3250 if oProgress is not None:
3251 if not oProgress.isCompleted() and oProgress.isCancelable():
3252 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
3253 try:
3254 oProgress.o.cancel();
3255 except:
3256 reporter.logXcpt();
3257 else:
3258 oProgress.wait();
3259 self.removeTask(oProgress);
3260
3261 # Check if the VM has terminated by itself before powering it off.
3262 fClose = True;
3263 fRc = True;
3264 if oSession.needsPoweringOff():
3265 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
3266 fRc = oSession.powerOff(fFudgeOnFailure = False);
3267 if fRc is not True:
3268 # power off failed, try terminate it in a nice manner.
3269 fRc = False;
3270 uPid = oSession.getPid();
3271 if uPid is not None:
3272 #
3273 # Collect some information about the VM process first to have
3274 # some state information for further investigation why powering off failed.
3275 #
3276 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3277
3278 # Exterminate...
3279 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
3280 fClose = base.processTerminate(uPid);
3281 if fClose is True:
3282 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3283 fClose = oSession.waitForTask(1000);
3284
3285 if fClose is not True:
3286 # Being nice failed...
3287 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
3288 % (uPid, oSession.sName));
3289 fClose = base.processKill(uPid);
3290 if fClose is True:
3291 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3292 fClose = oSession.waitForTask(1000);
3293 if fClose is not True:
3294 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
3295
3296 # The final steps.
3297 if fClose is True:
3298 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
3299 oSession.close();
3300 self.waitOnDirectSessionClose(oSession.oVM, 10000);
3301 try:
3302 eState = oSession.oVM.state;
3303 except:
3304 reporter.logXcpt();
3305 else:
3306 if eState == vboxcon.MachineState_Aborted:
3307 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
3308 self.removeTask(oSession);
3309
3310 #
3311 # Add the release log, debug log and a screenshot of the VM to the test report.
3312 #
3313 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3314 oSession.addLogsToReport();
3315
3316 # Add a screenshot if it has been requested and taken successfully.
3317 if sLastScreenshotPath is not None:
3318 if reporter.testErrorCount() > 0:
3319 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
3320 else:
3321 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
3322
3323 # Add the guest OS log if it has been requested and taken successfully.
3324 if sOsKernelLog is not None:
3325 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
3326
3327 # Add "info vgatext all" if we've got it.
3328 if sVgaText is not None:
3329 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
3330
3331 # Add the "info xxxx" items if we've got any.
3332 if asMiscInfos:
3333 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
3334
3335 # Add the host process info if we were able to retrieve it.
3336 if sHostProcessInfo is not None:
3337 reporter.log('Trying to annotate the VM process report, please stand by...');
3338 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
3339 'process/report/vm', 'Annotated VM process state');
3340 # Upload the raw log for manual annotation in case resolving failed.
3341 if not fRcTmp:
3342 reporter.log('Failed to annotate VM process report, uploading raw report');
3343 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
3344
3345 # Add the host process info for failed power off attempts if we were able to retrieve it.
3346 if sHostProcessInfoHung is not None:
3347 reporter.log('Trying to annotate the hung VM process report, please stand by...');
3348 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
3349 'process/report/vm', 'Annotated hung VM process state');
3350 # Upload the raw log for manual annotation in case resolving failed.
3351 if not fRcTmp:
3352 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3353 fRcTmp = reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
3354 'Hung VM process state');
3355 if not fRcTmp:
3356 try: reporter.log('******* START vmprocess-hung.log *******\n%s\n******* END vmprocess-hung.log *******\n'
3357 % (sHostProcessInfoHung,));
3358 except: pass; # paranoia
3359
3360
3361 return fRc;
3362
3363
3364 #
3365 # Some information query functions (mix).
3366 #
3367 # Methods require the VBox API. If the information is provided by both
3368 # the testboxscript as well as VBox API, we'll check if it matches.
3369 #
3370
3371 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
3372 """
3373 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
3374
3375 Returns True / False.
3376 Raises exception on environment / host mismatch.
3377 """
3378 fEnv = os.environ.get(sEnvVar, None);
3379 if fEnv is not None:
3380 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3381
3382 fVBox = None;
3383 self.importVBoxApi();
3384 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
3385 try:
3386 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
3387 except:
3388 if not fQuiet:
3389 reporter.logXcpt();
3390
3391 if fVBox is not None:
3392 if fEnv is not None:
3393 if fEnv != fVBox and not fQuiet:
3394 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
3395 % (fVBox, sEnum, fEnv, sEnvVar));
3396 return fEnv;
3397 return fVBox;
3398 if fEnv is not None:
3399 return fEnv;
3400 return False;
3401
3402 def hasHostHwVirt(self, fQuiet = False):
3403 """
3404 Checks if hardware assisted virtualization is supported by the host.
3405
3406 Returns True / False.
3407 Raises exception on environment / host mismatch.
3408 """
3409 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
3410
3411 def hasHostNestedPaging(self, fQuiet = False):
3412 """
3413 Checks if nested paging is supported by the host.
3414
3415 Returns True / False.
3416 Raises exception on environment / host mismatch.
3417 """
3418 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
3419 and self.hasHostHwVirt(fQuiet);
3420
3421 def hasHostNestedHwVirt(self, fQuiet = False):
3422 """
3423 Checks if nested hardware-assisted virtualization is supported by the host.
3424
3425 Returns True / False.
3426 Raises exception on environment / host mismatch.
3427 """
3428 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_HWVIRT', 'ProcessorFeature_NestedHWVirt', 6.0, fQuiet) \
3429 and self.hasHostHwVirt(fQuiet);
3430
3431 def hasHostLongMode(self, fQuiet = False):
3432 """
3433 Checks if the host supports 64-bit guests.
3434
3435 Returns True / False.
3436 Raises exception on environment / host mismatch.
3437 """
3438 # Note that the testboxscript doesn't export this variable atm.
3439 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
3440
3441 def getHostCpuCount(self, fQuiet = False):
3442 """
3443 Returns the number of CPUs on the host.
3444
3445 Returns True / False.
3446 Raises exception on environment / host mismatch.
3447 """
3448 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
3449 if cEnv is not None:
3450 cEnv = int(cEnv);
3451
3452 try:
3453 cVBox = self.oVBox.host.processorOnlineCount;
3454 except:
3455 if not fQuiet:
3456 reporter.logXcpt();
3457 cVBox = None;
3458
3459 if cVBox is not None:
3460 if cEnv is not None:
3461 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
3462 return cVBox;
3463 if cEnv is not None:
3464 return cEnv;
3465 return 1;
3466
3467 def _getHostCpuDesc(self, fQuiet = False):
3468 """
3469 Internal method used for getting the host CPU description from VBoxSVC.
3470 Returns description string, on failure an empty string is returned.
3471 """
3472 try:
3473 return self.oVBox.host.getProcessorDescription(0);
3474 except:
3475 if not fQuiet:
3476 reporter.logXcpt();
3477 return '';
3478
3479 def isHostCpuAmd(self, fQuiet = False):
3480 """
3481 Checks if the host CPU vendor is AMD.
3482
3483 Returns True / False.
3484 """
3485 sCpuDesc = self._getHostCpuDesc(fQuiet);
3486 return 'AMD' in sCpuDesc or sCpuDesc == 'AuthenticAMD';
3487
3488 def isHostCpuIntel(self, fQuiet = False):
3489 """
3490 Checks if the host CPU vendor is Intel.
3491
3492 Returns True / False.
3493 """
3494 sCpuDesc = self._getHostCpuDesc(fQuiet);
3495 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
3496
3497 def isHostCpuVia(self, fQuiet = False):
3498 """
3499 Checks if the host CPU vendor is VIA (or Centaur).
3500
3501 Returns True / False.
3502 """
3503 sCpuDesc = self._getHostCpuDesc(fQuiet);
3504 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
3505
3506 def isHostCpuShanghai(self, fQuiet = False):
3507 """
3508 Checks if the host CPU vendor is Shanghai (or Zhaoxin).
3509
3510 Returns True / False.
3511 """
3512 sCpuDesc = self._getHostCpuDesc(fQuiet);
3513 return sCpuDesc.startswith("ZHAOXIN") or sCpuDesc.strip(' ') == 'Shanghai';
3514
3515 def isHostCpuP4(self, fQuiet = False):
3516 """
3517 Checks if the host CPU is a Pentium 4 / Pentium D.
3518
3519 Returns True / False.
3520 """
3521 if not self.isHostCpuIntel(fQuiet):
3522 return False;
3523
3524 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
3525 return ((uFamilyModel >> 8) & 0xf) == 0xf;
3526
3527 def hasRawModeSupport(self, fQuiet = False):
3528 """
3529 Checks if raw-mode is supported by VirtualBox that the testbox is
3530 configured for it.
3531
3532 Returns True / False.
3533 Raises no exceptions.
3534
3535 Note! Differs from the rest in that we don't require the
3536 TESTBOX_WITH_RAW_MODE value to match the API. It is
3537 sometimes helpful to disable raw-mode on individual
3538 test boxes. (This probably goes for
3539 """
3540 # The environment variable can be used to disable raw-mode.
3541 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
3542 if fEnv is not None:
3543 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3544 if fEnv is False:
3545 return False;
3546
3547 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
3548 # with raw-mode support or not.
3549 self.importVBoxApi();
3550 if self.fpApiVer >= 5.0:
3551 try:
3552 fVBox = self.oVBox.systemProperties.rawModeSupported;
3553 except:
3554 if not fQuiet:
3555 reporter.logXcpt();
3556 fVBox = True;
3557 if fVBox is False:
3558 return False;
3559
3560 return True;
3561
3562 #
3563 # Testdriver execution methods.
3564 #
3565
3566 def handleTask(self, oTask, sMethod):
3567 """
3568 Callback method for handling unknown tasks in the various run loops.
3569
3570 The testdriver should override this if it already tasks running when
3571 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3572 Call super to handle unknown tasks.
3573
3574 Returns True if handled, False if not.
3575 """
3576 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3577 return False;
3578
3579 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3580 """
3581 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3582
3583 Returns False on error, logged.
3584 Returns task result on success.
3585 """
3586 # All async methods ends with the following two args.
3587 cMsTimeout = aArgs[-2];
3588 fIgnoreErrors = aArgs[-1];
3589
3590 fRemoveVm = self.addTask(oSession);
3591 fRemoveTxs = self.addTask(oTxsSession);
3592
3593 rc = fnAsync(*aArgs); # pylint: disable=star-args
3594 if rc is True:
3595 rc = False;
3596 oTask = self.waitForTasks(cMsTimeout + 1);
3597 if oTask is oTxsSession:
3598 if oTxsSession.isSuccess():
3599 rc = oTxsSession.getResult();
3600 elif fIgnoreErrors is True:
3601 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3602 else:
3603 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3604 else:
3605 oTxsSession.cancelTask();
3606 if oTask is None:
3607 if fIgnoreErrors is True:
3608 reporter.log( 'txsDoTask: The task timed out.');
3609 else:
3610 reporter.errorTimeout('txsDoTask: The task timed out.');
3611 elif oTask is oSession:
3612 reporter.error('txsDoTask: The VM terminated unexpectedly');
3613 else:
3614 if fIgnoreErrors is True:
3615 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
3616 else:
3617 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
3618 else:
3619 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
3620
3621 if fRemoveTxs:
3622 self.removeTask(oTxsSession);
3623 if fRemoveVm:
3624 self.removeTask(oSession);
3625 return rc;
3626
3627 # pylint: disable=missing-docstring
3628
3629 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3630 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3631 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3632
3633 def txsVer(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3634 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncVer,
3635 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3636
3637 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3638 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3639 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3640
3641 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3642 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3643 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3644
3645 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3646 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3647 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3648
3649 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3650 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3651 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3652
3653 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3654 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3655 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3656
3657 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3658 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3659 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3660
3661 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3662 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3663 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3664
3665 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
3666 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
3667 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3668
3669 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3670 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
3671 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3672
3673 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3674 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
3675 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3676
3677 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3678 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
3679 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3680
3681 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3682 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
3683 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3684
3685 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3686 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
3687 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3688
3689 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
3690 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
3691 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3692
3693 def txsDownloadFiles(self, oSession, oTxsSession, aasFiles, fAddToLog = True, fIgnoreErrors = False):
3694 """
3695 Convenience function to get files from the guest, storing them in the
3696 scratch and adding them to the test result set (optional, but default).
3697
3698 The aasFiles parameter contains an array of with guest-path + host-path
3699 pairs, optionally a file 'kind', description and an alternative upload
3700 filename can also be specified.
3701
3702 Host paths are relative to the scratch directory or they must be given
3703 in absolute form. The guest path should be using guest path style.
3704
3705 Returns True on success.
3706 Returns False on failure (unless fIgnoreErrors is set), logged.
3707 """
3708 for asEntry in aasFiles:
3709 # Unpack:
3710 sGstFile = asEntry[0];
3711 sHstFile = asEntry[1];
3712 sKind = asEntry[2] if len(asEntry) > 2 and asEntry[2] else 'misc/other';
3713 sDescription = asEntry[3] if len(asEntry) > 3 and asEntry[3] else '';
3714 sAltName = asEntry[4] if len(asEntry) > 4 and asEntry[4] else None;
3715 assert len(asEntry) <= 5 and sGstFile and sHstFile;
3716 if not os.path.isabs(sHstFile):
3717 sHstFile = os.path.join(self.sScratchPath, sHstFile);
3718
3719 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sHstFile,));
3720
3721 try: os.unlink(sHstFile); ## @todo txsDownloadFile doesn't truncate the output file.
3722 except: pass;
3723
3724 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sHstFile, 30 * 1000, fIgnoreErrors);
3725 if fRc:
3726 if fAddToLog:
3727 reporter.addLogFile(sHstFile, sKind, sDescription, sAltName);
3728 else:
3729 if fIgnoreErrors is not True:
3730 return reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sHstFile));
3731 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
3732 return True;
3733
3734 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
3735 cMsTimeout = 30000, fIgnoreErrors = False):
3736 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
3737 (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3738
3739 def txsPackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteSource, cMsTimeout = 30000, fIgnoreErrors = False):
3740 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncPackFile, \
3741 (sRemoteFile, sRemoteSource, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3742
3743 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3744 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
3745 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3746
3747 # pylint: enable=missing-docstring
3748
3749 def txsCdWait(self,
3750 oSession, # type: vboxwrappers.SessionWrapper
3751 oTxsSession, # type: txsclient.Session
3752 cMsTimeout = 30000, # type: int
3753 sFile = None # type: String
3754 ): # -> bool
3755 """
3756 Mostly an internal helper for txsRebootAndReconnectViaTcp and
3757 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
3758 ready. It does this by polling for a file it knows to exist on the CD.
3759
3760 Returns True on success.
3761
3762 Returns False on failure, logged.
3763 """
3764
3765 if sFile is None:
3766 sFile = 'valkit.txt';
3767
3768 reporter.log('txsCdWait: Waiting for file "%s" to become available ...' % (sFile,));
3769
3770 fRemoveVm = self.addTask(oSession);
3771 fRemoveTxs = self.addTask(oTxsSession);
3772 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3773 msStart = base.timestampMilli();
3774 cMsTimeout2 = cMsTimeout;
3775 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3776 if fRc is True:
3777 while True:
3778 # wait for it to complete.
3779 oTask = self.waitForTasks(cMsTimeout2 + 1);
3780 if oTask is not oTxsSession:
3781 oTxsSession.cancelTask();
3782 if oTask is None:
3783 reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
3784 % (base.timestampMilli() - msStart,));
3785 elif oTask is oSession:
3786 reporter.error('txsCdWait: The VM terminated unexpectedly');
3787 else:
3788 reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
3789 fRc = False;
3790 break;
3791 if oTxsSession.isSuccess():
3792 break;
3793
3794 # Check for timeout.
3795 cMsElapsed = base.timestampMilli() - msStart;
3796 if cMsElapsed >= cMsTimeout:
3797 reporter.error('txsCdWait: timed out');
3798 fRc = False;
3799 break;
3800 # delay.
3801 self.sleep(1);
3802
3803 # resubmit the task.
3804 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
3805 if cMsTimeout2 < 500:
3806 cMsTimeout2 = 500;
3807 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3808 if fRc is not True:
3809 reporter.error('txsCdWait: asyncIsFile failed');
3810 break;
3811 else:
3812 reporter.error('txsCdWait: asyncIsFile failed');
3813
3814 if not fRc:
3815 # Do some diagnosis to find out why this failed.
3816 ## @todo Identify guest OS type and only run one of the following commands.
3817 fIsNotWindows = True;
3818 reporter.log('txsCdWait: Listing root contents of ${CDROM}:');
3819 if fIsNotWindows:
3820 reporter.log('txsCdWait: Tiggering udevadm ...');
3821 oTxsSession.syncExec("/sbin/udevadm", ("/sbin/udevadm", "trigger", "--verbose"), fIgnoreErrors = True);
3822 time.sleep(15);
3823 oTxsSession.syncExec("/bin/ls", ("/bin/ls", "-al", "${CDROM}"), fIgnoreErrors = True);
3824 reporter.log('txsCdWait: Listing media directory:');
3825 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3826 reporter.log('txsCdWait: Listing mount points / drives:');
3827 oTxsSession.syncExec('/bin/mount', ('/bin/mount',), fIgnoreErrors = True);
3828 oTxsSession.syncExec('/bin/cat', ('/bin/cat', '/etc/fstab'), fIgnoreErrors = True);
3829 oTxsSession.syncExec('/bin/dmesg', ('/bin/dmesg',), fIgnoreErrors = True);
3830 oTxsSession.syncExec('/usr/bin/lshw', ('/usr/bin/lshw', '-c', 'disk'), fIgnoreErrors = True);
3831 oTxsSession.syncExec('/bin/journalctl',
3832 ('/bin/journalctl', '-x', '-b'), fIgnoreErrors = True);
3833 oTxsSession.syncExec('/bin/journalctl',
3834 ('/bin/journalctl', '-x', '-b', '/usr/lib/udisks2/udisksd'), fIgnoreErrors = True);
3835 oTxsSession.syncExec('/usr/bin/udisksctl',
3836 ('/usr/bin/udisksctl', 'info', '-b', '/dev/sr0'), fIgnoreErrors = True);
3837 oTxsSession.syncExec('/bin/systemctl',
3838 ('/bin/systemctl', 'status', 'udisks2'), fIgnoreErrors = True);
3839 oTxsSession.syncExec('/bin/ps',
3840 ('/bin/ps', '-a', '-u', '-x'), fIgnoreErrors = True);
3841 reporter.log('txsCdWait: Mounting manually ...');
3842 for _ in range(3):
3843 oTxsSession.syncExec('/bin/mount', ('/bin/mount', '/dev/sr0', '${CDROM}'), fIgnoreErrors = True);
3844 time.sleep(5);
3845 reporter.log('txsCdWait: Re-Listing media directory:');
3846 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3847 else:
3848 # ASSUMES that we always install Windows on drive C right now.
3849 sWinDir = "C:\\Windows\\System32\\";
3850 # Should work since WinXP Pro.
3851 oTxsSession.syncExec(sWinDir + "wbem\\WMIC.exe",
3852 ("WMIC.exe", "logicaldisk", "get",
3853 "deviceid, volumename, description"),
3854 fIgnoreErrors = True);
3855 oTxsSession.syncExec(sWinDir + " cmd.exe",
3856 ('cmd.exe', '/C', 'dir', '${CDROM}'),
3857 fIgnoreErrors = True);
3858
3859 if fRemoveTxs:
3860 self.removeTask(oTxsSession);
3861 if fRemoveVm:
3862 self.removeTask(oSession);
3863 return fRc;
3864
3865 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
3866 """
3867 Mostly an internal worker for connecting to TXS via TCP used by the
3868 *ViaTcp methods.
3869
3870 Returns a tuplet with True/False and TxsSession/None depending on the
3871 result. Errors are logged.
3872 """
3873
3874 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
3875 % (oSession, cMsTimeout, fNatForwardingForTxs));
3876
3877 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3878 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
3879 if oTxsConnect is not None:
3880 self.addTask(oTxsConnect);
3881 fRemoveVm = self.addTask(oSession);
3882 oTask = self.waitForTasks(cMsTimeout + 1);
3883 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
3884 self.removeTask(oTxsConnect);
3885 if oTask is oTxsConnect:
3886 oTxsSession = oTxsConnect.getResult();
3887 if oTxsSession is not None:
3888 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
3889 return (True, oTxsSession);
3890
3891 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
3892 else:
3893 oTxsConnect.cancelTask();
3894 if oTask is None:
3895 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
3896 elif oTask is oSession:
3897 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
3898 else:
3899 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
3900 if fRemoveVm:
3901 self.removeTask(oSession);
3902 else:
3903 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
3904 return (False, None);
3905
3906 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
3907 cMsCdWait = 30000, sFileCdWait = None, \
3908 fNatForwardingForTxs = False):
3909 """
3910 Starts the specified VM and tries to connect to its TXS via TCP.
3911 The VM will be powered off if TXS doesn't respond before the specified
3912 time has elapsed.
3913
3914 Returns a the VM and TXS sessions (a two tuple) on success. The VM
3915 session is in the task list, the TXS session is not.
3916 Returns (None, None) on failure, fully logged.
3917 """
3918
3919 # Zap the guest IP to make sure we're not getting a stale entry
3920 # (unless we're restoring the VM of course).
3921 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
3922 if oTestVM is None \
3923 or oTestVM.fSnapshotRestoreCurrent is False:
3924 try:
3925 oSession1 = self.openSession(self.getVmByName(sVmName));
3926 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
3927 oSession1.saveSettings(True);
3928 del oSession1;
3929 except:
3930 reporter.logXcpt();
3931
3932 # Start the VM.
3933 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
3934 reporter.flushall();
3935 oSession = self.startVmByName(sVmName);
3936 if oSession is not None:
3937 # Connect to TXS.
3938 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
3939 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
3940 if fRc is True:
3941 if fCdWait:
3942 # Wait for CD?
3943 reporter.log2('startVmAndConnectToTxsViaTcp: Waiting for file "%s" to become available ...' % (sFileCdWait,));
3944 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3945 if fRc is not True:
3946 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
3947
3948 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
3949 if sVer is not False:
3950 reporter.log('startVmAndConnectToTxsViaTcp: TestExecService version %s' % (sVer,));
3951 else:
3952 reporter.log('startVmAndConnectToTxsViaTcp: Unable to retrieve TestExecService version');
3953
3954 if fRc is True:
3955 # Success!
3956 return (oSession, oTxsSession);
3957 else:
3958 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
3959 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
3960 self.terminateVmBySession(oSession);
3961 return (None, None);
3962
3963 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
3964 cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
3965 """
3966 Executes the TXS reboot command
3967
3968 Returns A tuple of True and the new TXS session on success.
3969
3970 Returns A tuple of False and either the old TXS session or None on failure.
3971 """
3972 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
3973
3974 #
3975 # This stuff is a bit complicated because of rebooting being kind of
3976 # disruptive to the TXS and such... The protocol is that TXS will:
3977 # - ACK the reboot command.
3978 # - Shutdown the transport layer, implicitly disconnecting us.
3979 # - Execute the reboot operation.
3980 # - On failure, it will be re-init the transport layer and be
3981 # available pretty much immediately. UUID unchanged.
3982 # - On success, it will be respawed after the reboot (hopefully),
3983 # with a different UUID.
3984 #
3985 fRc = False;
3986 iStart = base.timestampMilli();
3987
3988 # Get UUID.
3989 cMsTimeout2 = min(60000, cMsTimeout);
3990 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
3991 if sUuidBefore is not False:
3992 # Reboot.
3993 cMsElapsed = base.timestampMilli() - iStart;
3994 cMsTimeout2 = cMsTimeout - cMsElapsed;
3995 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
3996 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3997 if fRc is True:
3998 # Reconnect.
3999 if fNatForwardingForTxs is True:
4000 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
4001 cMsElapsed = base.timestampMilli() - iStart;
4002 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
4003 if fRc is True:
4004 # Check the UUID.
4005 cMsElapsed = base.timestampMilli() - iStart;
4006 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
4007 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
4008 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
4009 if sUuidBefore is not False:
4010 if sUuidAfter != sUuidBefore:
4011 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
4012
4013 # Do CD wait if specified.
4014 if fCdWait:
4015 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
4016 if fRc is not True:
4017 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
4018
4019 sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
4020 if sVer is not False:
4021 reporter.log('txsRebootAndReconnectViaTcp: TestExecService version %s' % (sVer,));
4022 else:
4023 reporter.log('txsRebootAndReconnectViaTcp: Unable to retrieve TestExecService version');
4024 else:
4025 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
4026 else:
4027 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
4028 else:
4029 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
4030 else:
4031 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
4032 else:
4033 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
4034 return (fRc, oTxsSession);
4035
4036 # pylint: disable=too-many-locals,too-many-arguments
4037
4038 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4039 fCheckSessionStatus = False):
4040 """
4041 Executes the specified test task, waiting till it completes or times out.
4042
4043 The VM session (if any) must be in the task list.
4044
4045 Returns True if we executed the task and nothing abnormal happend.
4046 Query the process status from the TXS session.
4047
4048 Returns False if some unexpected task was signalled or we failed to
4049 submit the job.
4050
4051 If fCheckSessionStatus is set to True, the overall session status will be
4052 taken into account and logged as an error on failure.
4053 """
4054 reporter.testStart(sTestName);
4055 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4056
4057 # Submit the job.
4058 fRc = False;
4059 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4060 self.addTask(oTxsSession);
4061
4062 # Wait for the job to complete.
4063 while True:
4064 oTask = self.waitForTasks(cMsTimeout + 1);
4065 if oTask is None:
4066 if fCheckSessionStatus:
4067 reporter.error('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4068 else:
4069 reporter.log('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
4070 break;
4071 if oTask is oTxsSession:
4072 if fCheckSessionStatus \
4073 and not oTxsSession.isSuccess():
4074 reporter.error('txsRunTest: Test "%s" failed' % (sTestName,));
4075 else:
4076 fRc = True;
4077 reporter.log('txsRunTest: isSuccess=%s getResult=%s' \
4078 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4079 break;
4080 if not self.handleTask(oTask, 'txsRunTest'):
4081 break;
4082
4083 self.removeTask(oTxsSession);
4084 if not oTxsSession.pollTask():
4085 oTxsSession.cancelTask();
4086 else:
4087 reporter.error('txsRunTest: asyncExec failed');
4088
4089 reporter.testDone();
4090 return fRc;
4091
4092 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
4093 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
4094 """
4095 Executes the specified test task, waiting till it completes or times out,
4096 redirecting stdin, stdout and stderr to the given objects.
4097
4098 The VM session (if any) must be in the task list.
4099
4100 Returns True if we executed the task and nothing abnormal happend.
4101 Query the process status from the TXS session.
4102
4103 Returns False if some unexpected task was signalled or we failed to
4104 submit the job.
4105 """
4106 reporter.testStart(sTestName);
4107 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
4108
4109 # Submit the job.
4110 fRc = False;
4111 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
4112 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
4113 self.addTask(oTxsSession);
4114
4115 # Wait for the job to complete.
4116 while True:
4117 oTask = self.waitForTasks(cMsTimeout + 1);
4118 if oTask is None:
4119 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
4120 break;
4121 if oTask is oTxsSession:
4122 fRc = True;
4123 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
4124 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
4125 break;
4126 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
4127 break;
4128
4129 self.removeTask(oTxsSession);
4130 if not oTxsSession.pollTask():
4131 oTxsSession.cancelTask();
4132 else:
4133 reporter.error('txsRunTestRedirectStd: asyncExec failed');
4134
4135 reporter.testDone();
4136 return fRc;
4137
4138 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
4139 sExecName1, asArgs1,
4140 sExecName2, asArgs2,
4141 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
4142 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
4143 """
4144 Executes the specified test tasks, waiting till they complete or
4145 times out. The 1st task is started after the 2nd one.
4146
4147 The VM session (if any) must be in the task list.
4148
4149 Returns True if we executed the task and nothing abnormal happend.
4150 Query the process status from the TXS sessions.
4151
4152 Returns False if some unexpected task was signalled or we failed to
4153 submit the job.
4154 """
4155 reporter.testStart(sTestName);
4156
4157 # Submit the jobs.
4158 fRc = False;
4159 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
4160 self.adjustTimeoutMs(cMsTimeout)):
4161 self.addTask(oTxsSession1);
4162
4163 self.sleep(2); # fudge! grr
4164
4165 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
4166 self.adjustTimeoutMs(cMsTimeout)):
4167 self.addTask(oTxsSession2);
4168
4169 # Wait for the jobs to complete.
4170 cPendingJobs = 2;
4171 while True:
4172 oTask = self.waitForTasks(cMsTimeout + 1);
4173 if oTask is None:
4174 reporter.log('txsRunTest2: waitForTasks timed out');
4175 break;
4176
4177 if oTask is oTxsSession1 or oTask is oTxsSession2:
4178 if oTask is oTxsSession1: iTask = 1;
4179 else: iTask = 2;
4180 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
4181 % (iTask, oTask.isSuccess(), oTask.getResult()));
4182 self.removeTask(oTask);
4183 cPendingJobs -= 1;
4184 if cPendingJobs <= 0:
4185 fRc = True;
4186 break;
4187
4188 elif not self.handleTask(oTask, 'txsRunTest'):
4189 break;
4190
4191 self.removeTask(oTxsSession2);
4192 if not oTxsSession2.pollTask():
4193 oTxsSession2.cancelTask();
4194 else:
4195 reporter.error('txsRunTest2: asyncExec #2 failed');
4196
4197 self.removeTask(oTxsSession1);
4198 if not oTxsSession1.pollTask():
4199 oTxsSession1.cancelTask();
4200 else:
4201 reporter.error('txsRunTest2: asyncExec #1 failed');
4202
4203 reporter.testDone();
4204 return fRc;
4205
4206 # pylint: enable=too-many-locals,too-many-arguments
4207
4208
4209 #
4210 # Working with test results via serial port.
4211 #
4212
4213 class TxsMonitorComFile(base.TdTaskBase):
4214 """
4215 Class that monitors a COM output file.
4216 """
4217
4218 def __init__(self, sComRawFile, asStopWords = None):
4219 base.TdTaskBase.__init__(self, utils.getCallerName());
4220 self.sComRawFile = sComRawFile;
4221 self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
4222 self.sResult = None; ##< The result.
4223 self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
4224
4225 def toString(self):
4226 return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
4227 % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
4228
4229 def pollTask(self, fLocked = False):
4230 """
4231 Overrides TdTaskBase.pollTask() for the purpose of polling the file.
4232 """
4233 if not fLocked:
4234 self.lockTask();
4235
4236 sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
4237 if len(sFile) > self.cchDisplayed:
4238 sNew = sFile[self.cchDisplayed:];
4239 oMatch = self.oStopRegExp.search(sNew);
4240 if oMatch:
4241 # Done! Get result, flush all the output and signal the task.
4242 self.sResult = oMatch.group(1);
4243 for sLine in sNew.split('\n'):
4244 reporter.log('COM OUTPUT: %s' % (sLine,));
4245 self.cchDisplayed = len(sFile);
4246 self.signalTaskLocked();
4247 else:
4248 # Output whole lines only.
4249 offNewline = sFile.find('\n', self.cchDisplayed);
4250 while offNewline >= 0:
4251 reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
4252 self.cchDisplayed = offNewline + 1;
4253 offNewline = sFile.find('\n', self.cchDisplayed);
4254
4255 fRet = self.fSignalled;
4256 if not fLocked:
4257 self.unlockTask();
4258 return fRet;
4259
4260 # Our stuff.
4261 def getResult(self):
4262 """
4263 Returns the connected TXS session object on success.
4264 Returns None on failure or if the task has not yet completed.
4265 """
4266 self.oCv.acquire();
4267 sResult = self.sResult;
4268 self.oCv.release();
4269 return sResult;
4270
4271 def cancelTask(self):
4272 """ Cancels the task. """
4273 self.signalTask();
4274 return True;
4275
4276
4277 def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4278 """
4279 Monitors the COM output file for stop words (PASSED and FAILED by default).
4280
4281 Returns the stop word.
4282 Returns None on VM error and timeout.
4283 """
4284
4285 reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
4286
4287 oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
4288 self.addTask(oMonitorTask);
4289
4290 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4291 oTask = self.waitForTasks(cMsTimeout + 1);
4292 reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
4293
4294 if oTask is not oMonitorTask:
4295 oMonitorTask.cancelTask();
4296 self.removeTask(oMonitorTask);
4297
4298 oMonitorTask.pollTask();
4299 return oMonitorTask.getResult();
4300
4301
4302 def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4303 """
4304 Runs the specified VM and monitors the given COM output file for stop
4305 words (PASSED and FAILED by default).
4306
4307 The caller is assumed to have configured the VM to use the given
4308 file. The method will take no action to verify this.
4309
4310 Returns the stop word.
4311 Returns None on VM error and timeout.
4312 """
4313
4314 # Start the VM.
4315 reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4316 reporter.flushall();
4317 oSession = self.startVmByName(sVmName);
4318 if oSession is not None:
4319 # Let it run and then terminate it.
4320 sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
4321 self.terminateVmBySession(oSession);
4322 else:
4323 sRet = None;
4324 return sRet;
4325
4326 #
4327 # Other stuff
4328 #
4329
4330 def waitForGAs(self,
4331 oSession, # type: vboxwrappers.SessionWrapper
4332 cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
4333 """
4334 Waits for the guest additions to enter a certain state.
4335
4336 aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
4337 aenmWaitForActive - List facilities (type values) that must be active.
4338 aenmWaitForInactive - List facilities (type values) that must be inactive.
4339
4340 Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
4341
4342 Returns True on success, False w/ error logging on timeout or failure.
4343 """
4344 reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
4345
4346 #
4347 # Get IGuest:
4348 #
4349 try:
4350 oIGuest = oSession.o.console.guest;
4351 except:
4352 return reporter.errorXcpt();
4353
4354 #
4355 # Create a wait task:
4356 #
4357 from testdriver.vboxwrappers import AdditionsStatusTask;
4358 try:
4359 oGaStatusTask = AdditionsStatusTask(oSession = oSession,
4360 oIGuest = oIGuest,
4361 cMsTimeout = cMsTimeout,
4362 aenmWaitForRunLevels = aenmWaitForRunLevels,
4363 aenmWaitForActive = aenmWaitForActive,
4364 aenmWaitForInactive = aenmWaitForInactive);
4365 except:
4366 return reporter.errorXcpt();
4367
4368 #
4369 # Add the task and make sure the VM session is also present.
4370 #
4371 self.addTask(oGaStatusTask);
4372 fRemoveSession = self.addTask(oSession);
4373 oTask = self.waitForTasks(cMsTimeout + 1);
4374 reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
4375 self.removeTask(oGaStatusTask);
4376 if fRemoveSession:
4377 self.removeTask(oSession);
4378
4379 #
4380 # Digest the result.
4381 #
4382 if oTask is oGaStatusTask:
4383 fSucceeded = oGaStatusTask.getResult();
4384 if fSucceeded is True:
4385 reporter.log('waitForGAs: Succeeded.');
4386 else:
4387 reporter.error('waitForGAs: Failed.');
4388 else:
4389 oGaStatusTask.cancelTask();
4390 if oTask is None:
4391 reporter.error('waitForGAs: Timed out.');
4392 elif oTask is oSession:
4393 oSession.reportPrematureTermination('waitForGAs: ');
4394 else:
4395 reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
4396 fSucceeded = False;
4397 return fSucceeded;
4398
4399 @staticmethod
4400 def controllerTypeToName(eControllerType):
4401 """
4402 Translate a controller type to a standard controller name.
4403 """
4404 if eControllerType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
4405 sName = "IDE Controller";
4406 elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
4407 sName = "SATA Controller";
4408 elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
4409 sName = "SAS Controller";
4410 elif eControllerType in (vboxcon.StorageControllerType_LsiLogic, vboxcon.StorageControllerType_BusLogic,):
4411 sName = "SCSI Controller";
4412 elif eControllerType == vboxcon.StorageControllerType_NVMe:
4413 sName = "NVMe Controller";
4414 elif eControllerType == vboxcon.StorageControllerType_VirtioSCSI:
4415 sName = "VirtIO SCSI Controller";
4416 else:
4417 sName = "Storage Controller";
4418 return sName;
Note: See TracBrowser for help on using the repository browser.

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