VirtualBox

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

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

Validation Kit/testdriver: Added special cases for handling OL in getGuestSystemDir() + getGuestSystemAdminDir() to avoid symlinks.

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