[autotest] Support run_suite with suite package and SSP for Brillo.
This change allows one to run run_suite command without using
--run_prod_code for Brillo build.
create_suite_job will use test_suites and control_files packages from
the Brillo build to create suite job and its test jobs.
Server-side packaging is also supported for newer builds that have
autotest_server_package artifact build.
BUG=chromium:584705
TEST=run in local instance, unittest, verify in moblab
Change-Id: Ia96ca4de919b178302580c23f911bb6445016285
Reviewed-on: https://chromium-review.googlesource.com/332431
Commit-Ready: Dan Shi <dshi@google.com>
Tested-by: Dan Shi <dshi@google.com>
Reviewed-by: Dan Shi <dshi@google.com>
diff --git a/client/common_lib/cros/dev_server.py b/client/common_lib/cros/dev_server.py
index 489ccdd..d5a2fd4 100644
--- a/client/common_lib/cros/dev_server.py
+++ b/client/common_lib/cros/dev_server.py
@@ -1085,6 +1085,41 @@
raise DevServerException(error_message)
+ @remote_devserver_call()
+ def list_control_files(self, build, suite_name=''):
+ """Ask the devserver to list all control files for |build|.
+
+ @param build: The build (e.g. x86-mario-release/R18-1586.0.0-a1-b1514)
+ whose control files the caller wants listed.
+ @param suite_name: The name of the suite for which we require control
+ files.
+ @return None on failure, or a list of control file paths
+ (e.g. server/site_tests/autoupdate/control)
+ @raise DevServerException upon any return code that's not HTTP OK.
+ """
+ build = self.translate(build)
+ call = self.build_call('controlfiles', build=build,
+ suite_name=suite_name)
+ return self.run_call(call, readline=True)
+
+
+ @remote_devserver_call()
+ def get_control_file(self, build, control_path):
+ """Ask the devserver for the contents of a control file.
+
+ @param build: The build (e.g. x86-mario-release/R18-1586.0.0-a1-b1514)
+ whose control file the caller wants to fetch.
+ @param control_path: The file to fetch
+ (e.g. server/site_tests/autoupdate/control)
+ @return The contents of the desired file.
+ @raise DevServerException upon any return code that's not HTTP OK.
+ """
+ build = self.translate(build)
+ call = self.build_call('controlfiles', build=build,
+ control_path=control_path)
+ return self.run_call(call)
+
+
class ImageServer(ImageServerBase):
"""Class for DevServer that handles RPCs related to CrOS images.
@@ -1157,7 +1192,7 @@
@remote_devserver_call()
- def stage_artifacts(self, image, artifacts=None, files='',
+ def stage_artifacts(self, image=None, artifacts=None, files='',
archive_url=None):
"""Tell the devserver to download and stage |artifacts| from |image|.
@@ -1307,41 +1342,6 @@
@remote_devserver_call()
- def list_control_files(self, build, suite_name=''):
- """Ask the devserver to list all control files for |build|.
-
- @param build: The build (e.g. x86-mario-release/R18-1586.0.0-a1-b1514)
- whose control files the caller wants listed.
- @param suite_name: The name of the suite for which we require control
- files.
- @return None on failure, or a list of control file paths
- (e.g. server/site_tests/autoupdate/control)
- @raise DevServerException upon any return code that's not HTTP OK.
- """
- build = self.translate(build)
- call = self.build_call('controlfiles', build=build,
- suite_name=suite_name)
- return self.run_call(call, readline=True)
-
-
- @remote_devserver_call()
- def get_control_file(self, build, control_path):
- """Ask the devserver for the contents of a control file.
-
- @param build: The build (e.g. x86-mario-release/R18-1586.0.0-a1-b1514)
- whose control file the caller wants to fetch.
- @param control_path: The file to fetch
- (e.g. server/site_tests/autoupdate/control)
- @return The contents of the desired file.
- @raise DevServerException upon any return code that's not HTTP OK.
- """
- build = self.translate(build)
- call = self.build_call('controlfiles', build=build,
- control_path=control_path)
- return self.run_call(call)
-
-
- @remote_devserver_call()
def get_dependencies_file(self, build):
"""Ask the dev server for the contents of the suite dependencies file.
@@ -1497,8 +1497,8 @@
@remote_devserver_call()
- def stage_artifacts(self, target, build_id, branch, artifacts=None,
- files='', archive_url=None):
+ def stage_artifacts(self, target=None, build_id=None, branch=None,
+ image=None, artifacts=None, files='', archive_url=None):
"""Tell the devserver to download and stage |artifacts| from |image|.
This is the main call point for staging any specific artifacts for a
@@ -1512,6 +1512,8 @@
shamu-userdebug.
@param build_id: Build id of the android build to stage.
@param branch: Branch of the android build to stage.
+ @param image: Name of a build to test, in the format of
+ branch/target/build_id
@param artifacts: A list of artifacts.
@param files: A list of files to stage.
@param archive_url: Optional parameter that has the archive_url to stage
@@ -1520,6 +1522,12 @@
@raise DevServerException upon any return code that's not HTTP OK.
"""
+ if image and not target and not build_id and not branch:
+ branch, target, build_id = utils.parse_launch_control_build(image)
+ if not target or not build_id or not branch:
+ raise DevServerException('Must specify all build info (target, '
+ 'build_id and branch) to stage.')
+
android_build_info = {'target': target,
'build_id': build_id,
'branch': branch}
@@ -1625,7 +1633,7 @@
@return The actual build name to use.
"""
- branch, target, build_id = utils.parse_android_build(build_name)
+ branch, target, build_id = utils.parse_launch_control_build(build_name)
if build_id != 'LATEST':
return build_name
call = self.build_call('latestbuild', branch=branch, target=target,
@@ -1722,3 +1730,21 @@
return None
loads = sorted(loads, cmp=_compare_load)
return loads[0]['devserver']
+
+
+def resolve(build, hostname=None):
+ """Resolve a devserver can be used for given build and hostname.
+
+ @param build: Name of a build to stage on devserver, e.g.,
+ ChromeOS build: daisy-release/R50-1234.0.0
+ Launch Control build: git_mnc_release/shamu-eng
+ @param hostname: Hostname of a devserver for, default is None, which means
+ devserver is not restricted by the network location of the host.
+
+ @return: A DevServer instance that can be used to stage given build for the
+ given host.
+ """
+ if utils.is_launch_control_build(build):
+ return AndroidBuildServer.resolve(build, hostname)
+ else:
+ return ImageServer.resolve(build, hostname)
diff --git a/client/common_lib/site_utils.py b/client/common_lib/site_utils.py
index 6541c55..f269597 100644
--- a/client/common_lib/site_utils.py
+++ b/client/common_lib/site_utils.py
@@ -699,10 +699,10 @@
return default_ssid
-def parse_android_build(build_name):
- """Get branch, target, build_id from the given build_name.
+def parse_launch_control_build(build_name):
+ """Get branch, target, build_id from the given Launch Control build_name.
- @param build_name: Name of an Android build, should be formated as
+ @param build_name: Name of a Launch Control build, should be formated as
branch/target/build_id
@return: Tuple of branch, target, build_id
@@ -712,6 +712,18 @@
return branch, target, build_id
+def parse_android_target(target):
+ """Get board and build type from the given target.
+
+ @param target: Name of an Android build target, e.g., shamu-eng.
+
+ @return: Tuple of board, build_type
+ @raise ValueError: If the target is not correctly formated.
+ """
+ board, build_type = target.split('-')
+ return board, build_type
+
+
def parse_android_board_label(board_label_name):
"""Parse the os_type and board name from board label name of an
Android/Brillo board.
@@ -735,23 +747,44 @@
def parse_launch_control_target(target):
- """Parse the board name and target type from a Launch Control target.
+ """Parse the build target and type from a Launch Control target.
- The Launch Control target has the format of board-target_type, e.g.,
- shamu-eng or dragonboard-userdebug. This method extracts the board name
- and target type from the target name.
+ The Launch Control target has the format of build_target-build_type, e.g.,
+ shamu-eng or dragonboard-userdebug. This method extracts the build target
+ and type from the target name.
@param target: Name of a Launch Control target, e.g., shamu-eng.
- @return: (board, target_type), e.g., ('shamu', 'userdebug')
+ @return: (build_target, build_type), e.g., ('shamu', 'userdebug')
"""
- match = re.match('(?P<board>.+)-(?P<target_type>[^-]+)', target)
+ match = re.match('(?P<build_target>.+)-(?P<build_type>[^-]+)', target)
if match:
- return match.group('board'), match.group('target_type')
+ return match.group('build_target'), match.group('build_type')
else:
return None, None
+def is_launch_control_build(build):
+ """Check if a given build is a Launch Control build.
+
+ @param build: Name of a build, e.g.,
+ ChromeOS build: daisy-release/R50-1234.0.0
+ Launch Control build: git_mnc_release/shamu-eng
+
+ @return: True if the build name matches the pattern of a Launch Control
+ build, False otherwise.
+ """
+ try:
+ _, target, _ = parse_launch_control_build(build)
+ build_target, _ = parse_launch_control_target(target)
+ if build_target:
+ return True
+ except ValueError:
+ # parse_launch_control_build or parse_launch_control_target failed.
+ pass
+ return False
+
+
def which(exec_file):
"""Finds an executable file.
diff --git a/frontend/afe/site_rpc_interface.py b/frontend/afe/site_rpc_interface.py
index ac7e9a4..e709ba7 100644
--- a/frontend/afe/site_rpc_interface.py
+++ b/frontend/afe/site_rpc_interface.py
@@ -107,13 +107,13 @@
# Ensure components of |build| necessary for installing images are staged
# on the dev server. However set synchronous to False to allow other
# components to be downloaded in the background.
- ds = dev_server.ImageServer.resolve(build)
+ ds = dev_server.resolve(build)
timings[constants.DOWNLOAD_STARTED_TIME] = formatted_now()
timer = autotest_stats.Timer('control_files.stage.%s' % (
ds.get_server_name(ds.url()).replace('.', '_')))
try:
with timer:
- ds.stage_artifacts(build, ['test_suites'])
+ ds.stage_artifacts(image=build, artifacts=['test_suites'])
except dev_server.DevServerException as e:
raise error.StageControlFileFailure(
"Failed to stage %s: %s" % (build, e))
@@ -209,7 +209,7 @@
suite_name = canonicalize_suite_name(name)
if run_prod_code:
- ds = dev_server.ImageServer.resolve(build)
+ ds = dev_server.resolve(build)
keyvals = {}
getter = control_file_getter.FileSystemGetter(
[_CONFIG.get_config_value('SCHEDULER',
@@ -641,7 +641,7 @@
raise ValueError('Could not resolve build %s: %s' % (build, e))
try:
- ds.stage_artifacts(build, ['test_suites'])
+ ds.stage_artifacts(image=build, artifacts=['test_suites'])
except dev_server.DevServerException as e:
raise error.StageControlFileFailure(
'Failed to stage %s: %s' % (build, e))
diff --git a/frontend/afe/site_rpc_interface_unittest.py b/frontend/afe/site_rpc_interface_unittest.py
index 0606dbe..db4a300 100755
--- a/frontend/afe/site_rpc_interface_unittest.py
+++ b/frontend/afe/site_rpc_interface_unittest.py
@@ -64,7 +64,7 @@
def _setupDevserver(self):
self.mox.StubOutClassWithMocks(dev_server, 'ImageServer')
- dev_server.ImageServer.resolve(self._BUILD).AndReturn(self.dev_server)
+ dev_server.resolve(self._BUILD).AndReturn(self.dev_server)
def _mockDevServerGetter(self, get_control_file=True):
@@ -114,7 +114,7 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
self.dev_server.stage_artifacts(
- self._BUILD, ['test_suites']).AndRaise(
+ image=self._BUILD, artifacts=['test_suites']).AndRaise(
dev_server.DevServerException())
self.mox.ReplayAll()
self.assertRaises(error.StageControlFileFailure,
@@ -131,8 +131,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
@@ -153,8 +153,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
@@ -201,8 +201,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
@@ -225,8 +225,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
@@ -251,8 +251,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
@@ -276,8 +276,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
@@ -304,8 +304,8 @@
self.dev_server.url().AndReturn('mox_url')
self.dev_server.get_server_name(mox.IgnoreArg()).AndReturn('mox_url')
- self.dev_server.stage_artifacts(self._BUILD,
- ['test_suites']).AndReturn(True)
+ self.dev_server.stage_artifacts(
+ image=self._BUILD, artifacts=['test_suites']).AndReturn(True)
self.dev_server.url().AndReturn('mox_url')
job_id = 5
self._mockRpcUtils(job_id)
diff --git a/global_config.ini b/global_config.ini
index ec75011..baf6b47 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -82,6 +82,7 @@
# not have server side package built or with Autotest code change to support
# server-side packaging.
min_version_support_ssp: 6986
+min_launch_control_build_id_support_ssp: 2675445
# Set to True to allow servod to be started automatically in Moblab.
auto_start_servod: False
@@ -395,3 +396,4 @@
[ANDROID]
image_url_pattern: %s/static/%s
stable_version_dragonboard: git_mnc-brillo-dev/dragonboard-userdebug/2512766
+package_url_pattern: %s/static/%s
diff --git a/server/afe_utils.py b/server/afe_utils.py
index 65c1d46..319bd8a 100644
--- a/server/afe_utils.py
+++ b/server/afe_utils.py
@@ -193,3 +193,11 @@
add_version_label(host, image_name)
for attribute, value in host_attributes.items():
update_host_attribute(host, attribute, value)
+
+
+def get_labels(host, prefix):
+ """Get labels of a host with name started with given prefix.
+
+ @param prefix: Prefix of label names.
+ """
+ return AFE.get_labels(name__startswith=prefix, host__hostname=host.hostname)
diff --git a/server/autoserv b/server/autoserv
index 8de1f6d..c4b6a01 100755
--- a/server/autoserv
+++ b/server/autoserv
@@ -672,9 +672,10 @@
logging.warn(
'Autoserv is required to run with server-side packaging. '
'However, no server-side package can be found based on '
- '`--image`, host attribute job_repo_url or host label of '
- 'cros-version. The test will be executed without '
- 'server-side packaging supported.')
+ '`--image`, host attribute job_repo_url or host OS version '
+ 'label. It could be that the build to test is older than the '
+ 'minimum version that supports server-side packaging. The test '
+ 'will be executed without using erver-side packaging.')
if results:
logging.info("Results placed in %s" % results)
diff --git a/server/cros/dynamic_suite/dynamic_suite.py b/server/cros/dynamic_suite/dynamic_suite.py
index e5fa77a..ad869ec 100644
--- a/server/cros/dynamic_suite/dynamic_suite.py
+++ b/server/cros/dynamic_suite/dynamic_suite.py
@@ -571,8 +571,9 @@
# files we should schedule.
try:
if not spec.run_prod_code:
- spec.devserver.stage_artifacts(spec.test_source_build,
- ['control_files', 'test_suites'])
+ spec.devserver.stage_artifacts(
+ image=spec.test_source_build,
+ artifacts=['control_files', 'test_suites'])
except dev_server.DevServerException as e:
# If we can't get the control files, there's nothing to run.
raise error.AsynchronousBuildFailure(e)
diff --git a/server/cros/dynamic_suite/dynamic_suite_unittest.py b/server/cros/dynamic_suite/dynamic_suite_unittest.py
index df2fe6d..c4e5734 100755
--- a/server/cros/dynamic_suite/dynamic_suite_unittest.py
+++ b/server/cros/dynamic_suite/dynamic_suite_unittest.py
@@ -123,10 +123,11 @@
def testReimageAndSIGTERM(self):
"""Should reimage_and_run that causes a SIGTERM and fails cleanly."""
- def suicide(*_):
+ def suicide(*_, **__):
"""Send SIGTERM to current process to exit.
@param _: Ignored.
+ @param __: Ignored.
"""
os.kill(os.getpid(), signal.SIGTERM)
@@ -151,8 +152,9 @@
spec.test_source_build = Suite.get_test_source_build(self._BUILDS)
spec.devserver = self.mox.CreateMock(dev_server.ImageServer)
spec.devserver.stage_artifacts(
- spec.builds[provision.CROS_VERSION_PREFIX],
- ['control_files', 'test_suites']).WithSideEffects(suicide)
+ image=spec.builds[provision.CROS_VERSION_PREFIX],
+ artifacts=['control_files', 'test_suites']
+ ).WithSideEffects(suicide)
spec.run_prod_code = False
self.mox.ReplayAll()
diff --git a/server/cros/dynamic_suite/tools.py b/server/cros/dynamic_suite/tools.py
index 6fd5fe6..11507ae 100644
--- a/server/cros/dynamic_suite/tools.py
+++ b/server/cros/dynamic_suite/tools.py
@@ -40,9 +40,17 @@
return _CONFIG.get_config_value('CROS', 'infrastructure_user', type=str)
-def package_url_pattern():
- """Returns package_url_pattern from global_config."""
- return _CONFIG.get_config_value('CROS', 'package_url_pattern', type=str)
+def package_url_pattern(is_launch_control_build=False):
+ """Returns package_url_pattern from global_config.
+
+ @param is_launch_control_build: True if the package url is for Launch
+ Control build. Default is False.
+ """
+ if is_launch_control_build:
+ return _CONFIG.get_config_value('ANDROID', 'package_url_pattern',
+ type=str)
+ else:
+ return _CONFIG.get_config_value('CROS', 'package_url_pattern', type=str)
def try_job_timeout_mins():
@@ -62,14 +70,17 @@
return package_url_pattern() % (devserver_url, build)
-def get_devserver_build_from_package_url(package_url):
+def get_devserver_build_from_package_url(package_url,
+ is_launch_control_build=False):
"""The inverse method of get_package_url.
@param package_url: a string specifying the package url.
+ @param is_launch_control_build: True if the package url is for Launch
+ Control build. Default is False.
@return tuple containing the devserver_url, build.
"""
- pattern = package_url_pattern()
+ pattern = package_url_pattern(is_launch_control_build)
re_pattern = pattern.replace('%s', '(\S+)')
devserver_build_tuple = re.search(re_pattern, package_url).groups()
diff --git a/server/hosts/adb_host.py b/server/hosts/adb_host.py
index f260873..e5ddd82 100644
--- a/server/hosts/adb_host.py
+++ b/server/hosts/adb_host.py
@@ -12,12 +12,15 @@
import common
from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib import global_config
from autotest_lib.client.common_lib.cros import dev_server
from autotest_lib.client.common_lib.cros import retry
+from autotest_lib.server import afe_utils
from autotest_lib.server import autoserv_parser
from autotest_lib.server import constants as server_constants
from autotest_lib.server import utils
from autotest_lib.server.cros import provision
+from autotest_lib.server.cros.dynamic_suite import tools
from autotest_lib.server.cros.dynamic_suite import constants
from autotest_lib.server.hosts import abstract_ssh
from autotest_lib.server.hosts import adb_label
@@ -25,6 +28,8 @@
from autotest_lib.server.hosts import teststation_host
+CONFIG = global_config.global_config
+
ADB_CMD = 'adb'
FASTBOOT_CMD = 'fastboot'
SHELL_CMD = 'shell'
@@ -67,20 +72,22 @@
OS_TYPE_BRILLO = 'brillo'
# Regex to parse build name to get the detailed build information.
-BUILD_REGEX = ('(?P<BRANCH>([^/]+))/(?P<BOARD>([^/]+))-'
+BUILD_REGEX = ('(?P<BRANCH>([^/]+))/(?P<BUILD_TARGET>([^/]+))-'
'(?P<BUILD_TYPE>([^/]+))/(?P<BUILD_ID>([^/]+))')
# Regex to parse devserver url to get the detailed build information. Sample
# url: http://$devserver:8080/static/branch/target/build_id
DEVSERVER_URL_REGEX = '.*/%s/*' % BUILD_REGEX
-ANDROID_IMAGE_FILE_FMT = '%(board)s-img-%(build_id)s.zip'
+ANDROID_IMAGE_FILE_FMT = '%(build_target)s-img-%(build_id)s.zip'
ANDROID_BOOTLOADER = 'bootloader.img'
ANDROID_RADIO = 'radio.img'
ANDROID_BOOT = 'boot.img'
ANDROID_SYSTEM = 'system.img'
ANDROID_VENDOR = 'vendor.img'
BRILLO_VENDOR_PARTITIONS_FILE_FMT = (
- '%(board)s-vendor_partitions-%(build_id)s.zip')
+ '%(build_target)s-vendor_partitions-%(build_id)s.zip')
+AUTOTEST_SERVER_PACKAGE_FILE_FMT = (
+ '%(build_target)s-autotest_server_package-%(build_id)s.tar.bz2')
# Image files not inside the image zip file. These files should be downloaded
# directly from devserver.
@@ -116,6 +123,12 @@
_parser = autoserv_parser.autoserv_parser
+ # Minimum build id that supports server side packaging. Older builds may
+ # not have server side package built or with Autotest code change to support
+ # server-side packaging.
+ MIN_VERSION_SUPPORT_SSP = CONFIG.get_config_value(
+ 'AUTOSERV', 'min_launch_control_build_id_support_ssp', type=int)
+
@staticmethod
def check_host(host, timeout=10):
"""
@@ -590,7 +603,10 @@
serial = self.adb_serial
# ADB has a device state, if the device is not online, no
# subsequent ADB command will complete.
- if serial not in devices or not self.is_device_ready():
+ # DUT with single device connected may not have adb_serial set.
+ # Therefore, skip checking if serial is in the list of adb devices
+ # if self.adb_serial is not set.
+ if (serial and serial not in devices) or not self.is_device_ready():
logging.debug('Waiting for device to enter the ready state.')
return False
elif command == FASTBOOT_CMD:
@@ -988,8 +1004,8 @@
@param build_url: The url to use for downloading Android artifacts.
pattern: http://$devserver:###/static/branch/target/build_id
- @return: A dictionary of build information, including keys: board,
- branch, target, build_id.
+ @return: A dictionary of build information, including keys:
+ build_target, branch, target, build_id.
@raise AndroidInstallError: If failed to parse build_url.
"""
if not build_url:
@@ -997,9 +1013,9 @@
try:
match = re.match(DEVSERVER_URL_REGEX, build_url)
- return {'board': match.group('BOARD'),
+ return {'build_target': match.group('BUILD_TARGET'),
'branch': match.group('BRANCH'),
- 'target': ('%s-%s' % (match.group('BOARD'),
+ 'target': ('%s-%s' % (match.group('BUILD_TARGET'),
match.group('BUILD_TYPE'))),
'build_id': match.group('BUILD_ID')}
except (AttributeError, IndexError, ValueError) as e:
@@ -1104,7 +1120,7 @@
devserver = dev_server.AndroidBuildServer.resolve(build_name,
self.hostname)
build_name = devserver.translate(build_name)
- branch, target, build_id = utils.parse_android_build(build_name)
+ branch, target, build_id = utils.parse_launch_control_build(build_name)
is_brillo = os_type == OS_TYPE_BRILLO
devserver.trigger_download(target, build_id, branch,
is_brillo=is_brillo, synchronous=False)
@@ -1362,3 +1378,57 @@
def update_labels(self):
"""Update the labels for this testbed."""
self.labels.update_labels(self)
+
+
+ def stage_server_side_package(self, image=None):
+ """Stage autotest server-side package on devserver.
+
+ @param image: A build name, e.g., git_mnc_dev/shamu-eng/123
+
+ @return: A url to the autotest server-side package. Return None if
+ server-side package is not supported.
+ @raise: error.AutoservError if fail to locate the build to test with.
+ """
+ if image:
+ ds = dev_server.AndroidBuildServer.resolve(image, self.hostname)
+ else:
+ job_repo_url = afe_utils.get_host_attribute(
+ self, self.job_repo_url_attribute)
+ if job_repo_url:
+ devserver_url, image = (
+ tools.get_devserver_build_from_package_url(
+ job_repo_url, True))
+ ds = dev_server.AndroidBuildServer(devserver_url)
+ else:
+ labels = afe_utils.get_labels(self, self.VERSION_PREFIX)
+ if not labels:
+ raise error.AutoservError(
+ 'Failed to stage server-side package. The host has '
+ 'no job_report_url attribute or version label.')
+ image = labels[0].name[len(self.VERSION_PREFIX)+1:]
+ ds = dev_server.AndroidBuildServer.resolve(image, self.hostname)
+
+ branch, target, build_id = utils.parse_launch_control_build(image)
+ build_target, _ = utils.parse_launch_control_target(target)
+
+ # For any build older than MIN_VERSION_SUPPORT_SSP, server side
+ # packaging is not supported.
+ try:
+ if int(build_id) < self.MIN_VERSION_SUPPORT_SSP:
+ logging.warn('Build %s is older than %s. Server side packaging '
+ 'is disabled.', image,
+ self.MIN_VERSION_SUPPORT_SSP)
+ return None
+ except ValueError:
+ logging.warn('Failed to compare build id in %s with the minimum '
+ 'version that supports server side packaging. Server '
+ 'side packaging is disabled.', image)
+ return None
+
+ ds.stage_artifacts(target, build_id, branch,
+ artifacts=['autotest_server_package'])
+ autotest_server_package_name = (AUTOTEST_SERVER_PACKAGE_FILE_FMT %
+ {'build_target': build_target,
+ 'build_id': build_id})
+ return '%s/static/%s/%s' % (ds.url(), image,
+ autotest_server_package_name)
diff --git a/server/hosts/factory.py b/server/hosts/factory.py
index b820933..be85453 100644
--- a/server/hosts/factory.py
+++ b/server/hosts/factory.py
@@ -4,6 +4,7 @@
from contextlib import closing
from autotest_lib.client.bin import local_host
+from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error, global_config
from autotest_lib.server import utils as server_utils
from autotest_lib.server.hosts import cros_host, ssh_host
@@ -11,9 +12,9 @@
from autotest_lib.server.hosts import adb_host, testbed
-SSH_ENGINE = global_config.global_config.get_config_value('AUTOSERV',
- 'ssh_engine',
- type=str)
+CONFIG = global_config.global_config
+
+SSH_ENGINE = CONFIG.get_config_value('AUTOSERV', 'ssh_engine', type=str)
# Default ssh options used in creating a host.
DEFAULT_SSH_USER = 'root'
@@ -198,6 +199,24 @@
@returns: The target machine to be used for verify/repair.
"""
+ # For Brillo/Android devices connected to moblab, the `machine` name is
+ # either `localhost` or `127.0.0.1`. It needs to be translated to the host
+ # container IP if the code is running inside a container. This way, autoserv
+ # can ssh to the moblab and run actual adb/fastboot commands.
+ is_moblab = CONFIG.get_config_value('SSP', 'is_moblab', type=bool,
+ default=False)
+ hostname = machine['hostname'] if isinstance(machine, dict) else machine
+ if (utils.is_in_container() and is_moblab and
+ hostname in ['localhost', '127.0.0.1']):
+ hostname = CONFIG.get_config_value('SSP', 'host_container_ip', type=str,
+ default=None)
+ if isinstance(machine, dict):
+ machine['hostname'] = hostname
+ else:
+ machine = hostname
+ logging.debug('Hostname of machine is converted to %s for the test to '
+ 'run inside a container.', hostname)
+
# TODO(kevcheng): We'll want to have a smarter way of figuring out which
# host to create (checking host labels).
if server_utils.machine_is_testbed(machine):
diff --git a/site_utils/suite_scheduler/driver.py b/site_utils/suite_scheduler/driver.py
index e63697b..d3bebdc 100644
--- a/site_utils/suite_scheduler/driver.py
+++ b/site_utils/suite_scheduler/driver.py
@@ -213,7 +213,7 @@
else:
logging.info('Build is not a ChromeOS build, try to parse as a '
'Launch Control build.')
- _,target,_ = utils.parse_android_build(build_name)
+ _,target,_ = utils.parse_launch_control_build(build_name)
board = '%s-%s' % (os_type, utils.parse_launch_control_target(target)[0])
launch_control_builds = [build_name]
logging.info('Testing Launch Control build %s on %s', build_name,