VirtualBox

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

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

ValKit,++: Pylint 2.3.1 adjustments.

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