VirtualBox

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

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

ValKit/vbox.py: Added --vbox-svc-wait-debug option.

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