VirtualBox

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

Last change on this file since 84316 was 84316, checked in by vboxsync, 5 years ago

Validation Kit/testdriver: More diagnosis for txsCdWait().

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

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