VirtualBox

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

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

Validation Kit/testdriver: Moved more path handling code to the testdriver base class to unify path handling more.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 185.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: vbox.py 84454 2020-05-22 12:04:17Z 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: 84454 $"
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 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x',]:
2602 return 'C:\\Winnt\\System32';
2603 return 'C:\\Windows\\System32';
2604 if oTestVm.isOS2():
2605 return 'C:\\OS2\\DLL';
2606 return sPathPrefix + "/bin";
2607
2608 @staticmethod
2609 def getGuestSystemAdminDir(oTestVm, sPathPrefix = ''):
2610 """
2611 Helper for finding a system admin directory ("sbin") in the test VM that we can play around with.
2612 sPathPrefix can be used to specify other directories, such as /usr/local/sbin/ or /usr/sbin, for instance.
2613
2614 On Windows this is always the System32 directory, so this function can be used as
2615 basis for locating other files in or under that directory.
2616 On UNIX-y systems this always is the "sh" shell to guarantee a common shell syntax.
2617 """
2618 if oTestVm.isWindows():
2619 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x',]:
2620 return 'C:\\Winnt\\System32';
2621 return 'C:\\Windows\\System32';
2622 if oTestVm.isOS2():
2623 return 'C:\\OS2\\DLL'; ## @todo r=andy Not sure here.
2624 return sPathPrefix + "/sbin";
2625
2626 @staticmethod
2627 def getGuestSystemShell(oTestVm):
2628 """
2629 Helper for finding the default system shell in the test VM.
2630 """
2631 if oTestVm.isWindows():
2632 return TestDriver.getGuestSystemDir(oTestVm) + '\\cmd.exe';
2633 if oTestVm.isOS2():
2634 return TestDriver.getGuestSystemDir(oTestVm) + '\\..\\CMD.EXE';
2635 return "/bin/sh";
2636
2637 @staticmethod
2638 def getGuestSystemFileForReading(oTestVm):
2639 """
2640 Helper for finding a file in the test VM that we can read.
2641 """
2642 if oTestVm.isWindows():
2643 return TestDriver.getGuestSystemDir(oTestVm) + '\\ntdll.dll';
2644 if oTestVm.isOS2():
2645 return TestDriver.getGuestSystemDir(oTestVm) + '\\DOSCALL1.DLL';
2646 return "/bin/sh";
2647
2648 def getVmByName(self, sName):
2649 """
2650 Get a test VM by name. Returns None if not found, logged.
2651 """
2652 # Look it up in our 'cache'.
2653 for oVM in self.aoVMs:
2654 try:
2655 #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
2656 if oVM.name == sName:
2657 return oVM;
2658 except:
2659 reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
2660
2661 # Look it up the standard way.
2662 return self.addTestMachine(sName, fQuiet = True);
2663
2664 def getVmByUuid(self, sUuid):
2665 """
2666 Get a test VM by uuid. Returns None if not found, logged.
2667 """
2668 # Look it up in our 'cache'.
2669 for oVM in self.aoVMs:
2670 try:
2671 if oVM.id == sUuid:
2672 return oVM;
2673 except:
2674 reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
2675
2676 # Look it up the standard way.
2677 return self.addTestMachine(sUuid, fQuiet = True);
2678
2679 def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
2680 """
2681 Waits for a progress object to complete. Returns the status code.
2682 """
2683 # Wait for progress no longer than cMsTimeout time period.
2684 tsStart = datetime.datetime.now()
2685 while True:
2686 self.processPendingEvents();
2687 try:
2688 if oProgress.completed:
2689 break;
2690 except:
2691 return -1;
2692 self.processPendingEvents();
2693
2694 tsNow = datetime.datetime.now()
2695 tsDelta = tsNow - tsStart
2696 if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
2697 if fErrorOnTimeout:
2698 reporter.errorTimeout('Timeout while waiting for progress.')
2699 return -1
2700
2701 reporter.doPollWork('vbox.TestDriver.waitOnProgress');
2702 try: oProgress.waitForCompletion(cMsInterval);
2703 except: return -2;
2704
2705 try: rc = oProgress.resultCode;
2706 except: rc = -2;
2707 self.processPendingEvents();
2708 return rc;
2709
2710 def waitOnDirectSessionClose(self, oVM, cMsTimeout):
2711 """
2712 Waits for the VM process to close it's current direct session.
2713
2714 Returns None.
2715 """
2716 # Get the original values so we're not subject to
2717 try:
2718 eCurState = oVM.sessionState;
2719 if self.fpApiVer >= 5.0:
2720 sCurName = sOrgName = oVM.sessionName;
2721 else:
2722 sCurName = sOrgName = oVM.sessionType;
2723 if self.fpApiVer >= 4.2:
2724 iCurPid = iOrgPid = oVM.sessionPID;
2725 else:
2726 iCurPid = iOrgPid = oVM.sessionPid;
2727 except Exception as oXcpt:
2728 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2729 reporter.logXcpt();
2730 self.processPendingEvents();
2731 return None;
2732 self.processPendingEvents();
2733
2734 msStart = base.timestampMilli();
2735 while iCurPid == iOrgPid \
2736 and sCurName == sOrgName \
2737 and sCurName != '' \
2738 and base.timestampMilli() - msStart < cMsTimeout \
2739 and eCurState in (vboxcon.SessionState_Unlocking, vboxcon.SessionState_Spawning, vboxcon.SessionState_Locked,):
2740 self.processEvents(1000);
2741 try:
2742 eCurState = oVM.sessionState;
2743 sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
2744 iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
2745 except Exception as oXcpt:
2746 if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
2747 reporter.logXcpt();
2748 break;
2749 self.processPendingEvents();
2750 self.processPendingEvents();
2751 return None;
2752
2753 def uploadStartupLogFile(self, oVM, sVmName):
2754 """
2755 Uploads the VBoxStartup.log when present.
2756 """
2757 fRc = True;
2758 try:
2759 sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
2760 except:
2761 reporter.logXcpt();
2762 fRc = False;
2763 else:
2764 if os.path.isfile(sLogFile):
2765 reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
2766 sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
2767 return fRc;
2768
2769 def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
2770 """
2771 Annotates the given VM process report and uploads it if successfull.
2772 """
2773 fRc = False;
2774 if self.oBuild is not None and self.oBuild.sInstallPath is not None:
2775 oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
2776 self.getBuildOs(), self.getBuildArch(),
2777 fnLog = reporter.log);
2778 fRcTmp = oResolver.prepareEnv();
2779 if fRcTmp:
2780 reporter.log('Successfully prepared environment');
2781 sReportDbgSym = oResolver.annotateReport(sProcessReport);
2782 if sReportDbgSym is not None:
2783 reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
2784 fRc = True;
2785 else:
2786 reporter.log('Annotating report failed');
2787 oResolver.cleanupEnv();
2788 return fRc;
2789
2790 def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=too-many-locals,too-many-statements
2791 """
2792 Start the VM, returning the VM session and progress object on success.
2793 The session is also added to the task list and to the aoRemoteSessions set.
2794
2795 asEnv is a list of string on the putenv() form.
2796
2797 On failure (None, None) is returned and an error is logged.
2798 """
2799 # Massage and check the input.
2800 if sType is None:
2801 sType = self.sSessionType;
2802 if sName is None:
2803 try: sName = oVM.name;
2804 except: sName = 'bad-vm-handle';
2805 reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
2806 if oVM is None:
2807 return (None, None);
2808
2809 ## @todo Do this elsewhere.
2810 # Hack alert. Disables all annoying GUI popups.
2811 if sType == 'gui' and not self.aoRemoteSessions:
2812 try:
2813 self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
2814 if self.fpApiVer >= 3.2:
2815 self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
2816 else:
2817 self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
2818 self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
2819 self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
2820 self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
2821 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
2822 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
2823 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
2824 self.oVBox.setExtraData('GUI/UpdateDate', 'never');
2825 self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
2826 except:
2827 reporter.logXcpt();
2828
2829 # The UUID for the name.
2830 try:
2831 sUuid = oVM.id;
2832 except:
2833 reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
2834 return (None, None);
2835 self.processPendingEvents();
2836
2837 # Construct the environment.
2838 sLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
2839 try: os.remove(sLogFile);
2840 except: pass;
2841 if self.sLogSessionDest:
2842 sLogDest = self.sLogSessionDest;
2843 else:
2844 sLogDest = 'file=%s' % (sLogFile,);
2845 asEnvFinal = [
2846 'VBOX_LOG=%s' % (self.sLogSessionGroups,),
2847 'VBOX_LOG_FLAGS=%s' % (self.sLogSessionFlags,),
2848 'VBOX_LOG_DEST=nodeny %s' % (sLogDest,),
2849 'VBOX_RELEASE_LOG_FLAGS=append time',
2850 ];
2851 if sType == 'gui':
2852 asEnvFinal.append('VBOX_GUI_DBG_ENABLED=1');
2853 if asEnv is not None and asEnv:
2854 asEnvFinal += asEnv;
2855
2856 # Shortcuts for local testing.
2857 oProgress = oWrapped = None;
2858 oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
2859 try:
2860 if oTestVM is not None \
2861 and oTestVM.fSnapshotRestoreCurrent is True:
2862 if oVM.state is vboxcon.MachineState_Running:
2863 reporter.log2('Machine "%s" already running.' % (sName,));
2864 oProgress = None;
2865 oWrapped = self.openSession(oVM);
2866 else:
2867 reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
2868 oSessionWrapperRestore = self.openSession(oVM);
2869 if oSessionWrapperRestore is not None:
2870 oSnapshotCur = oVM.currentSnapshot;
2871 if oSnapshotCur is not None:
2872 reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
2873 oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
2874 reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
2875 else:
2876 reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
2877 oSessionWrapperRestore.close();
2878 except:
2879 reporter.errorXcpt();
2880 return (None, None);
2881
2882 # Open a remote session, wait for this operation to complete.
2883 # (The loop is a kludge to deal with us racing the closing of the
2884 # direct session of a previous VM run. See waitOnDirectSessionClose.)
2885 if oWrapped is None:
2886 for i in range(10):
2887 try:
2888 if self.fpApiVer < 4.3 \
2889 or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
2890 oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=no-member
2891 elif self.fpApiVer < 5.2 \
2892 or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
2893 oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=no-member
2894 else:
2895 oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=no-member,no-value-for-parameter
2896 if self.fpApiVer < 3.3:
2897 oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, '\n'.join(asEnvFinal));
2898 else:
2899 if self.uApiRevision >= self.makeApiRevision(6, 1, 0, 1):
2900 oProgress = oVM.launchVMProcess(oSession, sType, asEnvFinal);
2901 else:
2902 oProgress = oVM.launchVMProcess(oSession, sType, '\n'.join(asEnvFinal));
2903 break;
2904 except:
2905 if i == 9:
2906 reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
2907 return (None, None);
2908 oSession = None;
2909 if i >= 0:
2910 reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=line-too-long
2911 self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
2912 if fWait and oProgress is not None:
2913 rc = self.waitOnProgress(oProgress);
2914 if rc < 0:
2915 self.waitOnDirectSessionClose(oVM, 5000);
2916
2917 # VM failed to power up, still collect VBox.log, need to wrap the session object
2918 # in order to use the helper for adding the log files to the report.
2919 from testdriver.vboxwrappers import SessionWrapper;
2920 oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2921 oTmp.addLogsToReport();
2922 try:
2923 if oSession is not None:
2924 oSession.close();
2925 except: pass;
2926 reportError(oProgress, 'failed to open session for "%s"' % (sName));
2927 self.uploadStartupLogFile(oVM, sName);
2928 return (None, None);
2929 reporter.log2('waitOnProgress -> %s' % (rc,));
2930
2931 # Wrap up the session object and push on to the list before returning it.
2932 if oWrapped is None:
2933 from testdriver.vboxwrappers import SessionWrapper;
2934 oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, sLogFile);
2935
2936 oWrapped.registerEventHandlerForTask();
2937 self.aoRemoteSessions.append(oWrapped);
2938 if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
2939 reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
2940 % (oWrapped, len(self.aoRemoteSessions) - 1,
2941 self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
2942 self.addTask(oWrapped);
2943
2944 reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
2945
2946 from testdriver.vboxwrappers import ProgressWrapper;
2947 return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
2948 'starting %s' % (sName,)) if oProgress else None);
2949
2950 def startVm(self, oVM, sType=None, sName = None, asEnv = None):
2951 """ Simplified version of startVmEx. """
2952 oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
2953 return oSession;
2954
2955 def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
2956 """
2957 Start the VM, returning the VM session and progress object on success.
2958 The session is also added to the task list and to the aoRemoteSessions set.
2959
2960 On failure (None, None) is returned and an error is logged.
2961 """
2962 oVM = self.getVmByName(sName);
2963 if oVM is None:
2964 return (None, None);
2965 return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
2966
2967 def startVmByName(self, sName, sType=None, asEnv = None):
2968 """
2969 Start the VM, returning the VM session on success. The session is
2970 also added to the task list and to the aoRemoteSessions set.
2971
2972 On failure None is returned and an error is logged.
2973 """
2974 oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
2975 return oSession;
2976
2977 def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=too-many-statements
2978 """
2979 Terminates the VM specified by oSession and adds the release logs to
2980 the test report.
2981
2982 This will try achieve this by using powerOff, but will resort to
2983 tougher methods if that fails.
2984
2985 The session will always be removed from the task list.
2986 The session will be closed unless we fail to kill the process.
2987 The session will be removed from the remote session list if closed.
2988
2989 The progress object (a wrapper!) is for teleportation and similar VM
2990 operations, it will be attempted canceled before powering off the VM.
2991 Failures are logged but ignored.
2992 The progress object will always be removed from the task list.
2993
2994 Returns True if powerOff and session close both succeed.
2995 Returns False if on failure (logged), including when we successfully
2996 kill the VM process.
2997 """
2998 reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
2999
3000 # Call getPid first to make sure the PID is cached in the wrapper.
3001 oSession.getPid();
3002
3003 #
3004 # If the host is out of memory, just skip all the info collection as it
3005 # requires memory too and seems to wedge.
3006 #
3007 sHostProcessInfo = None;
3008 sHostProcessInfoHung = None;
3009 sLastScreenshotPath = None;
3010 sOsKernelLog = None;
3011 sVgaText = None;
3012 asMiscInfos = [];
3013
3014 if not oSession.fHostMemoryLow:
3015 # Try to fetch the VM process info before meddling with its state.
3016 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3017 sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
3018
3019 #
3020 # Pause the VM if we're going to take any screenshots or dig into the
3021 # guest. Failures are quitely ignored.
3022 #
3023 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3024 try:
3025 if oSession.oVM.state in [ vboxcon.MachineState_Running,
3026 vboxcon.MachineState_LiveSnapshotting,
3027 vboxcon.MachineState_Teleporting ]:
3028 oSession.o.console.pause();
3029 except:
3030 reporter.logXcpt();
3031
3032 #
3033 # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
3034 #
3035 if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
3036 sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
3037 fRc = oSession.takeScreenshot(sLastScreenshotPath);
3038 if fRc is not True:
3039 sLastScreenshotPath = None;
3040
3041 # Query the OS kernel log from the debugger if appropriate/requested.
3042 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3043 sOsKernelLog = oSession.queryOsKernelLog();
3044
3045 # Do "info vgatext all" separately.
3046 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3047 sVgaText = oSession.queryDbgInfoVgaText();
3048
3049 # Various infos (do after kernel because of symbols).
3050 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3051 # Dump the guest stack for all CPUs.
3052 cCpus = oSession.getCpuCount();
3053 if cCpus > 0:
3054 for iCpu in xrange(0, cCpus):
3055 sThis = oSession.queryDbgGuestStack(iCpu);
3056 if sThis:
3057 asMiscInfos += [
3058 '================ start guest stack VCPU %s ================\n' % (iCpu,),
3059 sThis,
3060 '================ end guest stack VCPU %s ==================\n' % (iCpu,),
3061 ];
3062
3063 for sInfo, sArg in [ ('mode', 'all'),
3064 ('fflags', ''),
3065 ('cpumguest', 'verbose all'),
3066 ('cpumguestinstr', 'symbol all'),
3067 ('exits', ''),
3068 ('pic', ''),
3069 ('apic', ''),
3070 ('apiclvt', ''),
3071 ('apictimer', ''),
3072 ('ioapic', ''),
3073 ('pit', ''),
3074 ('phys', ''),
3075 ('clocks', ''),
3076 ('timers', ''),
3077 ('gdtguest', ''),
3078 ('ldtguest', ''),
3079 ]:
3080 if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
3081 continue;
3082 sThis = oSession.queryDbgInfo(sInfo, sArg);
3083 if sThis:
3084 if sThis[-1] != '\n':
3085 sThis += '\n';
3086 asMiscInfos += [
3087 '================ start %s %s ================\n' % (sInfo, sArg),
3088 sThis,
3089 '================ end %s %s ==================\n' % (sInfo, sArg),
3090 ];
3091
3092 #
3093 # Terminate the VM
3094 #
3095
3096 # Cancel the progress object if specified.
3097 if oProgress is not None:
3098 if not oProgress.isCompleted() and oProgress.isCancelable():
3099 reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
3100 try:
3101 oProgress.o.cancel();
3102 except:
3103 reporter.logXcpt();
3104 else:
3105 oProgress.wait();
3106 self.removeTask(oProgress);
3107
3108 # Check if the VM has terminated by itself before powering it off.
3109 fClose = True;
3110 fRc = True;
3111 if oSession.needsPoweringOff():
3112 reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
3113 fRc = oSession.powerOff(fFudgeOnFailure = False);
3114 if fRc is not True:
3115 # power off failed, try terminate it in a nice manner.
3116 fRc = False;
3117 uPid = oSession.getPid();
3118 if uPid is not None:
3119 #
3120 # Collect some information about the VM process first to have
3121 # some state information for further investigation why powering off failed.
3122 #
3123 sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
3124
3125 # Exterminate...
3126 reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
3127 fClose = base.processTerminate(uPid);
3128 if fClose is True:
3129 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3130 fClose = oSession.waitForTask(1000);
3131
3132 if fClose is not True:
3133 # Being nice failed...
3134 reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
3135 % (uPid, oSession.sName));
3136 fClose = base.processKill(uPid);
3137 if fClose is True:
3138 self.waitOnDirectSessionClose(oSession.oVM, 5000);
3139 fClose = oSession.waitForTask(1000);
3140 if fClose is not True:
3141 reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
3142
3143 # The final steps.
3144 if fClose is True:
3145 reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
3146 oSession.close();
3147 self.waitOnDirectSessionClose(oSession.oVM, 10000);
3148 try:
3149 eState = oSession.oVM.state;
3150 except:
3151 reporter.logXcpt();
3152 else:
3153 if eState == vboxcon.MachineState_Aborted:
3154 reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
3155 self.removeTask(oSession);
3156
3157 #
3158 # Add the release log, debug log and a screenshot of the VM to the test report.
3159 #
3160 if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
3161 oSession.addLogsToReport();
3162
3163 # Add a screenshot if it has been requested and taken successfully.
3164 if sLastScreenshotPath is not None:
3165 if reporter.testErrorCount() > 0:
3166 reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
3167 else:
3168 reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
3169
3170 # Add the guest OS log if it has been requested and taken successfully.
3171 if sOsKernelLog is not None:
3172 reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
3173
3174 # Add "info vgatext all" if we've got it.
3175 if sVgaText is not None:
3176 reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
3177
3178 # Add the "info xxxx" items if we've got any.
3179 if asMiscInfos:
3180 reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
3181
3182 # Add the host process info if we were able to retrieve it.
3183 if sHostProcessInfo is not None:
3184 reporter.log('Trying to annotate the VM process report, please stand by...');
3185 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
3186 'process/report/vm', 'Annotated VM process state');
3187 # Upload the raw log for manual annotation in case resolving failed.
3188 if not fRcTmp:
3189 reporter.log('Failed to annotate VM process report, uploading raw report');
3190 reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
3191
3192 # Add the host process info for failed power off attempts if we were able to retrieve it.
3193 if sHostProcessInfoHung is not None:
3194 reporter.log('Trying to annotate the hung VM process report, please stand by...');
3195 fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
3196 'process/report/vm', 'Annotated hung VM process state');
3197 # Upload the raw log for manual annotation in case resolving failed.
3198 if not fRcTmp:
3199 reporter.log('Failed to annotate hung VM process report, uploading raw report');
3200 reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
3201 'Hung VM process state');
3202
3203 return fRc;
3204
3205
3206 #
3207 # Some information query functions (mix).
3208 #
3209 # Methods require the VBox API. If the information is provided by both
3210 # the testboxscript as well as VBox API, we'll check if it matches.
3211 #
3212
3213 def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
3214 """
3215 Common Worker for hasHostNestedPaging() and hasHostHwVirt().
3216
3217 Returns True / False.
3218 Raises exception on environment / host mismatch.
3219 """
3220 fEnv = os.environ.get(sEnvVar, None);
3221 if fEnv is not None:
3222 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3223
3224 fVBox = None;
3225 self.importVBoxApi();
3226 if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
3227 try:
3228 fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
3229 except:
3230 if not fQuiet:
3231 reporter.logXcpt();
3232
3233 if fVBox is not None:
3234 if fEnv is not None:
3235 if fEnv != fVBox and not fQuiet:
3236 reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
3237 % (fVBox, sEnum, fEnv, sEnvVar));
3238 return fEnv;
3239 return fVBox;
3240 if fEnv is not None:
3241 return fEnv;
3242 return False;
3243
3244 def hasHostHwVirt(self, fQuiet = False):
3245 """
3246 Checks if hardware assisted virtualization is supported by the host.
3247
3248 Returns True / False.
3249 Raises exception on environment / host mismatch.
3250 """
3251 return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
3252
3253 def hasHostNestedPaging(self, fQuiet = False):
3254 """
3255 Checks if nested paging is supported by the host.
3256
3257 Returns True / False.
3258 Raises exception on environment / host mismatch.
3259 """
3260 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
3261 and self.hasHostHwVirt(fQuiet);
3262
3263 def hasHostNestedHwVirt(self, fQuiet = False):
3264 """
3265 Checks if nested hardware-assisted virtualization is supported by the host.
3266
3267 Returns True / False.
3268 Raises exception on environment / host mismatch.
3269 """
3270 return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_HWVIRT', 'ProcessorFeature_NestedHWVirt', 6.0, fQuiet) \
3271 and self.hasHostHwVirt(fQuiet);
3272
3273 def hasHostLongMode(self, fQuiet = False):
3274 """
3275 Checks if the host supports 64-bit guests.
3276
3277 Returns True / False.
3278 Raises exception on environment / host mismatch.
3279 """
3280 # Note that the testboxscript doesn't export this variable atm.
3281 return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
3282
3283 def getHostCpuCount(self, fQuiet = False):
3284 """
3285 Returns the number of CPUs on the host.
3286
3287 Returns True / False.
3288 Raises exception on environment / host mismatch.
3289 """
3290 cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
3291 if cEnv is not None:
3292 cEnv = int(cEnv);
3293
3294 try:
3295 cVBox = self.oVBox.host.processorOnlineCount;
3296 except:
3297 if not fQuiet:
3298 reporter.logXcpt();
3299 cVBox = None;
3300
3301 if cVBox is not None:
3302 if cEnv is not None:
3303 assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
3304 return cVBox;
3305 if cEnv is not None:
3306 return cEnv;
3307 return 1;
3308
3309 def _getHostCpuDesc(self, fQuiet = False):
3310 """
3311 Internal method used for getting the host CPU description from VBoxSVC.
3312 Returns description string, on failure an empty string is returned.
3313 """
3314 try:
3315 return self.oVBox.host.getProcessorDescription(0);
3316 except:
3317 if not fQuiet:
3318 reporter.logXcpt();
3319 return '';
3320
3321 def isHostCpuAmd(self, fQuiet = False):
3322 """
3323 Checks if the host CPU vendor is AMD.
3324
3325 Returns True / False.
3326 """
3327 sCpuDesc = self._getHostCpuDesc(fQuiet);
3328 return 'AMD' in sCpuDesc or sCpuDesc == 'AuthenticAMD';
3329
3330 def isHostCpuIntel(self, fQuiet = False):
3331 """
3332 Checks if the host CPU vendor is Intel.
3333
3334 Returns True / False.
3335 """
3336 sCpuDesc = self._getHostCpuDesc(fQuiet);
3337 return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
3338
3339 def isHostCpuVia(self, fQuiet = False):
3340 """
3341 Checks if the host CPU vendor is VIA (or Centaur).
3342
3343 Returns True / False.
3344 """
3345 sCpuDesc = self._getHostCpuDesc(fQuiet);
3346 return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
3347
3348 def isHostCpuShanghai(self, fQuiet = False):
3349 """
3350 Checks if the host CPU vendor is Shanghai (or Zhaoxin).
3351
3352 Returns True / False.
3353 """
3354 sCpuDesc = self._getHostCpuDesc(fQuiet);
3355 return sCpuDesc.startswith("ZHAOXIN") or sCpuDesc.strip(' ') == 'Shanghai';
3356
3357 def isHostCpuP4(self, fQuiet = False):
3358 """
3359 Checks if the host CPU is a Pentium 4 / Pentium D.
3360
3361 Returns True / False.
3362 """
3363 if not self.isHostCpuIntel(fQuiet):
3364 return False;
3365
3366 (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
3367 return ((uFamilyModel >> 8) & 0xf) == 0xf;
3368
3369 def hasRawModeSupport(self, fQuiet = False):
3370 """
3371 Checks if raw-mode is supported by VirtualBox that the testbox is
3372 configured for it.
3373
3374 Returns True / False.
3375 Raises no exceptions.
3376
3377 Note! Differs from the rest in that we don't require the
3378 TESTBOX_WITH_RAW_MODE value to match the API. It is
3379 sometimes helpful to disable raw-mode on individual
3380 test boxes. (This probably goes for
3381 """
3382 # The environment variable can be used to disable raw-mode.
3383 fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
3384 if fEnv is not None:
3385 fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
3386 if fEnv is False:
3387 return False;
3388
3389 # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
3390 # with raw-mode support or not.
3391 self.importVBoxApi();
3392 if self.fpApiVer >= 5.0:
3393 try:
3394 fVBox = self.oVBox.systemProperties.rawModeSupported;
3395 except:
3396 if not fQuiet:
3397 reporter.logXcpt();
3398 fVBox = True;
3399 if fVBox is False:
3400 return False;
3401
3402 return True;
3403
3404 #
3405 # Testdriver execution methods.
3406 #
3407
3408 def handleTask(self, oTask, sMethod):
3409 """
3410 Callback method for handling unknown tasks in the various run loops.
3411
3412 The testdriver should override this if it already tasks running when
3413 calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
3414 Call super to handle unknown tasks.
3415
3416 Returns True if handled, False if not.
3417 """
3418 reporter.error('%s: unknown task %s' % (sMethod, oTask));
3419 return False;
3420
3421 def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
3422 """
3423 Generic TXS task wrapper which waits both on the TXS and the session tasks.
3424
3425 Returns False on error, logged.
3426
3427 Returns task result on success.
3428 """
3429 # All async methods ends with the following two args.
3430 cMsTimeout = aArgs[-2];
3431 fIgnoreErrors = aArgs[-1];
3432
3433 fRemoveVm = self.addTask(oSession);
3434 fRemoveTxs = self.addTask(oTxsSession);
3435
3436 rc = fnAsync(*aArgs); # pylint: disable=star-args
3437 if rc is True:
3438 rc = False;
3439 oTask = self.waitForTasks(cMsTimeout + 1);
3440 if oTask is oTxsSession:
3441 if oTxsSession.isSuccess():
3442 rc = oTxsSession.getResult();
3443 elif fIgnoreErrors is True:
3444 reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3445 else:
3446 reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
3447 else:
3448 oTxsSession.cancelTask();
3449 if oTask is None:
3450 if fIgnoreErrors is True:
3451 reporter.log( 'txsDoTask: The task timed out.');
3452 else:
3453 reporter.errorTimeout('txsDoTask: The task timed out.');
3454 elif oTask is oSession:
3455 reporter.error('txsDoTask: The VM terminated unexpectedly');
3456 else:
3457 if fIgnoreErrors is True:
3458 reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
3459 else:
3460 reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
3461 else:
3462 reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
3463
3464 if fRemoveTxs:
3465 self.removeTask(oTxsSession);
3466 if fRemoveVm:
3467 self.removeTask(oSession);
3468 return rc;
3469
3470 # pylint: disable=missing-docstring
3471
3472 def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3473 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
3474 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3475
3476 def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
3477 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3478 (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3479
3480 def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3481 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
3482 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3483
3484 def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
3485 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
3486 (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3487
3488 def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
3489 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
3490 (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3491
3492 def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3493 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
3494 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3495
3496 def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3497 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
3498 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3499
3500 def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3501 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
3502 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3503
3504 def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
3505 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
3506 (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3507
3508 def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3509 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
3510 (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3511
3512 def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3513 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
3514 (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3515
3516 def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
3517 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
3518 (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3519
3520 def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3521 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
3522 (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3523
3524 def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
3525 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
3526 (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3527
3528 def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
3529 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
3530 (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3531
3532 def txsDownloadFiles(self, oSession, oTxsSession, asFiles, fIgnoreErrors = False):
3533 """
3534 Convenience function to get files from the guest and stores it
3535 into the scratch directory for later (manual) review.
3536
3537 Returns True on success.
3538
3539 Returns False on failure, logged.
3540 """
3541 fRc = True;
3542 for sGstFile in asFiles:
3543 sTmpFile = os.path.join(self.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
3544 reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
3545 # First try to remove (unlink) an existing temporary file, as we don't truncate the file.
3546 try: os.unlink(sTmpFile);
3547 except: pass;
3548 ## @todo Check for already existing files on the host and create a new
3549 # name for the current file to download.
3550 fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sTmpFile, 30 * 1000, fIgnoreErrors);
3551 if fRc:
3552 reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
3553 else:
3554 if fIgnoreErrors is not True:
3555 reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
3556 return fRc;
3557 reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
3558 return True;
3559
3560 def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
3561 cMsTimeout = 30000, fIgnoreErrors = False):
3562 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
3563 (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3564
3565 def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
3566 return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
3567 (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
3568
3569 # pylint: enable=missing-docstring
3570
3571 def txsCdWait(self,
3572 oSession, # type: vboxwrappers.SessionWrapper
3573 oTxsSession, # type: txsclient.Session
3574 cMsTimeout = 30000, # type: int
3575 sFile = None # type: String
3576 ): # -> bool
3577 """
3578 Mostly an internal helper for txsRebootAndReconnectViaTcp and
3579 startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
3580 ready. It does this by polling for a file it knows to exist on the CD.
3581
3582 Returns True on success.
3583
3584 Returns False on failure, logged.
3585 """
3586
3587 if sFile is None:
3588 sFile = 'valkit.txt';
3589
3590 reporter.log('txsCdWait: Waiting for file "%s" to become available ...' % (sFile,));
3591
3592 fRemoveVm = self.addTask(oSession);
3593 fRemoveTxs = self.addTask(oTxsSession);
3594 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3595 msStart = base.timestampMilli();
3596 cMsTimeout2 = cMsTimeout;
3597 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3598 if fRc is True:
3599 while True:
3600 # wait for it to complete.
3601 oTask = self.waitForTasks(cMsTimeout2 + 1);
3602 if oTask is not oTxsSession:
3603 oTxsSession.cancelTask();
3604 if oTask is None:
3605 reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
3606 % (base.timestampMilli() - msStart,));
3607 elif oTask is oSession:
3608 reporter.error('txsCdWait: The VM terminated unexpectedly');
3609 else:
3610 reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
3611 fRc = False;
3612 break;
3613 if oTxsSession.isSuccess():
3614 break;
3615
3616 # Check for timeout.
3617 cMsElapsed = base.timestampMilli() - msStart;
3618 if cMsElapsed >= cMsTimeout:
3619 reporter.error('txsCdWait: timed out');
3620 fRc = False;
3621 break;
3622 # delay.
3623 self.sleep(1);
3624
3625 # resubmit the task.
3626 cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
3627 if cMsTimeout2 < 500:
3628 cMsTimeout2 = 500;
3629 fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
3630 if fRc is not True:
3631 reporter.error('txsCdWait: asyncIsFile failed');
3632 break;
3633 else:
3634 reporter.error('txsCdWait: asyncIsFile failed');
3635
3636 if not fRc:
3637 # Do some diagnosis to find out why this failed.
3638 ## @todo Identify guest OS type and only run one of the following commands.
3639 fIsNotWindows = True;
3640 reporter.log('txsCdWait: Listing root contents of ${CDROM}:');
3641 if fIsNotWindows:
3642 reporter.log('txsCdWait: Tiggering udevadm ...');
3643 oTxsSession.syncExec("/sbin/udevadm", ("/sbin/udevadm", "trigger", "--verbose"), fIgnoreErrors = True);
3644 time.sleep(15);
3645 oTxsSession.syncExec("/bin/ls", ("/bin/ls", "-al", "${CDROM}"), fIgnoreErrors = True);
3646 reporter.log('txsCdWait: Listing media directory:');
3647 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3648 reporter.log('txsCdWait: Listing mount points / drives:');
3649 oTxsSession.syncExec('/bin/mount', ('/bin/mount',), fIgnoreErrors = True);
3650 oTxsSession.syncExec('/bin/cat', ('/bin/cat', '/etc/fstab'), fIgnoreErrors = True);
3651 oTxsSession.syncExec('/bin/dmesg', ('/bin/dmesg',), fIgnoreErrors = True);
3652 oTxsSession.syncExec('/usr/bin/lshw', ('/usr/bin/lshw', '-c', 'disk'), fIgnoreErrors = True);
3653 oTxsSession.syncExec('/bin/journalctl',
3654 ('/bin/journalctl', '-x', '-b'), fIgnoreErrors = True);
3655 oTxsSession.syncExec('/bin/journalctl',
3656 ('/bin/journalctl', '-x', '-b', '/usr/lib/udisks2/udisksd'), fIgnoreErrors = True);
3657 oTxsSession.syncExec('/usr/bin/udisksctl',
3658 ('/usr/bin/udisksctl', 'info', '-b', '/dev/sr0'), fIgnoreErrors = True);
3659 oTxsSession.syncExec('/bin/systemctl',
3660 ('/bin/systemctl', 'status', 'udisks2'), fIgnoreErrors = True);
3661 oTxsSession.syncExec('/bin/ps',
3662 ('/bin/ps', '-a', '-u', '-x'), fIgnoreErrors = True);
3663 reporter.log('txsCdWait: Mounting manually ...');
3664 for _ in range(3):
3665 oTxsSession.syncExec('/bin/mount', ('/bin/mount', '/dev/sr0', '${CDROM}'), fIgnoreErrors = True);
3666 time.sleep(5);
3667 reporter.log('txsCdWait: Re-Listing media directory:');
3668 oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
3669 else:
3670 # ASSUMES that we always install Windows on drive C right now.
3671 sWinDir = "C:\\Windows\\System32\\";
3672 # Should work since WinXP Pro.
3673 oTxsSession.syncExec(sWinDir + "wbem\\WMIC.exe",
3674 ("WMIC.exe", "logicaldisk", "get",
3675 "deviceid, volumename, description"),
3676 fIgnoreErrors = True);
3677 oTxsSession.syncExec(sWinDir + " cmd.exe",
3678 ('cmd.exe', '/C', 'dir', '${CDROM}'),
3679 fIgnoreErrors = True);
3680
3681 if fRemoveTxs:
3682 self.removeTask(oTxsSession);
3683 if fRemoveVm:
3684 self.removeTask(oSession);
3685 return fRc;
3686
3687 def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
3688 """
3689 Mostly an internal worker for connecting to TXS via TCP used by the
3690 *ViaTcp methods.
3691
3692 Returns a tuplet with True/False and TxsSession/None depending on the
3693 result. Errors are logged.
3694 """
3695
3696 reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
3697 % (oSession, cMsTimeout, fNatForwardingForTxs));
3698
3699 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
3700 oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
3701 if oTxsConnect is not None:
3702 self.addTask(oTxsConnect);
3703 fRemoveVm = self.addTask(oSession);
3704 oTask = self.waitForTasks(cMsTimeout + 1);
3705 reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
3706 self.removeTask(oTxsConnect);
3707 if oTask is oTxsConnect:
3708 oTxsSession = oTxsConnect.getResult();
3709 if oTxsSession is not None:
3710 reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
3711 return (True, oTxsSession);
3712
3713 reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
3714 else:
3715 oTxsConnect.cancelTask();
3716 if oTask is None:
3717 reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
3718 elif oTask is oSession:
3719 oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
3720 else:
3721 reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
3722 if fRemoveVm:
3723 self.removeTask(oSession);
3724 else:
3725 reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
3726 return (False, None);
3727
3728 def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
3729 cMsCdWait = 30000, sFileCdWait = None, \
3730 fNatForwardingForTxs = False):
3731 """
3732 Starts the specified VM and tries to connect to its TXS via TCP.
3733 The VM will be powered off if TXS doesn't respond before the specified
3734 time has elapsed.
3735
3736 Returns a the VM and TXS sessions (a two tuple) on success. The VM
3737 session is in the task list, the TXS session is not.
3738 Returns (None, None) on failure, fully logged.
3739 """
3740
3741 # Zap the guest IP to make sure we're not getting a stale entry
3742 # (unless we're restoring the VM of course).
3743 oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
3744 if oTestVM is None \
3745 or oTestVM.fSnapshotRestoreCurrent is False:
3746 try:
3747 oSession1 = self.openSession(self.getVmByName(sVmName));
3748 oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
3749 oSession1.saveSettings(True);
3750 del oSession1;
3751 except:
3752 reporter.logXcpt();
3753
3754 # Start the VM.
3755 reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
3756 reporter.flushall();
3757 oSession = self.startVmByName(sVmName);
3758 if oSession is not None:
3759 # Connect to TXS.
3760 reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
3761 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
3762 if fRc is True:
3763 if fCdWait:
3764 # Wait for CD?
3765 reporter.log2('startVmAndConnectToTxsViaTcp: Waiting for file "%s" to become available ...' % (sFileCdWait,));
3766 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3767 if fRc is not True:
3768 reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
3769 if fRc is True:
3770 # Success!
3771 return (oSession, oTxsSession);
3772 else:
3773 reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
3774 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
3775 self.terminateVmBySession(oSession);
3776 return (None, None);
3777
3778 def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
3779 cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
3780 """
3781 Executes the TXS reboot command
3782
3783 Returns A tuple of True and the new TXS session on success.
3784
3785 Returns A tuple of False and either the old TXS session or None on failure.
3786 """
3787 reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
3788
3789 #
3790 # This stuff is a bit complicated because of rebooting being kind of
3791 # disruptive to the TXS and such... The protocol is that TXS will:
3792 # - ACK the reboot command.
3793 # - Shutdown the transport layer, implicitly disconnecting us.
3794 # - Execute the reboot operation.
3795 # - On failure, it will be re-init the transport layer and be
3796 # available pretty much immediately. UUID unchanged.
3797 # - On success, it will be respawed after the reboot (hopefully),
3798 # with a different UUID.
3799 #
3800 fRc = False;
3801 iStart = base.timestampMilli();
3802
3803 # Get UUID.
3804 cMsTimeout2 = min(60000, cMsTimeout);
3805 sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
3806 if sUuidBefore is not False:
3807 # Reboot.
3808 cMsElapsed = base.timestampMilli() - iStart;
3809 cMsTimeout2 = cMsTimeout - cMsElapsed;
3810 fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
3811 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3812 if fRc is True:
3813 # Reconnect.
3814 if fNatForwardingForTxs is True:
3815 self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
3816 cMsElapsed = base.timestampMilli() - iStart;
3817 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
3818 if fRc is True:
3819 # Check the UUID.
3820 cMsElapsed = base.timestampMilli() - iStart;
3821 cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
3822 sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
3823 (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
3824 if sUuidBefore is not False:
3825 if sUuidAfter != sUuidBefore:
3826 reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
3827
3828 # Do CD wait if specified.
3829 if fCdWait:
3830 fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
3831 if fRc is not True:
3832 reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
3833 else:
3834 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
3835 else:
3836 reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
3837 else:
3838 reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
3839 else:
3840 reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
3841 else:
3842 reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
3843 return (fRc, oTxsSession);
3844
3845 # pylint: disable=too-many-locals,too-many-arguments
3846
3847 def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
3848 fCheckSessionStatus = False):
3849 """
3850 Executes the specified test task, waiting till it completes or times out.
3851
3852 The VM session (if any) must be in the task list.
3853
3854 Returns True if we executed the task and nothing abnormal happend.
3855 Query the process status from the TXS session.
3856
3857 Returns False if some unexpected task was signalled or we failed to
3858 submit the job.
3859
3860 If fCheckSessionStatus is set to True, the overall session status will be
3861 taken into account and logged as an error on failure.
3862 """
3863 reporter.testStart(sTestName);
3864 reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3865
3866 # Submit the job.
3867 fRc = False;
3868 if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3869 self.addTask(oTxsSession);
3870
3871 # Wait for the job to complete.
3872 while True:
3873 oTask = self.waitForTasks(cMsTimeout + 1);
3874 if oTask is None:
3875 if fCheckSessionStatus:
3876 reporter.error('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
3877 else:
3878 reporter.log('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
3879 break;
3880 if oTask is oTxsSession:
3881 if fCheckSessionStatus \
3882 and not oTxsSession.isSuccess():
3883 reporter.error('txsRunTest: Test "%s" failed' % (sTestName,));
3884 else:
3885 fRc = True;
3886 reporter.log('txsRunTest: isSuccess=%s getResult=%s' \
3887 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3888 break;
3889 if not self.handleTask(oTask, 'txsRunTest'):
3890 break;
3891
3892 self.removeTask(oTxsSession);
3893 if not oTxsSession.pollTask():
3894 oTxsSession.cancelTask();
3895 else:
3896 reporter.error('txsRunTest: asyncExec failed');
3897
3898 reporter.testDone();
3899 return fRc;
3900
3901 def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
3902 oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
3903 """
3904 Executes the specified test task, waiting till it completes or times out,
3905 redirecting stdin, stdout and stderr to the given objects.
3906
3907 The VM session (if any) must be in the task list.
3908
3909 Returns True if we executed the task and nothing abnormal happend.
3910 Query the process status from the TXS session.
3911
3912 Returns False if some unexpected task was signalled or we failed to
3913 submit the job.
3914 """
3915 reporter.testStart(sTestName);
3916 reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
3917
3918 # Submit the job.
3919 fRc = False;
3920 if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
3921 oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
3922 self.addTask(oTxsSession);
3923
3924 # Wait for the job to complete.
3925 while True:
3926 oTask = self.waitForTasks(cMsTimeout + 1);
3927 if oTask is None:
3928 reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
3929 break;
3930 if oTask is oTxsSession:
3931 fRc = True;
3932 reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
3933 % (oTxsSession.isSuccess(), oTxsSession.getResult()));
3934 break;
3935 if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
3936 break;
3937
3938 self.removeTask(oTxsSession);
3939 if not oTxsSession.pollTask():
3940 oTxsSession.cancelTask();
3941 else:
3942 reporter.error('txsRunTestRedirectStd: asyncExec failed');
3943
3944 reporter.testDone();
3945 return fRc;
3946
3947 def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
3948 sExecName1, asArgs1,
3949 sExecName2, asArgs2,
3950 asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
3951 asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
3952 """
3953 Executes the specified test tasks, waiting till they complete or
3954 times out. The 1st task is started after the 2nd one.
3955
3956 The VM session (if any) must be in the task list.
3957
3958 Returns True if we executed the task and nothing abnormal happend.
3959 Query the process status from the TXS sessions.
3960
3961 Returns False if some unexpected task was signalled or we failed to
3962 submit the job.
3963 """
3964 reporter.testStart(sTestName);
3965
3966 # Submit the jobs.
3967 fRc = False;
3968 if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
3969 self.adjustTimeoutMs(cMsTimeout)):
3970 self.addTask(oTxsSession1);
3971
3972 self.sleep(2); # fudge! grr
3973
3974 if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
3975 self.adjustTimeoutMs(cMsTimeout)):
3976 self.addTask(oTxsSession2);
3977
3978 # Wait for the jobs to complete.
3979 cPendingJobs = 2;
3980 while True:
3981 oTask = self.waitForTasks(cMsTimeout + 1);
3982 if oTask is None:
3983 reporter.log('txsRunTest2: waitForTasks timed out');
3984 break;
3985
3986 if oTask is oTxsSession1 or oTask is oTxsSession2:
3987 if oTask is oTxsSession1: iTask = 1;
3988 else: iTask = 2;
3989 reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
3990 % (iTask, oTask.isSuccess(), oTask.getResult()));
3991 self.removeTask(oTask);
3992 cPendingJobs -= 1;
3993 if cPendingJobs <= 0:
3994 fRc = True;
3995 break;
3996
3997 elif not self.handleTask(oTask, 'txsRunTest'):
3998 break;
3999
4000 self.removeTask(oTxsSession2);
4001 if not oTxsSession2.pollTask():
4002 oTxsSession2.cancelTask();
4003 else:
4004 reporter.error('txsRunTest2: asyncExec #2 failed');
4005
4006 self.removeTask(oTxsSession1);
4007 if not oTxsSession1.pollTask():
4008 oTxsSession1.cancelTask();
4009 else:
4010 reporter.error('txsRunTest2: asyncExec #1 failed');
4011
4012 reporter.testDone();
4013 return fRc;
4014
4015 # pylint: enable=too-many-locals,too-many-arguments
4016
4017
4018 #
4019 # Working with test results via serial port.
4020 #
4021
4022 class TxsMonitorComFile(base.TdTaskBase):
4023 """
4024 Class that monitors a COM output file.
4025 """
4026
4027 def __init__(self, sComRawFile, asStopWords = None):
4028 base.TdTaskBase.__init__(self, utils.getCallerName());
4029 self.sComRawFile = sComRawFile;
4030 self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
4031 self.sResult = None; ##< The result.
4032 self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
4033
4034 def toString(self):
4035 return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
4036 % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
4037
4038 def pollTask(self, fLocked = False):
4039 """
4040 Overrides TdTaskBase.pollTask() for the purpose of polling the file.
4041 """
4042 if not fLocked:
4043 self.lockTask();
4044
4045 sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
4046 if len(sFile) > self.cchDisplayed:
4047 sNew = sFile[self.cchDisplayed:];
4048 oMatch = self.oStopRegExp.search(sNew);
4049 if oMatch:
4050 # Done! Get result, flush all the output and signal the task.
4051 self.sResult = oMatch.group(1);
4052 for sLine in sNew.split('\n'):
4053 reporter.log('COM OUTPUT: %s' % (sLine,));
4054 self.cchDisplayed = len(sFile);
4055 self.signalTaskLocked();
4056 else:
4057 # Output whole lines only.
4058 offNewline = sFile.find('\n', self.cchDisplayed);
4059 while offNewline >= 0:
4060 reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
4061 self.cchDisplayed = offNewline + 1;
4062 offNewline = sFile.find('\n', self.cchDisplayed);
4063
4064 fRet = self.fSignalled;
4065 if not fLocked:
4066 self.unlockTask();
4067 return fRet;
4068
4069 # Our stuff.
4070 def getResult(self):
4071 """
4072 Returns the connected TXS session object on success.
4073 Returns None on failure or if the task has not yet completed.
4074 """
4075 self.oCv.acquire();
4076 sResult = self.sResult;
4077 self.oCv.release();
4078 return sResult;
4079
4080 def cancelTask(self):
4081 """ Cancels the task. """
4082 self.signalTask();
4083 return True;
4084
4085
4086 def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4087 """
4088 Monitors the COM output file for stop words (PASSED and FAILED by default).
4089
4090 Returns the stop word.
4091 Returns None on VM error and timeout.
4092 """
4093
4094 reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
4095
4096 oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
4097 self.addTask(oMonitorTask);
4098
4099 cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
4100 oTask = self.waitForTasks(cMsTimeout + 1);
4101 reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
4102
4103 if oTask is not oMonitorTask:
4104 oMonitorTask.cancelTask();
4105 self.removeTask(oMonitorTask);
4106
4107 oMonitorTask.pollTask();
4108 return oMonitorTask.getResult();
4109
4110
4111 def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
4112 """
4113 Runs the specified VM and monitors the given COM output file for stop
4114 words (PASSED and FAILED by default).
4115
4116 The caller is assumed to have configured the VM to use the given
4117 file. The method will take no action to verify this.
4118
4119 Returns the stop word.
4120 Returns None on VM error and timeout.
4121 """
4122
4123 # Start the VM.
4124 reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
4125 reporter.flushall();
4126 oSession = self.startVmByName(sVmName);
4127 if oSession is not None:
4128 # Let it run and then terminate it.
4129 sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
4130 self.terminateVmBySession(oSession);
4131 else:
4132 sRet = None;
4133 return sRet;
4134
4135 #
4136 # Other stuff
4137 #
4138
4139 def waitForGAs(self,
4140 oSession, # type: vboxwrappers.SessionWrapper
4141 cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
4142 """
4143 Waits for the guest additions to enter a certain state.
4144
4145 aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
4146 aenmWaitForActive - List facilities (type values) that must be active.
4147 aenmWaitForInactive - List facilities (type values) that must be inactive.
4148
4149 Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
4150
4151 Returns True on success, False w/ error logging on timeout or failure.
4152 """
4153 reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
4154
4155 #
4156 # Get IGuest:
4157 #
4158 try:
4159 oIGuest = oSession.o.console.guest;
4160 except:
4161 return reporter.errorXcpt();
4162
4163 #
4164 # Create a wait task:
4165 #
4166 from testdriver.vboxwrappers import AdditionsStatusTask;
4167 try:
4168 oGaStatusTask = AdditionsStatusTask(oSession = oSession,
4169 oIGuest = oIGuest,
4170 cMsTimeout = cMsTimeout,
4171 aenmWaitForRunLevels = aenmWaitForRunLevels,
4172 aenmWaitForActive = aenmWaitForActive,
4173 aenmWaitForInactive = aenmWaitForInactive);
4174 except:
4175 return reporter.errorXcpt();
4176
4177 #
4178 # Add the task and make sure the VM session is also present.
4179 #
4180 self.addTask(oGaStatusTask);
4181 fRemoveSession = self.addTask(oSession);
4182 oTask = self.waitForTasks(cMsTimeout + 1);
4183 reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
4184 self.removeTask(oGaStatusTask);
4185 if fRemoveSession:
4186 self.removeTask(oSession);
4187
4188 #
4189 # Digest the result.
4190 #
4191 if oTask is oGaStatusTask:
4192 fSucceeded = oGaStatusTask.getResult();
4193 if fSucceeded is True:
4194 reporter.log('waitForGAs: Succeeded.');
4195 else:
4196 reporter.error('waitForGAs: Failed.');
4197 else:
4198 oGaStatusTask.cancelTask();
4199 if oTask is None:
4200 reporter.error('waitForGAs: Timed out.');
4201 elif oTask is oSession:
4202 oSession.reportPrematureTermination('waitForGAs: ');
4203 else:
4204 reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
4205 fSucceeded = False;
4206 return fSucceeded;
4207
4208 @staticmethod
4209 def controllerTypeToName(eControllerType):
4210 """
4211 Translate a controller type to a standard controller name.
4212 """
4213 if eControllerType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
4214 sName = "IDE Controller";
4215 elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
4216 sName = "SATA Controller";
4217 elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
4218 sName = "SAS Controller";
4219 elif eControllerType in (vboxcon.StorageControllerType_LsiLogic, vboxcon.StorageControllerType_BusLogic,):
4220 sName = "SCSI Controller";
4221 elif eControllerType == vboxcon.StorageControllerType_NVMe:
4222 sName = "NVMe Controller";
4223 elif eControllerType == vboxcon.StorageControllerType_VirtioSCSI:
4224 sName = "VirtIO SCSI Controller";
4225 else:
4226 sName = "Storage Controller";
4227 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