[autotest] Add a harness for running dynamic suites from your workstation
To enable development on the dynamic suite infrastructure.
BUG=None
TEST=./server/autoserv test_suites/dev_harness
Change-Id: Ib2a63d19f919f5e256f0a36dbca33e1b52f92875
Reviewed-on: https://gerrit.chromium.org/gerrit/13240
Commit-Ready: Chris Masone <cmasone@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
Reviewed-by: Scott Zawalski <scottz@chromium.org>
diff --git a/global_config.ini b/global_config.ini
index 32dcbb1..37fa226 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -130,3 +130,9 @@
[CROS]
source_tree: /usr/local/google/chromeos
+dev_server: http://172.22.50.205:8080
+# The below should be %(dev_server)s/update/%%(name)s so that we'd fill in
+# the above value for dev_server when this config is parsed, but leave the
+# name field to be populated later. Sadly, that doesn't parse.
+image_url_pattern: %(dev_server)s/update/%%s
+package_url_pattern: %(dev_server)s/static/archive/%%s/autotest
diff --git a/server/cros/dynamic_suite.py b/server/cros/dynamic_suite.py
index 066661d..0104416 100644
--- a/server/cros/dynamic_suite.py
+++ b/server/cros/dynamic_suite.py
@@ -4,12 +4,23 @@
import common
import compiler, logging, os, random, re, time
-from autotest_lib.client.common_lib import control_data, error, utils
+from autotest_lib.client.common_lib import control_data, global_config, error
+from autotest_lib.client.common_lib import utils
from autotest_lib.server.cros import control_file_getter
from autotest_lib.server import frontend
VERSION_PREFIX = 'cros-version-'
+CONFIG = global_config.global_config
+
+
+def _image_url_pattern():
+ return CONFIG.get_config_value('CROS', 'image_url_pattern', type=str)
+
+
+def _package_url_pattern():
+ return CONFIG.get_config_value('CROS', 'package_url_pattern', type=str)
+
class Reimager(object):
"""
@@ -35,7 +46,11 @@
[os.path.join(autotest_dir, 'server/site_tests')])
- def attempt(self, url, name, num, board, record):
+ def skip(self, g):
+ return 'SKIP_IMAGE' in g and g['SKIP_IMAGE']
+
+
+ def attempt(self, name, num, board, record):
"""
Synchronously attempt to reimage some machines.
@@ -43,7 +58,6 @@
image at |url| called |name|. Wait for completion, polling every
10s, and log results with |record| upon completion.
- @param url: the URL of the image to install.
@param name: the name of the image to install (must be unique).
@param num: how many devices to reimage.
@param board: which kind of devices to reimage.
@@ -54,7 +68,7 @@
"""
record('START', None, 'try new image')
self._ensure_version_label(VERSION_PREFIX+name)
- canary = self._schedule_reimage_job(url, name, num, board)
+ canary = self._schedule_reimage_job(name, num, board)
logging.debug('Created re-imaging job: %d', canary.id)
while len(self._afe.get_jobs(id=canary.id, not_yet_run=True)) > 0:
time.sleep(10)
@@ -103,30 +117,27 @@
return control_file + control_file_in
- def _schedule_reimage_job(self, url, name, num_machines, board):
+ def _schedule_reimage_job(self, name, num_machines, board):
"""
Schedules the reimaging of |num_machines| |board| devices with |image|.
Sends an RPC to the autotest frontend to enqueue reimaging jobs on
|num_machines| devices of type |board|
- @param url: the URL of the image to install.
@param name: the name of the image to install (must be unique).
- @param num: how many devices to reimage.
+ @param num_machines: how many devices to reimage.
@param board: which kind of devices to reimage.
@return a frontend.Job object for the reimaging job we scheduled.
"""
control_file = self._inject_vars(
- { 'image_url': url,
+ { 'image_url': _image_url_pattern() % name,
'image_name': name },
self._cf_getter.get_control_file_contents_by_name('autoupdate'))
- dargs = { 'control_file': control_file,
- 'name': name + '-try',
- 'control_type': 'Server',
- 'meta_hosts': [board] * num_machines }
-
- return self._afe.create_job(**dargs)
+ return self._afe.create_job(control_file=control_file,
+ name=name + '-try',
+ control_type='Server',
+ meta_hosts=[board] * num_machines)
def _report_results(self, job, record):
diff --git a/server/cros/dynamic_suite_unittest.py b/server/cros/dynamic_suite_unittest.py
index 226657e..696ce31 100755
--- a/server/cros/dynamic_suite_unittest.py
+++ b/server/cros/dynamic_suite_unittest.py
@@ -34,7 +34,7 @@
@var _BOARD: fake board to reimage
"""
- _URL = 'http://nothing'
+ _URL = 'http://nothing/%s'
_NAME = 'name'
_NUM = 4
_BOARD = 'board'
@@ -149,15 +149,18 @@
cf_getter.get_control_file_contents_by_name('autoupdate').AndReturn('')
self.reimager._cf_getter = cf_getter
+ # Fake out getting the image URL pattern.
+ self.mox.StubOutWithMock(dynamic_suite, '_image_url_pattern')
+ dynamic_suite._image_url_pattern().AndReturn(self._URL)
+
self.afe.create_job(
control_file=mox.And(mox.StrContains(self._NAME),
- mox.StrContains(self._URL)),
+ mox.StrContains(self._URL % self._NAME)),
name=mox.StrContains(self._NAME),
control_type='Server',
meta_hosts=[self._BOARD] * self._NUM)
self.mox.ReplayAll()
- self.reimager._schedule_reimage_job(self._URL, self._NAME,
- self._NUM, self._BOARD)
+ self.reimager._schedule_reimage_job(self._NAME, self._NUM, self._BOARD)
def expect_attempt(self, success):
@@ -171,8 +174,7 @@
self.reimager._ensure_version_label(mox.StrContains(self._NAME))
self.mox.StubOutWithMock(self.reimager, '_schedule_reimage_job')
- self.reimager._schedule_reimage_job(self._URL,
- self._NAME,
+ self.reimager._schedule_reimage_job(self._NAME,
self._NUM,
self._BOARD).AndReturn(canary)
if success is not None:
@@ -194,8 +196,7 @@
rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
rjob.record('END GOOD', mox.IgnoreArg(), mox.IgnoreArg())
self.mox.ReplayAll()
- self.reimager.attempt(self._URL, self._NAME,
- self._NUM, self._BOARD, rjob.record)
+ self.reimager.attempt(self._NAME, self._NUM, self._BOARD, rjob.record)
def testFailedReimage(self):
@@ -206,8 +207,7 @@
rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
self.mox.ReplayAll()
- self.reimager.attempt(self._URL, self._NAME,
- self._NUM, self._BOARD, rjob.record)
+ self.reimager.attempt(self._NAME, self._NUM, self._BOARD, rjob.record)
def testReimageThatNeverHappened(self):
@@ -219,8 +219,7 @@
rjob.record('FAIL', mox.IgnoreArg(), canary.name, mox.IgnoreArg())
rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
self.mox.ReplayAll()
- self.reimager.attempt(self._URL, self._NAME,
- self._NUM, self._BOARD, rjob.record)
+ self.reimager.attempt(self._NAME, self._NUM, self._BOARD, rjob.record)
class SuiteTest(mox.MoxTestBase):
@@ -394,7 +393,7 @@
"""Compares this object to a recorded status."""
return self._equals_record(*args)
- def _equals_record(self, status, subdir, name, reason):
+ def _equals_record(self, status, subdir, name, reason=None):
"""Compares this object and fields of recorded status."""
if 'aborted' in self.entry and self.entry['aborted']:
return status == 'ABORT'
diff --git a/server/site_server_job.py b/server/site_server_job.py
index be5208c..d796ff7 100644
--- a/server/site_server_job.py
+++ b/server/site_server_job.py
@@ -151,4 +151,3 @@
self.record('START', None, skipped_test.test_name)
self.record('INFO', None, skipped_test.test_name, msg)
self.record('END TEST_NA', None, skipped_test.test_name, msg)
-
diff --git a/test_suites/control.bvt b/test_suites/control.bvt
index 385b470..abe47e6 100755
--- a/test_suites/control.bvt
+++ b/test_suites/control.bvt
@@ -15,24 +15,21 @@
DOC = """
This is the Build Verification Test suite. It should consist of SHORT tests
that validate critical functionality -- ability to acquire connectivity, perform
-crash reporting, get updates, and allow a user to log in, among other things..
+crash reporting, get updates, and allow a user to log in, among other things.
+
+@param image_name: The name of the image to test.
+ Ex: x86-mario-r17/R17-1412.33.0-a1-b29
+@param board: The board to test on. Ex: netbook_MARIO_MP
+@param SKIP_IMAGE: (optional) If present and True, don't re-image devices.
"""
import common
from autotest_lib.server.cros import dynamic_suite
-
-# These params should be injected by the thing scheduling the job
-image_url = 'http://172.22.50.205:8080/update/x86-mario-r17/R17-1388.0.0-a1-b1323'
-image_name ='x86-mario-r17/R17-1388.0.0-a1-b1323'
-board = 'netbook_MARIO_MP'
-
-# This is pretty much just here for testing.
-SKIP_IMAGE = True
-
suite_tag = 'bvt'
reimager = dynamic_suite.Reimager(job.autodir)
-if SKIP_IMAGE or reimager.attempt(image_url, image_name, 4, board, job.record):
+if (reimager.skip(globals()) or
+ reimager.attempt(image_name, 4, board, job.record)):
bvt = dynamic_suite.Suite.create_from_name(suite_tag, job.autodir)
bvt.run_and_wait(image_name, job.record, add_experimental=True)
diff --git a/test_suites/control.dummy b/test_suites/control.dummy
index e301614..dc4553f 100644
--- a/test_suites/control.dummy
+++ b/test_suites/control.dummy
@@ -20,18 +20,10 @@
import common
from autotest_lib.server.cros import dynamic_suite
-
-# These params should be injected by the thing scheduling the job
-image_url = 'http://172.22.50.205:8080/update/x86-mario-r17/R17-1388.0.0-a1-b1323'
-image_name ='x86-mario-r17/R17-1388.0.0-a1-b1323'
-board = 'netbook_MARIO_MP'
-
-# This is pretty much just here for testing.
-SKIP_IMAGE = False
-
suite_tag = 'dummy'
reimager = dynamic_suite.Reimager(job.autodir)
-if SKIP_IMAGE or reimager.attempt(image_url, image_name, 4, board, job.record):
+if (reimager.skip(globals()) or
+ reimager.attempt(image_name, 4, board, job.record)):
bvt = dynamic_suite.Suite.create_from_name(suite_tag, job.autodir)
bvt.run_and_wait(image_name, job.record, add_experimental=True)
diff --git a/test_suites/dev_harness b/test_suites/dev_harness
new file mode 100644
index 0000000..ad09d14
--- /dev/null
+++ b/test_suites/dev_harness
@@ -0,0 +1,14 @@
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+new_globals = globals()
+new_globals['image_name'] = 'x86-mario-r17/R17-1412.33.0-a1-b29'
+new_globals['board'] = 'netbook_MARIO_MP'
+new_globals['SKIP_IMAGE'] = True
+
+import os
+
+execfile(os.path.join(job.autodir, 'test_suites/control.dummy'),
+ new_globals,
+ locals())