blob: cc728d56eae6ae9bea894fdda035b205113f829f [file] [log] [blame]
Don Garrett8db752c2014-10-17 16:56:55 -07001#!/usr/bin/python
2# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Runs on autotest servers from a cron job to self update them.
7
8This script is designed to run on all autotest servers to allow them to
9automatically self-update based on the manifests used to create their (existing)
10repos.
11"""
12
13from __future__ import print_function
14
15import ConfigParser
Don Garrett03432d62014-11-19 18:18:35 -080016import argparse
Don Garrett8db752c2014-10-17 16:56:55 -070017import os
Don Garrett699b4b32014-12-11 13:10:15 -080018import re
Dan Shicf278042016-04-06 21:16:34 -070019import socket
Don Garrett8db752c2014-10-17 16:56:55 -070020import subprocess
21import sys
22import time
23
24import common
25
26from autotest_lib.client.common_lib import global_config
Dan Shiac6fdbf2016-04-09 17:36:28 -070027from autotest_lib.server import utils as server_utils
Dan Shicf278042016-04-06 21:16:34 -070028from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
Don Garrett8db752c2014-10-17 16:56:55 -070029
Dan Shiac6fdbf2016-04-09 17:36:28 -070030
Don Garrettd0321722014-11-18 16:03:33 -080031# How long after restarting a service do we watch it to see if it's stable.
Don Garrett6073ba92015-07-23 15:01:39 -070032SERVICE_STABILITY_TIMER = 120
Don Garrettd0321722014-11-18 16:03:33 -080033
Dan Shicf278042016-04-06 21:16:34 -070034# A list of commands that only applies to primary server. For example,
35# test_importer should only be run in primary master scheduler. If two servers
36# are both running test_importer, there is a chance to fail as both try to
37# update the same table.
38PRIMARY_ONLY_COMMANDS = ['test_importer']
Shuqian Zhaoa3438a52016-09-20 15:11:02 -070039# A dict to map update_commands defined in config file to repos or files that
40# decide whether need to update these commands. E.g. if no changes under
41# frontend repo, no need to update afe.
Aviv Keshet85622032016-10-03 03:17:26 -070042COMMANDS_TO_REPOS_DICT = {'afe': 'frontend/',
43 'tko': 'tko/'}
Shuqian Zhao697f7ed2016-10-21 14:58:28 -070044BUILD_EXTERNALS_COMMAND = 'build_externals'
Allen Li43b275a2016-10-04 15:14:11 -070045# Services present on all hosts.
Allen Lida9e81c2016-10-18 18:43:04 -070046# TODO(ayatane): Temporarily stop starting sysmon
47# UNIVERSAL_SERVICES = ['sysmon']
48UNIVERSAL_SERVICES = []
Dan Shicf278042016-04-06 21:16:34 -070049
Dan Shiac6fdbf2016-04-09 17:36:28 -070050AFE = frontend_wrappers.RetryingAFE(
51 server=server_utils.get_global_afe_hostname(), timeout_min=5,
52 delay_sec=10)
Don Garrett8db752c2014-10-17 16:56:55 -070053
54class DirtyTreeException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080055 """Raised when the tree has been modified in an unexpected way."""
Don Garrett8db752c2014-10-17 16:56:55 -070056
57
58class UnknownCommandException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080059 """Raised when we try to run a command name with no associated command."""
Don Garrett8db752c2014-10-17 16:56:55 -070060
61
62class UnstableServices(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080063 """Raised if a service appears unstable after restart."""
Don Garrett8db752c2014-10-17 16:56:55 -070064
65
Don Garrett35711212014-12-18 14:33:41 -080066def strip_terminal_codes(text):
67 """This function removes all terminal formatting codes from a string.
68
69 @param text: String of text to cleanup.
70 @returns String with format codes removed.
71 """
72 ESC = '\x1b'
73 return re.sub(ESC+r'\[[^m]*m', '', text)
74
75
Don Garrett8db752c2014-10-17 16:56:55 -070076def verify_repo_clean():
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070077 """This function cleans the current repo then verifies that it is valid.
Don Garrett8db752c2014-10-17 16:56:55 -070078
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070079 @raises DirtyTreeException if the repo is still not clean.
Don Garrett8db752c2014-10-17 16:56:55 -070080 @raises subprocess.CalledProcessError on a repo command failure.
81 """
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070082 subprocess.check_output(['git', 'reset', '--hard'])
Don Garrett8db752c2014-10-17 16:56:55 -070083 out = subprocess.check_output(['repo', 'status'], stderr=subprocess.STDOUT)
Don Garrett35711212014-12-18 14:33:41 -080084 out = strip_terminal_codes(out).strip()
Don Garrett699b4b32014-12-11 13:10:15 -080085
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070086 if not 'working directory clean' in out:
Dan Shicf278042016-04-06 21:16:34 -070087 raise DirtyTreeException(out)
Don Garrett8db752c2014-10-17 16:56:55 -070088
Don Garrett8db752c2014-10-17 16:56:55 -070089
90def repo_versions():
91 """This function collects the versions of all git repos in the general repo.
92
Don Garrettfa2c1c42014-12-11 12:11:49 -080093 @returns A dictionary mapping project names to git hashes for HEAD.
Don Garrett8db752c2014-10-17 16:56:55 -070094 @raises subprocess.CalledProcessError on a repo command failure.
95 """
Don Garrettfa2c1c42014-12-11 12:11:49 -080096 cmd = ['repo', 'forall', '-p', '-c', 'pwd && git log -1 --format=%h']
Don Garrett35711212014-12-18 14:33:41 -080097 output = strip_terminal_codes(subprocess.check_output(cmd))
Don Garrettfa2c1c42014-12-11 12:11:49 -080098
99 # The expected output format is:
100
101 # project chrome_build/
102 # /dir/holding/chrome_build
103 # 73dee9d
104 #
105 # project chrome_release/
106 # /dir/holding/chrome_release
107 # 9f3a5d8
108
109 lines = output.splitlines()
110
111 PROJECT_PREFIX = 'project '
112
113 project_heads = {}
114 for n in range(0, len(lines), 4):
115 project_line = lines[n]
116 project_dir = lines[n+1]
117 project_hash = lines[n+2]
118 # lines[n+3] is a blank line, but doesn't exist for the final block.
119
120 # Convert 'project chrome_build/' -> 'chrome_build'
121 assert project_line.startswith(PROJECT_PREFIX)
122 name = project_line[len(PROJECT_PREFIX):].rstrip('/')
123
124 project_heads[name] = (project_dir, project_hash)
125
126 return project_heads
Don Garrett8db752c2014-10-17 16:56:55 -0700127
128
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700129def repo_versions_to_decide_whether_run_cmd_update():
130 """Collect versions of repos/files defined in COMMANDS_TO_REPOS_DICT.
131
132 For the update_commands defined in config files, no need to run the command
133 every time. Only run it when the repos/files related to the commands have
134 been changed.
135
136 @returns A set of tuples: {(cmd, repo_version), ()...}
137 """
138 results = set()
139 for cmd, repo in COMMANDS_TO_REPOS_DICT.iteritems():
140 version = subprocess.check_output(
141 ['git', 'log', '-1', '--pretty=tformat:%h',
142 '%s/%s' % (common.autotest_dir, repo)])
143 results.add((cmd, version.strip()))
144 return results
145
146
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700147def repo_sync(update_push_servers=False):
Don Garrett8db752c2014-10-17 16:56:55 -0700148 """Perform a repo sync.
149
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700150 @param update_push_servers: If True, then update test_push servers to ToT.
151 Otherwise, update server to prod branch.
Don Garrett8db752c2014-10-17 16:56:55 -0700152 @raises subprocess.CalledProcessError on a repo command failure.
153 """
Don Garrettd0321722014-11-18 16:03:33 -0800154 subprocess.check_output(['repo', 'sync'])
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700155 if update_push_servers:
156 print('Updating push servers, checkout cros/master')
157 subprocess.check_output(['git', 'checkout', 'cros/master'])
158 else:
159 print('Updating server to prod branch')
160 subprocess.check_output(['git', 'checkout', 'cros/prod'])
Don Garrett8db752c2014-10-17 16:56:55 -0700161
162
Don Garrettd0321722014-11-18 16:03:33 -0800163def discover_update_commands():
164 """Lookup the commands to run on this server.
Don Garrett8db752c2014-10-17 16:56:55 -0700165
Don Garrettd0321722014-11-18 16:03:33 -0800166 These commonly come from shadow_config.ini, since they vary by server type.
Don Garrett8db752c2014-10-17 16:56:55 -0700167
Don Garrettd0321722014-11-18 16:03:33 -0800168 @returns List of command names in string format.
Don Garrett8db752c2014-10-17 16:56:55 -0700169 """
Don Garrett8db752c2014-10-17 16:56:55 -0700170 try:
Don Garrettd0321722014-11-18 16:03:33 -0800171 return global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700172 'UPDATE', 'commands', type=list)
173
174 except (ConfigParser.NoSectionError, global_config.ConfigError):
Don Garrettd0321722014-11-18 16:03:33 -0800175 return []
Don Garrett8db752c2014-10-17 16:56:55 -0700176
Don Garrettd0321722014-11-18 16:03:33 -0800177
178def discover_restart_services():
179 """Find the services that need restarting on the current server.
180
181 These commonly come from shadow_config.ini, since they vary by server type.
182
183 @returns List of service names in string format.
184 """
Allen Li43b275a2016-10-04 15:14:11 -0700185 services = list(UNIVERSAL_SERVICES)
Don Garrett8db752c2014-10-17 16:56:55 -0700186 try:
Allen Li43b275a2016-10-04 15:14:11 -0700187 # Look up services from shadow_config.ini.
188 extra_services = global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700189 'UPDATE', 'services', type=list)
Allen Li43b275a2016-10-04 15:14:11 -0700190 services.extend(extra_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700191 except (ConfigParser.NoSectionError, global_config.ConfigError):
Allen Li43b275a2016-10-04 15:14:11 -0700192 pass
193 return services
Don Garrett8db752c2014-10-17 16:56:55 -0700194
Don Garrett8db752c2014-10-17 16:56:55 -0700195
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700196def update_command(cmd_tag, dryrun=False, use_chromite_master=False):
Don Garrettd0321722014-11-18 16:03:33 -0800197 """Restart a command.
Don Garrett8db752c2014-10-17 16:56:55 -0700198
Don Garrettd0321722014-11-18 16:03:33 -0800199 The command name is looked up in global_config.ini to find the full command
200 to run, then it's executed.
Don Garrett8db752c2014-10-17 16:56:55 -0700201
Don Garrettd0321722014-11-18 16:03:33 -0800202 @param cmd_tag: Which command to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800203 @param dryrun: If true print the command that would have been run.
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700204 @param use_chromite_master: True if updating chromite to master, rather
205 than prod.
Don Garrett8db752c2014-10-17 16:56:55 -0700206
Don Garrettd0321722014-11-18 16:03:33 -0800207 @raises UnknownCommandException If cmd_tag can't be looked up.
208 @raises subprocess.CalledProcessError on a command failure.
209 """
210 # Lookup the list of commands to consider. They are intended to be
211 # in global_config.ini so that they can be shared everywhere.
212 cmds = dict(global_config.global_config.config.items(
213 'UPDATE_COMMANDS'))
Don Garrett8db752c2014-10-17 16:56:55 -0700214
Don Garrettd0321722014-11-18 16:03:33 -0800215 if cmd_tag not in cmds:
216 raise UnknownCommandException(cmd_tag, cmds)
Don Garrett8db752c2014-10-17 16:56:55 -0700217
Don Garrettd0321722014-11-18 16:03:33 -0800218 expanded_command = cmds[cmd_tag].replace('AUTOTEST_REPO',
219 common.autotest_dir)
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700220 # When updating push servers, pass an arg to build_externals to update
221 # chromite to master branch for testing
222 if use_chromite_master and cmd_tag == BUILD_EXTERNALS_COMMAND:
223 expanded_command += ' --use_chromite_master'
Don Garrett8db752c2014-10-17 16:56:55 -0700224
Don Garrett699b4b32014-12-11 13:10:15 -0800225 print('Running: %s: %s' % (cmd_tag, expanded_command))
Don Garrett03432d62014-11-19 18:18:35 -0800226 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800227 print('Skip: %s' % expanded_command)
Don Garrett03432d62014-11-19 18:18:35 -0800228 else:
Don Garrett4769c902015-01-05 15:58:56 -0800229 try:
230 subprocess.check_output(expanded_command, shell=True,
231 stderr=subprocess.STDOUT)
232 except subprocess.CalledProcessError as e:
233 print('FAILED:')
234 print(e.output)
235 raise
Don Garrett8db752c2014-10-17 16:56:55 -0700236
Don Garrett8db752c2014-10-17 16:56:55 -0700237
Don Garrett03432d62014-11-19 18:18:35 -0800238def restart_service(service_name, dryrun=False):
Don Garrettd0321722014-11-18 16:03:33 -0800239 """Restart a service.
240
241 Restarts the standard service with "service <name> restart".
242
243 @param service_name: The name of the service to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800244 @param dryrun: Don't really run anything, just print out the command.
Don Garrettd0321722014-11-18 16:03:33 -0800245
246 @raises subprocess.CalledProcessError on a command failure.
247 """
Don Garrett03432d62014-11-19 18:18:35 -0800248 cmd = ['sudo', 'service', service_name, 'restart']
Don Garrett699b4b32014-12-11 13:10:15 -0800249 print('Restarting: %s' % service_name)
Don Garrett03432d62014-11-19 18:18:35 -0800250 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800251 print('Skip: %s' % ' '.join(cmd))
Don Garrett03432d62014-11-19 18:18:35 -0800252 else:
Don Garrett03432d62014-11-19 18:18:35 -0800253 subprocess.check_call(cmd)
Don Garrettd0321722014-11-18 16:03:33 -0800254
255
256def service_status(service_name):
257 """Return the results "status <name>" for a given service.
258
259 This string is expected to contain the pid, and so to change is the service
260 is shutdown or restarted for any reason.
261
262 @param service_name: The name of the service to check on.
Don Garrett03432d62014-11-19 18:18:35 -0800263
Don Garrettd0321722014-11-18 16:03:33 -0800264 @returns The output of the external command.
265 Ex: autofs start/running, process 1931
266
267 @raises subprocess.CalledProcessError on a command failure.
268 """
269 return subprocess.check_output(['sudo', 'status', service_name])
270
271
Dan Shi57d4c732015-01-22 18:38:50 -0800272def restart_services(service_names, dryrun=False, skip_service_status=False):
Don Garrettd0321722014-11-18 16:03:33 -0800273 """Restart services as needed for the current server type.
274
275 Restart the listed set of services, and watch to see if they are stable for
276 at least SERVICE_STABILITY_TIMER. It restarts all services quickly,
277 waits for that delay, then verifies the status of all of them.
278
279 @param service_names: The list of service to restart and monitor.
Don Garrett03432d62014-11-19 18:18:35 -0800280 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800281 @param skip_service_status: Set to True to skip service status check.
282 Default is False.
Don Garrettd0321722014-11-18 16:03:33 -0800283
284 @raises subprocess.CalledProcessError on a command failure.
Don Garrett03432d62014-11-19 18:18:35 -0800285 @raises UnstableServices if any services are unstable after restart.
Don Garrettd0321722014-11-18 16:03:33 -0800286 """
287 service_statuses = {}
288
Don Garrett03432d62014-11-19 18:18:35 -0800289 if dryrun:
290 for name in service_names:
291 restart_service(name, dryrun=True)
292 return
293
Don Garrettd0321722014-11-18 16:03:33 -0800294 # Restart each, and record the status (including pid).
295 for name in service_names:
296 restart_service(name)
297 service_statuses[name] = service_status(name)
298
Dan Shi57d4c732015-01-22 18:38:50 -0800299 # Skip service status check if --skip-service-status is specified. Used for
300 # servers in backup status.
301 if skip_service_status:
302 print('--skip-service-status is specified, skip checking services.')
303 return
304
Don Garrettd0321722014-11-18 16:03:33 -0800305 # Wait for a while to let the services settle.
306 time.sleep(SERVICE_STABILITY_TIMER)
307
308 # Look for any services that changed status.
309 unstable_services = [n for n in service_names
310 if service_status(n) != service_statuses[n]]
311
312 # Report any services having issues.
313 if unstable_services:
314 raise UnstableServices(unstable_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700315
316
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700317def run_deploy_actions(cmds_skip=set(), dryrun=False,
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700318 skip_service_status=False, use_chromite_master=False):
Don Garrettfa2c1c42014-12-11 12:11:49 -0800319 """Run arbitrary update commands specified in global.ini.
Don Garrett8db752c2014-10-17 16:56:55 -0700320
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700321 @param cmds_skip: cmds no need to run since the corresponding repo/file
322 does not change.
Don Garrett03432d62014-11-19 18:18:35 -0800323 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800324 @param skip_service_status: Set to True to skip service status check.
325 Default is False.
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700326 @param use_chromite_master: True if updating chromite to master, rather
327 than prod.
Don Garrett8db752c2014-10-17 16:56:55 -0700328
Don Garrett03432d62014-11-19 18:18:35 -0800329 @raises subprocess.CalledProcessError on a command failure.
330 @raises UnstableServices if any services are unstable after restart.
331 """
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700332 defined_cmds = set(discover_update_commands())
333 cmds = defined_cmds - cmds_skip
Don Garrettd0321722014-11-18 16:03:33 -0800334 if cmds:
335 print('Running update commands:', ', '.join(cmds))
336 for cmd in cmds:
Dan Shicf278042016-04-06 21:16:34 -0700337 if (cmd in PRIMARY_ONLY_COMMANDS and
338 not AFE.run('get_servers', hostname=socket.getfqdn(),
339 status='primary')):
340 print('Command %s is only applicable to primary servers.' % cmd)
341 continue
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700342 update_command(cmd, dryrun=dryrun,
343 use_chromite_master=use_chromite_master)
Don Garrettd0321722014-11-18 16:03:33 -0800344
345 services = discover_restart_services()
346 if services:
Don Garrett03432d62014-11-19 18:18:35 -0800347 print('Restarting Services:', ', '.join(services))
Dan Shi57d4c732015-01-22 18:38:50 -0800348 restart_services(services, dryrun=dryrun,
349 skip_service_status=skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800350
351
Don Garrettfa2c1c42014-12-11 12:11:49 -0800352def report_changes(versions_before, versions_after):
353 """Produce a report describing what changed in all repos.
354
355 @param versions_before: Results of repo_versions() from before the update.
356 @param versions_after: Results of repo_versions() from after the update.
357
358 @returns string containing a human friendly changes report.
359 """
360 result = []
361
Don Garrett35711212014-12-18 14:33:41 -0800362 if versions_after:
363 for project in sorted(set(versions_before.keys() + versions_after.keys())):
364 result.append('%s:' % project)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800365
Don Garrett35711212014-12-18 14:33:41 -0800366 _, before_hash = versions_before.get(project, (None, None))
367 after_dir, after_hash = versions_after.get(project, (None, None))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800368
Don Garrett35711212014-12-18 14:33:41 -0800369 if project not in versions_before:
370 result.append('Added.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800371
Don Garrett35711212014-12-18 14:33:41 -0800372 elif project not in versions_after:
373 result.append('Removed.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800374
Don Garrett35711212014-12-18 14:33:41 -0800375 elif before_hash == after_hash:
376 result.append('No Change.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800377
Don Garrett35711212014-12-18 14:33:41 -0800378 else:
379 hashes = '%s..%s' % (before_hash, after_hash)
380 cmd = ['git', 'log', hashes, '--oneline']
381 out = subprocess.check_output(cmd, cwd=after_dir,
382 stderr=subprocess.STDOUT)
383 result.append(out.strip())
Don Garrettfa2c1c42014-12-11 12:11:49 -0800384
Don Garrett35711212014-12-18 14:33:41 -0800385 result.append('')
386 else:
387 for project in sorted(versions_before.keys()):
388 _, before_hash = versions_before[project]
389 result.append('%s: %s' % (project, before_hash))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800390 result.append('')
391
392 return '\n'.join(result)
393
394
Don Garrett03432d62014-11-19 18:18:35 -0800395def parse_arguments(args):
396 """Parse command line arguments.
397
398 @param args: The command line arguments to parse. (ususally sys.argsv[1:])
399
Don Garrett40036362014-12-08 15:52:44 -0800400 @returns An argparse.Namespace populated with argument values.
Don Garrett03432d62014-11-19 18:18:35 -0800401 """
402 parser = argparse.ArgumentParser(
403 description='Command to update an autotest server.')
404 parser.add_argument('--skip-verify', action='store_false',
405 dest='verify', default=True,
406 help='Disable verification of a clean repository.')
407 parser.add_argument('--skip-update', action='store_false',
408 dest='update', default=True,
409 help='Skip the repository source code update.')
410 parser.add_argument('--skip-actions', action='store_false',
411 dest='actions', default=True,
412 help='Skip the post update actions.')
413 parser.add_argument('--skip-report', action='store_false',
414 dest='report', default=True,
415 help='Skip the git version report.')
Don Garrette3718912014-12-05 13:11:44 -0800416 parser.add_argument('--actions-only', action='store_true',
417 help='Run the post update actions (restart services).')
Don Garrett03432d62014-11-19 18:18:35 -0800418 parser.add_argument('--dryrun', action='store_true',
419 help='Don\'t actually run any commands, just log.')
Dan Shi57d4c732015-01-22 18:38:50 -0800420 parser.add_argument('--skip-service-status', action='store_true',
421 help='Skip checking the service status.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700422 parser.add_argument('--update_push_servers', action='store_true',
423 help='Indicate to update test_push server. If not '
424 'specify, then update server to production.')
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700425 parser.add_argument('--force_update', action='store_true',
426 help='Force to run the update commands for afe, tko '
427 'and build_externals')
Don Garrett03432d62014-11-19 18:18:35 -0800428
429 results = parser.parse_args(args)
430
Don Garrette3718912014-12-05 13:11:44 -0800431 if results.actions_only:
432 results.verify = False
433 results.update = False
434 results.report = False
435
Don Garrett03432d62014-11-19 18:18:35 -0800436 # TODO(dgarrett): Make these behaviors support dryrun.
437 if results.dryrun:
438 results.verify = False
439 results.update = False
440
441 return results
442
443
Allen Lie8c4ea42016-10-05 18:08:29 -0700444class ChangeDir(object):
445
446 """Context manager for changing to a directory temporarily."""
447
448 def __init__(self, dir):
449 self.new_dir = dir
450 self.old_dir = None
451
452 def __enter__(self):
453 self.old_dir = os.getcwd()
454 os.chdir(self.new_dir)
455
456 def __exit__(self, exc_type, exc_val, exc_tb):
457 os.chdir(self.old_dir)
458
459
Allen Li1f5a1682016-10-06 15:23:06 -0700460def update_chromeos():
461 """Update /usr/local/google/chromeos repo."""
462 print('Updating /usr/local/google/chromeos')
463 with ChangeDir('/usr/local/google/chromeos'):
464 ret = subprocess.call(['repo', 'sync'])
Allen Lie8c4ea42016-10-05 18:08:29 -0700465 if ret != 0:
466 print('Update failed, exited with status: %d' % ret)
467
468
Don Garrett03432d62014-11-19 18:18:35 -0800469def main(args):
470 """Main method."""
471 os.chdir(common.autotest_dir)
472 global_config.global_config.parse_config_file()
473
474 behaviors = parse_arguments(args)
475
476 if behaviors.verify:
Don Garrettd0321722014-11-18 16:03:33 -0800477 try:
Don Garrett03432d62014-11-19 18:18:35 -0800478 print('Checking tree status:')
479 verify_repo_clean()
480 print('Clean.')
481 except DirtyTreeException as e:
482 print('Local tree is dirty, can\'t perform update safely.')
483 print()
484 print('repo status:')
Don Garrettd0321722014-11-18 16:03:33 -0800485 print(e.args[0])
486 return 1
Don Garrett8db752c2014-10-17 16:56:55 -0700487
Don Garrett35711212014-12-18 14:33:41 -0800488 versions_before = repo_versions()
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700489 versions_after = set()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700490 cmd_versions_before = repo_versions_to_decide_whether_run_cmd_update()
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700491 cmd_versions_after = set()
Don Garrettfa2c1c42014-12-11 12:11:49 -0800492
Don Garrett03432d62014-11-19 18:18:35 -0800493 if behaviors.update:
Don Garrett03432d62014-11-19 18:18:35 -0800494 print('Updating Repo.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700495 repo_sync(behaviors.update_push_servers)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800496 versions_after = repo_versions()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700497 cmd_versions_after = repo_versions_to_decide_whether_run_cmd_update()
Don Garrett03432d62014-11-19 18:18:35 -0800498
Allen Li1f5a1682016-10-06 15:23:06 -0700499 update_chromeos()
Allen Lie8c4ea42016-10-05 18:08:29 -0700500
Don Garrett03432d62014-11-19 18:18:35 -0800501 if behaviors.actions:
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700502 # If the corresponding repo/file not change, no need to run the cmd.
503 cmds_skip = (set() if behaviors.force_update else
504 {t[0] for t in cmd_versions_before & cmd_versions_after})
Don Garrett03432d62014-11-19 18:18:35 -0800505 try:
Dan Shi57d4c732015-01-22 18:38:50 -0800506 run_deploy_actions(
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700507 cmds_skip, behaviors.dryrun, behaviors.skip_service_status,
508 use_chromite_master=behaviors.update_push_servers)
Don Garrett03432d62014-11-19 18:18:35 -0800509 except UnstableServices as e:
510 print('The following services were not stable after '
511 'the update:')
512 print(e.args[0])
513 return 1
514
Don Garrett35711212014-12-18 14:33:41 -0800515 if behaviors.report:
Don Garrettfa2c1c42014-12-11 12:11:49 -0800516 print('Changes:')
517 print(report_changes(versions_before, versions_after))
Don Garrett8db752c2014-10-17 16:56:55 -0700518
519
520if __name__ == '__main__':
Don Garrett03432d62014-11-19 18:18:35 -0800521 sys.exit(main(sys.argv[1:]))