VirtualBox

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

最後變更 在這個檔案從106889是 106785,由 vboxsync 提交於 5 月 前

ValKit/vbox.py: Fixed unbound variable error in errorXcpt call in registerDerivedEventHandler. [oops]

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

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