diff options
Diffstat (limited to 'src/VBox/ValidationKit/docs')
-rw-r--r-- | src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html | 1357 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt | 1062 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/Makefile.kmk | 56 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/TestBoxImaging.html | 736 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/TestBoxImaging.txt | 370 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html | 445 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt | 114 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/WindbgPython.txt | 10 | ||||
-rwxr-xr-x | src/VBox/ValidationKit/docs/testbox-maintenance.sh | 400 | ||||
-rwxr-xr-x | src/VBox/ValidationKit/docs/testbox-pxe-conf.sh | 152 | ||||
-rw-r--r-- | src/VBox/ValidationKit/docs/valkit.txt | 1 |
11 files changed, 4703 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html new file mode 100644 index 00000000..be262a09 --- /dev/null +++ b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html @@ -0,0 +1,1357 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.12: http://docutils.sourceforge.net/" /> +<title></title> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: AutomaticTestingRevamp.html $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title, .code .error { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em ; + margin-right: 2em } + +pre.code .ln { color: grey; } /* line numbers */ +pre.code, code { background-color: #eeeeee } +pre.code .comment, code .comment { color: #5C6576 } +pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } +pre.code .literal.string, code .literal.string { color: #0C5404 } +pre.code .name.builtin, code .name.builtin { color: #352B84 } +pre.code .deleted, code .deleted { background-color: #DEB0A1} +pre.code .inserted, code .inserted { background-color: #A3D289} + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +/* "booktabs" style (no vertical lines) */ +table.docutils.booktabs { + border: 0px; + border-top: 2px solid; + border-bottom: 2px solid; + border-collapse: collapse; +} +table.docutils.booktabs * { + border: 0px; +} +table.docutils.booktabs th { + border-bottom: thin solid; + text-align: left; +} + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document"> + + +<div class="section" id="revamp-of-automatic-virtualbox-testing"> +<h1>Revamp of Automatic VirtualBox Testing</h1> +<div class="section" id="introduction"> +<h2>Introduction</h2> +<p>This is the design document for a revamped automatic testing framework. +The revamp aims at replacing the current tinderbox based testing by a new +system that is written from scratch.</p> +<p>The old system is not easy to work with and was never meant to be used for +managing tests, after all it just a simple a build manager tailored for +contiguous building. Modifying the existing tinderbox system to do what +we want would require fundamental changes that would render it useless as +a build manager, it would therefore end up as a fork. The amount of work +required would probably be about the same as writing a new system from +scratch. Other considerations, such as the license of the tinderbox +system (MPL) and language it is realized in (Perl), are also in favor of +doing it from scratch.</p> +<p>The language envisioned for the new automatic testing framework is Python. This +is for several reasons:</p> +<blockquote> +<ul class="simple"> +<li>The VirtualBox API has Python bindings.</li> +<li>Python is used quite a bit inside Sun (dunno about Oracle).</li> +<li>Works relatively well with Apache for the server side bits.</li> +<li>It is more difficult to produce write-only code in Python (alias the +we-don't-like-perl argument).</li> +<li>You don't need to compile stuff.</li> +</ul> +</blockquote> +<p>Note that the author of this document has no special training as a test +engineer and may therefore be using the wrong terms here and there. The +primary focus is to express what we need to do in order to improve +testing.</p> +<p>This document is written in reStructuredText (rst) which just happens to +be used by Python, the primary language for this revamp. For more +information on reStructuredText: <a class="reference external" href="http://docutils.sourceforge.net/rst.html">http://docutils.sourceforge.net/rst.html</a></p> +</div> +</div> +<div class="section" id="definitions-glossary"> +<h1>Definitions / Glossary</h1> +<dl class="docutils"> +<dt>sub-test driver</dt> +<dd>A set of test cases that can be used by more than one test driver. Could +also be called a test unit, in the pascal sense of unit, if it wasn't so +easily confused with 'unit test'.</dd> +<dt>test</dt> +<dd>This is somewhat ambiguous and this document try avoid using it where +possible. When used it normally refers to doing testing by executing one or +more testcases.</dd> +<dt>test case</dt> +<dd>A set of inputs, test programs and expected results. It validates system +requirements and generates a pass or failed status. A basic unit of testing. +Note that we use the term in a rather broad sense.</dd> +<dt>test driver</dt> +<dd>A program/script used to execute a test. Also known as a test harness. +Generally abbreviated 'td'. It can have sub-test drivers.</dd> +<dt>test manager</dt> +<dd>Software managing the automatic testing. This is a web application that runs +on a dedicated server (tindertux).</dd> +<dt>test set</dt> +<dd>The output of testing activity. Logs, results, ++. Our usage of this should +probably be renamed to 'test run'.</dd> +<dt>test group</dt> +<dd>A collection of related test cases.</dd> +<dt>testbox</dt> +<dd>A computer that does testing.</dd> +<dt>testbox script</dt> +<dd>Script executing orders from the test manager on a testbox. Started +automatically upon bootup.</dd> +<dt>testing</dt> +<dd>todo</dd> +<dt>TODO: Check that we've got all this right and make them more exact</dt> +<dd>where possible.</dd> +</dl> +<p>See also <a class="reference external" href="http://encyclopedia2.thefreedictionary.com/testing%20types">http://encyclopedia2.thefreedictionary.com/testing%20types</a> +and <a class="reference external" href="http://www.aptest.com/glossary.html">http://www.aptest.com/glossary.html</a> .</p> +</div> +<div class="section" id="objectives"> +<h1>Objectives</h1> +<blockquote> +<ul class="simple"> +<li>A scalable test manager (>200 testboxes).</li> +<li>Optimize the web user interface (WUI) for typical workflows and analysis.</li> +<li>Efficient and flexibile test configuration.</li> +<li>Import test result from other test systems (logo testing, VDI, ++).</li> +<li>Easy to add lots of new testscripts.</li> +<li>Run tests locally without a manager.</li> +<li>Revamp a bit at the time.</li> +</ul> +</blockquote> +</div> +<div class="section" id="the-testbox-side"> +<h1>The Testbox Side</h1> +<p>Each testbox has a unique name corresponding to its DNS zone entry. When booted +a testbox script is started automatically. This script will query the test +manager for orders and execute them. The core order downloads and executes a +test driver with parameters (configuration) from the server. The test driver +does all the necessary work for executing the test. In a typical VirtualBox +test this means picking a build, installing it, configuring VMs, running the +test VMs, collecting the results, submitting them to the server, and finally +cleaning up afterwards.</p> +<p>The testbox environment which the test drivers are executed in will have a +number of environment variables for determining location of the source images +and other test data, scratch space, test set id, server URL, and so on and so +forth.</p> +<p>On startup, the testbox script will look for crash dumps and similar on +systems where this is possible. If any sign of a crash is found, it will +put any dumps and reports in the upload directory and inform the test +manager before reporting for duty. In order to generate the proper file +names and report the crash in the right test set as well as prevent +reporting crashes unrelated to automatic testing, the testbox script will +keep information (test set id, ++) in a separate scratch directory +(${TESTBOX_PATH_SCRATCH}/../testbox) and make sure it is synced to the +disk (both files and directories).</p> +<p>After checking for crashes, the testbox script will clean up any previous test +which might be around. This involves first invoking the test script in cleanup +mode and the wiping the scratch space.</p> +<p>When reporting for duty the script will submit information about the host: OS +name, OS version, OS bitness, CPU vendor, total number of cores, VT-x support, +AMD-V support, amount of memory, amount of scratch space, and anything else that +can be found useful for scheduling tests or filtering test configurations.</p> +<div class="section" id="testbox-script-orders"> +<h2>Testbox Script Orders</h2> +<p>The orders are kept in a queue on the server and the testbox script will fetch +them one by one. Orders that cannot be executed at the moment will be masked in +the query from the testbox.</p> +<dl class="docutils"> +<dt>Execute Test Driver</dt> +<dd>Downloads and executes the a specified test driver with the given +configuration (arguments). Only one test driver can be executed at a time. +The server can specify more than one ZIP file to be downloaded and unpacked +before executing the test driver. The testbox script may cache these zip +files using http time stamping.</dd> +<dt>Abort Test Driver</dt> +<dd>Aborts the current test driver. This will drop a hint to the driver and give +it 60 seconds to shut down the normal way. If that fails, the testbox script +will kill the driver processes (SIGKILL or equivalent), invoke the +testdriver in cleanup mode, and finally wipe the scratch area. Should either +of the last two steps fail in some way, the testbox will be rebooted.</dd> +<dt>Idle</dt> +<dd>Ask again in X seconds, where X is specified by the server.</dd> +<dt>Reboot</dt> +<dd>Reboot the testbox. If a test driver is current running, an attempt at +aborting it (Abort Test Driver) will be made first.</dd> +<dt>Update</dt> +<dd>Updates the testbox script. The order includes a server relative path to the +new testbox script. This can only be executed when no test driver is +currently being executed.</dd> +</dl> +</div> +<div class="section" id="testbox-environment-variables"> +<h2>Testbox Environment: Variables</h2> +<dl class="docutils"> +<dt>COMSPEC</dt> +<dd>This will be set to C:WindowsSystem32cmd.exe on Windows.</dd> +<dt>PATH</dt> +<dd>This will contain the kBuild binary directory for the host platform.</dd> +<dt>SHELL</dt> +<dd>This will be set to point to kmk_ash(.exe) on all platforms.</dd> +<dt>TESTBOX_NAME</dt> +<dd>The testbox name. +This is not required by the local reporter.</dd> +<dt>TESTBOX_PATH_BUILDS</dt> +<dd>The absolute path to where the build repository can be found. This should be +a read only mount when possible.</dd> +<dt>TESTBOX_PATH_RESOURCES</dt> +<dd>The absolute path to where static test resources like ISOs and VDIs can be +found. The test drivers knows the layout of this. This should be a read only +mount when possible.</dd> +<dt>TESTBOX_PATH_SCRATCH</dt> +<dd>The absolute path to the scratch space. This is the current directory when +starting the test driver. It will be wiped automatically after executing the +test. +(Envisioned as ${TESTBOX_PATH_SCRIPTS}/../scratch and that +${TESTBOX_PATH_SCRATCH}/ will be automatically wiped by the testbox script.)</dd> +<dt>TESTBOX_PATH_SCRIPTS</dt> +<dd>The absolute path to the test driver and the other files that was unzipped +together with it. This is also where the test-driver-abort file will be put. +(Envisioned as ${TESTBOX_PATH_SCRATCH}/../driver, see above.)</dd> +<dt>TESTBOX_PATH_UPLOAD</dt> +<dd>The absolute path to the upload directory for the testbox. This is for +putting VOBs, PNGs, core dumps, crash dumps, and such on. The files should be +bzipped or zipped if they aren't compress already. The names should contain +the testbox and test set ID.</dd> +<dt>TESTBOX_REPORTER</dt> +<dd>The name of the test reporter back end. If not present, it will default to +the local reporter.</dd> +<dt>TESTBOX_TEST_SET_ID</dt> +<dd>The test set ID if we're running. +This is not required by the local reporter.</dd> +<dt>TESTBOX_MANAGER_URL</dt> +<dd>The URL to the test manager. +This is not required by the local reporter.</dd> +<dt>TESTBOX_XYZ</dt> +<dd>There will probably be some more of these.</dd> +</dl> +</div> +<div class="section" id="testbox-environment-core-utilities"> +<h2>Testbox Environment: Core Utilities</h2> +<p>The testbox will not provide the typical unix /bin and /usr/bin utilities. In +other words, cygwin will not be used on Windows!</p> +<p>The testbox will provide the unixy utilties that ships with kBuild and possibly +some additional ones from tools/<em>.</em>/bin in the VirtualBox tree (wget, unzip, +zip, and so on). The test drivers will avoid invoking any of these utilites +directly and instead rely on generic utility methods in the test driver +framework. That way we can more easily reimplement the functionality of the +core utilites and drop the dependency on them. It also allows us to quickly +work around platform specific oddities and bugs.</p> +</div> +<div class="section" id="test-drivers"> +<h2>Test Drivers</h2> +<p>The test drivers are programs that will do the actual testing. In addition to +run under the testbox script, they can be executed in the VirtualBox development +environment. This is important for bug analysis and for simplifying local +testing by the developers before commiting changes. It also means the test +drivers can be developed locally in the VirtualBox development environment.</p> +<p>The main difference between executing a driver under the testbox script and +running it manually is that there is no test manager in the latter case. The +test result reporter will not talk to the server, but report things to a local +log file and/or standard out/err. When invoked manually, all the necessary +arguments will need to be specified by hand of course - it should be possible +to extract them from a test set as well.</p> +<p>For the early implementation stages, an implementation of the reporter interface +that talks to the tinderbox base test manager will be needed. This will be +dropped later on when a new test manager is ready.</p> +<p>As hinted at in other sections, there will be a common framework +(libraries/packages/classes) for taking care of the tedious bits that every +test driver needs to do. Sharing code is essential to easing test driver +development as well as reducing their complexity. The framework will contain:</p> +<blockquote> +<ul> +<li><p class="first">A generic way of submitting output. This will be a generic interface with +multiple implementation, the TESTBOX_REPORTER environment variable +will decide which of them to use. The interface will have very specific +methods to allow the reporter to do a best possible job in reporting the +results to the test manager.</p> +</li> +<li><dl class="first docutils"> +<dt>Helpers for typical tasks, like:</dt> +<dd><ul class="first last simple"> +<li>Copying files.</li> +<li>Deleting files, directory trees and scratch space.</li> +<li>Unzipping files.</li> +<li>Creating ISOs</li> +<li>And such things.</li> +</ul> +</dd> +</dl> +</li> +<li><p class="first">Helpers for installing and uninstalling VirtualBox.</p> +</li> +<li><p class="first">Helpers for defining VMs. (The VBox API where available.)</p> +</li> +<li><p class="first">Helpers for controlling VMs. (The VBox API where available.)</p> +</li> +</ul> +</blockquote> +<p>The VirtualBox bits will be separate from the more generic ones, simply because +this is cleaner it will allow us to reuse the system for testing other products.</p> +<p>The framework will be packaged in a zip file other than the test driver so we +don't waste time and space downloading the same common code.</p> +<p>The test driver will poll for the file +${TESTBOX_PATH_SCRIPTS}/test-driver-abort and abort all testing when it sees it.</p> +<p>The test driver can be invoked in three modes: execute, help and cleanup. The +default is execute mode, the help shows an configuration summary and the cleanup +is for cleaning up after a reboot or aborted run. The latter is done by the +testbox script on startup and after abort - the driver is expected to clean up +by itself after a normal run.</p> +</div> +</div> +<div class="section" id="the-server-side"> +<h1>The Server Side</h1> +<p>The server side will be implemented using a webserver (apache), a database +(postgres) and cgi scripts (Python). In addition a cron job (Python) running +once a minute will generate static html for frequently used pages and maybe +execute some other tasks for driving the testing forwards. The order queries +from the testbox script is the primary driving force in the system. The total +makes up the test manager.</p> +<p>The test manager can be split up into three rough parts:</p> +<blockquote> +<ul class="simple"> +<li>Configuration (of tests, testgroups and testboxes).</li> +<li>Execution (of tests, collecting and organizing the output).</li> +<li>Analysis (of test output, mostly about presentation).</li> +</ul> +</blockquote> +</div> +<div class="section" id="test-manager-requirements"> +<h1>Test Manager: Requirements</h1> +<p>List of requirements:</p> +<blockquote> +<ul> +<li><p class="first">Two level testing - L1 quick smoke tests and L2 longer tests performed on +builds passing L1. (Klaus (IIRC) ment this could be realized using +test dependency.)</p> +</li> +<li><p class="first">Black listing builds (by revision or similar) known to be bad.</p> +</li> +<li><p class="first">Distinguish between build types so we can do a portion of the testing with +strict builds.</p> +</li> +<li><p class="first">Easy to re-configure build source for testing different branch or for +testing a release candidate. (Directory based is fine.)</p> +</li> +<li><p class="first">Useful to be able to partition testboxes (run specific builds on some +boxes, let an engineer have a few boxes for a while).</p> +</li> +<li><p class="first">Interation with ILOM/...: reset systems.</p> +</li> +<li><p class="first">Be able to suspend testing on selected testboxes when doing maintance +(where automatically resuming testing on reboot is undesired) or similar +activity.</p> +</li> +<li><p class="first">Abort testing on seleced testboxes.</p> +</li> +<li><p class="first">Scheduling of tests requiring more than one testbox.</p> +</li> +<li><p class="first">Scheduling of tests that cannot be executing concurrently on several +machines because of some global resource like an iSCSI target.</p> +</li> +<li><p class="first">Jump the scheduling queue. Scheduling of specified test the next time a +testbox is available (optionally specifying which testbox to schedule it +on).</p> +</li> +<li><dl class="first docutils"> +<dt>Configure tests with variable configuration to get better coverage. Two modes:</dt> +<dd><ul class="first last simple"> +<li>TM generates the permutations based on one or more sets of test script arguments.</li> +<li>Each configuration permuation is specified manually.</li> +</ul> +</dd> +</dl> +</li> +<li><p class="first">Test specification needs to be flexible (select tests, disable test, test +scheduling (run certain tests nightly), ... ).</p> +</li> +<li><p class="first">Test scheduling by hour+weekday and by priority.</p> +</li> +<li><p class="first">Test dependencies (test A depends on test B being successful).</p> +</li> +<li><p class="first">Historize all configuration data, in particular test configs (permutations +included) and testboxes.</p> +</li> +<li><p class="first">Test sets has at a minimum a build reference, a testbox reference and a +primary log associated with it.</p> +</li> +<li><dl class="first docutils"> +<dt>Test sets stores further result as a recursive collection of:</dt> +<dd><ul class="first last simple"> +<li>hierachical subtest name (slash sep)</li> +<li>test parameters / config</li> +<li>bool fail/succ</li> +<li>attributes (typed?)</li> +<li>test time</li> +<li>e.g. throughput</li> +<li>subresults</li> +<li>log</li> +<li>screenshots, video,...</li> +</ul> +</dd> +</dl> +</li> +<li><p class="first">The test sets database structure needs to designed such that data mining +can be done in an efficient manner.</p> +</li> +<li><p class="first">Presentation/analysis: graphs!, categorize bugs, columns reorganizing +grouped by test (hierarchical), overviews, result for last day.</p> +</li> +</ul> +</blockquote> +</div> +<div class="section" id="test-manager-configuration"> +<h1>Test Manager: Configuration</h1> +<div class="section" id="testboxes"> +<h2>Testboxes</h2> +<p>Configuration of testboxes doesn't involve much work normally. A testbox +is added manually to the test manager by entering the DNS entry and/or IP +address (the test manager resolves the missing one when necessary) as well as +the system UUID (when obtainable - should be displayed by the testbox script +installer). Queries from unregistered testboxes will be declined as a kind of +security measure, the incident should be logged in the webserver log if +possible. In later dealings with the client the System UUID will be the key +identifier. It's permittable for the IP address to change when the testbox +isn't online, but not while testing (just imagine live migration tests and +network tests). Ideally, the testboxes should not change IP address.</p> +<p>The testbox edit function must allow changing the name and system UUID.</p> +<p>One further idea for the testbox configuration is indicating what they are +capable of to filter out tests and test configurations that won't work on that +testbox. To examplify this take the ACP2 installation test. If the test +manager does not make sure the testbox have VT-x or AMD-v capabilities, the test +is surely going to fail. Other testbox capabilities would be total number of +CPU cores, memory size, scratch space. These testbox capabilities should be +collected automatically on bootup by the testbox script together with OS name, +OS version and OS bitness.</p> +<p>A final thought, instead of outright declining all requests from new testboxes, +we could record the unregistered testboxes with ip, UUID, name, os info and +capabilities but mark them as inactive. The test operator can then activate +them on an activation page or edit the testbox or something.</p> +</div> +<div class="section" id="testcases"> +<h2>Testcases</h2> +<p>We use the term testcase for a test.</p> +</div> +<div class="section" id="testgroups"> +<h2>Testgroups</h2> +<p>Testcases are organized into groups. A testcase can be member of more than one +group. The testcase gets a priority assigned to it in connection with the +group membership.</p> +<p>Testgroups are picked up by a testbox partition (aka scheduling group) and a +prioirty, scheduling time restriction and dependencies on other test groups are +associated with the assignment. A testgroup can be used by several testbox +partitions.</p> +<p>(This used to be called 'testsuites' but was renamed to avoid confusion with +the VBox Test Suite.)</p> +</div> +<div class="section" id="scheduling"> +<h2>Scheduling</h2> +<p>The initial scheduler will be modelled after what we're doing already on in the +tinderbox driven testing. It's best described as a best effort continuous +integration scheduler. Meaning, it will always use the latest build suitable +for a testcase. It will schedule on a testcase level, using the combined +priority of the testcase in the test group and the test group with the testbox +partition, trying to spread the test case argument varation out accordingly +over the whole scheduilng queue. Which argument variation to start with, is +not undefined (random would be best).</p> +<p>Later, we may add other schedulers as needed.</p> +</div> +</div> +<div class="section" id="the-test-manager-database"> +<h1>The Test Manager Database</h1> +<p>First a general warning:</p> +<blockquote> +The guys working on this design are not database experts, web +programming experts or similar, rather we are low level guys +who's main job is x86 & AMD64 virtualization. So, please don't +be too hard on us. :-)</blockquote> +<p>A logical table layout can be found in TestManagerDatabaseMap.png (created by +Oracle SQL Data Modeler, stored in TestManagerDatabase.dmd). The physical +database layout can be found in TestManagerDatabaseInit.pgsql postgreSQL +script. The script is commented.</p> +<div class="section" id="data-history"> +<h2>Data History</h2> +<p>We need to somehow track configuration changes over time. We also need to +be able to query the exact configuration a test set was run with so we can +understand and make better use of the results.</p> +<p>There are different techniques for archiving this, one is tuple-versioning +( <a class="reference external" href="http://en.wikipedia.org/wiki/Tuple-versioning">http://en.wikipedia.org/wiki/Tuple-versioning</a> ), another is log trigger +( <a class="reference external" href="http://en.wikipedia.org/wiki/Log_trigger">http://en.wikipedia.org/wiki/Log_trigger</a> ). We use tuple-versioning in +this database, with 'effective' as start date field name and 'expire' as +the end (exclusive).</p> +<p>Tuple-versioning has a shortcomming wrt to keys, both primary and foreign. +The primary key of a table employing tuple-versioning is really +'id' + 'valid_period', where the latter is expressed using two fields +([effective...expire-1]). Only, how do you tell the database engine that +it should not allow overlapping valid_periods? Useful suggestions are +welcomed. :-)</p> +<p>Foreign key references to a table using tuple-versioning is running into +trouble because of the time axsis and that to our knowledge foreign keys +must reference exactly one row in the other table. When time is involved +what we wish to tell the database is that at any given time, there actually +is exactly one row we want to match in the other table, only we've no idea +how to express this. So, many foreign keys are not expressed in SQL of this +database.</p> +<p>In some cases, we extend the tuple-versioning with a generation ID so that +normal foreign key referencing can be used. We only use this for recording +(references in testset) and scheduling (schedqueue), as using it more widely +would force updates (gen_id changes) to propagate into all related tables.</p> +<dl class="docutils"> +<dt>See also:</dt> +<dd><ul class="first last simple"> +<li><a class="reference external" href="http://en.wikipedia.org/wiki/Slowly_changing_dimension">http://en.wikipedia.org/wiki/Slowly_changing_dimension</a></li> +<li><a class="reference external" href="http://en.wikipedia.org/wiki/Change_data_capture">http://en.wikipedia.org/wiki/Change_data_capture</a></li> +<li><a class="reference external" href="http://en.wikipedia.org/wiki/Temporal_database">http://en.wikipedia.org/wiki/Temporal_database</a></li> +</ul> +</dd> +</dl> +</div> +</div> +<div class="section" id="test-manager-execution"> +<h1>Test Manager: Execution</h1> +</div> +<div class="section" id="test-manager-scenarios"> +<h1>Test Manager: Scenarios</h1> +<div class="section" id="testbox-signs-on-at-bootup"> +<h2>#1 - Testbox Signs On (At Bootup)</h2> +<dl class="docutils"> +<dt>The testbox supplies a number of inputs when reporting for duty:</dt> +<dd><ul class="first last simple"> +<li>IP address.</li> +<li>System UUID.</li> +<li>OS name.</li> +<li>OS version.</li> +<li>CPU architecture.</li> +<li>CPU count (= threads).</li> +<li>CPU VT-x/AMD-V capability.</li> +<li>CPU nested paging capability.</li> +<li>Chipset I/O MMU capability.</li> +<li>Memory size.</li> +<li>Scratch size space (for testing).</li> +<li>Testbox Script revision.</li> +</ul> +</dd> +<dt>Results:</dt> +<dd><ul class="first last simple"> +<li>ACK or NACK.</li> +<li>Testbox ID and name on ACK.</li> +</ul> +</dd> +</dl> +<p>After receiving a ACK the testbox will ask for work to do, i.e. continue with +scenario #2. In the NACK case, it will sleep for 60 seconds and try again.</p> +<p>Actions:</p> +<ol class="arabic"> +<li><p class="first">Validate the testbox by looking the UUID up in the TestBoxes table. +If not found, NACK the request. SQL:</p> +<pre class="literal-block"> +SELECT idTestBox, sName +FROM TestBoxes +WHERE uuidSystem = :sUuid + AND tsExpire = 'infinity'::timestamp; +</pre> +</li> +<li><p class="first">Check if any of the information by testbox script has changed. The two +sizes are normalized first, memory size rounded to nearest 4 MB and scratch +space is rounded down to nearest 64 MB. If anything changed, insert a new +row in the testbox table and historize the current one, i.e. set +OLD.tsExpire to NEW.tsEffective and get a new value for NEW.idGenTestBox.</p> +</li> +<li><dl class="first docutils"> +<dt>Check with TestBoxStatuses:</dt> +<dd><ol class="first last loweralpha simple"> +<li>If there is an row for the testbox in it already clean up change it +to 'idle' state and deal with any open testset like described in +scenario #9.</li> +<li>If there is no row, add one with 'idle' state.</li> +</ol> +</dd> +</dl> +</li> +<li><p class="first">ACK the request and pass back the idTestBox.</p> +</li> +</ol> +<dl class="docutils"> +<dt>Note! Testbox.enabled is not checked here, that is only relevant when it asks</dt> +<dd>for a new task (scenario #2 and #5).</dd> +<dt>Note! Should the testbox script detect changes in any of the inputs, it should</dt> +<dd>redo the sign in.</dd> +<dt>Note! In scenario #8, the box will not sign on until it has done the reboot and</dt> +<dd>cleanup reporting!</dd> +</dl> +</div> +<div class="section" id="testbox-asks-for-work-to-do"> +<h2>#2 - Testbox Asks For Work To Do</h2> +<dl class="docutils"> +<dt>Inputs:</dt> +<dd><ul class="first last simple"> +<li>The testbox is supplying its IP indirectly.</li> +<li>The testbox should supply its UUID and ID directly.</li> +</ul> +</dd> +<dt>Results:</dt> +<dd><ul class="first last simple"> +<li>IDLE, WAIT, EXEC, REBOOT, UPGRADE, UPGRADE-AND-REBOOT, SPECIAL or DEAD.</li> +</ul> +</dd> +</dl> +<p>Actions:</p> +<ol class="arabic"> +<li><p class="first">Validate the ID and IP by selecting the currently valid testbox row:</p> +<pre class="literal-block"> +SELECT idGenTestBox, fEnabled, idSchedGroup, enmPendingCmd +FROM TestBoxes +WHERE id = :id + AND uuidSystem = :sUuid + AND ip = :ip + AND tsExpire = 'infinity'::timestamp; +</pre> +<p>If NOT found return DEAD to the testbox client (it will go back to sign on +mode and retry every 60 seconds or so - see scenario #1).</p> +<dl class="docutils"> +<dt>Note! The WUI will do all necessary clean-ups when deleting a testbox, so</dt> +<dd><p class="first last">contrary to the initial plans, we don't need to do anything more for +the DEAD status.</p> +</dd> +</dl> +</li> +<li><p class="first">Check with TestBoxStatuses (maybe joined with query from 1).</p> +<p>If enmState is 'gang-gathering': Goto scenario #6 on timeout or pending +'abort' or 'reboot' command. Otherwise, tell the testbox to WAIT [done].</p> +<p>If enmState is 'gang-testing': The gang has been gathered and execution +has been triggered. Goto 5.</p> +<p>If enmState is not 'idle', change it to 'idle'.</p> +<p>If idTestSet is not NULL, CALL scenario #9 to it up.</p> +<p>If there is a pending abort command, remove it.</p> +<p>If there is a pending command and the old state doesn't indicate that it was +being executed, GOTO scenario #3.</p> +<dl class="docutils"> +<dt>Note! There should be a TestBoxStatuses row after executing scenario #1,</dt> +<dd><p class="first last">however should none be found for some funky reason, returning DEAD +will fix the problem (see above)</p> +</dd> +</dl> +</li> +<li><p class="first">If the testbox was marked as disabled, respond with an IDLE command to the +testbox [done]. (Note! Must do this after TestBoxStatuses maintainance from +point 2, or abandoned tests won't be cleaned up after a testbox is disabled.)</p> +</li> +<li><p class="first">Consider testcases in the scheduling queue, pick the first one which the +testbox can execute. There is a concurrency issue here, so we put and +exclusive lock on the SchedQueues table while considering its content.</p> +<p>The cursor we open looks something like this:</p> +<pre class="literal-block"> +SELECT idItem, idGenTestCaseArgs, + idTestSetGangLeader, cMissingGangMembers +FROM SchedQueues +WHERE idSchedGroup = :idSchedGroup + AND ( bmHourlySchedule is NULL + OR get_bit(bmHourlySchedule, :iHourOfWeek) = 1 ) --< does this work? +ORDER BY ASC idItem; +</pre> +</li> +</ol> +<blockquote> +<p>If there no rows are returned (this can happen because no testgroups are +associated with this scheduling group, the scheduling group is disabled, +or because the queue is being regenerated), we will tell the testbox to +IDLE [done].</p> +<dl class="docutils"> +<dt>For each returned row we will:</dt> +<dd><ol class="first last loweralpha"> +<li><p class="first">Check testcase/group dependencies.</p> +</li> +<li><p class="first">Select a build (and default testsuite) satisfying the dependencies.</p> +</li> +<li><p class="first">Check the testcase requirements with that build in mind.</p> +</li> +<li><p class="first">If idTestSetGangLeader is NULL, try allocate the necessary resources.</p> +</li> +<li><p class="first">If it didn't check out, fetch the next row and redo from (a).</p> +</li> +<li><p class="first">Tentatively create a new test set row.</p> +</li> +<li><dl class="first docutils"> +<dt>If not gang scheduling:</dt> +<dd><ul class="first last simple"> +<li>Next state: 'testing'</li> +</ul> +</dd> +<dt>ElIf we're the last gang participant:</dt> +<dd><ul class="first last simple"> +<li>Set idTestSetGangLeader to NULL.</li> +<li>Set cMissingGangMembers to 0.</li> +<li>Next state: 'gang-testing'</li> +</ul> +</dd> +<dt>ElIf we're the first gang member:</dt> +<dd><ul class="first last simple"> +<li>Set cMissingGangMembers to TestCaseArgs.cGangMembers - 1.</li> +<li>Set idTestSetGangLeader to our idTestSet.</li> +<li>Next state: 'gang-gathering'</li> +</ul> +</dd> +<dt>Else:</dt> +<dd><ul class="first last simple"> +<li>Decrement cMissingGangMembers.</li> +<li>Next state: 'gang-gathering'</li> +</ul> +</dd> +<dt>If we're not gang scheduling OR cMissingGangMembers is 0:</dt> +<dd><p class="first last">Move the scheduler queue entry to the end of the queue.</p> +</dd> +</dl> +<p>Update our TestBoxStatuses row with the new state and test set. +COMMIT;</p> +</li> +</ol> +</dd> +</dl> +</blockquote> +<ol class="arabic" start="5"> +<li><dl class="first docutils"> +<dt>If state is 'testing' or 'gang-testing':</dt> +<dd><p class="first">EXEC reponse.</p> +<p class="last">The EXEC response for a gang scheduled testcase includes a number of +extra arguments so that the script knows the position of the testbox +it is running on and of the other members. This means the that the +TestSet.iGangMemberNo is passed using --gang-member-no and the IP +addresses of the all gang members using --gang-ipv4-<memb-no> <ip>.</p> +</dd> +<dt>Else (state is 'gang-gathering'):</dt> +<dd><p class="first last">WAIT</p> +</dd> +</dl> +</li> +</ol> +</div> +<div class="section" id="pending-command-when-testbox-asks-for-work"> +<h2>#3 - Pending Command When Testbox Asks For Work</h2> +<p>This is a subfunction of scenario #2 and #5.</p> +<p>As seen in scenario #2, the testbox will send 'abort' commands to /dev/null +when it finds one when not executing a test. This includes when it reports +that the test has completed (no need to abort a completed test, wasting lot +of effort when standing at the finish line).</p> +<p>The other commands, though, are passed back to the testbox. The testbox +script will respond with an ACK or NACK as it sees fit. If NACKed, the +pending command will be removed (pending_cmd set to none) and that's it. +If ACKed, the state of the testbox will change to that appropriate for the +command and the pending_cmd set to none. Should the testbox script fail to +respond, the command will be repeated the next time it asks for work.</p> +</div> +<div class="section" id="testbox-uploads-results-during-test"> +<h2>#4 - Testbox Uploads Results During Test</h2> +<p>TODO</p> +</div> +<div class="section" id="testbox-completes-test-and-asks-for-work"> +<h2>#5 - Testbox Completes Test and Asks For Work</h2> +<p>This is very similar to scenario #2</p> +<p>TODO</p> +</div> +<div class="section" id="gang-gathering-timeout"> +<h2>#6 - Gang Gathering Timeout</h2> +<p>This is a subfunction of scenario #2.</p> +<p>When gathering a gang of testboxes for a testcase, we do not want to wait +forever and have testboxes doing nothing for hours while waiting for partners. +So, the gathering has a reasonable timeout (imagine something like 20-30 mins).</p> +<p>Also, we need some way of dealing with 'abort' and 'reboot' commands being +issued while waiting. The easy way out is pretent it's a time out.</p> +<p>When changing the status to 'gang-timeout' we have to be careful. First of all, +we need to exclusively lock the SchedQueues and TestBoxStatuses (in that order) +and re-query our status. If it changed redo the checks in scenario #2 point 2.</p> +<p>If we still want to timeout/abort, change the state from 'gang-gathering' to +'gang-gathering-timedout' on all the gang members that has gathered so far. +Then reset the scheduling queue record and move it to the end of the queue.</p> +<p>When acting on 'gang-timeout' the TM will fail the testset in a manner similar +to scenario #9. No need to repeat that.</p> +</div> +<div class="section" id="gang-cleanup"> +<h2>#7 - Gang Cleanup</h2> +<p>When a testbox completes a gang scheduled test, we will have to serialize +resource cleanup (both globally and on testboxes) as they stop. More details +can be found in the documentation of 'gang-cleanup'.</p> +<p>So, the transition from 'gang-testing' is always to 'gang-cleanup'. When we +can safely leave 'gang-cleanup' is decided by the query:</p> +<pre class="literal-block"> +SELECT COUNT(*) +FROM TestBoxStatuses, + TestSets +WHERE TestSets.idTestSetGangLeader = :idTestSetGangLeader + AND TestSets.idTestBox = TestBoxStatuses.idTestBox + AND TestBoxStatuses.enmState = 'gang-running'::TestBoxState_T; +</pre> +<p>As long as there are testboxes still running, we stay in the 'gang-cleanup' +state. Once there are none, we continue closing the testset and such.</p> +</div> +<div class="section" id="testbox-reports-a-crash-during-test-execution"> +<h2>#8 - Testbox Reports A Crash During Test Execution</h2> +<p>TODO</p> +</div> +<div class="section" id="cleaning-up-abandoned-testcase"> +<h2>#9 - Cleaning Up Abandoned Testcase</h2> +<p>This is a subfunction of scenario #1 and #2. The actions taken are the same in +both situations. The precondition for taking this path is that the row in the +testboxstatus table is refering to a testset (i.e. testset_id is not NULL).</p> +<p>Actions:</p> +<ol class="arabic"> +<li><dl class="first docutils"> +<dt>If the testset is incomplete, we need to completed:</dt> +<dd><ol class="first last loweralpha simple"> +<li>Add a message to the root TestResults row, creating one if necesary, +that explains that the test was abandoned. This is done +by inserting/finding the string into/in TestResultStrTab and adding +a row to TestResultMsgs with idStrMsg set to that string id and +enmLevel set to 'failure'.</li> +<li>Mark the testset as failed.</li> +</ol> +</dd> +</dl> +</li> +<li><p class="first">Free any global resources referenced by the test set. This is done by +deleting all rows in GlobalResourceStatuses matching the testbox id.</p> +</li> +<li><p class="first">Set the idTestSet to NULL in the TestBoxStatuses row.</p> +</li> +</ol> +</div> +<div class="section" id="cleaning-up-a-disabled-dead-testbox"> +<h2>#10 - Cleaning Up a Disabled/Dead TestBox</h2> +<p>The UI needs to be able to clean up the remains of a testbox which for some +reason is out of action. Normal cleaning up of abandoned testcases requires +that the testbox signs on or asks for work, but if the testbox is dead or +in some way indisposed, it won't be doing any of that. So, the testbox +sheriff needs to have a way of cleaning up after it.</p> +<p>It's basically a manual scenario #9 but with some safe guards, like checking +that the box hasn't been active for the last 1-2 mins (max idle/wait time * 2).</p> +<dl class="docutils"> +<dt>Note! When disabling a box that still executing the testbox script, this</dt> +<dd>cleanup isn't necessary as it will happen automatically. Also, it's +probably desirable that the testbox finishes what ever it is doing first +before going dormant.</dd> +</dl> +</div> +</div> +<div class="section" id="test-manager-analysis"> +<h1>Test Manager: Analysis</h1> +<p>One of the testbox sheriff's tasks is to try figure out the reason why something +failed. The test manager will provide facilities for doing so from very early +in it's implementation.</p> +<p>We need to work out some useful status reports for the early implementation. +Later there will be more advanced analysis tools, where for instance we can +create graphs from selected test result values or test execution times.</p> +</div> +<div class="section" id="implementation-plan"> +<h1>Implementation Plan</h1> +<p>This has changed for various reasons. The current plan is to implement the +infrastructure (TM & testbox script) first and do a small deployment with the +2-5 test drivers in the Testsuite as basis. Once the bugs are worked out, we +will convert the rest of the tests and start adding new ones.</p> +<p>We just need to finally get this done, no point in doing it piecemeal by now!</p> +<div class="section" id="test-manager-implementation-sub-tasks"> +<h2>Test Manager Implementation Sub-Tasks</h2> +<p>The implementation of the test manager and adjusting/completing of the testbox +script and the test drivers are tasks which can be done by more than one +person. Splitting up the TM implementation into smaller tasks should allow +parallel development of different tasks and get us working code sooner.</p> +</div> +<div class="section" id="milestone-1"> +<h2>Milestone #1</h2> +<p>The goal is to getting the fundamental testmanager engine implemented, debugged +and working. With the exception of testboxes, the configuration will be done +via SQL inserts.</p> +<p>Tasks in somewhat prioritized order:</p> +<blockquote> +<ul class="simple"> +<li>Kick off test manager. It will live in testmanager/. Salvage as much as +possible from att/testserv. Create basic source and file layout.</li> +<li>Adjust the testbox script, part one. There currently is a testbox script +in att/testbox, this shall be moved up into testboxscript/. The script +needs to be adjusted according to the specification layed down earlier +in this document. Installers or installation scripts for all relevant +host OSes are required. Left for part two is result reporting beyond the +primary log. This task must be 100% feature complete, on all host OSes, +there is no room for FIXME, XXX or @todo here.</li> +<li>Implement the schedule queue generator.</li> +<li>Implement the testbox dispatcher in TM. Support all the testbox script +responses implemented above, including upgrading the testbox script.</li> +<li>Implement simple testbox management page.</li> +<li>Implement some basic activity and result reports so that we can see +what's going on.</li> +<li>Create a testmanager / testbox test setup. This lives in selftest/.<ol class="arabic"> +<li>Set up something that runs, no fiddly bits. Debug till it works.</li> +<li>Create a setup that tests testgroup dependencies, i.e. real tests +depending on smoke tests.</li> +<li>Create a setup that exercises testcase dependency.</li> +<li>Create a setup that exercises global resource allocation.</li> +<li>Create a setup that exercises gang scheduling.</li> +</ol> +</li> +<li>Check that all features work.</li> +</ul> +</blockquote> +</div> +<div class="section" id="milestone-2"> +<h2>Milestone #2</h2> +<p>The goal is getting to VBox testing.</p> +<p>Tasks in somewhat prioritized order:</p> +<blockquote> +<ul class="simple"> +<li>Implement full result reporting in the testbox script and testbox driver. +A testbox script specific reporter needs to be implemented for the +testdriver framework. The testbox script needs to forward the results to +the test manager, or alternatively the testdriver report can talk +directly to the TM.</li> +<li>Implement the test manager side of the test result reporting.</li> +<li>Extend the selftest with some setup that report all kinds of test +results.</li> +<li>Implement script/whatever feeding builds to the test manager from the +tinderboxes.</li> +<li>The toplevel test driver is a VBox thing that must be derived from the +base TestDriver class or maybe the VBox one. It should move from +toptestdriver to testdriver and be renamed to vboxtltd or smth.</li> +<li>Create a vbox testdriver that boots the t-xppro VM once and that's it.</li> +<li>Create a selftest setup which tests booting t-xppro taking builds from +the tinderbox.</li> +</ul> +</blockquote> +</div> +<div class="section" id="milestone-3"> +<h2>Milestone #3</h2> +<p>The goal for this milestone is configuration and converting current testscases, +the result will be the a minimal test deployment (4-5 new testboxes).</p> +<p>Tasks in somewhat prioritized order:</p> +<blockquote> +<ul class="simple"> +<li>Implement testcase configuration.</li> +<li>Implement testgroup configuration.</li> +<li>Implement build source configuration.</li> +<li>Implement scheduling group configuration.</li> +<li>Implement global resource configuration.</li> +<li>Re-visit the testbox configuration.</li> +<li>Black listing of builds.</li> +<li>Implement simple failure analysis and reporting.</li> +<li>Implement the initial smoke tests modelled on the current smoke tests.</li> +<li>Implement installation tests for Windows guests.</li> +<li>Implement installation tests for Linux guests.</li> +<li>Implement installation tests for Solaris guest.</li> +<li>Implement installation tests for OS/2 guest.</li> +<li>Set up a small test deployment.</li> +</ul> +</blockquote> +</div> +<div class="section" id="further-work"> +<h2>Further work</h2> +<p>After milestone #3 has been reached and issues found by the other team members +have been addressed, we will probably go for full deployment.</p> +<p>Beyond this point we will need to improve reporting and analysis. There may be +configuration aspects needing reporting as well.</p> +<p>Once deployed, a golden rule will be that all new features shall have test +coverage. Preferrably, implemented by someone else and prior to the feature +implementation.</p> +</div> +</div> +<div class="section" id="discussion-logs"> +<h1>Discussion Logs</h1> +<div class="section" id="various-discussions-with-michal-and-or-klaus"> +<h2>2009-07-21,22,23 Various Discussions with Michal and/or Klaus</h2> +<ul class="simple"> +<li>Scheduling of tests requiring more than one testbox.</li> +<li>Scheduling of tests that cannot be executing concurrently on several machines +because of some global resource like an iSCSI target.</li> +<li>Manually create the test config permutations instead of having the test +manager create all possible ones and wasting time.</li> +<li>Distinguish between built types so we can run smoke tests on strick builds as +well as release ones.</li> +</ul> +</div> +<div class="section" id="brief-discussion-with-michal"> +<h2>2009-07-20 Brief Discussion with Michal</h2> +<ul class="simple"> +<li>Installer for the testbox script to make bringing up a new testbox even +smoother.</li> +</ul> +</div> +<div class="section" id="raw-input"> +<h2>2009-07-16 Raw Input</h2> +<ul> +<li><dl class="first docutils"> +<dt>test set. recursive collection of:</dt> +<dd><ul class="first last simple"> +<li>hierachical subtest name (slash sep)</li> +<li>test parameters / config</li> +<li>bool fail/succ</li> +<li>attributes (typed?)</li> +<li>test time</li> +<li>e.g. throughput</li> +<li>subresults</li> +<li>log</li> +<li>screenshots,....</li> +</ul> +</dd> +</dl> +</li> +<li><p class="first">client package (zip) dl from server (maybe client caching)</p> +</li> +<li><dl class="first docutils"> +<dt>thoughts on bits to do at once.</dt> +<dd><ul class="first last simple"> +<li>We <em>really</em> need the basic bits ASAP.</li> +<li>client -> support for test driver</li> +<li>server -> controls configs</li> +<li>cleanup on both sides</li> +</ul> +</dd> +</dl> +</li> +</ul> +</div> +<div class="section" id="id1"> +<h2>2009-07-15 Raw Input</h2> +<ul class="simple"> +<li>testing should start automatically</li> +<li>switching to branch too tedious</li> +<li>useful to be able to partition testboxes (run specific builds on some boxes, let an engineer have a few boxes for a while).</li> +<li>test specification needs to be more flexible (select tests, disable test, test scheduling (run certain tests nightly), ... )</li> +<li>testcase dependencies (blacklisting builds, run smoketests on box A before long tests on box B, ...)</li> +<li>more testing flexibility, more test than just install/moke. For instance unit tests, benchmarks, ...</li> +<li>presentation/analysis: graphs!, categorize bugs, columns reorganizing grouped by test (hierarchical), overviews, result for last day.</li> +<li>testcase specificion, variables (e.g. I/O-APIC, SMP, HWVIRT, SATA...) as sub-tests</li> +<li>interation with ILOM/...: reset systems</li> +<li>Changes needs LDAP authentication</li> +<li>historize all configuration w/ name</li> +<li>ability to run testcase locally (provided the VDI/ISO/whatever extra requirements can be met).</li> +</ul> +<hr class="docutils" /> +<table class="docutils footnote" frame="void" id="id2" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label">[1]</td><td>no such footnote</td></tr> +</tbody> +</table> +<hr class="docutils" /> +<table class="docutils field-list" frame="void" rules="none"> +<col class="field-name" /> +<col class="field-body" /> +<tbody valign="top"> +<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: AutomaticTestingRevamp.html $</td> +</tr> +<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2010-2017 Oracle Corporation.</td> +</tr> +</tbody> +</table> +</div> +</div> +</div> +</body> +</html> diff --git a/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt new file mode 100644 index 00000000..525384b6 --- /dev/null +++ b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt @@ -0,0 +1,1062 @@ + +Revamp of Automatic VirtualBox Testing +====================================== + + +Introduction +------------ + +This is the design document for a revamped automatic testing framework. +The revamp aims at replacing the current tinderbox based testing by a new +system that is written from scratch. + +The old system is not easy to work with and was never meant to be used for +managing tests, after all it just a simple a build manager tailored for +contiguous building. Modifying the existing tinderbox system to do what +we want would require fundamental changes that would render it useless as +a build manager, it would therefore end up as a fork. The amount of work +required would probably be about the same as writing a new system from +scratch. Other considerations, such as the license of the tinderbox +system (MPL) and language it is realized in (Perl), are also in favor of +doing it from scratch. + +The language envisioned for the new automatic testing framework is Python. This +is for several reasons: + + - The VirtualBox API has Python bindings. + - Python is used quite a bit inside Sun (dunno about Oracle). + - Works relatively well with Apache for the server side bits. + - It is more difficult to produce write-only code in Python (alias the + we-don't-like-perl argument). + - You don't need to compile stuff. + +Note that the author of this document has no special training as a test +engineer and may therefore be using the wrong terms here and there. The +primary focus is to express what we need to do in order to improve +testing. + +This document is written in reStructuredText (rst) which just happens to +be used by Python, the primary language for this revamp. For more +information on reStructuredText: http://docutils.sourceforge.net/rst.html + + +Definitions / Glossary +====================== + +sub-test driver + A set of test cases that can be used by more than one test driver. Could + also be called a test unit, in the pascal sense of unit, if it wasn't so + easily confused with 'unit test'. + +test + This is somewhat ambiguous and this document try avoid using it where + possible. When used it normally refers to doing testing by executing one or + more testcases. + +test case + A set of inputs, test programs and expected results. It validates system + requirements and generates a pass or failed status. A basic unit of testing. + Note that we use the term in a rather broad sense. + +test driver + A program/script used to execute a test. Also known as a test harness. + Generally abbreviated 'td'. It can have sub-test drivers. + +test manager + Software managing the automatic testing. This is a web application that runs + on a dedicated server (tindertux). + +test set + The output of testing activity. Logs, results, ++. Our usage of this should + probably be renamed to 'test run'. + +test group + A collection of related test cases. + +testbox + A computer that does testing. + +testbox script + Script executing orders from the test manager on a testbox. Started + automatically upon bootup. + +testing + todo + +TODO: Check that we've got all this right and make them more exact + where possible. + +See also http://encyclopedia2.thefreedictionary.com/testing%20types +and http://www.aptest.com/glossary.html . + + + +Objectives +========== + + - A scalable test manager (>200 testboxes). + - Optimize the web user interface (WUI) for typical workflows and analysis. + - Efficient and flexibile test configuration. + - Import test result from other test systems (logo testing, VDI, ++). + - Easy to add lots of new testscripts. + - Run tests locally without a manager. + - Revamp a bit at the time. + + + +The Testbox Side +================ + +Each testbox has a unique name corresponding to its DNS zone entry. When booted +a testbox script is started automatically. This script will query the test +manager for orders and execute them. The core order downloads and executes a +test driver with parameters (configuration) from the server. The test driver +does all the necessary work for executing the test. In a typical VirtualBox +test this means picking a build, installing it, configuring VMs, running the +test VMs, collecting the results, submitting them to the server, and finally +cleaning up afterwards. + +The testbox environment which the test drivers are executed in will have a +number of environment variables for determining location of the source images +and other test data, scratch space, test set id, server URL, and so on and so +forth. + +On startup, the testbox script will look for crash dumps and similar on +systems where this is possible. If any sign of a crash is found, it will +put any dumps and reports in the upload directory and inform the test +manager before reporting for duty. In order to generate the proper file +names and report the crash in the right test set as well as prevent +reporting crashes unrelated to automatic testing, the testbox script will +keep information (test set id, ++) in a separate scratch directory +(${TESTBOX_PATH_SCRATCH}/../testbox) and make sure it is synced to the +disk (both files and directories). + +After checking for crashes, the testbox script will clean up any previous test +which might be around. This involves first invoking the test script in cleanup +mode and the wiping the scratch space. + +When reporting for duty the script will submit information about the host: OS +name, OS version, OS bitness, CPU vendor, total number of cores, VT-x support, +AMD-V support, amount of memory, amount of scratch space, and anything else that +can be found useful for scheduling tests or filtering test configurations. + + + +Testbox Script Orders +--------------------- + +The orders are kept in a queue on the server and the testbox script will fetch +them one by one. Orders that cannot be executed at the moment will be masked in +the query from the testbox. + +Execute Test Driver + Downloads and executes the a specified test driver with the given + configuration (arguments). Only one test driver can be executed at a time. + The server can specify more than one ZIP file to be downloaded and unpacked + before executing the test driver. The testbox script may cache these zip + files using http time stamping. + +Abort Test Driver + Aborts the current test driver. This will drop a hint to the driver and give + it 60 seconds to shut down the normal way. If that fails, the testbox script + will kill the driver processes (SIGKILL or equivalent), invoke the + testdriver in cleanup mode, and finally wipe the scratch area. Should either + of the last two steps fail in some way, the testbox will be rebooted. + +Idle + Ask again in X seconds, where X is specified by the server. + +Reboot + Reboot the testbox. If a test driver is current running, an attempt at + aborting it (Abort Test Driver) will be made first. + +Update + Updates the testbox script. The order includes a server relative path to the + new testbox script. This can only be executed when no test driver is + currently being executed. + + +Testbox Environment: Variables +------------------------------ + +COMSPEC + This will be set to C:\Windows\System32\cmd.exe on Windows. + +PATH + This will contain the kBuild binary directory for the host platform. + +SHELL + This will be set to point to kmk_ash(.exe) on all platforms. + +TESTBOX_NAME + The testbox name. + This is not required by the local reporter. + +TESTBOX_PATH_BUILDS + The absolute path to where the build repository can be found. This should be + a read only mount when possible. + +TESTBOX_PATH_RESOURCES + The absolute path to where static test resources like ISOs and VDIs can be + found. The test drivers knows the layout of this. This should be a read only + mount when possible. + +TESTBOX_PATH_SCRATCH + The absolute path to the scratch space. This is the current directory when + starting the test driver. It will be wiped automatically after executing the + test. + (Envisioned as ${TESTBOX_PATH_SCRIPTS}/../scratch and that + ${TESTBOX_PATH_SCRATCH}/ will be automatically wiped by the testbox script.) + +TESTBOX_PATH_SCRIPTS + The absolute path to the test driver and the other files that was unzipped + together with it. This is also where the test-driver-abort file will be put. + (Envisioned as ${TESTBOX_PATH_SCRATCH}/../driver, see above.) + +TESTBOX_PATH_UPLOAD + The absolute path to the upload directory for the testbox. This is for + putting VOBs, PNGs, core dumps, crash dumps, and such on. The files should be + bzipped or zipped if they aren't compress already. The names should contain + the testbox and test set ID. + +TESTBOX_REPORTER + The name of the test reporter back end. If not present, it will default to + the local reporter. + +TESTBOX_TEST_SET_ID + The test set ID if we're running. + This is not required by the local reporter. + +TESTBOX_MANAGER_URL + The URL to the test manager. + This is not required by the local reporter. + +TESTBOX_XYZ + There will probably be some more of these. + + +Testbox Environment: Core Utilities +----------------------------------- + +The testbox will not provide the typical unix /bin and /usr/bin utilities. In +other words, cygwin will not be used on Windows! + +The testbox will provide the unixy utilties that ships with kBuild and possibly +some additional ones from tools/*.*/bin in the VirtualBox tree (wget, unzip, +zip, and so on). The test drivers will avoid invoking any of these utilites +directly and instead rely on generic utility methods in the test driver +framework. That way we can more easily reimplement the functionality of the +core utilites and drop the dependency on them. It also allows us to quickly +work around platform specific oddities and bugs. + + +Test Drivers +------------ + +The test drivers are programs that will do the actual testing. In addition to +run under the testbox script, they can be executed in the VirtualBox development +environment. This is important for bug analysis and for simplifying local +testing by the developers before commiting changes. It also means the test +drivers can be developed locally in the VirtualBox development environment. + +The main difference between executing a driver under the testbox script and +running it manually is that there is no test manager in the latter case. The +test result reporter will not talk to the server, but report things to a local +log file and/or standard out/err. When invoked manually, all the necessary +arguments will need to be specified by hand of course - it should be possible +to extract them from a test set as well. + +For the early implementation stages, an implementation of the reporter interface +that talks to the tinderbox base test manager will be needed. This will be +dropped later on when a new test manager is ready. + +As hinted at in other sections, there will be a common framework +(libraries/packages/classes) for taking care of the tedious bits that every +test driver needs to do. Sharing code is essential to easing test driver +development as well as reducing their complexity. The framework will contain: + + - A generic way of submitting output. This will be a generic interface with + multiple implementation, the TESTBOX_REPORTER environment variable + will decide which of them to use. The interface will have very specific + methods to allow the reporter to do a best possible job in reporting the + results to the test manager. + + - Helpers for typical tasks, like: + - Copying files. + - Deleting files, directory trees and scratch space. + - Unzipping files. + - Creating ISOs + - And such things. + + - Helpers for installing and uninstalling VirtualBox. + + - Helpers for defining VMs. (The VBox API where available.) + + - Helpers for controlling VMs. (The VBox API where available.) + +The VirtualBox bits will be separate from the more generic ones, simply because +this is cleaner it will allow us to reuse the system for testing other products. + +The framework will be packaged in a zip file other than the test driver so we +don't waste time and space downloading the same common code. + +The test driver will poll for the file +${TESTBOX_PATH_SCRIPTS}/test-driver-abort and abort all testing when it sees it. + +The test driver can be invoked in three modes: execute, help and cleanup. The +default is execute mode, the help shows an configuration summary and the cleanup +is for cleaning up after a reboot or aborted run. The latter is done by the +testbox script on startup and after abort - the driver is expected to clean up +by itself after a normal run. + + + +The Server Side +=============== + +The server side will be implemented using a webserver (apache), a database +(postgres) and cgi scripts (Python). In addition a cron job (Python) running +once a minute will generate static html for frequently used pages and maybe +execute some other tasks for driving the testing forwards. The order queries +from the testbox script is the primary driving force in the system. The total +makes up the test manager. + +The test manager can be split up into three rough parts: + + - Configuration (of tests, testgroups and testboxes). + - Execution (of tests, collecting and organizing the output). + - Analysis (of test output, mostly about presentation). + + +Test Manager: Requirements +========================== + +List of requirements: + + - Two level testing - L1 quick smoke tests and L2 longer tests performed on + builds passing L1. (Klaus (IIRC) ment this could be realized using + test dependency.) + - Black listing builds (by revision or similar) known to be bad. + - Distinguish between build types so we can do a portion of the testing with + strict builds. + - Easy to re-configure build source for testing different branch or for + testing a release candidate. (Directory based is fine.) + - Useful to be able to partition testboxes (run specific builds on some + boxes, let an engineer have a few boxes for a while). + - Interation with ILOM/...: reset systems. + - Be able to suspend testing on selected testboxes when doing maintance + (where automatically resuming testing on reboot is undesired) or similar + activity. + - Abort testing on seleced testboxes. + - Scheduling of tests requiring more than one testbox. + - Scheduling of tests that cannot be executing concurrently on several + machines because of some global resource like an iSCSI target. + - Jump the scheduling queue. Scheduling of specified test the next time a + testbox is available (optionally specifying which testbox to schedule it + on). + - Configure tests with variable configuration to get better coverage. Two modes: + - TM generates the permutations based on one or more sets of test script arguments. + - Each configuration permuation is specified manually. + - Test specification needs to be flexible (select tests, disable test, test + scheduling (run certain tests nightly), ... ). + - Test scheduling by hour+weekday and by priority. + - Test dependencies (test A depends on test B being successful). + - Historize all configuration data, in particular test configs (permutations + included) and testboxes. + - Test sets has at a minimum a build reference, a testbox reference and a + primary log associated with it. + - Test sets stores further result as a recursive collection of: + - hierachical subtest name (slash sep) + - test parameters / config + - bool fail/succ + - attributes (typed?) + - test time + - e.g. throughput + - subresults + - log + - screenshots, video,... + - The test sets database structure needs to designed such that data mining + can be done in an efficient manner. + - Presentation/analysis: graphs!, categorize bugs, columns reorganizing + grouped by test (hierarchical), overviews, result for last day. + + + +Test Manager: Configuration +=========================== + + +Testboxes +--------- + +Configuration of testboxes doesn't involve much work normally. A testbox +is added manually to the test manager by entering the DNS entry and/or IP +address (the test manager resolves the missing one when necessary) as well as +the system UUID (when obtainable - should be displayed by the testbox script +installer). Queries from unregistered testboxes will be declined as a kind of +security measure, the incident should be logged in the webserver log if +possible. In later dealings with the client the System UUID will be the key +identifier. It's permittable for the IP address to change when the testbox +isn't online, but not while testing (just imagine live migration tests and +network tests). Ideally, the testboxes should not change IP address. + +The testbox edit function must allow changing the name and system UUID. + +One further idea for the testbox configuration is indicating what they are +capable of to filter out tests and test configurations that won't work on that +testbox. To examplify this take the ACP2 installation test. If the test +manager does not make sure the testbox have VT-x or AMD-v capabilities, the test +is surely going to fail. Other testbox capabilities would be total number of +CPU cores, memory size, scratch space. These testbox capabilities should be +collected automatically on bootup by the testbox script together with OS name, +OS version and OS bitness. + +A final thought, instead of outright declining all requests from new testboxes, +we could record the unregistered testboxes with ip, UUID, name, os info and +capabilities but mark them as inactive. The test operator can then activate +them on an activation page or edit the testbox or something. + + +Testcases +--------- + +We use the term testcase for a test. + + +Testgroups +---------- + +Testcases are organized into groups. A testcase can be member of more than one +group. The testcase gets a priority assigned to it in connection with the +group membership. + +Testgroups are picked up by a testbox partition (aka scheduling group) and a +prioirty, scheduling time restriction and dependencies on other test groups are +associated with the assignment. A testgroup can be used by several testbox +partitions. + +(This used to be called 'testsuites' but was renamed to avoid confusion with +the VBox Test Suite.) + + +Scheduling +---------- + +The initial scheduler will be modelled after what we're doing already on in the +tinderbox driven testing. It's best described as a best effort continuous +integration scheduler. Meaning, it will always use the latest build suitable +for a testcase. It will schedule on a testcase level, using the combined +priority of the testcase in the test group and the test group with the testbox +partition, trying to spread the test case argument varation out accordingly +over the whole scheduilng queue. Which argument variation to start with, is +not undefined (random would be best). + +Later, we may add other schedulers as needed. + + + +The Test Manager Database +========================= + +First a general warning: + + The guys working on this design are not database experts, web + programming experts or similar, rather we are low level guys + who's main job is x86 & AMD64 virtualization. So, please don't + be too hard on us. :-) + + +A logical table layout can be found in TestManagerDatabaseMap.png (created by +Oracle SQL Data Modeler, stored in TestManagerDatabase.dmd). The physical +database layout can be found in TestManagerDatabaseInit.pgsql postgreSQL +script. The script is commented. + + +Data History +------------ + +We need to somehow track configuration changes over time. We also need to +be able to query the exact configuration a test set was run with so we can +understand and make better use of the results. + +There are different techniques for archiving this, one is tuple-versioning +( http://en.wikipedia.org/wiki/Tuple-versioning ), another is log trigger +( http://en.wikipedia.org/wiki/Log_trigger ). We use tuple-versioning in +this database, with 'effective' as start date field name and 'expire' as +the end (exclusive). + +Tuple-versioning has a shortcomming wrt to keys, both primary and foreign. +The primary key of a table employing tuple-versioning is really +'id' + 'valid_period', where the latter is expressed using two fields +([effective...expire-1]). Only, how do you tell the database engine that +it should not allow overlapping valid_periods? Useful suggestions are +welcomed. :-) + +Foreign key references to a table using tuple-versioning is running into +trouble because of the time axsis and that to our knowledge foreign keys +must reference exactly one row in the other table. When time is involved +what we wish to tell the database is that at any given time, there actually +is exactly one row we want to match in the other table, only we've no idea +how to express this. So, many foreign keys are not expressed in SQL of this +database. + +In some cases, we extend the tuple-versioning with a generation ID so that +normal foreign key referencing can be used. We only use this for recording +(references in testset) and scheduling (schedqueue), as using it more widely +would force updates (gen_id changes) to propagate into all related tables. + +See also: + - http://en.wikipedia.org/wiki/Slowly_changing_dimension + - http://en.wikipedia.org/wiki/Change_data_capture + - http://en.wikipedia.org/wiki/Temporal_database + + + +Test Manager: Execution +======================= + + + +Test Manager: Scenarios +======================= + + + +#1 - Testbox Signs On (At Bootup) +--------------------------------- + +The testbox supplies a number of inputs when reporting for duty: + - IP address. + - System UUID. + - OS name. + - OS version. + - CPU architecture. + - CPU count (= threads). + - CPU VT-x/AMD-V capability. + - CPU nested paging capability. + - Chipset I/O MMU capability. + - Memory size. + - Scratch size space (for testing). + - Testbox Script revision. + +Results: + - ACK or NACK. + - Testbox ID and name on ACK. + +After receiving a ACK the testbox will ask for work to do, i.e. continue with +scenario #2. In the NACK case, it will sleep for 60 seconds and try again. + + +Actions: + +1. Validate the testbox by looking the UUID up in the TestBoxes table. + If not found, NACK the request. SQL:: + + SELECT idTestBox, sName + FROM TestBoxes + WHERE uuidSystem = :sUuid + AND tsExpire = 'infinity'::timestamp; + +2. Check if any of the information by testbox script has changed. The two + sizes are normalized first, memory size rounded to nearest 4 MB and scratch + space is rounded down to nearest 64 MB. If anything changed, insert a new + row in the testbox table and historize the current one, i.e. set + OLD.tsExpire to NEW.tsEffective and get a new value for NEW.idGenTestBox. + +3. Check with TestBoxStatuses: + a) If there is an row for the testbox in it already clean up change it + to 'idle' state and deal with any open testset like described in + scenario #9. + b) If there is no row, add one with 'idle' state. + +4. ACK the request and pass back the idTestBox. + + +Note! Testbox.enabled is not checked here, that is only relevant when it asks + for a new task (scenario #2 and #5). + +Note! Should the testbox script detect changes in any of the inputs, it should + redo the sign in. + +Note! In scenario #8, the box will not sign on until it has done the reboot and + cleanup reporting! + + +#2 - Testbox Asks For Work To Do +--------------------------------- + + +Inputs: + - The testbox is supplying its IP indirectly. + - The testbox should supply its UUID and ID directly. + +Results: + - IDLE, WAIT, EXEC, REBOOT, UPGRADE, UPGRADE-AND-REBOOT, SPECIAL or DEAD. + +Actions: + +1. Validate the ID and IP by selecting the currently valid testbox row:: + + SELECT idGenTestBox, fEnabled, idSchedGroup, enmPendingCmd + FROM TestBoxes + WHERE id = :id + AND uuidSystem = :sUuid + AND ip = :ip + AND tsExpire = 'infinity'::timestamp; + + If NOT found return DEAD to the testbox client (it will go back to sign on + mode and retry every 60 seconds or so - see scenario #1). + + Note! The WUI will do all necessary clean-ups when deleting a testbox, so + contrary to the initial plans, we don't need to do anything more for + the DEAD status. + +2. Check with TestBoxStatuses (maybe joined with query from 1). + + If enmState is 'gang-gathering': Goto scenario #6 on timeout or pending + 'abort' or 'reboot' command. Otherwise, tell the testbox to WAIT [done]. + + If enmState is 'gang-testing': The gang has been gathered and execution + has been triggered. Goto 5. + + If enmState is not 'idle', change it to 'idle'. + + If idTestSet is not NULL, CALL scenario #9 to it up. + + If there is a pending abort command, remove it. + + If there is a pending command and the old state doesn't indicate that it was + being executed, GOTO scenario #3. + + Note! There should be a TestBoxStatuses row after executing scenario #1, + however should none be found for some funky reason, returning DEAD + will fix the problem (see above) + +3. If the testbox was marked as disabled, respond with an IDLE command to the + testbox [done]. (Note! Must do this after TestBoxStatuses maintainance from + point 2, or abandoned tests won't be cleaned up after a testbox is disabled.) + +4. Consider testcases in the scheduling queue, pick the first one which the + testbox can execute. There is a concurrency issue here, so we put and + exclusive lock on the SchedQueues table while considering its content. + + The cursor we open looks something like this:: + + SELECT idItem, idGenTestCaseArgs, + idTestSetGangLeader, cMissingGangMembers + FROM SchedQueues + WHERE idSchedGroup = :idSchedGroup + AND ( bmHourlySchedule is NULL + OR get_bit(bmHourlySchedule, :iHourOfWeek) = 1 ) --< does this work? + ORDER BY ASC idItem; + + If there no rows are returned (this can happen because no testgroups are + associated with this scheduling group, the scheduling group is disabled, + or because the queue is being regenerated), we will tell the testbox to + IDLE [done]. + + For each returned row we will: + a) Check testcase/group dependencies. + b) Select a build (and default testsuite) satisfying the dependencies. + c) Check the testcase requirements with that build in mind. + d) If idTestSetGangLeader is NULL, try allocate the necessary resources. + e) If it didn't check out, fetch the next row and redo from (a). + f) Tentatively create a new test set row. + g) If not gang scheduling: + - Next state: 'testing' + ElIf we're the last gang participant: + - Set idTestSetGangLeader to NULL. + - Set cMissingGangMembers to 0. + - Next state: 'gang-testing' + ElIf we're the first gang member: + - Set cMissingGangMembers to TestCaseArgs.cGangMembers - 1. + - Set idTestSetGangLeader to our idTestSet. + - Next state: 'gang-gathering' + Else: + - Decrement cMissingGangMembers. + - Next state: 'gang-gathering' + + If we're not gang scheduling OR cMissingGangMembers is 0: + Move the scheduler queue entry to the end of the queue. + + Update our TestBoxStatuses row with the new state and test set. + COMMIT; + +5. If state is 'testing' or 'gang-testing': + EXEC reponse. + + The EXEC response for a gang scheduled testcase includes a number of + extra arguments so that the script knows the position of the testbox + it is running on and of the other members. This means the that the + TestSet.iGangMemberNo is passed using --gang-member-no and the IP + addresses of the all gang members using --gang-ipv4-<memb-no> <ip>. + Else (state is 'gang-gathering'): + WAIT + + + +#3 - Pending Command When Testbox Asks For Work +----------------------------------------------- + +This is a subfunction of scenario #2 and #5. + +As seen in scenario #2, the testbox will send 'abort' commands to /dev/null +when it finds one when not executing a test. This includes when it reports +that the test has completed (no need to abort a completed test, wasting lot +of effort when standing at the finish line). + +The other commands, though, are passed back to the testbox. The testbox +script will respond with an ACK or NACK as it sees fit. If NACKed, the +pending command will be removed (pending_cmd set to none) and that's it. +If ACKed, the state of the testbox will change to that appropriate for the +command and the pending_cmd set to none. Should the testbox script fail to +respond, the command will be repeated the next time it asks for work. + + + +#4 - Testbox Uploads Results During Test +---------------------------------------- + + +TODO + + +#5 - Testbox Completes Test and Asks For Work +--------------------------------------------- + +This is very similar to scenario #2 + +TODO + + +#6 - Gang Gathering Timeout +--------------------------- + +This is a subfunction of scenario #2. + +When gathering a gang of testboxes for a testcase, we do not want to wait +forever and have testboxes doing nothing for hours while waiting for partners. +So, the gathering has a reasonable timeout (imagine something like 20-30 mins). + +Also, we need some way of dealing with 'abort' and 'reboot' commands being +issued while waiting. The easy way out is pretent it's a time out. + +When changing the status to 'gang-timeout' we have to be careful. First of all, +we need to exclusively lock the SchedQueues and TestBoxStatuses (in that order) +and re-query our status. If it changed redo the checks in scenario #2 point 2. + +If we still want to timeout/abort, change the state from 'gang-gathering' to +'gang-gathering-timedout' on all the gang members that has gathered so far. +Then reset the scheduling queue record and move it to the end of the queue. + + +When acting on 'gang-timeout' the TM will fail the testset in a manner similar +to scenario #9. No need to repeat that. + + + +#7 - Gang Cleanup +----------------- + +When a testbox completes a gang scheduled test, we will have to serialize +resource cleanup (both globally and on testboxes) as they stop. More details +can be found in the documentation of 'gang-cleanup'. + +So, the transition from 'gang-testing' is always to 'gang-cleanup'. When we +can safely leave 'gang-cleanup' is decided by the query:: + + SELECT COUNT(*) + FROM TestBoxStatuses, + TestSets + WHERE TestSets.idTestSetGangLeader = :idTestSetGangLeader + AND TestSets.idTestBox = TestBoxStatuses.idTestBox + AND TestBoxStatuses.enmState = 'gang-running'::TestBoxState_T; + +As long as there are testboxes still running, we stay in the 'gang-cleanup' +state. Once there are none, we continue closing the testset and such. + + + +#8 - Testbox Reports A Crash During Test Execution +-------------------------------------------------- + +TODO + + +#9 - Cleaning Up Abandoned Testcase +----------------------------------- + +This is a subfunction of scenario #1 and #2. The actions taken are the same in +both situations. The precondition for taking this path is that the row in the +testboxstatus table is refering to a testset (i.e. testset_id is not NULL). + + +Actions: + +1. If the testset is incomplete, we need to completed: + a) Add a message to the root TestResults row, creating one if necesary, + that explains that the test was abandoned. This is done + by inserting/finding the string into/in TestResultStrTab and adding + a row to TestResultMsgs with idStrMsg set to that string id and + enmLevel set to 'failure'. + b) Mark the testset as failed. + +2. Free any global resources referenced by the test set. This is done by + deleting all rows in GlobalResourceStatuses matching the testbox id. + +3. Set the idTestSet to NULL in the TestBoxStatuses row. + + + +#10 - Cleaning Up a Disabled/Dead TestBox +----------------------------------------- + +The UI needs to be able to clean up the remains of a testbox which for some +reason is out of action. Normal cleaning up of abandoned testcases requires +that the testbox signs on or asks for work, but if the testbox is dead or +in some way indisposed, it won't be doing any of that. So, the testbox +sheriff needs to have a way of cleaning up after it. + +It's basically a manual scenario #9 but with some safe guards, like checking +that the box hasn't been active for the last 1-2 mins (max idle/wait time * 2). + + +Note! When disabling a box that still executing the testbox script, this + cleanup isn't necessary as it will happen automatically. Also, it's + probably desirable that the testbox finishes what ever it is doing first + before going dormant. + + + +Test Manager: Analysis +======================= + +One of the testbox sheriff's tasks is to try figure out the reason why something +failed. The test manager will provide facilities for doing so from very early +in it's implementation. + + +We need to work out some useful status reports for the early implementation. +Later there will be more advanced analysis tools, where for instance we can +create graphs from selected test result values or test execution times. + + + +Implementation Plan +=================== + +This has changed for various reasons. The current plan is to implement the +infrastructure (TM & testbox script) first and do a small deployment with the +2-5 test drivers in the Testsuite as basis. Once the bugs are worked out, we +will convert the rest of the tests and start adding new ones. + +We just need to finally get this done, no point in doing it piecemeal by now! + + +Test Manager Implementation Sub-Tasks +------------------------------------- + +The implementation of the test manager and adjusting/completing of the testbox +script and the test drivers are tasks which can be done by more than one +person. Splitting up the TM implementation into smaller tasks should allow +parallel development of different tasks and get us working code sooner. + + +Milestone #1 +------------ + +The goal is to getting the fundamental testmanager engine implemented, debugged +and working. With the exception of testboxes, the configuration will be done +via SQL inserts. + +Tasks in somewhat prioritized order: + + - Kick off test manager. It will live in testmanager/. Salvage as much as + possible from att/testserv. Create basic source and file layout. + + - Adjust the testbox script, part one. There currently is a testbox script + in att/testbox, this shall be moved up into testboxscript/. The script + needs to be adjusted according to the specification layed down earlier + in this document. Installers or installation scripts for all relevant + host OSes are required. Left for part two is result reporting beyond the + primary log. This task must be 100% feature complete, on all host OSes, + there is no room for FIXME, XXX or @todo here. + + - Implement the schedule queue generator. + + - Implement the testbox dispatcher in TM. Support all the testbox script + responses implemented above, including upgrading the testbox script. + + - Implement simple testbox management page. + + - Implement some basic activity and result reports so that we can see + what's going on. + + - Create a testmanager / testbox test setup. This lives in selftest/. + + 1. Set up something that runs, no fiddly bits. Debug till it works. + 2. Create a setup that tests testgroup dependencies, i.e. real tests + depending on smoke tests. + 3. Create a setup that exercises testcase dependency. + 4. Create a setup that exercises global resource allocation. + 5. Create a setup that exercises gang scheduling. + + - Check that all features work. + + +Milestone #2 +------------ + +The goal is getting to VBox testing. + +Tasks in somewhat prioritized order: + + - Implement full result reporting in the testbox script and testbox driver. + A testbox script specific reporter needs to be implemented for the + testdriver framework. The testbox script needs to forward the results to + the test manager, or alternatively the testdriver report can talk + directly to the TM. + + - Implement the test manager side of the test result reporting. + + - Extend the selftest with some setup that report all kinds of test + results. + + - Implement script/whatever feeding builds to the test manager from the + tinderboxes. + + - The toplevel test driver is a VBox thing that must be derived from the + base TestDriver class or maybe the VBox one. It should move from + toptestdriver to testdriver and be renamed to vboxtltd or smth. + + - Create a vbox testdriver that boots the t-xppro VM once and that's it. + + - Create a selftest setup which tests booting t-xppro taking builds from + the tinderbox. + + +Milestone #3 +------------ + +The goal for this milestone is configuration and converting current testscases, +the result will be the a minimal test deployment (4-5 new testboxes). + +Tasks in somewhat prioritized order: + + - Implement testcase configuration. + + - Implement testgroup configuration. + + - Implement build source configuration. + + - Implement scheduling group configuration. + + - Implement global resource configuration. + + - Re-visit the testbox configuration. + + - Black listing of builds. + + - Implement simple failure analysis and reporting. + + - Implement the initial smoke tests modelled on the current smoke tests. + + - Implement installation tests for Windows guests. + + - Implement installation tests for Linux guests. + + - Implement installation tests for Solaris guest. + + - Implement installation tests for OS/2 guest. + + - Set up a small test deployment. + + +Further work +------------ + +After milestone #3 has been reached and issues found by the other team members +have been addressed, we will probably go for full deployment. + +Beyond this point we will need to improve reporting and analysis. There may be +configuration aspects needing reporting as well. + +Once deployed, a golden rule will be that all new features shall have test +coverage. Preferrably, implemented by someone else and prior to the feature +implementation. + + + + +Discussion Logs +=============== + +2009-07-21,22,23 Various Discussions with Michal and/or Klaus +------------------------------------------------------------- + +- Scheduling of tests requiring more than one testbox. +- Scheduling of tests that cannot be executing concurrently on several machines + because of some global resource like an iSCSI target. +- Manually create the test config permutations instead of having the test + manager create all possible ones and wasting time. +- Distinguish between built types so we can run smoke tests on strick builds as + well as release ones. + + +2009-07-20 Brief Discussion with Michal +---------------------------------------- + +- Installer for the testbox script to make bringing up a new testbox even + smoother. + + +2009-07-16 Raw Input +-------------------- + +- test set. recursive collection of: + - hierachical subtest name (slash sep) + - test parameters / config + - bool fail/succ + - attributes (typed?) + - test time + - e.g. throughput + - subresults + - log + - screenshots,.... + +- client package (zip) dl from server (maybe client caching) + + +- thoughts on bits to do at once. + - We *really* need the basic bits ASAP. + - client -> support for test driver + - server -> controls configs + - cleanup on both sides + + +2009-07-15 Raw Input +-------------------- + +- testing should start automatically +- switching to branch too tedious +- useful to be able to partition testboxes (run specific builds on some boxes, let an engineer have a few boxes for a while). +- test specification needs to be more flexible (select tests, disable test, test scheduling (run certain tests nightly), ... ) +- testcase dependencies (blacklisting builds, run smoketests on box A before long tests on box B, ...) +- more testing flexibility, more test than just install/moke. For instance unit tests, benchmarks, ... +- presentation/analysis: graphs!, categorize bugs, columns reorganizing grouped by test (hierarchical), overviews, result for last day. +- testcase specificion, variables (e.g. I/O-APIC, SMP, HWVIRT, SATA...) as sub-tests +- interation with ILOM/...: reset systems +- Changes needs LDAP authentication +- historize all configuration w/ name +- ability to run testcase locally (provided the VDI/ISO/whatever extra requirements can be met). + + +----- + +.. [1] no such footnote + +----- + +:Status: $Id: AutomaticTestingRevamp.txt $ +:Copyright: Copyright (C) 2010-2017 Oracle Corporation. + diff --git a/src/VBox/ValidationKit/docs/Makefile.kmk b/src/VBox/ValidationKit/docs/Makefile.kmk new file mode 100644 index 00000000..658c2a94 --- /dev/null +++ b/src/VBox/ValidationKit/docs/Makefile.kmk @@ -0,0 +1,56 @@ +# $Id: Makefile.kmk $ +## @file +# VirtualBox Validation Kit - Makefile for generating .html from .txt. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# + +DEPTH = ../../../.. +include $(KBUILD_PATH)/header.kmk + +# Figure out where rst2html.py is. +ifndef VBOX_RST2HTML + VBOX_RST2HTML := $(firstword $(which $(foreach pyver, 3.2 3.1 3.0 2.8 2.7 2.6 2.5 2.4 ,rst2html-$(pyver).py) ) ) + ifeq ($(VBOX_RST2HTML),) + if $(KBUILD_HOST) == "win" && $(VBOX_BLD_PYTHON) != "" && $(dir $(VBOX_BLD_PYTHON)) != "./" + VBOX_RST2HTML := $(dir $(VBOX_BLD_PYTHON))Scripts/rst2html.py + else + VBOX_RST2HTML := rst2html.py + endif + endif + if1of ($(KBUILD_HOST),win) + VBOX_RST2HTML := $(VBOX_BLD_PYTHON) $(VBOX_RST2HTML) + endif +endif + +GENERATED_FILES = AutomaticTestingRevamp.html VBoxValidationKitReadMe.html TestBoxImaging.html + +all: $(GENERATED_FILES) + +$(foreach html,$(GENERATED_FILES) \ +,$(eval $(html): $(basename $(html)).txt ; $$(REDIRECT) -E LC_ALL=C -- $$(VBOX_RST2HTML) --no-generator $$< $$@)) + +$(foreach html,$(GENERATED_FILES), $(eval $(basename $(html)).o:: $(html))) # editor compile aliases + +clean: + kmk_builtin_rm -f -- $(GENERATED_FILES) + diff --git a/src/VBox/ValidationKit/docs/TestBoxImaging.html b/src/VBox/ValidationKit/docs/TestBoxImaging.html new file mode 100644 index 00000000..4064ab99 --- /dev/null +++ b/src/VBox/ValidationKit/docs/TestBoxImaging.html @@ -0,0 +1,736 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.12: http://docutils.sourceforge.net/" /> +<title></title> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: TestBoxImaging.html $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title, .code .error { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em ; + margin-right: 2em } + +pre.code .ln { color: grey; } /* line numbers */ +pre.code, code { background-color: #eeeeee } +pre.code .comment, code .comment { color: #5C6576 } +pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } +pre.code .literal.string, code .literal.string { color: #0C5404 } +pre.code .name.builtin, code .name.builtin { color: #352B84 } +pre.code .deleted, code .deleted { background-color: #DEB0A1} +pre.code .inserted, code .inserted { background-color: #A3D289} + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +/* "booktabs" style (no vertical lines) */ +table.docutils.booktabs { + border: 0px; + border-top: 2px solid; + border-bottom: 2px solid; + border-collapse: collapse; +} +table.docutils.booktabs * { + border: 0px; +} +table.docutils.booktabs th { + border-bottom: thin solid; + text-align: left; +} + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document"> + + +<div class="section" id="testbox-imaging-backup-restore"> +<h1>Testbox Imaging (Backup / Restore)</h1> +<div class="section" id="introduction"> +<h2>Introduction</h2> +<p>This document is explores deloying a very simple drive imaging solution to help +avoid needing to manually reinstall testboxes when a disk goes bust or the OS +install seems to be corrupted.</p> +</div> +</div> +<div class="section" id="definitions-glossary"> +<h1>Definitions / Glossary</h1> +<p>See AutomaticTestingRevamp.txt.</p> +</div> +<div class="section" id="objectives"> +<h1>Objectives</h1> +<blockquote> +<ul class="simple"> +<li>Off site, no admin interaction (no need for ILOM or similar).</li> +<li>OS independent.</li> +<li>Space and bandwidth efficient.</li> +<li>As automatic as possible.</li> +<li>Logging.</li> +</ul> +</blockquote> +</div> +<div class="section" id="overview-of-the-solution"> +<h1>Overview of the Solution</h1> +<p>Here is a brief summary:</p> +<blockquote> +<ul class="simple"> +<li>Always boot testboxes via PXE using PXELINUX.</li> +<li>Default configuration is local boot (hard disk / SSD)</li> +<li>Restore/backup action triggered by machine specific PXE config.</li> +<li>Boots special debian maintenance install off NFS.</li> +<li>A maintenance service (systemd style) does the work.</li> +<li>The service reads action from TFTP location and performs it.</li> +<li>When done the service removes the TFTP machine specific config +and reboots the system.</li> +</ul> +</blockquote> +<dl class="docutils"> +<dt>Maintenance actions are:</dt> +<dd><ul class="first last simple"> +<li>backup</li> +<li>backup-again</li> +<li>restore</li> +<li>refresh-info</li> +<li>rescue</li> +</ul> +</dd> +</dl> +<p>Possible modifier that indicates a subset of disk on testboxes with other OSes +installed. Support for partition level backup/restore is not explored here.</p> +<div class="section" id="how-to-use"> +<h2>How to use</h2> +<p>To perform one of the above maintenance actions on a testbox, run the +<tt class="docutils literal"><span class="pre">testbox-pxe-conf.sh</span></tt> script:</p> +<pre class="literal-block"> +/mnt/testbox-tftp/pxeclient.cfg/testbox-pxe-conf.sh 10.165.98.220 rescue +</pre> +<p>Then trigger a reboot. The box will then boot the NFS rooted debian image and +execute the maintenance action. On success, it will remove the testbox hex-IP +config file and reboot again.</p> +</div> +</div> +<div class="section" id="storage-server"> +<h1>Storage Server</h1> +<p>The storage server will have three areas used here. Using NFS for all three +avoids extra work getting CIFS sharing right too (NFS is already a pain).</p> +<blockquote> +<ol class="arabic simple"> +<li>/export/testbox-tftp - TFTP config area. Read-write.</li> +<li>/export/testbox-backup - Images and logs. Read-write.</li> +<li>/export/testbox-nfsroot - Custom debian. Read-only, no root squash.</li> +</ol> +</blockquote> +</div> +<div class="section" id="tftp-export-testbox-tftp"> +<h1>TFTP (/export/testbox-tftp)</h1> +<p>The testbox-tftp share needs to be writable, root squashing is okay.</p> +<p>We need files from both PXELINUX and SYSLINUX to make this work now. On a +debian system, the <tt class="docutils literal">pxelinux</tt> and <tt class="docutils literal">syslinux</tt> packages needs to be +installed. We actually do this further down when setting up the nfsroot, so +it's possible to get them from there by postponing this step a little. On +debian 8.6.0 the PXELINUX files are found in <tt class="docutils literal">/usr/lib/PXELINUX</tt> and the +SYSLINUX ones in <tt class="docutils literal">/usr/lib/syslinux</tt>.</p> +<p>The initial PXE image as well as associated modules comes in three variants, +BIOS, 32-bit EFI and 64-bit EFI. We'll only need the BIOS one for now. +Perform the following copy operations:</p> +<pre class="literal-block"> +cp /usr/lib/PXELINUX/pxelinux.0 /mnt/testbox-tftp/ +cp /usr/lib/syslinux/modules/*/ldlinux.* /mnt/testbox-tftp/ +cp -R /usr/lib/syslinux/modules/bios /mnt/testbox-tftp/ +cp -R /usr/lib/syslinux/modules/efi32 /mnt/testbox-tftp/ +cp -R /usr/lib/syslinux/modules/efi64 /mnt/testbox-tftp/ +</pre> +<p>For simplicitly, all the testboxes boot using good old fashioned BIOS, no EFI. +However, it doesn't really hurt to be prepared.</p> +<p>The PXELINUX related files goes in the root of the testbox-tftp share. (As +mentioned further down, these can be installed on a debian system by running +<tt class="docutils literal"><span class="pre">apt-get</span> install pxelinux syslinux</tt>.) We need the <tt class="docutils literal">*pxelinux.0</tt> files +typically found in <tt class="docutils literal">/usr/lib/PXELINUX/</tt> on debian systems (recent ones +anyway). It is possible we may need one ore more fo the modules <a class="footnote-reference" href="#id6" id="id1">[1]</a> that +ships with PXELINUX/SYSLINUX, so do copy <tt class="docutils literal">/usr/lib/syslinux/modules</tt> to +<tt class="docutils literal"><span class="pre">testbox-tftp/modules</span></tt> as well.</p> +<p>The directory layout related to the configuration files is dictated by the +PXELINUX configuration file searching algorithm <a class="footnote-reference" href="#id7" id="id2">[2]</a>. Create a subdirectory +<tt class="docutils literal">pxelinux.cfg/</tt> under <tt class="docutils literal"><span class="pre">testbox-tftp</span></tt> and create the world readable file +<tt class="docutils literal">default</tt> with the following content:</p> +<pre class="literal-block"> +PATH bios +DEFAULT local-boot +LABEL local-boot +LOCALBOOT +</pre> +<p>This will make the default behavior to boot the local disk system.</p> +<p>Copy the <tt class="docutils literal"><span class="pre">testbox-pxe-conf.sh</span></tt> script file found in the same directory as +this document to <tt class="docutils literal"><span class="pre">/mnt/testbox-tftp/pxelinux.cfg/</span></tt>. Edit the copy to correct +the IP addresses near the top, as well as any linux, TFTP and PXE details near +the bottom of the file. This script will generate the PXE configuration file +when performing maintenance on a testbox.</p> +</div> +<div class="section" id="images-and-logs-export-testbox-backup"> +<h1>Images and logs (/export/testbox-backup)</h1> +<p>The testbox-backup share needs to be writable, root squashing is okay.</p> +<p>In the root there must be a file <tt class="docutils literal"><span class="pre">testbox-backup</span></tt> so we can easily tell +whether we've actually mounted the share or are just staring at an empty mount +point directory.</p> +<p>The <tt class="docutils literal"><span class="pre">testbox-maintenance.sh</span></tt> script maintains a global log in the root +directory that's called <tt class="docutils literal">maintenance.log</tt>. Errors will be logged there as +well as a ping and the action.</p> +<p>We use a directory layout based on dotted decimal IP addresses here, so for a +server with the IP 10.40.41.42 all its file will be under <tt class="docutils literal">10.40.41.42/</tt>:</p> +<dl class="docutils"> +<dt><tt class="docutils literal"><hostname></tt></dt> +<dd>The name of the testbox (empty file). Help finding a testbox by name.</dd> +<dt><tt class="docutils literal"><span class="pre">testbox-info.txt</span></tt></dt> +<dd>Information about the testbox. Starting off with the name, decimal IP, +PXELINUX style hexadecimal IP, and more.</dd> +<dt><tt class="docutils literal">maintenance.log</tt></dt> +<dd>Maintenance log file recording what the maintenance service does.</dd> +<dt><tt class="docutils literal"><span class="pre">disk-devices.lst</span></tt></dt> +<dd>Optional list of disk devices to consider backuping up or restoring. This is +intended for testboxes with additional disks that are used for other purposes +and should touched.</dd> +<dt><tt class="docutils literal">sda.raw.gz</tt></dt> +<dd>The gzipped raw copy of the sda device of the testbox.</dd> +<dt><tt class="docutils literal"><span class="pre">sd[bcdefgh].raw.gz</span></tt></dt> +<dd>The gzipped raw copy sdb, sdc, sde, sdf, sdg, sdh, etc if any of them exists +and are disks/SSDs.</dd> +<dt>Note! If it turns out we can be certain to get a valid host name, we might just</dt> +<dd>switch to use the hostname as the directory name instead of the IP.</dd> +</dl> +</div> +<div class="section" id="debian-nfs-root-export-testbox-nfsroot"> +<h1>Debian NFS root (/export/testbox-nfsroot)</h1> +<p>The testbox-nfsroot share should be read-only and must <strong>not</strong> have root +squashing enabled. Also, make sure setting the set-uid-bit is allowed by the +server, or <tt class="docutils literal">su` and ``sudo</tt> won't work</p> +<p>There are several ways of creating a debian nfsroot, but since we've got a +tool like VirtualBox around we've just installed it in a VM, prepared it, +and copied it onto the NFS server share.</p> +<p>As of writing debian 8.6.0 is current, so a minimal 64-bit install of it was +done in a VM. After installation the following modifications was done:</p> +<blockquote> +<ul> +<li><p class="first"><tt class="docutils literal"><span class="pre">apt-get</span> install pxelinux syslinux <span class="pre">initramfs-tools</span> zip gddrescue sudo joe</tt> +and optionally <tt class="docutils literal"><span class="pre">apt-get</span> install smbclient <span class="pre">cifs-utils</span></tt>.</p> +</li> +<li><p class="first"><tt class="docutils literal">/etc/default/grub</tt> was modified to set <tt class="docutils literal">GRUB_CMDLINE_LINUX_DEFAULT</tt> to +<tt class="docutils literal">""</tt> instead of <tt class="docutils literal">"quiet"</tt>. This allows us to see messages during boot +and perhaps spot why something doesn't work on a testbox. Regenerate the +grub configuration file by running <tt class="docutils literal"><span class="pre">update-grub</span></tt> afterwards.</p> +</li> +<li><p class="first"><tt class="docutils literal">/etc/sudoers</tt> was modified to allow the <tt class="docutils literal">vbox</tt> user use sudo without +requring any password.</p> +</li> +<li><p class="first">Create the directory <tt class="docutils literal">/etc/systemd/system/getty@tty1.service.d</tt> and create +the file <tt class="docutils literal">noclear.conf</tt> in it with the following content:</p> +<pre class="literal-block"> +[Service] +TTYVTDisallocate=no +</pre> +<p>This stops getty from clearing VT1 and let us see the tail of the boot up +messages, which includes messages from the testbox-maintenance service.</p> +</li> +<li><p class="first">Mount the testbox-nfsroot under <tt class="docutils literal">/mnt/</tt> with write privileges. (The write +privileges are temporary - don't forget to remove them later on.):</p> +<pre class="literal-block"> +mount -t nfs myserver.com:/export/testbox-nfsroot +</pre> +<p>Note! Adding <tt class="docutils literal"><span class="pre">-o</span> nfsvers=3</tt> may help with some NTFv4 servers.</p> +</li> +<li><p class="first">Copy the debian root and dev file system onto nfsroot. If you have ssh +access to the NFS server, the quickest way to do it is to use <tt class="docutils literal">tar</tt>:</p> +<pre class="literal-block"> +tar -cz --one-file-system -f /mnt/testbox-maintenance-nfsroot.tar.gz . dev/ +</pre> +<p>An alternative is <tt class="docutils literal">cp <span class="pre">-ax</span> . /mnt/. && cp <span class="pre">-ax</span> dev/. /mnt/dev/.</tt> but this +is quite a bit slower, obviously.</p> +</li> +<li><p class="first">Edit <tt class="docutils literal">/etc/ssh/sshd_config</tt> setting <tt class="docutils literal">PermitRootLogin</tt> to <tt class="docutils literal">yes</tt> so we can ssh +in as root later on.</p> +</li> +<li><p class="first">chroot into the nfsroot: <tt class="docutils literal">chroot /mnt/</tt></p> +<blockquote> +<ul> +<li><p class="first"><tt class="docutils literal">mount <span class="pre">-o</span> proc proc /proc</tt></p> +</li> +<li><p class="first"><tt class="docutils literal">mount <span class="pre">-o</span> sysfs sysfs /sys</tt></p> +</li> +<li><p class="first"><tt class="docutils literal">mkdir <span class="pre">/mnt/testbox-tftp</span> <span class="pre">/mnt/testbox-backup</span></tt></p> +</li> +<li><p class="first">Recreate <tt class="docutils literal">/etc/fstab</tt> with:</p> +<pre class="literal-block"> +proc /proc proc defaults 0 0 +/dev/nfs / nfs defaults 1 1 +10.42.1.1:/export/testbox-tftp /mnt/testbox-tftp nfs tcp,nfsvers=3,noauto 2 2 +10.42.1.1:/export/testbox-backup /mnt/testbox-backup nfs tcp,nfsvers=3,noauto 3 3 +</pre> +<p>We use NFS version 3 as that works better for our NFS server and client, +remove if not necessary. The <tt class="docutils literal">noauto</tt> option is to work around mount +trouble during early bootup on some of our boxes.</p> +</li> +<li><p class="first">Do <tt class="docutils literal">mount <span class="pre">/mnt/testbox-tftp</span> && mount <span class="pre">/mnt/testbox-backup</span></tt> to mount the +two shares. This may be a good time to execute the instructions in the +sections above relating to these two shares.</p> +</li> +<li><p class="first">Edit <tt class="docutils literal"><span class="pre">/etc/initramfs-tools/initramfs.conf</span></tt> and change the <tt class="docutils literal">MODULES</tt> +value from <tt class="docutils literal">most</tt> to <tt class="docutils literal">netboot</tt>.</p> +</li> +<li><p class="first">Append <tt class="docutils literal">aufs</tt> to <tt class="docutils literal"><span class="pre">/etc/initramfs-tools/modules</span></tt>. The advanced +multi-layered unification filesystem (aufs) enables us to use a +read-only NFS root. <a class="footnote-reference" href="#id8" id="id3">[3]</a> <a class="footnote-reference" href="#id9" id="id4">[4]</a> <a class="footnote-reference" href="#id10" id="id5">[5]</a></p> +</li> +<li><p class="first">Create <tt class="docutils literal"><span class="pre">/etc/initramfs-tools/scripts/init-bottom/00_aufs_init</span></tt> as +an executable file with the following content:</p> +<pre class="literal-block"> +#!/bin/sh +# Don't run during update-initramfs: +case "$1" in + prereqs) + exit 0; + ;; +esac + +modprobe aufs +mkdir -p /ro /rw /aufs +mount -t tmpfs tmpfs /rw -o noatime,mode=0755 +mount --move $rootmnt /ro +mount -t aufs aufs /aufs -o noatime,dirs=/rw:/ro=ro +mkdir -p /aufs/rw /aufs/ro +mount --move /ro /aufs/ro +mount --move /rw /aufs/rw +mount --move /aufs /root +exit 0 +</pre> +</li> +<li><p class="first">Update the init ramdisk: <tt class="docutils literal"><span class="pre">update-initramfs</span> <span class="pre">-u</span> <span class="pre">-k</span> all</tt></p> +<dl class="docutils"> +<dt>Note! It may be necessary to do <tt class="docutils literal">mount <span class="pre">-t</span> tmpfs tmpfs /var/tmp</tt> to help</dt> +<dd><p class="first last">this operation succeed.</p> +</dd> +</dl> +</li> +<li><p class="first">Copy <tt class="docutils literal">/boot</tt> to <tt class="docutils literal"><span class="pre">/mnt/testbox-tftp/maintenance-boot/</span></tt>.</p> +</li> +<li><p class="first">Copy the <tt class="docutils literal"><span class="pre">testbox-maintenance.sh</span></tt> file found in the same directory as this +document to <tt class="docutils literal">/root/scripts/</tt> (need to create the dir) and make it +executable.</p> +</li> +<li><p class="first">Create the systemd service file for the maintenance service as +<tt class="docutils literal"><span class="pre">/etc/systemd/system/testbox-maintenance.service</span></tt> with the content:</p> +<pre class="literal-block"> +[Unit] +Description=Testbox Maintenance +After=network.target +Before=getty@tty1.service + +[Service] +Type=oneshot +RemainAfterExit=True +ExecStart=/root/scripts/testbox-maintenance.sh +ExecStartPre=/bin/echo -e \033%G +ExecReload=/bin/kill -HUP $MAINPID +WorkingDirectory=/tmp +Environment=TERM=xterm +StandardOutput=journal+console + +[Install] +WantedBy=multi-user.target +</pre> +</li> +<li><p class="first">Enable our service: <tt class="docutils literal">systemctl enable <span class="pre">/etc/systemd/system/testbox-maintenance.service</span></tt></p> +</li> +<li><p class="first">xxxx ... more ???</p> +</li> +<li><p class="first">Before leaving the chroot, do <tt class="docutils literal">mount /proc /sys <span class="pre">/mnt/testbox-*</span></tt>.</p> +</li> +</ul> +</blockquote> +</li> +<li><p class="first">Testing the setup from a VM is kind of useful (if the nfs server can be +convinced to accept root nfs mounts from non-privileged clinet ports):</p> +<blockquote> +<ul> +<li><p class="first">Create a VM using the 64-bit debian profile. Let's call it "pxe-vm".</p> +</li> +<li><p class="first">Mount the TFTP share somewhere, like M: or /mnt/testbox-tftp.</p> +</li> +<li><p class="first">Reconfigure the NAT DHCP and TFTP bits:</p> +<pre class="literal-block"> +VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/AboveDriver NAT +VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Action mergeconfig +VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/TFTPPrefix M:/ +VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/BootFile pxelinux.0 +</pre> +</li> +<li><p class="first">Create the file <tt class="docutils literal"><span class="pre">testbox-tftp/pxelinux.cfg/0A00020F</span></tt> containing:</p> +<pre class="literal-block"> +PATH bios +DEFAULT maintenance +LABEL maintenance + MENU LABEL Maintenance (NFS) + KERNEL maintenance-boot/vmlinuz-3.16.0-4-amd64 + APPEND initrd=maintenance-boot/initrd.img-3.16.0-4-amd64 ro ip=dhcp aufs=tmpfs \ + boot=nfs root=/dev/nfs nfsroot=10.42.1.1:/export/testbox-nfsroot +LABEL local-boot +LOCALBOOT +</pre> +</li> +</ul> +</blockquote> +</li> +</ul> +</blockquote> +</div> +<div class="section" id="troubleshooting"> +<h1>Troubleshooting</h1> +<dl class="docutils"> +<dt><tt class="docutils literal"><span class="pre">PXE-E11</span></tt> or something like <tt class="docutils literal">No ARP reply</tt></dt> +<dd>You probably got the TFTP and DHCP on different machines. Try move the TFTP +to the same machine as the DHCP, then the PXE stack won't have to do any +additional ARP resolving. Google results suggest that a congested network +could use the ARP reply to get lost. Our suspicion is that it might also be +related to the PXE stack shipping with the NIC.</dd> +</dl> +<hr class="docutils" /> +<table class="docutils footnote" frame="void" id="id6" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td>See <a class="reference external" href="http://www.syslinux.org/wiki/index.php?title=Category:Modules">http://www.syslinux.org/wiki/index.php?title=Category:Modules</a></td></tr> +</tbody> +</table> +<table class="docutils footnote" frame="void" id="id7" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label"><a class="fn-backref" href="#id2">[2]</a></td><td>See <a class="reference external" href="http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration">http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration</a></td></tr> +</tbody> +</table> +<table class="docutils footnote" frame="void" id="id8" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label"><a class="fn-backref" href="#id3">[3]</a></td><td>See <a class="reference external" href="https://en.wikipedia.org/wiki/Aufs">https://en.wikipedia.org/wiki/Aufs</a></td></tr> +</tbody> +</table> +<table class="docutils footnote" frame="void" id="id9" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label"><a class="fn-backref" href="#id4">[4]</a></td><td>See <a class="reference external" href="http://shitwefoundout.com/wiki/Diskless_ubuntu">http://shitwefoundout.com/wiki/Diskless_ubuntu</a></td></tr> +</tbody> +</table> +<table class="docutils footnote" frame="void" id="id10" rules="none"> +<colgroup><col class="label" /><col /></colgroup> +<tbody valign="top"> +<tr><td class="label"><a class="fn-backref" href="#id5">[5]</a></td><td>See <a class="reference external" href="http://debianaddict.com/2012/06/19/diskless-debian-linux-booting-via-dhcppxenfstftp/">http://debianaddict.com/2012/06/19/diskless-debian-linux-booting-via-dhcppxenfstftp/</a></td></tr> +</tbody> +</table> +<hr class="docutils" /> +<table class="docutils field-list" frame="void" rules="none"> +<col class="field-name" /> +<col class="field-body" /> +<tbody valign="top"> +<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: TestBoxImaging.html $</td> +</tr> +<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2010-2017 Oracle Corporation.</td> +</tr> +</tbody> +</table> +</div> +</div> +</body> +</html> diff --git a/src/VBox/ValidationKit/docs/TestBoxImaging.txt b/src/VBox/ValidationKit/docs/TestBoxImaging.txt new file mode 100644 index 00000000..6b7d02cc --- /dev/null +++ b/src/VBox/ValidationKit/docs/TestBoxImaging.txt @@ -0,0 +1,370 @@ + +Testbox Imaging (Backup / Restore) +================================== + + +Introduction +------------ + +This document is explores deloying a very simple drive imaging solution to help +avoid needing to manually reinstall testboxes when a disk goes bust or the OS +install seems to be corrupted. + + +Definitions / Glossary +====================== + +See AutomaticTestingRevamp.txt. + + +Objectives +========== + + - Off site, no admin interaction (no need for ILOM or similar). + - OS independent. + - Space and bandwidth efficient. + - As automatic as possible. + - Logging. + + +Overview of the Solution +======================== + +Here is a brief summary: + + - Always boot testboxes via PXE using PXELINUX. + - Default configuration is local boot (hard disk / SSD) + - Restore/backup action triggered by machine specific PXE config. + - Boots special debian maintenance install off NFS. + - A maintenance service (systemd style) does the work. + - The service reads action from TFTP location and performs it. + - When done the service removes the TFTP machine specific config + and reboots the system. + +Maintenance actions are: + - backup + - backup-again + - restore + - refresh-info + - rescue + +Possible modifier that indicates a subset of disk on testboxes with other OSes +installed. Support for partition level backup/restore is not explored here. + + +How to use +---------- + +To perform one of the above maintenance actions on a testbox, run the +``testbox-pxe-conf.sh`` script:: + + /mnt/testbox-tftp/pxeclient.cfg/testbox-pxe-conf.sh 10.165.98.220 rescue + +Then trigger a reboot. The box will then boot the NFS rooted debian image and +execute the maintenance action. On success, it will remove the testbox hex-IP +config file and reboot again. + + +Storage Server +============== + +The storage server will have three areas used here. Using NFS for all three +avoids extra work getting CIFS sharing right too (NFS is already a pain). + + 1. /export/testbox-tftp - TFTP config area. Read-write. + 2. /export/testbox-backup - Images and logs. Read-write. + 3. /export/testbox-nfsroot - Custom debian. Read-only, no root squash. + + +TFTP (/export/testbox-tftp) +============================ + +The testbox-tftp share needs to be writable, root squashing is okay. + +We need files from both PXELINUX and SYSLINUX to make this work now. On a +debian system, the ``pxelinux`` and ``syslinux`` packages needs to be +installed. We actually do this further down when setting up the nfsroot, so +it's possible to get them from there by postponing this step a little. On +debian 8.6.0 the PXELINUX files are found in ``/usr/lib/PXELINUX`` and the +SYSLINUX ones in ``/usr/lib/syslinux``. + +The initial PXE image as well as associated modules comes in three variants, +BIOS, 32-bit EFI and 64-bit EFI. We'll only need the BIOS one for now. +Perform the following copy operations:: + + cp /usr/lib/PXELINUX/pxelinux.0 /mnt/testbox-tftp/ + cp /usr/lib/syslinux/modules/*/ldlinux.* /mnt/testbox-tftp/ + cp -R /usr/lib/syslinux/modules/bios /mnt/testbox-tftp/ + cp -R /usr/lib/syslinux/modules/efi32 /mnt/testbox-tftp/ + cp -R /usr/lib/syslinux/modules/efi64 /mnt/testbox-tftp/ + + +For simplicitly, all the testboxes boot using good old fashioned BIOS, no EFI. +However, it doesn't really hurt to be prepared. + +The PXELINUX related files goes in the root of the testbox-tftp share. (As +mentioned further down, these can be installed on a debian system by running +``apt-get install pxelinux syslinux``.) We need the ``*pxelinux.0`` files +typically found in ``/usr/lib/PXELINUX/`` on debian systems (recent ones +anyway). It is possible we may need one ore more fo the modules [1]_ that +ships with PXELINUX/SYSLINUX, so do copy ``/usr/lib/syslinux/modules`` to +``testbox-tftp/modules`` as well. + + +The directory layout related to the configuration files is dictated by the +PXELINUX configuration file searching algorithm [2]_. Create a subdirectory +``pxelinux.cfg/`` under ``testbox-tftp`` and create the world readable file +``default`` with the following content:: + + PATH bios + DEFAULT local-boot + LABEL local-boot + LOCALBOOT + +This will make the default behavior to boot the local disk system. + +Copy the ``testbox-pxe-conf.sh`` script file found in the same directory as +this document to ``/mnt/testbox-tftp/pxelinux.cfg/``. Edit the copy to correct +the IP addresses near the top, as well as any linux, TFTP and PXE details near +the bottom of the file. This script will generate the PXE configuration file +when performing maintenance on a testbox. + + +Images and logs (/export/testbox-backup) +========================================= + +The testbox-backup share needs to be writable, root squashing is okay. + +In the root there must be a file ``testbox-backup`` so we can easily tell +whether we've actually mounted the share or are just staring at an empty mount +point directory. + +The ``testbox-maintenance.sh`` script maintains a global log in the root +directory that's called ``maintenance.log``. Errors will be logged there as +well as a ping and the action. + +We use a directory layout based on dotted decimal IP addresses here, so for a +server with the IP 10.40.41.42 all its file will be under ``10.40.41.42/``: + +``<hostname>`` + The name of the testbox (empty file). Help finding a testbox by name. + +``testbox-info.txt`` + Information about the testbox. Starting off with the name, decimal IP, + PXELINUX style hexadecimal IP, and more. + +``maintenance.log`` + Maintenance log file recording what the maintenance service does. + +``disk-devices.lst`` + Optional list of disk devices to consider backuping up or restoring. This is + intended for testboxes with additional disks that are used for other purposes + and should touched. + +``sda.raw.gz`` + The gzipped raw copy of the sda device of the testbox. + +``sd[bcdefgh].raw.gz`` + The gzipped raw copy sdb, sdc, sde, sdf, sdg, sdh, etc if any of them exists + and are disks/SSDs. + + +Note! If it turns out we can be certain to get a valid host name, we might just + switch to use the hostname as the directory name instead of the IP. + + +Debian NFS root (/export/testbox-nfsroot) +========================================== + +The testbox-nfsroot share should be read-only and must **not** have root +squashing enabled. Also, make sure setting the set-uid-bit is allowed by the +server, or ``su` and ``sudo`` won't work + +There are several ways of creating a debian nfsroot, but since we've got a +tool like VirtualBox around we've just installed it in a VM, prepared it, +and copied it onto the NFS server share. + +As of writing debian 8.6.0 is current, so a minimal 64-bit install of it was +done in a VM. After installation the following modifications was done: + + - ``apt-get install pxelinux syslinux initramfs-tools zip gddrescue sudo joe`` + and optionally ``apt-get install smbclient cifs-utils``. + + - ``/etc/default/grub`` was modified to set ``GRUB_CMDLINE_LINUX_DEFAULT`` to + ``""`` instead of ``"quiet"``. This allows us to see messages during boot + and perhaps spot why something doesn't work on a testbox. Regenerate the + grub configuration file by running ``update-grub`` afterwards. + + - ``/etc/sudoers`` was modified to allow the ``vbox`` user use sudo without + requring any password. + + - Create the directory ``/etc/systemd/system/getty@tty1.service.d`` and create + the file ``noclear.conf`` in it with the following content:: + + [Service] + TTYVTDisallocate=no + + This stops getty from clearing VT1 and let us see the tail of the boot up + messages, which includes messages from the testbox-maintenance service. + + - Mount the testbox-nfsroot under ``/mnt/`` with write privileges. (The write + privileges are temporary - don't forget to remove them later on.):: + + mount -t nfs myserver.com:/export/testbox-nfsroot + + Note! Adding ``-o nfsvers=3`` may help with some NTFv4 servers. + + - Copy the debian root and dev file system onto nfsroot. If you have ssh + access to the NFS server, the quickest way to do it is to use ``tar``:: + + tar -cz --one-file-system -f /mnt/testbox-maintenance-nfsroot.tar.gz . dev/ + + An alternative is ``cp -ax . /mnt/. && cp -ax dev/. /mnt/dev/.`` but this + is quite a bit slower, obviously. + + - Edit ``/etc/ssh/sshd_config`` setting ``PermitRootLogin`` to ``yes`` so we can ssh + in as root later on. + + - chroot into the nfsroot: ``chroot /mnt/`` + + - ``mount -o proc proc /proc`` + + - ``mount -o sysfs sysfs /sys`` + + - ``mkdir /mnt/testbox-tftp /mnt/testbox-backup`` + + - Recreate ``/etc/fstab`` with:: + + proc /proc proc defaults 0 0 + /dev/nfs / nfs defaults 1 1 + 10.42.1.1:/export/testbox-tftp /mnt/testbox-tftp nfs tcp,nfsvers=3,noauto 2 2 + 10.42.1.1:/export/testbox-backup /mnt/testbox-backup nfs tcp,nfsvers=3,noauto 3 3 + + We use NFS version 3 as that works better for our NFS server and client, + remove if not necessary. The ``noauto`` option is to work around mount + trouble during early bootup on some of our boxes. + + - Do ``mount /mnt/testbox-tftp && mount /mnt/testbox-backup`` to mount the + two shares. This may be a good time to execute the instructions in the + sections above relating to these two shares. + + - Edit ``/etc/initramfs-tools/initramfs.conf`` and change the ``MODULES`` + value from ``most`` to ``netboot``. + + - Append ``aufs`` to ``/etc/initramfs-tools/modules``. The advanced + multi-layered unification filesystem (aufs) enables us to use a + read-only NFS root. [3]_ [4]_ [5]_ + + - Create ``/etc/initramfs-tools/scripts/init-bottom/00_aufs_init`` as + an executable file with the following content:: + + #!/bin/sh + # Don't run during update-initramfs: + case "$1" in + prereqs) + exit 0; + ;; + esac + + modprobe aufs + mkdir -p /ro /rw /aufs + mount -t tmpfs tmpfs /rw -o noatime,mode=0755 + mount --move $rootmnt /ro + mount -t aufs aufs /aufs -o noatime,dirs=/rw:/ro=ro + mkdir -p /aufs/rw /aufs/ro + mount --move /ro /aufs/ro + mount --move /rw /aufs/rw + mount --move /aufs /root + exit 0 + + - Update the init ramdisk: ``update-initramfs -u -k all`` + + Note! It may be necessary to do ``mount -t tmpfs tmpfs /var/tmp`` to help + this operation succeed. + + - Copy ``/boot`` to ``/mnt/testbox-tftp/maintenance-boot/``. + + - Copy the ``testbox-maintenance.sh`` file found in the same directory as this + document to ``/root/scripts/`` (need to create the dir) and make it + executable. + + - Create the systemd service file for the maintenance service as + ``/etc/systemd/system/testbox-maintenance.service`` with the content:: + + [Unit] + Description=Testbox Maintenance + After=network.target + Before=getty@tty1.service + + [Service] + Type=oneshot + RemainAfterExit=True + ExecStart=/root/scripts/testbox-maintenance.sh + ExecStartPre=/bin/echo -e \033%G + ExecReload=/bin/kill -HUP $MAINPID + WorkingDirectory=/tmp + Environment=TERM=xterm + StandardOutput=journal+console + + [Install] + WantedBy=multi-user.target + + - Enable our service: ``systemctl enable /etc/systemd/system/testbox-maintenance.service`` + + - xxxx ... more ??? + + - Before leaving the chroot, do ``mount /proc /sys /mnt/testbox-*``. + + + - Testing the setup from a VM is kind of useful (if the nfs server can be + convinced to accept root nfs mounts from non-privileged clinet ports): + + - Create a VM using the 64-bit debian profile. Let's call it "pxe-vm". + - Mount the TFTP share somewhere, like M: or /mnt/testbox-tftp. + - Reconfigure the NAT DHCP and TFTP bits:: + + VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/AboveDriver NAT + VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Action mergeconfig + VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/TFTPPrefix M:/ + VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/BootFile pxelinux.0 + + - Create the file ``testbox-tftp/pxelinux.cfg/0A00020F`` containing:: + + PATH bios + DEFAULT maintenance + LABEL maintenance + MENU LABEL Maintenance (NFS) + KERNEL maintenance-boot/vmlinuz-3.16.0-4-amd64 + APPEND initrd=maintenance-boot/initrd.img-3.16.0-4-amd64 ro ip=dhcp aufs=tmpfs \ + boot=nfs root=/dev/nfs nfsroot=10.42.1.1:/export/testbox-nfsroot + LABEL local-boot + LOCALBOOT + + +Troubleshooting +=============== + +``PXE-E11`` or something like ``No ARP reply`` + You probably got the TFTP and DHCP on different machines. Try move the TFTP + to the same machine as the DHCP, then the PXE stack won't have to do any + additional ARP resolving. Google results suggest that a congested network + could use the ARP reply to get lost. Our suspicion is that it might also be + related to the PXE stack shipping with the NIC. + + + +----- + +.. [1] See http://www.syslinux.org/wiki/index.php?title=Category:Modules +.. [2] See http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration +.. [3] See https://en.wikipedia.org/wiki/Aufs +.. [4] See http://shitwefoundout.com/wiki/Diskless_ubuntu +.. [5] See http://debianaddict.com/2012/06/19/diskless-debian-linux-booting-via-dhcppxenfstftp/ + + +----- + +:Status: $Id: TestBoxImaging.txt $ +:Copyright: Copyright (C) 2010-2017 Oracle Corporation. + + diff --git a/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html new file mode 100644 index 00000000..6c51a17e --- /dev/null +++ b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html @@ -0,0 +1,445 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.12: http://docutils.sourceforge.net/" /> +<title>The VirtualBox Validation Kit</title> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: VBoxValidationKitReadMe.html $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title, .code .error { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em ; + margin-right: 2em } + +pre.code .ln { color: grey; } /* line numbers */ +pre.code, code { background-color: #eeeeee } +pre.code .comment, code .comment { color: #5C6576 } +pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } +pre.code .literal.string, code .literal.string { color: #0C5404 } +pre.code .name.builtin, code .name.builtin { color: #352B84 } +pre.code .deleted, code .deleted { background-color: #DEB0A1} +pre.code .inserted, code .inserted { background-color: #A3D289} + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +/* "booktabs" style (no vertical lines) */ +table.docutils.booktabs { + border: 0px; + border-top: 2px solid; + border-bottom: 2px solid; + border-collapse: collapse; +} +table.docutils.booktabs * { + border: 0px; +} +table.docutils.booktabs th { + border-bottom: thin solid; + text-align: left; +} + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document" id="the-virtualbox-validation-kit"> +<h1 class="title">The VirtualBox Validation Kit</h1> + +<div class="section" id="introduction"> +<h1>Introduction</h1> +<p>The VirtualBox Validation Kit is our new public tool for doing automated +testing of VirtualBox. We are continually working on adding new features +and guest operating systems to our battery of tests.</p> +<p>We warmly welcome contributions, new ideas for good tests and fixes.</p> +</div> +<div class="section" id="directory-layout"> +<h1>Directory Layout</h1> +<dl class="docutils"> +<dt>./docs/</dt> +<dd><p class="first">The documentation for the test suite mostly lives here, the exception being +readme.txt files that are better off living near what they concern.</p> +<p class="last">For a definition of terms used here, see the Definitions / Glossary section +of ./docs/AutomaticTestingRevamp.txt / ./docs/AutomaticTestingRevamp.html.</p> +</dd> +<dt>./testdriver/</dt> +<dd><p class="first">Python module implementing the base test drivers and supporting stuff. +The base test driver implementation is found in ./testdriver/base.py while +the VBox centric specialization is in ./testdriver/vbox.py. Various VBox +API wrappers that makes things easier to use and glosses over a lot of API +version differences that live in ./testdriver/vboxwrappers.py.</p> +<p>Test VM collections are often managed thru ./testdriver/vboxtestvms.py, but +doesn't necessarily have to be, it's up to the individual test driver.</p> +<p>For logging, reporting result, uploading useful files and such we have a +reporter singleton sub-package, ./testdriver/reporter.py. It implements +both local (for local testing) and remote (for testboxes + test manager) +reporting.</p> +<p class="last">There is also a VBoxTXS client implementation in txsclient.py and a stacked +test driver for installing VBox (vboxinstaller.py). Most test drivers will +use the TXS client indirectly thru vbox.py methods. The installer driver +is a special trick for the testbox+testmanager setup.</p> +</dd> +<dt>./tests/</dt> +<dd>The python scripts driving the tests. These are organized by what they +test and are all derived from the base classes in ./testdriver (mostly from +vbox.py of course). Most tests use one or more VMs from a standard set of +preconfigured VMs defined by ./testdriver/vboxtestvms.py (mentioned above), +though the installation tests used prepared ISOs and floppy images.</dd> +<dt>./vms/</dt> +<dd>Text documents describing the preconfigured test VMs defined by +./testdrive/vboxtestvms.py. This will also contain description of how to +prepare installation ISOs when we get around to it (soon).</dd> +<dt>./utils/</dt> +<dd><p class="first">Test utilities and lower level test programs, compiled from C, C++ and +Assembly mostly. Generally available for both host and guest, i.e. in the +zip and on the VBoxValidationKit.iso respectively.</p> +<p>The Test eXecution Service (VBoxTXS) found in ./utils/TestExecServ is one +of the more important utilities. It implements a remote execution service +for running programs/tests inside VMs and on other test boxes. See +./utils/TestExecServ/vboxtxs-readme.txt for more details.</p> +<p class="last">A simple network bandwidth and latency test program can be found in +./utils/network/NetPerf.cpp.</p> +</dd> +<dt>./bootsectors/</dt> +<dd><p class="first">Boot sector test environment. This allows creating floppy images in +assembly that tests specific CPU or device behaviour. Most tests can be +put on a USB stick, floppy or similar and booted up on real hardware for +comparison. All floppy images can be used for manual testing by developers +and most will be used by test drivers (./tests/<em>/td</em>.py) sooner or later.</p> +<p class="last">The boot sector environment is heavily bound to yasm and it's ability to +link binary images for single assembly input units. There is a "library" +of standard initialization code and runtime code, which include switch to +all (well V8086 mode is still missing, but we'll get that done eventually) +processor modes and paging modes. The image specific code is split into +init/driver code and test template, the latter can be instantiated for each +process execution+paging mode.</p> +</dd> +<dt>./common/</dt> +<dd>Python package containing common python code.</dd> +<dt>./testboxscript/</dt> +<dd>The testbox script. This is installed on testboxes used for automatic +testing with the testmanager.</dd> +<dt>./testmanager/</dt> +<dd>The VirtualBox Test Manager (server side code). This is written in Python +and currently uses postgresql as database backend for no particular reason +other than that it was already installed on the server the test manager was +going to run on. It's relatively generic, though there are of course +things in there that are of more use when testing VirtualBox than other +things. A more detailed account (though perhaps a little dated) of the +test manager can be found in ./docs/AutomaticTestingRevamp.txt and +./docs/AutomaticTestingRevamp.html.</dd> +<dt>./testanalysis/</dt> +<dd>A start a local test result analysis, comparing network test output. We'll +probably be picking this up again later.</dd> +<dt>./snippets/</dt> +<dd>Various code snippets that may be turned into real tests at some point.</dd> +</dl> +<table class="docutils field-list" frame="void" rules="none"> +<col class="field-name" /> +<col class="field-body" /> +<tbody valign="top"> +<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: VBoxValidationKitReadMe.html $</td> +</tr> +<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2010-2017 Oracle Corporation.</td> +</tr> +</tbody> +</table> +</div> +</div> +</body> +</html> diff --git a/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt new file mode 100644 index 00000000..4afab974 --- /dev/null +++ b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt @@ -0,0 +1,114 @@ + +The VirtualBox Validation Kit +============================= + + +Introduction +------------ + +The VirtualBox Validation Kit is our new public tool for doing automated +testing of VirtualBox. We are continually working on adding new features +and guest operating systems to our battery of tests. + +We warmly welcome contributions, new ideas for good tests and fixes. + + +Directory Layout +---------------- + +./docs/ + The documentation for the test suite mostly lives here, the exception being + readme.txt files that are better off living near what they concern. + + For a definition of terms used here, see the Definitions / Glossary section + of ./docs/AutomaticTestingRevamp.txt / ./docs/AutomaticTestingRevamp.html. + +./testdriver/ + Python module implementing the base test drivers and supporting stuff. + The base test driver implementation is found in ./testdriver/base.py while + the VBox centric specialization is in ./testdriver/vbox.py. Various VBox + API wrappers that makes things easier to use and glosses over a lot of API + version differences that live in ./testdriver/vboxwrappers.py. + + Test VM collections are often managed thru ./testdriver/vboxtestvms.py, but + doesn't necessarily have to be, it's up to the individual test driver. + + For logging, reporting result, uploading useful files and such we have a + reporter singleton sub-package, ./testdriver/reporter.py. It implements + both local (for local testing) and remote (for testboxes + test manager) + reporting. + + There is also a VBoxTXS client implementation in txsclient.py and a stacked + test driver for installing VBox (vboxinstaller.py). Most test drivers will + use the TXS client indirectly thru vbox.py methods. The installer driver + is a special trick for the testbox+testmanager setup. + +./tests/ + The python scripts driving the tests. These are organized by what they + test and are all derived from the base classes in ./testdriver (mostly from + vbox.py of course). Most tests use one or more VMs from a standard set of + preconfigured VMs defined by ./testdriver/vboxtestvms.py (mentioned above), + though the installation tests used prepared ISOs and floppy images. + +./vms/ + Text documents describing the preconfigured test VMs defined by + ./testdrive/vboxtestvms.py. This will also contain description of how to + prepare installation ISOs when we get around to it (soon). + +./utils/ + Test utilities and lower level test programs, compiled from C, C++ and + Assembly mostly. Generally available for both host and guest, i.e. in the + zip and on the VBoxValidationKit.iso respectively. + + The Test eXecution Service (VBoxTXS) found in ./utils/TestExecServ is one + of the more important utilities. It implements a remote execution service + for running programs/tests inside VMs and on other test boxes. See + ./utils/TestExecServ/vboxtxs-readme.txt for more details. + + A simple network bandwidth and latency test program can be found in + ./utils/network/NetPerf.cpp. + +./bootsectors/ + Boot sector test environment. This allows creating floppy images in + assembly that tests specific CPU or device behaviour. Most tests can be + put on a USB stick, floppy or similar and booted up on real hardware for + comparison. All floppy images can be used for manual testing by developers + and most will be used by test drivers (./tests/*/td*.py) sooner or later. + + The boot sector environment is heavily bound to yasm and it's ability to + link binary images for single assembly input units. There is a "library" + of standard initialization code and runtime code, which include switch to + all (well V8086 mode is still missing, but we'll get that done eventually) + processor modes and paging modes. The image specific code is split into + init/driver code and test template, the latter can be instantiated for each + process execution+paging mode. + +./common/ + Python package containing common python code. + +./testboxscript/ + The testbox script. This is installed on testboxes used for automatic + testing with the testmanager. + +./testmanager/ + The VirtualBox Test Manager (server side code). This is written in Python + and currently uses postgresql as database backend for no particular reason + other than that it was already installed on the server the test manager was + going to run on. It's relatively generic, though there are of course + things in there that are of more use when testing VirtualBox than other + things. A more detailed account (though perhaps a little dated) of the + test manager can be found in ./docs/AutomaticTestingRevamp.txt and + ./docs/AutomaticTestingRevamp.html. + +./testanalysis/ + A start a local test result analysis, comparing network test output. We'll + probably be picking this up again later. + +./snippets/ + Various code snippets that may be turned into real tests at some point. + + + +:Status: $Id: VBoxValidationKitReadMe.txt $ +:Copyright: Copyright (C) 2010-2017 Oracle Corporation. + diff --git a/src/VBox/ValidationKit/docs/WindbgPython.txt b/src/VBox/ValidationKit/docs/WindbgPython.txt new file mode 100644 index 00000000..198ec917 --- /dev/null +++ b/src/VBox/ValidationKit/docs/WindbgPython.txt @@ -0,0 +1,10 @@ +$Id: WindbgPython.txt $ + +Just a couple of useful windbg commands: + +Show python filenames + frame line number (not statement) up the call stack: +!for_each_frame ".block { dt python27!_frame qwo(!f) f_lineno; da qwo(qwo(qwo(!f)+0x20) + 50) + 20 } " + +Same, alternative version: +!for_each_frame .if ( $spat("${@#FunctionName}","*PyEval_EvalFrameEx*") ) { .printf "python frame: line %d\npython frame: filename %ma\n", @@c++(f->f_lineno), qwo(qwo(qwo(!f)+0x20) + 50) + 20 } + diff --git a/src/VBox/ValidationKit/docs/testbox-maintenance.sh b/src/VBox/ValidationKit/docs/testbox-maintenance.sh new file mode 100755 index 00000000..715d6c46 --- /dev/null +++ b/src/VBox/ValidationKit/docs/testbox-maintenance.sh @@ -0,0 +1,400 @@ +#!/bin/bash +# $Id: testbox-maintenance.sh $ +## @file +# VirtualBox Validation Kit - testbox mainenance service +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# + + +# +# Global Variables (config first). +# +MY_REBOOT_WHEN_DONE="yes" +#MY_REBOOT_WHEN_DONE="" # enable this for debugging the script + +MY_TFTP_ROOT="/mnt/testbox-tftp" +MY_BACKUP_ROOT="/mnt/testbox-backup" +MY_BACKUP_MNT_TEST_FILE="/mnt/testbox-backup/testbox-backup" +MY_GLOBAL_LOG_FILE="${MY_BACKUP_ROOT}/maintenance.log" +MY_DD_BLOCK_SIZE=256K + +MY_IP="" +MY_BACKUP_DIR="" +MY_LOG_FILE="" +MY_PXELINUX_CFG_FILE="" + + +## +# Info message. +# +InfoMsg() +{ + echo $*; + if test -n "${MY_LOG_FILE}"; then + echo "`date -uIsec`: ${MY_IP}: info:" $* >> ${MY_LOG_FILE}; + fi +} + + +## +# Error message and reboot+exit. First argument is exit code. +# +ErrorMsgExit() +{ + MY_RET=$1 + shift + echo "testbox-maintenance.sh: error:" $* >&2; + # Append to the testbox log. + if test -n "${MY_LOG_FILE}"; then + echo "`date -uIsec`: ${MY_IP}: error:" $* >> "${MY_LOG_FILE}"; + fi + # Append to the global log. + if test -f "${MY_BACKUP_MNT_TEST_FILE}"; then + echo "`date -uIsec`: ${MY_IP}: error:" $* >> "${MY_GLOBAL_LOG_FILE}"; + fi + + # + # On error we normally wait 5min before rebooting to avoid repeating the + # same error too many time before the admin finds out. We choose NOT to + # remove the PXE config file here because (a) the admin might otherwise + # not notice something went wrong, (b) the system could easily be in a + # weird unbootable state, (c) the problem might be temporary. + # + # While debugging, we just exit here. + # + if test -n "${MY_REBOOT_WHEN_DONE}"; then + sleep 5m + echo "testbox-maintenance.sh: rebooting (after error)" >&2; + reboot + fi + exit ${MY_RET} +} + +# +# Try figure out the IP address of the box and the hostname from it again. +# +MY_IP=` hostname -I | cut -f1 -d' ' | head -1 ` +if test -z "${MY_IP}" -o `echo "${MY_IP}" | wc -w` -ne "1" -o "${MY_IP}" = "127.0.0.1"; then + ErrorMsgExit 10 "Failed to get a good IP! (MY_IP=${MY_IP})" +fi +MY_HOSTNAME=`getent hosts "${MY_IP}" | sed -s 's/[[:space:]][[:space:]]*/ /g' | cut -d' ' -f2 ` +if test -z "${MY_HOSTNAME}"; then + MY_HOSTNAME="unknown"; +fi + +# Derive the backup dir and log file name from it. +if test ! -f "${MY_BACKUP_MNT_TEST_FILE}"; then + mount "${MY_BACKUP_ROOT}" + if test ! -f "${MY_BACKUP_MNT_TEST_FILE}"; then + echo "Retrying mounting '${MY_BACKUP_ROOT}' in 15 seconds..." >&2 + sleep 15 + mount "${MY_BACKUP_ROOT}" + fi + if test ! -f "${MY_BACKUP_MNT_TEST_FILE}"; then + ErrorMsgExit 11 "Backup directory is not mounted." + fi +fi +MY_BACKUP_DIR="${MY_BACKUP_ROOT}/${MY_IP}" +MY_LOG_FILE="${MY_BACKUP_DIR}/maintenance.log" +mkdir -p "${MY_BACKUP_DIR}" +echo "================ `date -uIsec`: ${MY_IP}: ${MY_HOSTNAME} starts a new session ================" >> "${MY_LOG_FILE}" +echo "`date -uIsec`: ${MY_IP}: ${MY_HOSTNAME} says hi." >> "${MY_GLOBAL_LOG_FILE}" +InfoMsg "MY_IP=${MY_IP}<eol>" + +# +# Redirect stderr+stdout thru tee and to a log file on the server. +# +MY_OUTPUT_LOG_FILE="${MY_BACKUP_DIR}/maintenance-output.log" +echo "" >> "${MY_OUTPUT_LOG_FILE}" +echo "================ `date -uIsec`: ${MY_IP}: ${MY_HOSTNAME} starts a new session ================" >> "${MY_OUTPUT_LOG_FILE}" +exec &> >(tee -a "${MY_OUTPUT_LOG_FILE}") + +# +# Convert the IP address to PXELINUX hex format, then check that we've got +# a config file on the TFTP share that we later can remove. We consider it a +# fatal failure if we don't because we've probably got the wrong IP and we'll +# be stuck doing the same stuff over and over again. +# +MY_TMP=`echo "${MY_IP}" | sed -e 's/\./ /g' ` +MY_IP_HEX=`printf "%02X%02X%02X%02X" ${MY_TMP}` +InfoMsg "MY_IP_HEX=${MY_IP_HEX}<eol>" + +if test ! -f "${MY_TFTP_ROOT}/pxelinux.0"; then + mount "${MY_TFTP_ROOT}" + if test ! -f "${MY_TFTP_ROOT}/pxelinux.0"; then + echo "Retrying mounting '${MY_TFTP_ROOT}' in 15 seconds..." >&2 + sleep 15 + mount "${MY_BACKUP_ROOT}" + fi + if test ! -f "${MY_TFTP_ROOT}/pxelinux.0"; then + ErrorMsgExit 12 "TFTP share mounted or mixxing pxelinux.0 in the root." + fi +fi + +MY_PXELINUX_CFG_FILE="${MY_TFTP_ROOT}/pxelinux.cfg/${MY_IP_HEX}" +if test ! -f "${MY_PXELINUX_CFG_FILE}"; then + ErrorMsgExit 13 "No pxelinux.cfg file found (${MY_PXELINUX_CFG_FILE}) - wrong IP?" +fi + +# +# Dig the action out of from the kernel command line. +# +if test -n "${MY_REBOOT_WHEN_DONE}"; then + InfoMsg "/proc/cmdline: `cat /proc/cmdline`" + set `cat /proc/cmdline` +else + InfoMsg "Using script command line: $*" +fi +MY_ACTION=not-found +while test $# -ge 1; do + case "$1" in + testbox-action-*) + MY_ACTION="$1" + ;; + esac + shift +done +if test "${MY_ACTION}" = "not-found"; then + ErrorMsgExit 14 "No action given. Expected testbox-action-backup, testbox-action-backup-again, testbox-action-restore," \ + "testbox-action-refresh-info, or testbox-action-rescue on the kernel command line."; +fi + +# Validate and shorten the action. +case "${MY_ACTION}" in + testbox-action-backup) + MY_ACTION="backup"; + ;; + testbox-action-backup-again) + MY_ACTION="backup-again"; + ;; + testbox-action-restore) + MY_ACTION="restore"; + ;; + testbox-action-refresh-info) + MY_ACTION="refresh-info"; + ;; + testbox-action-rescue) + MY_ACTION="rescue"; + ;; + *) ErrorMsgExit 15 "Invalid action '${MY_ACTION}'"; + ;; +esac + +# Log the action in both logs. +echo "`date -uIsec`: ${MY_IP}: info: Executing '${MY_ACTION}'." >> "${MY_GLOBAL_LOG_FILE}"; + +# +# Generate missing info for this testbox if backing up. +# +MY_INFO_FILE="${MY_BACKUP_DIR}/testbox-info.txt" +if test '!' -f "${MY_INFO_FILE}" \ + -o "${MY_ACTION}" = "backup" \ + -o "${MY_ACTION}" = "backup-again" \ + -o "${MY_ACTION}" = "refresh-info" ; +then + echo "IP: ${MY_IP}" > ${MY_INFO_FILE}; + echo "HEX-IP: ${MY_IP_HEX}" >> ${MY_INFO_FILE}; + echo "Hostname: ${MY_HOSTNAME}" >> ${MY_INFO_FILE}; + echo "" >> ${MY_INFO_FILE}; + echo "**** cat /proc/cpuinfo ****" >> ${MY_INFO_FILE}; + echo "**** cat /proc/cpuinfo ****" >> ${MY_INFO_FILE}; + echo "**** cat /proc/cpuinfo ****" >> ${MY_INFO_FILE}; + cat /proc/cpuinfo >> ${MY_INFO_FILE}; + echo "" >> ${MY_INFO_FILE}; + echo "**** lspci -vvv ****" >> ${MY_INFO_FILE}; + echo "**** lspci -vvv ****" >> ${MY_INFO_FILE}; + echo "**** lspci -vvv ****" >> ${MY_INFO_FILE}; + lspci -vvv >> ${MY_INFO_FILE} 2>&1; + echo "" >> ${MY_INFO_FILE}; + echo "**** biosdecode ****" >> ${MY_INFO_FILE}; + echo "**** biosdecode ****" >> ${MY_INFO_FILE}; + echo "**** biosdecode ****" >> ${MY_INFO_FILE}; + biosdecode >> ${MY_INFO_FILE} 2>&1; + echo "" >> ${MY_INFO_FILE}; + echo "**** dmidecode ****" >> ${MY_INFO_FILE}; + echo "**** dmidecode ****" >> ${MY_INFO_FILE}; + echo "**** dmidecode ****" >> ${MY_INFO_FILE}; + dmidecode >> ${MY_INFO_FILE} 2>&1; + echo "" >> ${MY_INFO_FILE}; + echo "**** fdisk -l ****" >> ${MY_INFO_FILE}; + echo "**** fdisk -l ****" >> ${MY_INFO_FILE}; + echo "**** fdisk -l ****" >> ${MY_INFO_FILE}; + fdisk -l >> ${MY_INFO_FILE} 2>&1; + echo "" >> ${MY_INFO_FILE}; + echo "**** dmesg ****" >> ${MY_INFO_FILE}; + echo "**** dmesg ****" >> ${MY_INFO_FILE}; + echo "**** dmesg ****" >> ${MY_INFO_FILE}; + dmesg >> ${MY_INFO_FILE} 2>&1; + + # + # Get the raw ACPI tables and whatnot since we can. Use zip as tar will + # zero pad virtual files due to wrong misleading size returned by stat (4K). + # + # Note! /sys/firmware/dmi/entries/15-0/system_event_log/raw_event_log has been + # see causing fatal I/O errors, so skip all raw_event_log files. + # + zip -qr9 "${MY_BACKUP_DIR}/testbox-info.zip" \ + /proc/cpuinfo \ + /sys/firmware/ \ + -x "*/raw_event_log" +fi + +if test '!' -f "${MY_BACKUP_DIR}/${MY_HOSTNAME}" -a "${MY_HOSTNAME}" != "unknown"; then + echo "${MY_HOSTNAME}" > "${MY_BACKUP_DIR}/${MY_HOSTNAME}" +fi + +if test '!' -f "${MY_BACKUP_DIR}/${MY_IP_HEX}"; then + echo "${MY_IP}" > "${MY_BACKUP_DIR}/${MY_IP_HEX}" +fi + +# +# Assemble a list of block devices using /sys/block/* and some filtering. +# +if test -f "${MY_BACKUP_DIR}/disk-devices.lst"; then + MY_BLOCK_DEVS=`cat ${MY_BACKUP_DIR}/disk-devices.lst \ + | sed -e 's/[[:space:]][::space::]]*/ /g' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' `; + if test -z "${MY_BLOCK_DEVS}"; then + ErrorMsgExit 17 "No block devices found via sys/block." + fi + InfoMsg "disk-device.lst: MY_BLOCK_DEVS=${MY_BLOCK_DEVS}"; +else + MY_BLOCK_DEVS=""; + for MY_DEV in `ls /sys/block`; do + case "${MY_DEV}" in + [sh]d*) + MY_BLOCK_DEVS="${MY_BLOCK_DEVS} ${MY_DEV}" + ;; + *) InfoMsg "Ignoring /sys/block/${MY_DEV}"; + ;; + esac + done + if test -z "${MY_BLOCK_DEVS}"; then + ErrorMsgExit 17 "No block devices found via /sys/block." + fi + InfoMsg "/sys/block: MY_BLOCK_DEVS=${MY_BLOCK_DEVS}"; +fi + +# +# Take action +# +case "${MY_ACTION}" in + # + # Create a backup. The 'backup' action refuses to overwrite an + # existing backup, but is otherwise identical to 'backup-again'. + # + backup|backup-again) + for MY_DEV in ${MY_BLOCK_DEVS}; do + MY_DST="${MY_BACKUP_DIR}/${MY_DEV}.gz" + if test -f "${MY_DST}"; then + if test "${MY_ACTION}" != 'backup-again'; then + ErrorMsgExit 18 "${MY_DST} already exists" + fi + InfoMsg "${MY_DST} already exists" + fi + done + + # Do the backing up. + for MY_DEV in ${MY_BLOCK_DEVS}; do + MY_SRC="/dev/${MY_DEV}" + MY_DST="${MY_BACKUP_DIR}/${MY_DEV}.gz" + if test -f "${MY_DST}"; then + mv -f "${MY_DST}" "${MY_DST}.old"; + fi + if test -b "${MY_SRC}"; then + InfoMsg "Backing up ${MY_SRC} to ${MY_DST}..."; + dd if="${MY_SRC}" bs=${MY_DD_BLOCK_SIZE} | gzip -c > "${MY_DST}"; + MY_RCS=("${PIPESTATUS[@]}"); + if test "${MY_RCS[0]}" -eq 0 -a "${MY_RCS[1]}" -eq 0; then + InfoMsg "Successfully backed up ${MY_SRC} to ${MY_DST}"; + else + rm -f "${MY_DST}"; + ErrorMsgExit 19 "There was a problem backing up ${MY_SRC} to ${MY_DST}: dd => ${MY_RCS[0]}; gzip => ${MY_RCS[1]}"; + fi + else + InfoMsg "Skipping ${MY_SRC} as it either doesn't exist or isn't a block device"; + fi + done + ;; + + # + # Restore existing. + # + restore) + for MY_DEV in ${MY_BLOCK_DEVS}; do + MY_SRC="${MY_BACKUP_DIR}/${MY_DEV}.gz" + MY_DST="/dev/${MY_DEV}" + if test -b "${MY_DST}"; then + if test -f "${MY_SRC}"; then + InfoMsg "Restoring ${MY_SRC} onto ${MY_DST}..."; + gunzip -c "${MY_SRC}" | dd of="${MY_DST}" bs=${MY_DD_BLOCK_SIZE} iflag=fullblock; + MY_RCS=("${PIPESTATUS[@]}"); + if test ${MY_RCS[0]} -eq 0 -a ${MY_RCS[1]} -eq 0; then + InfoMsg "Successfully restored ${MY_SRC} onto ${MY_DST}"; + else + ErrorMsgExit 20 "There was a problem restoring ${MY_SRC} onto ${MY_DST}: dd => ${MY_RCS[1]}; gunzip => ${MY_RCS[0]}"; + fi + else + InfoMsg "Skipping ${MY_DST} because ${MY_SRC} does not exist."; + fi + else + InfoMsg "Skipping ${MY_DST} as it either doesn't exist or isn't a block device."; + fi + done + ;; + + # + # Nothing else to do for refresh-info. + # + refresh-info) + ;; + + # + # For the rescue action, we just quit without removing the PXE config or + # rebooting the box. The admin will do that once the system has been rescued. + # + rescue) + InfoMsg "rescue: exiting. Admin must remove PXE config and reboot manually when done." + exit 0; + ;; + + *) ErrorMsgExit 98 "Huh? MY_ACTION='${MY_ACTION}'" + ;; +esac + +# +# If we get here, remove the PXE config and reboot immediately. +# +InfoMsg "'${MY_ACTION}' - done"; +if test -n "${MY_REBOOT_WHEN_DONE}"; then + sync + if rm -f "${MY_PXELINUX_CFG_FILE}"; then + InfoMsg "removed ${MY_PXELINUX_CFG_FILE}"; + else + ErrorMsgExit 99 "failed to remove ${MY_PXELINUX_CFG_FILE}"; + fi + sync + InfoMsg "rebooting"; + reboot +fi +exit 0 + diff --git a/src/VBox/ValidationKit/docs/testbox-pxe-conf.sh b/src/VBox/ValidationKit/docs/testbox-pxe-conf.sh new file mode 100755 index 00000000..ce1c58a3 --- /dev/null +++ b/src/VBox/ValidationKit/docs/testbox-pxe-conf.sh @@ -0,0 +1,152 @@ +#!/bin/bash +# $Id: testbox-pxe-conf.sh $ +## @file +# VirtualBox Validation Kit - testbox pxe config emitter. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# + + +# +# Global Variables (config first). +# +MY_NFS_SERVER_IP="10.165.98.101" +MY_GATEWAY_IP="10.165.98.1" +MY_NETMASK="255.255.254.0" +MY_ETH_DEV="eth0" +MY_AUTO_CFG="none" + +# options +MY_PXELINUX_CFG_DIR="/mnt/testbox-tftp/pxelinux.cfg" +MY_ACTION="" +MY_IP="" +MY_IP_HEX="" + +# +# Parse arguments. +# +while test "$#" -ge 1; do + MY_ARG=$1 + shift + case "${MY_ARG}" in + -c|--cfg-dir) + MY_PXELINUX_CFG_DIR="$1"; + shift; + if test -z "${MY_PXELINUX_CFG_DIR}"; then + echo "syntax error: Empty pxeclient.cfg path." >&2; + exit 2; + fi + ;; + + -h|--help) + echo "usage: testbox-pxe-conf.sh: [-c /mnt/testbox-tftp/pxelinux.cfg] <ip> <action>"; + echo "Actions: backup, backup-again, restore, refresh-info, rescue"; + exit 0; + ;; + -*) + echo "syntax error: Invalid option: ${MY_ARG}" >&2; + exit 2; + ;; + + *) if test -z "$MY_ARG"; then + echo "syntax error: Empty argument" >&2; + exit 2; + fi + if test -z "${MY_IP}"; then + # Split up the IP if possible, if not do gethostbyname on the argument. + MY_TMP=`echo "${MY_ARG}" | sed -e 's/\./ /g'` + if test `echo "${MY_TMP}" | wc -w` -ne 4 \ + || ! printf "%02X%02X%02X%02X" ${MY_TMP} > /dev/null 2>&1; then + MY_TMP2=`getent hosts "${MY_ARG}" | head -1 | cut -d' ' -f1`; + MY_TMP=`echo "${MY_TMP2}" | sed -e 's/\./ /g'` + if test `echo "${MY_TMP}" | wc -w` -eq 4 \ + && printf "%02X%02X%02X%02X" ${MY_TMP} > /dev/null 2>&1; then + echo "info: resolved '${MY_ARG}' as '${MY_TMP2}'"; + MY_ARG="${MY_TMP2}"; + else + echo "syntax error: Invalid IP: ${MY_ARG}" >&2; + exit 2; + fi + fi + MY_IP_HEX=`printf "%02X%02X%02X%02X" ${MY_TMP}`; + MY_IP="${MY_ARG}"; + else + if test -z "${MY_ACTION}"; then + case "${MY_ARG}" in + backup|backup-again|restore|refresh-info|rescue) + MY_ACTION="${MY_ARG}"; + ;; + *) + echo "syntax error: Invalid action: ${MY_ARG}" >&2; + exit 2; + ;; + esac + else + echo "syntax error: Too many arguments" >&2; + exit 2; + fi + fi + ;; + esac +done + +if test -z "${MY_ACTION}"; then + echo "syntax error: Insufficient arguments" >&2; + exit 2; +fi +if test ! -d "${MY_PXELINUX_CFG_DIR}"; then + echo "error: pxeclient.cfg path does not point to a directory: ${MY_PXELINUX_CFG_DIR}" >&2; + exit 1; +fi +if test ! -f "${MY_PXELINUX_CFG_DIR}/default"; then + echo "error: pxeclient.cfg path does contain a 'default' file: ${MY_PXELINUX_CFG_DIR}" >&2; + exit 1; +fi + + +# +# Produce the file. +# Using echo here so we can split up the APPEND line more easily. +# +MY_CFG_FILE="${MY_PXELINUX_CFG_DIR}/${MY_IP_HEX}" +set +e +echo "PATH bios" > "${MY_CFG_FILE}"; +echo "DEFAULT maintenance" >> "${MY_CFG_FILE}"; +echo "LABEL maintenance" >> "${MY_CFG_FILE}"; +echo " MENU LABEL Maintenance (NFS)" >> "${MY_CFG_FILE}"; +echo " KERNEL maintenance-boot/vmlinuz-3.16.0-4-amd64" >> "${MY_CFG_FILE}"; +echo -n " APPEND initrd=maintenance-boot/initrd.img-3.16.0-4-amd64 testbox-action-${MY_ACTION}" >> "${MY_CFG_FILE}"; +echo -n " ro aufs=tmpfs boot=nfs root=/dev/nfs" >> "${MY_CFG_FILE}"; +echo -n " nfsroot=${MY_NFS_SERVER_IP}:/export/testbox-nfsroot,ro,tcp" >> "${MY_CFG_FILE}"; +echo -n " nfsvers=3 nfsrootdebug" >> "${MY_CFG_FILE}"; +if test "${MY_AUTO_CFG}" = "none"; then + # Note! Only 6 arguments to ip! Userland ipconfig utility barfs if autoconf and dns options are given. + echo -n " ip=${MY_IP}:${MY_NFS_SERVER_IP}:${MY_GATEWAY_IP}:${MY_NETMASK}:maintenance:${MY_ETH_DEV}" >> "${MY_CFG_FILE}"; +else + echo -n " ip=${MY_AUTO_CFG}" >> "${MY_CFG_FILE}"; +fi +echo "" >> "${MY_CFG_FILE}"; +echo "LABEL local-boot" >> "${MY_CFG_FILE}"; +echo "LOCALBOOT" >> "${MY_CFG_FILE}"; +echo "Successfully generated '${MY_CFG_FILE}'." +exit 0; + diff --git a/src/VBox/ValidationKit/docs/valkit.txt b/src/VBox/ValidationKit/docs/valkit.txt new file mode 100644 index 00000000..9e94eff5 --- /dev/null +++ b/src/VBox/ValidationKit/docs/valkit.txt @@ -0,0 +1 @@ +The VirtualBox ValidationKit ISO. |