blob: c06362fd6b6517e732d97dc8dcfee90e01f75f29 [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/'}
Allen Li43b275a2016-10-04 15:14:11 -070044# Services present on all hosts.
45UNIVERSAL_SERVICES = ['sysmon']
Dan Shicf278042016-04-06 21:16:34 -070046
Dan Shiac6fdbf2016-04-09 17:36:28 -070047AFE = frontend_wrappers.RetryingAFE(
48 server=server_utils.get_global_afe_hostname(), timeout_min=5,
49 delay_sec=10)
Don Garrett8db752c2014-10-17 16:56:55 -070050
51class DirtyTreeException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080052 """Raised when the tree has been modified in an unexpected way."""
Don Garrett8db752c2014-10-17 16:56:55 -070053
54
55class UnknownCommandException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080056 """Raised when we try to run a command name with no associated command."""
Don Garrett8db752c2014-10-17 16:56:55 -070057
58
59class UnstableServices(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080060 """Raised if a service appears unstable after restart."""
Don Garrett8db752c2014-10-17 16:56:55 -070061
62
Don Garrett35711212014-12-18 14:33:41 -080063def strip_terminal_codes(text):
64 """This function removes all terminal formatting codes from a string.
65
66 @param text: String of text to cleanup.
67 @returns String with format codes removed.
68 """
69 ESC = '\x1b'
70 return re.sub(ESC+r'\[[^m]*m', '', text)
71
72
Don Garrett8db752c2014-10-17 16:56:55 -070073def verify_repo_clean():
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070074 """This function cleans the current repo then verifies that it is valid.
Don Garrett8db752c2014-10-17 16:56:55 -070075
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070076 @raises DirtyTreeException if the repo is still not clean.
Don Garrett8db752c2014-10-17 16:56:55 -070077 @raises subprocess.CalledProcessError on a repo command failure.
78 """
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070079 subprocess.check_output(['git', 'reset', '--hard'])
Don Garrett8db752c2014-10-17 16:56:55 -070080 out = subprocess.check_output(['repo', 'status'], stderr=subprocess.STDOUT)
Don Garrett35711212014-12-18 14:33:41 -080081 out = strip_terminal_codes(out).strip()
Don Garrett699b4b32014-12-11 13:10:15 -080082
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070083 if not 'working directory clean' in out:
Dan Shicf278042016-04-06 21:16:34 -070084 raise DirtyTreeException(out)
Don Garrett8db752c2014-10-17 16:56:55 -070085
Don Garrett8db752c2014-10-17 16:56:55 -070086
87def repo_versions():
88 """This function collects the versions of all git repos in the general repo.
89
Don Garrettfa2c1c42014-12-11 12:11:49 -080090 @returns A dictionary mapping project names to git hashes for HEAD.
Don Garrett8db752c2014-10-17 16:56:55 -070091 @raises subprocess.CalledProcessError on a repo command failure.
92 """
Don Garrettfa2c1c42014-12-11 12:11:49 -080093 cmd = ['repo', 'forall', '-p', '-c', 'pwd && git log -1 --format=%h']
Don Garrett35711212014-12-18 14:33:41 -080094 output = strip_terminal_codes(subprocess.check_output(cmd))
Don Garrettfa2c1c42014-12-11 12:11:49 -080095
96 # The expected output format is:
97
98 # project chrome_build/
99 # /dir/holding/chrome_build
100 # 73dee9d
101 #
102 # project chrome_release/
103 # /dir/holding/chrome_release
104 # 9f3a5d8
105
106 lines = output.splitlines()
107
108 PROJECT_PREFIX = 'project '
109
110 project_heads = {}
111 for n in range(0, len(lines), 4):
112 project_line = lines[n]
113 project_dir = lines[n+1]
114 project_hash = lines[n+2]
115 # lines[n+3] is a blank line, but doesn't exist for the final block.
116
117 # Convert 'project chrome_build/' -> 'chrome_build'
118 assert project_line.startswith(PROJECT_PREFIX)
119 name = project_line[len(PROJECT_PREFIX):].rstrip('/')
120
121 project_heads[name] = (project_dir, project_hash)
122
123 return project_heads
Don Garrett8db752c2014-10-17 16:56:55 -0700124
125
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700126def repo_versions_to_decide_whether_run_cmd_update():
127 """Collect versions of repos/files defined in COMMANDS_TO_REPOS_DICT.
128
129 For the update_commands defined in config files, no need to run the command
130 every time. Only run it when the repos/files related to the commands have
131 been changed.
132
133 @returns A set of tuples: {(cmd, repo_version), ()...}
134 """
135 results = set()
136 for cmd, repo in COMMANDS_TO_REPOS_DICT.iteritems():
137 version = subprocess.check_output(
138 ['git', 'log', '-1', '--pretty=tformat:%h',
139 '%s/%s' % (common.autotest_dir, repo)])
140 results.add((cmd, version.strip()))
141 return results
142
143
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700144def repo_sync(update_push_servers=False):
Don Garrett8db752c2014-10-17 16:56:55 -0700145 """Perform a repo sync.
146
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700147 @param update_push_servers: If True, then update test_push servers to ToT.
148 Otherwise, update server to prod branch.
Don Garrett8db752c2014-10-17 16:56:55 -0700149 @raises subprocess.CalledProcessError on a repo command failure.
150 """
Don Garrettd0321722014-11-18 16:03:33 -0800151 subprocess.check_output(['repo', 'sync'])
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700152 if update_push_servers:
153 print('Updating push servers, checkout cros/master')
154 subprocess.check_output(['git', 'checkout', 'cros/master'])
155 else:
156 print('Updating server to prod branch')
157 subprocess.check_output(['git', 'checkout', 'cros/prod'])
Don Garrett8db752c2014-10-17 16:56:55 -0700158
159
Don Garrettd0321722014-11-18 16:03:33 -0800160def discover_update_commands():
161 """Lookup the commands to run on this server.
Don Garrett8db752c2014-10-17 16:56:55 -0700162
Don Garrettd0321722014-11-18 16:03:33 -0800163 These commonly come from shadow_config.ini, since they vary by server type.
Don Garrett8db752c2014-10-17 16:56:55 -0700164
Don Garrettd0321722014-11-18 16:03:33 -0800165 @returns List of command names in string format.
Don Garrett8db752c2014-10-17 16:56:55 -0700166 """
Don Garrett8db752c2014-10-17 16:56:55 -0700167 try:
Don Garrettd0321722014-11-18 16:03:33 -0800168 return global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700169 'UPDATE', 'commands', type=list)
170
171 except (ConfigParser.NoSectionError, global_config.ConfigError):
Don Garrettd0321722014-11-18 16:03:33 -0800172 return []
Don Garrett8db752c2014-10-17 16:56:55 -0700173
Don Garrettd0321722014-11-18 16:03:33 -0800174
175def discover_restart_services():
176 """Find the services that need restarting on the current server.
177
178 These commonly come from shadow_config.ini, since they vary by server type.
179
180 @returns List of service names in string format.
181 """
Allen Li43b275a2016-10-04 15:14:11 -0700182 services = list(UNIVERSAL_SERVICES)
Don Garrett8db752c2014-10-17 16:56:55 -0700183 try:
Allen Li43b275a2016-10-04 15:14:11 -0700184 # Look up services from shadow_config.ini.
185 extra_services = global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700186 'UPDATE', 'services', type=list)
Allen Li43b275a2016-10-04 15:14:11 -0700187 services.extend(extra_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700188 except (ConfigParser.NoSectionError, global_config.ConfigError):
Allen Li43b275a2016-10-04 15:14:11 -0700189 pass
190 return services
Don Garrett8db752c2014-10-17 16:56:55 -0700191
Don Garrett8db752c2014-10-17 16:56:55 -0700192
Don Garrett03432d62014-11-19 18:18:35 -0800193def update_command(cmd_tag, dryrun=False):
Don Garrettd0321722014-11-18 16:03:33 -0800194 """Restart a command.
Don Garrett8db752c2014-10-17 16:56:55 -0700195
Don Garrettd0321722014-11-18 16:03:33 -0800196 The command name is looked up in global_config.ini to find the full command
197 to run, then it's executed.
Don Garrett8db752c2014-10-17 16:56:55 -0700198
Don Garrettd0321722014-11-18 16:03:33 -0800199 @param cmd_tag: Which command to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800200 @param dryrun: If true print the command that would have been run.
Don Garrett8db752c2014-10-17 16:56:55 -0700201
Don Garrettd0321722014-11-18 16:03:33 -0800202 @raises UnknownCommandException If cmd_tag can't be looked up.
203 @raises subprocess.CalledProcessError on a command failure.
204 """
205 # Lookup the list of commands to consider. They are intended to be
206 # in global_config.ini so that they can be shared everywhere.
207 cmds = dict(global_config.global_config.config.items(
208 'UPDATE_COMMANDS'))
Don Garrett8db752c2014-10-17 16:56:55 -0700209
Don Garrettd0321722014-11-18 16:03:33 -0800210 if cmd_tag not in cmds:
211 raise UnknownCommandException(cmd_tag, cmds)
Don Garrett8db752c2014-10-17 16:56:55 -0700212
Don Garrettd0321722014-11-18 16:03:33 -0800213 expanded_command = cmds[cmd_tag].replace('AUTOTEST_REPO',
214 common.autotest_dir)
Don Garrett8db752c2014-10-17 16:56:55 -0700215
Don Garrett699b4b32014-12-11 13:10:15 -0800216 print('Running: %s: %s' % (cmd_tag, expanded_command))
Don Garrett03432d62014-11-19 18:18:35 -0800217 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800218 print('Skip: %s' % expanded_command)
Don Garrett03432d62014-11-19 18:18:35 -0800219 else:
Don Garrett4769c902015-01-05 15:58:56 -0800220 try:
221 subprocess.check_output(expanded_command, shell=True,
222 stderr=subprocess.STDOUT)
223 except subprocess.CalledProcessError as e:
224 print('FAILED:')
225 print(e.output)
226 raise
Don Garrett8db752c2014-10-17 16:56:55 -0700227
Don Garrett8db752c2014-10-17 16:56:55 -0700228
Don Garrett03432d62014-11-19 18:18:35 -0800229def restart_service(service_name, dryrun=False):
Don Garrettd0321722014-11-18 16:03:33 -0800230 """Restart a service.
231
232 Restarts the standard service with "service <name> restart".
233
234 @param service_name: The name of the service to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800235 @param dryrun: Don't really run anything, just print out the command.
Don Garrettd0321722014-11-18 16:03:33 -0800236
237 @raises subprocess.CalledProcessError on a command failure.
238 """
Don Garrett03432d62014-11-19 18:18:35 -0800239 cmd = ['sudo', 'service', service_name, 'restart']
Don Garrett699b4b32014-12-11 13:10:15 -0800240 print('Restarting: %s' % service_name)
Don Garrett03432d62014-11-19 18:18:35 -0800241 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800242 print('Skip: %s' % ' '.join(cmd))
Don Garrett03432d62014-11-19 18:18:35 -0800243 else:
Don Garrett03432d62014-11-19 18:18:35 -0800244 subprocess.check_call(cmd)
Don Garrettd0321722014-11-18 16:03:33 -0800245
246
247def service_status(service_name):
248 """Return the results "status <name>" for a given service.
249
250 This string is expected to contain the pid, and so to change is the service
251 is shutdown or restarted for any reason.
252
253 @param service_name: The name of the service to check on.
Don Garrett03432d62014-11-19 18:18:35 -0800254
Don Garrettd0321722014-11-18 16:03:33 -0800255 @returns The output of the external command.
256 Ex: autofs start/running, process 1931
257
258 @raises subprocess.CalledProcessError on a command failure.
259 """
260 return subprocess.check_output(['sudo', 'status', service_name])
261
262
Dan Shi57d4c732015-01-22 18:38:50 -0800263def restart_services(service_names, dryrun=False, skip_service_status=False):
Don Garrettd0321722014-11-18 16:03:33 -0800264 """Restart services as needed for the current server type.
265
266 Restart the listed set of services, and watch to see if they are stable for
267 at least SERVICE_STABILITY_TIMER. It restarts all services quickly,
268 waits for that delay, then verifies the status of all of them.
269
270 @param service_names: The list of service to restart and monitor.
Don Garrett03432d62014-11-19 18:18:35 -0800271 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800272 @param skip_service_status: Set to True to skip service status check.
273 Default is False.
Don Garrettd0321722014-11-18 16:03:33 -0800274
275 @raises subprocess.CalledProcessError on a command failure.
Don Garrett03432d62014-11-19 18:18:35 -0800276 @raises UnstableServices if any services are unstable after restart.
Don Garrettd0321722014-11-18 16:03:33 -0800277 """
278 service_statuses = {}
279
Don Garrett03432d62014-11-19 18:18:35 -0800280 if dryrun:
281 for name in service_names:
282 restart_service(name, dryrun=True)
283 return
284
Don Garrettd0321722014-11-18 16:03:33 -0800285 # Restart each, and record the status (including pid).
286 for name in service_names:
Allen Li88ae2b92016-10-18 17:48:54 -0700287 # TODO(ayatane): temporary hack for crbug.com/657194
288 if name == 'sysmon':
289 cmd = ['sudo', 'service', 'sysmon', 'stop']
290 subprocess.check_call(cmd)
291 continue
Don Garrettd0321722014-11-18 16:03:33 -0800292 restart_service(name)
293 service_statuses[name] = service_status(name)
294
Dan Shi57d4c732015-01-22 18:38:50 -0800295 # Skip service status check if --skip-service-status is specified. Used for
296 # servers in backup status.
297 if skip_service_status:
298 print('--skip-service-status is specified, skip checking services.')
299 return
300
Don Garrettd0321722014-11-18 16:03:33 -0800301 # Wait for a while to let the services settle.
302 time.sleep(SERVICE_STABILITY_TIMER)
303
304 # Look for any services that changed status.
305 unstable_services = [n for n in service_names
306 if service_status(n) != service_statuses[n]]
307
308 # Report any services having issues.
309 if unstable_services:
310 raise UnstableServices(unstable_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700311
312
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700313def run_deploy_actions(cmds_skip=set(), dryrun=False,
314 skip_service_status=False):
Don Garrettfa2c1c42014-12-11 12:11:49 -0800315 """Run arbitrary update commands specified in global.ini.
Don Garrett8db752c2014-10-17 16:56:55 -0700316
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700317 @param cmds_skip: cmds no need to run since the corresponding repo/file
318 does not change.
Don Garrett03432d62014-11-19 18:18:35 -0800319 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800320 @param skip_service_status: Set to True to skip service status check.
321 Default is False.
Don Garrett8db752c2014-10-17 16:56:55 -0700322
Don Garrett03432d62014-11-19 18:18:35 -0800323 @raises subprocess.CalledProcessError on a command failure.
324 @raises UnstableServices if any services are unstable after restart.
325 """
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700326 defined_cmds = set(discover_update_commands())
327 cmds = defined_cmds - cmds_skip
Don Garrettd0321722014-11-18 16:03:33 -0800328 if cmds:
329 print('Running update commands:', ', '.join(cmds))
330 for cmd in cmds:
Dan Shicf278042016-04-06 21:16:34 -0700331 if (cmd in PRIMARY_ONLY_COMMANDS and
332 not AFE.run('get_servers', hostname=socket.getfqdn(),
333 status='primary')):
334 print('Command %s is only applicable to primary servers.' % cmd)
335 continue
Don Garrett03432d62014-11-19 18:18:35 -0800336 update_command(cmd, dryrun=dryrun)
Don Garrettd0321722014-11-18 16:03:33 -0800337
338 services = discover_restart_services()
339 if services:
Don Garrett03432d62014-11-19 18:18:35 -0800340 print('Restarting Services:', ', '.join(services))
Dan Shi57d4c732015-01-22 18:38:50 -0800341 restart_services(services, dryrun=dryrun,
342 skip_service_status=skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800343
344
Don Garrettfa2c1c42014-12-11 12:11:49 -0800345def report_changes(versions_before, versions_after):
346 """Produce a report describing what changed in all repos.
347
348 @param versions_before: Results of repo_versions() from before the update.
349 @param versions_after: Results of repo_versions() from after the update.
350
351 @returns string containing a human friendly changes report.
352 """
353 result = []
354
Don Garrett35711212014-12-18 14:33:41 -0800355 if versions_after:
356 for project in sorted(set(versions_before.keys() + versions_after.keys())):
357 result.append('%s:' % project)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800358
Don Garrett35711212014-12-18 14:33:41 -0800359 _, before_hash = versions_before.get(project, (None, None))
360 after_dir, after_hash = versions_after.get(project, (None, None))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800361
Don Garrett35711212014-12-18 14:33:41 -0800362 if project not in versions_before:
363 result.append('Added.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800364
Don Garrett35711212014-12-18 14:33:41 -0800365 elif project not in versions_after:
366 result.append('Removed.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800367
Don Garrett35711212014-12-18 14:33:41 -0800368 elif before_hash == after_hash:
369 result.append('No Change.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800370
Don Garrett35711212014-12-18 14:33:41 -0800371 else:
372 hashes = '%s..%s' % (before_hash, after_hash)
373 cmd = ['git', 'log', hashes, '--oneline']
374 out = subprocess.check_output(cmd, cwd=after_dir,
375 stderr=subprocess.STDOUT)
376 result.append(out.strip())
Don Garrettfa2c1c42014-12-11 12:11:49 -0800377
Don Garrett35711212014-12-18 14:33:41 -0800378 result.append('')
379 else:
380 for project in sorted(versions_before.keys()):
381 _, before_hash = versions_before[project]
382 result.append('%s: %s' % (project, before_hash))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800383 result.append('')
384
385 return '\n'.join(result)
386
387
Don Garrett03432d62014-11-19 18:18:35 -0800388def parse_arguments(args):
389 """Parse command line arguments.
390
391 @param args: The command line arguments to parse. (ususally sys.argsv[1:])
392
Don Garrett40036362014-12-08 15:52:44 -0800393 @returns An argparse.Namespace populated with argument values.
Don Garrett03432d62014-11-19 18:18:35 -0800394 """
395 parser = argparse.ArgumentParser(
396 description='Command to update an autotest server.')
397 parser.add_argument('--skip-verify', action='store_false',
398 dest='verify', default=True,
399 help='Disable verification of a clean repository.')
400 parser.add_argument('--skip-update', action='store_false',
401 dest='update', default=True,
402 help='Skip the repository source code update.')
403 parser.add_argument('--skip-actions', action='store_false',
404 dest='actions', default=True,
405 help='Skip the post update actions.')
406 parser.add_argument('--skip-report', action='store_false',
407 dest='report', default=True,
408 help='Skip the git version report.')
Don Garrette3718912014-12-05 13:11:44 -0800409 parser.add_argument('--actions-only', action='store_true',
410 help='Run the post update actions (restart services).')
Don Garrett03432d62014-11-19 18:18:35 -0800411 parser.add_argument('--dryrun', action='store_true',
412 help='Don\'t actually run any commands, just log.')
Dan Shi57d4c732015-01-22 18:38:50 -0800413 parser.add_argument('--skip-service-status', action='store_true',
414 help='Skip checking the service status.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700415 parser.add_argument('--update_push_servers', action='store_true',
416 help='Indicate to update test_push server. If not '
417 'specify, then update server to production.')
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700418 parser.add_argument('--force_update', action='store_true',
419 help='Force to run the update commands for afe, tko '
420 'and build_externals')
Don Garrett03432d62014-11-19 18:18:35 -0800421
422 results = parser.parse_args(args)
423
Don Garrette3718912014-12-05 13:11:44 -0800424 if results.actions_only:
425 results.verify = False
426 results.update = False
427 results.report = False
428
Don Garrett03432d62014-11-19 18:18:35 -0800429 # TODO(dgarrett): Make these behaviors support dryrun.
430 if results.dryrun:
431 results.verify = False
432 results.update = False
433
434 return results
435
436
Allen Lie8c4ea42016-10-05 18:08:29 -0700437class ChangeDir(object):
438
439 """Context manager for changing to a directory temporarily."""
440
441 def __init__(self, dir):
442 self.new_dir = dir
443 self.old_dir = None
444
445 def __enter__(self):
446 self.old_dir = os.getcwd()
447 os.chdir(self.new_dir)
448
449 def __exit__(self, exc_type, exc_val, exc_tb):
450 os.chdir(self.old_dir)
451
452
Allen Li1f5a1682016-10-06 15:23:06 -0700453def update_chromeos():
454 """Update /usr/local/google/chromeos repo."""
455 print('Updating /usr/local/google/chromeos')
456 with ChangeDir('/usr/local/google/chromeos'):
457 ret = subprocess.call(['repo', 'sync'])
Allen Lie8c4ea42016-10-05 18:08:29 -0700458 if ret != 0:
459 print('Update failed, exited with status: %d' % ret)
460
461
Don Garrett03432d62014-11-19 18:18:35 -0800462def main(args):
463 """Main method."""
464 os.chdir(common.autotest_dir)
465 global_config.global_config.parse_config_file()
466
467 behaviors = parse_arguments(args)
468
469 if behaviors.verify:
Don Garrettd0321722014-11-18 16:03:33 -0800470 try:
Don Garrett03432d62014-11-19 18:18:35 -0800471 print('Checking tree status:')
472 verify_repo_clean()
473 print('Clean.')
474 except DirtyTreeException as e:
475 print('Local tree is dirty, can\'t perform update safely.')
476 print()
477 print('repo status:')
Don Garrettd0321722014-11-18 16:03:33 -0800478 print(e.args[0])
479 return 1
Don Garrett8db752c2014-10-17 16:56:55 -0700480
Don Garrett35711212014-12-18 14:33:41 -0800481 versions_before = repo_versions()
482 versions_after = {}
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700483 cmd_versions_before = repo_versions_to_decide_whether_run_cmd_update()
484 cmd_versions_after = {}
Don Garrettfa2c1c42014-12-11 12:11:49 -0800485
Don Garrett03432d62014-11-19 18:18:35 -0800486 if behaviors.update:
Don Garrett03432d62014-11-19 18:18:35 -0800487 print('Updating Repo.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700488 repo_sync(behaviors.update_push_servers)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800489 versions_after = repo_versions()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700490 cmd_versions_after = repo_versions_to_decide_whether_run_cmd_update()
Don Garrett03432d62014-11-19 18:18:35 -0800491
Allen Li1f5a1682016-10-06 15:23:06 -0700492 update_chromeos()
Allen Lie8c4ea42016-10-05 18:08:29 -0700493
Don Garrett03432d62014-11-19 18:18:35 -0800494 if behaviors.actions:
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700495 # If the corresponding repo/file not change, no need to run the cmd.
496 cmds_skip = (set() if behaviors.force_update else
497 {t[0] for t in cmd_versions_before & cmd_versions_after})
Don Garrett03432d62014-11-19 18:18:35 -0800498 try:
Dan Shi57d4c732015-01-22 18:38:50 -0800499 run_deploy_actions(
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700500 cmds_skip, behaviors.dryrun, behaviors.skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800501 except UnstableServices as e:
502 print('The following services were not stable after '
503 'the update:')
504 print(e.args[0])
505 return 1
506
Don Garrett35711212014-12-18 14:33:41 -0800507 if behaviors.report:
Don Garrettfa2c1c42014-12-11 12:11:49 -0800508 print('Changes:')
509 print(report_changes(versions_before, versions_after))
Don Garrett8db752c2014-10-17 16:56:55 -0700510
511
512if __name__ == '__main__':
Don Garrett03432d62014-11-19 18:18:35 -0800513 sys.exit(main(sys.argv[1:]))