blob: 1aab496548deb154e8b19a8f8d378665b9e07180 [file] [log] [blame]
Dan Shieada9042013-12-02 15:44:59 -08001#!/usr/bin/python
2#
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Script to stage servo image and full payload in all devservers.
8
9This script should be executed in a server running autotest scheduler. It is
10used to accomplish following tasks:
111. Get the latest staged build in devserver as the build that's currently used
12by servo.
132. Stage the latest or a specified beaglebone build from Google Storage
14in all devservers.
153. Show an error if a specified build was successfully staged, but a devserver
16has newer build staged.
17
18"""
19
20import argparse
21import logging
22import sys
23
24import common
25from autotest_lib.client.common_lib import global_config
26from autotest_lib.client.common_lib.cros import dev_server
27from devserver import gsutil_util
28
29
Dan Shia63c6bc2014-01-22 13:42:59 -080030DEFAULT_BUILD_TARGET = global_config.global_config.get_config_value(
31 'CROS', 'servo_builder')
Dan Shieada9042013-12-02 15:44:59 -080032IMAGE_STORAGE_SERVER = global_config.global_config.get_config_value('CROS',
33 'image_storage_server', type=str)
34
35
36def get_all_devservers():
37 """Get a list of ImageServer objects for all available devservers.
38
39 @return: A list of ImageServer objects for all available devservers.
40 """
41 return [dev_server.ImageServer(server) for server in
42 dev_server._get_dev_server_list()]
43
44
45def get_latest_build(build_target):
46 """Get the latest build of beaglebone image in Google Storage.
47
48 @param build_target: build target to look up for the latest build.
49 @return: build name of latest beaglebone image in Google Storage,
Dan Shib9428482014-01-07 17:10:17 -080050 e.g., beaglebone_servo-release/R33-4936.0.0
Dan Shieada9042013-12-02 15:44:59 -080051 """
52 archive_url = IMAGE_STORAGE_SERVER + build_target
53 return gsutil_util.GetLatestVersionFromGSDir(archive_url)
54
55
56def stage_build(devserver, build, build_target=DEFAULT_BUILD_TARGET):
57 """Stage build and its respective image in a devserver
58
59 @param devserver: an instance of ImageServer.
60 @param build: name of the beaglebone build to stage, e.g., R33-4936.0.0.
61 @param build_target: build target of the build to be staged, default
Dan Shib9428482014-01-07 17:10:17 -080062 is set to DEFAULT_BUILD_TARGET.
Dan Shieada9042013-12-02 15:44:59 -080063 @raise DevServerException: upon any return code that's not HTTP OK when
64 making stage_artifacts RPC.
65 """
66 image = '%s/%s' % (build_target, build)
67 # Stage base_image will download image.zip,
68 # unzip chromiumos_base_image.tar.xz, which contains
69 # chromiumos_base_image.bin.
70 artifacts = ['full_payload', 'base_image']
71 logging.info('Staging %s using devserver %s...', image, devserver.url())
72 devserver.stage_artifacts(image=image, artifacts=artifacts)
73 logging.info('Staging completed successfully.')
74
75
76def parse_arguments(argv):
77 """
78 Parse command line arguments
79
80 @param argv: argument list to parse
81 @returns: parsed arguments.
82 @raises SystemExit if arguments are malformed, or required arguments
83 are not present.
84 """
85 description = 'Stage latest or a specific build at all devservers.'
86 parser = argparse.ArgumentParser(description=description)
87 parser.add_argument('-i', '--build', action='store', default=None,
88 help='Optional argument to specify a build to stage, '
89 'e.g., R33-5079.0.0. Do not include the build '
90 'target here.')
91 parser.add_argument('-t', '--build_target', action='store',
92 default=DEFAULT_BUILD_TARGET,
93 help='Optional argument to specify a build target, '
Dan Shib9428482014-01-07 17:10:17 -080094 'e.g., trybot-beaglebone_servo-release. Default '
95 'value is set to %s.' % DEFAULT_BUILD_TARGET)
Dan Shieada9042013-12-02 15:44:59 -080096 return parser.parse_args(argv)
97
98
99def main(argv):
100 """Main entrance of the script.
101
102 @param argv: arguments list
103 """
104 arguments = parse_arguments(argv)
105 build_target = arguments.build_target
106 devservers = get_all_devservers()
107 if not devservers:
108 raise Exception('No devserver found. Script failed to stage any build. '
109 'Please check your shadow_config.ini file about '
110 'devserver setting.')
111
112 if arguments.build:
113 build_to_stage = arguments.build
114 else:
115 build_to_stage = get_latest_build(build_target)
116 logging.info('Latest build for %s is %s.', build_target, build_to_stage)
117
118 try:
119 # The build must be staged in all devservers. Any devserver failed to
120 # stage the build shall fail the script. When in prod, a beaglebone may
121 # hit any available devserver to update the latest image staged in the
122 # devserver, without requesting to stage the build. That's why it's
123 # essential for all devservers to have the same build staged.
124 for devserver in devservers:
125 stage_build(devserver, build_to_stage, build_target)
126 except dev_server.DevServerException as e:
127 logging.error('Staging build %s failed with error: \n%s',
128 build_to_stage, e)
129 # Check if latest_staged_builds are all the same, if so, delete any
130 # staged |build_to_stage| in all devservers.
131 latest_staged_builds = [devserver.get_latest_build_in_server(
132 build_target) for devserver in devservers]
133 if not all(b == latest_staged_builds[0] for b in latest_staged_builds):
134 logging.error(('devservers have different latest builds for build '
135 'target %s. You should try to fix the problem, and '
136 'run this script again to make sure all devservers '
137 'have the same latest build staged.'), build_target)
138 sys.exit(1)
139
140 # Save the latest build in each devserver in a list, will be used to check
141 # if they are newer builds compared to the build to be staged.
142 latest_staged_builds = [devserver.get_latest_build_in_server(build_target)
143 for devserver in devservers]
144
145 # Compare the previous latest build staged in devserver and build_to_stage.
146 # If the previous latest builder is newer, post a warning that user should
147 # manually delete any newer builds in each devserver.
148 if not all(b == build_to_stage for b in latest_staged_builds):
149 logging.error('Following devservers have staged newer build. You need '
150 'to manually delete any build newer than %s in each '
151 'devserver to guarantee beaglebone can be upgraded to the'
152 ' desired build.\n', build_to_stage)
153
154 for devserver, build in zip(devservers, latest_staged_builds):
155 if build != build_to_stage:
156 logging.error('%s\t%s', devserver.url(), build)
157
158
159if __name__ == '__main__':
160 main(sys.argv[1:])