blob: 1aab496548deb154e8b19a8f8d378665b9e07180 [file] [log] [blame] [edit]
#!/usr/bin/python
#
# Copyright (c) 2013 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.
"""Script to stage servo image and full payload in all devservers.
This script should be executed in a server running autotest scheduler. It is
used to accomplish following tasks:
1. Get the latest staged build in devserver as the build that's currently used
by servo.
2. Stage the latest or a specified beaglebone build from Google Storage
in all devservers.
3. Show an error if a specified build was successfully staged, but a devserver
has newer build staged.
"""
import argparse
import logging
import sys
import common
from autotest_lib.client.common_lib import global_config
from autotest_lib.client.common_lib.cros import dev_server
from devserver import gsutil_util
DEFAULT_BUILD_TARGET = global_config.global_config.get_config_value(
'CROS', 'servo_builder')
IMAGE_STORAGE_SERVER = global_config.global_config.get_config_value('CROS',
'image_storage_server', type=str)
def get_all_devservers():
"""Get a list of ImageServer objects for all available devservers.
@return: A list of ImageServer objects for all available devservers.
"""
return [dev_server.ImageServer(server) for server in
dev_server._get_dev_server_list()]
def get_latest_build(build_target):
"""Get the latest build of beaglebone image in Google Storage.
@param build_target: build target to look up for the latest build.
@return: build name of latest beaglebone image in Google Storage,
e.g., beaglebone_servo-release/R33-4936.0.0
"""
archive_url = IMAGE_STORAGE_SERVER + build_target
return gsutil_util.GetLatestVersionFromGSDir(archive_url)
def stage_build(devserver, build, build_target=DEFAULT_BUILD_TARGET):
"""Stage build and its respective image in a devserver
@param devserver: an instance of ImageServer.
@param build: name of the beaglebone build to stage, e.g., R33-4936.0.0.
@param build_target: build target of the build to be staged, default
is set to DEFAULT_BUILD_TARGET.
@raise DevServerException: upon any return code that's not HTTP OK when
making stage_artifacts RPC.
"""
image = '%s/%s' % (build_target, build)
# Stage base_image will download image.zip,
# unzip chromiumos_base_image.tar.xz, which contains
# chromiumos_base_image.bin.
artifacts = ['full_payload', 'base_image']
logging.info('Staging %s using devserver %s...', image, devserver.url())
devserver.stage_artifacts(image=image, artifacts=artifacts)
logging.info('Staging completed successfully.')
def parse_arguments(argv):
"""
Parse command line arguments
@param argv: argument list to parse
@returns: parsed arguments.
@raises SystemExit if arguments are malformed, or required arguments
are not present.
"""
description = 'Stage latest or a specific build at all devservers.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-i', '--build', action='store', default=None,
help='Optional argument to specify a build to stage, '
'e.g., R33-5079.0.0. Do not include the build '
'target here.')
parser.add_argument('-t', '--build_target', action='store',
default=DEFAULT_BUILD_TARGET,
help='Optional argument to specify a build target, '
'e.g., trybot-beaglebone_servo-release. Default '
'value is set to %s.' % DEFAULT_BUILD_TARGET)
return parser.parse_args(argv)
def main(argv):
"""Main entrance of the script.
@param argv: arguments list
"""
arguments = parse_arguments(argv)
build_target = arguments.build_target
devservers = get_all_devservers()
if not devservers:
raise Exception('No devserver found. Script failed to stage any build. '
'Please check your shadow_config.ini file about '
'devserver setting.')
if arguments.build:
build_to_stage = arguments.build
else:
build_to_stage = get_latest_build(build_target)
logging.info('Latest build for %s is %s.', build_target, build_to_stage)
try:
# The build must be staged in all devservers. Any devserver failed to
# stage the build shall fail the script. When in prod, a beaglebone may
# hit any available devserver to update the latest image staged in the
# devserver, without requesting to stage the build. That's why it's
# essential for all devservers to have the same build staged.
for devserver in devservers:
stage_build(devserver, build_to_stage, build_target)
except dev_server.DevServerException as e:
logging.error('Staging build %s failed with error: \n%s',
build_to_stage, e)
# Check if latest_staged_builds are all the same, if so, delete any
# staged |build_to_stage| in all devservers.
latest_staged_builds = [devserver.get_latest_build_in_server(
build_target) for devserver in devservers]
if not all(b == latest_staged_builds[0] for b in latest_staged_builds):
logging.error(('devservers have different latest builds for build '
'target %s. You should try to fix the problem, and '
'run this script again to make sure all devservers '
'have the same latest build staged.'), build_target)
sys.exit(1)
# Save the latest build in each devserver in a list, will be used to check
# if they are newer builds compared to the build to be staged.
latest_staged_builds = [devserver.get_latest_build_in_server(build_target)
for devserver in devservers]
# Compare the previous latest build staged in devserver and build_to_stage.
# If the previous latest builder is newer, post a warning that user should
# manually delete any newer builds in each devserver.
if not all(b == build_to_stage for b in latest_staged_builds):
logging.error('Following devservers have staged newer build. You need '
'to manually delete any build newer than %s in each '
'devserver to guarantee beaglebone can be upgraded to the'
' desired build.\n', build_to_stage)
for devserver, build in zip(devservers, latest_staged_builds):
if build != build_to_stage:
logging.error('%s\t%s', devserver.url(), build)
if __name__ == '__main__':
main(sys.argv[1:])