VirtualBox

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

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

Validation Kit/testdriver: Also skip logging of OnCursorPositionChanged events; too noisy when locally debugging stuff.

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

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