VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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

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