| #!/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:]) |