VirtualBox

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

Last change on this file since 94590 was 94590, checked in by vboxsync, 3 years ago

ValidationKit/testdriver/vbox.py: Introduce a mechanism for forgetting a VM, required for testing VM unregistering since otherwise the object refcount doesn't drop to 0, influencing the API behavior.

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

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