137 lines
5.9 KiB
Python
137 lines
5.9 KiB
Python
# Copyright (c) 2014 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.
|
|
|
|
import logging
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server.cros import moblab_test
|
|
from autotest_lib.server.hosts import moblab_host
|
|
from autotest_lib.utils import labellib
|
|
|
|
|
|
_CLEANUP_TIME_M = 5
|
|
_MOBLAB_IMAGE_STORAGE = '/mnt/moblab/static'
|
|
|
|
class moblab_RunSuite(moblab_test.MoblabTest):
|
|
"""
|
|
Moblab run suite test. Ensures that a Moblab can run a suite from start
|
|
to finish by kicking off a suite which will have the Moblab stage an
|
|
image, provision its DUTs and run the tests.
|
|
"""
|
|
version = 1
|
|
|
|
|
|
def run_once(self, host, suite_name, moblab_suite_max_retries,
|
|
target_build='', clear_devserver_cache=True,
|
|
test_timeout_hint_m=None):
|
|
"""Runs a suite on a Moblab Host against its test DUTS.
|
|
|
|
@param host: Moblab Host that will run the suite.
|
|
@param suite_name: Name of the suite to run.
|
|
@param moblab_suite_max_retries: The maximum number of test retries
|
|
allowed within the suite launched on moblab.
|
|
@param target_build: Optional build to be use in the run_suite
|
|
call on moblab. This argument is passed as is to run_suite. It
|
|
must be a sensible build target for the board of the sub-DUTs
|
|
attached to the moblab.
|
|
@param clear_devserver_cache: If True, image cache of the devserver
|
|
running on moblab is cleared before running the test to validate
|
|
devserver imaging staging flow.
|
|
@param test_timeout_hint_m: (int) Optional overall timeout for the test.
|
|
For this test, it is very important to collect post failure data
|
|
from the moblab device. If the overall timeout is provided, the
|
|
test will try to fail early to save some time for log collection
|
|
from the DUT.
|
|
|
|
@raises AutoservRunError if the suite does not complete successfully.
|
|
"""
|
|
self._host = host
|
|
|
|
self._maybe_clear_devserver_cache(clear_devserver_cache)
|
|
# Fetch the board of the DUT's assigned to this Moblab. There should
|
|
# only be one type.
|
|
try:
|
|
dut = host.afe.get_hosts()[0]
|
|
except IndexError:
|
|
raise error.TestFail('All hosts for this MobLab are down. Please '
|
|
'request the lab admins to take a look.')
|
|
|
|
labels = labellib.LabelsMapping(dut.labels)
|
|
board = labels['board']
|
|
|
|
if not target_build:
|
|
stable_version_map = host.afe.get_stable_version_map(
|
|
host.afe.CROS_IMAGE_TYPE)
|
|
target_build = stable_version_map.get_image_name(board)
|
|
|
|
logging.info('Running suite: %s.', suite_name)
|
|
cmd = ("%s/site_utils/run_suite.py --pool='' --board=%s --build=%s "
|
|
"--suite_name=%s --retry=True " "--max_retries=%d" %
|
|
(moblab_host.AUTOTEST_INSTALL_DIR, board, target_build,
|
|
suite_name, moblab_suite_max_retries))
|
|
cmd, run_suite_timeout_s = self._append_run_suite_timeout(
|
|
cmd,
|
|
test_timeout_hint_m,
|
|
)
|
|
|
|
logging.debug('Run suite command: %s', cmd)
|
|
try:
|
|
result = host.run_as_moblab(cmd, timeout=run_suite_timeout_s)
|
|
except error.AutoservRunError as e:
|
|
if _is_run_suite_error_critical(e.result_obj.exit_status):
|
|
raise
|
|
else:
|
|
logging.debug('Suite Run Output:\n%s', result.stdout)
|
|
# Cache directory can contain large binaries like CTS/CTS zip files
|
|
# no need to offload those in the results.
|
|
# The cache is owned by root user
|
|
host.run('rm -fR /mnt/moblab/results/shared/cache',
|
|
timeout=600)
|
|
|
|
def _append_run_suite_timeout(self, cmd, test_timeout_hint_m):
|
|
"""Modify given run_suite command with timeout.
|
|
|
|
@param cmd: run_suite command str.
|
|
@param test_timeout_hint_m: (int) timeout for the test, or None.
|
|
@return cmd, run_suite_timeout_s: cmd is the updated command str,
|
|
run_suite_timeout_s is the timeout to use for the run_suite
|
|
call, in seconds.
|
|
"""
|
|
if test_timeout_hint_m is None:
|
|
return cmd, 10800
|
|
|
|
# Arguments passed in via test_args may be all str, depending on how
|
|
# they're passed in.
|
|
test_timeout_hint_m = int(test_timeout_hint_m)
|
|
elasped_m = self.elapsed.total_seconds() / 60
|
|
run_suite_timeout_m = (
|
|
test_timeout_hint_m - elasped_m - _CLEANUP_TIME_M)
|
|
logging.info('Overall test timeout hint provided (%d minutes)',
|
|
test_timeout_hint_m)
|
|
logging.info('%d minutes have already elasped', elasped_m)
|
|
logging.info(
|
|
'Keeping %d minutes for cleanup, will allow %d minutes for '
|
|
'the suite to run.', _CLEANUP_TIME_M, run_suite_timeout_m)
|
|
cmd += ' --timeout_mins %d' % run_suite_timeout_m
|
|
return cmd, run_suite_timeout_m * 60
|
|
|
|
def _maybe_clear_devserver_cache(self, clear_devserver_cache):
|
|
# When passed in via test_args, all arguments are str
|
|
if not isinstance(clear_devserver_cache, bool):
|
|
clear_devserver_cache = (clear_devserver_cache.lower() == 'true')
|
|
if clear_devserver_cache:
|
|
self._host.run('rm -rf %s/*' % _MOBLAB_IMAGE_STORAGE)
|
|
|
|
|
|
def _is_run_suite_error_critical(return_code):
|
|
# We can't actually import run_suite here because importing run_suite pulls
|
|
# in certain MySQLdb dependencies that fail to load in the context of a
|
|
# test.
|
|
# OTOH, these return codes are unlikely to change because external users /
|
|
# builders depend on them.
|
|
return return_code not in (
|
|
0, # run_suite.RETURN_CODES.OK
|
|
2, # run_suite.RETURN_CODES.WARNING
|
|
)
|