VirtualBox

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

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

ValKit/vbox.py: Adjusting version check for the IMachine::launchVMProcess change preps. bugref:9341

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