VirtualBox

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

Last change on this file since 69558 was 69558, checked in by vboxsync, 7 years ago

testdriver/vbox.py: experimental hack for bugref:9037

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

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