blob: 091e4aac6ad627dfe6e6d80a4276938a1eb6e2b7 [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:
287 restart_service(name)
288 service_statuses[name] = service_status(name)
289
Dan Shi57d4c732015-01-22 18:38:50 -0800290 # Skip service status check if --skip-service-status is specified. Used for
291 # servers in backup status.
292 if skip_service_status:
293 print('--skip-service-status is specified, skip checking services.')
294 return
295
Don Garrettd0321722014-11-18 16:03:33 -0800296 # Wait for a while to let the services settle.
297 time.sleep(SERVICE_STABILITY_TIMER)
298
299 # Look for any services that changed status.
300 unstable_services = [n for n in service_names
301 if service_status(n) != service_statuses[n]]
302
303 # Report any services having issues.
304 if unstable_services:
305 raise UnstableServices(unstable_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700306
307
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700308def run_deploy_actions(cmds_skip=set(), dryrun=False,
309 skip_service_status=False):
Don Garrettfa2c1c42014-12-11 12:11:49 -0800310 """Run arbitrary update commands specified in global.ini.
Don Garrett8db752c2014-10-17 16:56:55 -0700311
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700312 @param cmds_skip: cmds no need to run since the corresponding repo/file
313 does not change.
Don Garrett03432d62014-11-19 18:18:35 -0800314 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800315 @param skip_service_status: Set to True to skip service status check.
316 Default is False.
Don Garrett8db752c2014-10-17 16:56:55 -0700317
Don Garrett03432d62014-11-19 18:18:35 -0800318 @raises subprocess.CalledProcessError on a command failure.
319 @raises UnstableServices if any services are unstable after restart.
320 """
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700321 defined_cmds = set(discover_update_commands())
322 cmds = defined_cmds - cmds_skip
Don Garrettd0321722014-11-18 16:03:33 -0800323 if cmds:
324 print('Running update commands:', ', '.join(cmds))
325 for cmd in cmds:
Dan Shicf278042016-04-06 21:16:34 -0700326 if (cmd in PRIMARY_ONLY_COMMANDS and
327 not AFE.run('get_servers', hostname=socket.getfqdn(),
328 status='primary')):
329 print('Command %s is only applicable to primary servers.' % cmd)
330 continue
Don Garrett03432d62014-11-19 18:18:35 -0800331 update_command(cmd, dryrun=dryrun)
Don Garrettd0321722014-11-18 16:03:33 -0800332
333 services = discover_restart_services()
334 if services:
Don Garrett03432d62014-11-19 18:18:35 -0800335 print('Restarting Services:', ', '.join(services))
Dan Shi57d4c732015-01-22 18:38:50 -0800336 restart_services(services, dryrun=dryrun,
337 skip_service_status=skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800338
339
Don Garrettfa2c1c42014-12-11 12:11:49 -0800340def report_changes(versions_before, versions_after):
341 """Produce a report describing what changed in all repos.
342
343 @param versions_before: Results of repo_versions() from before the update.
344 @param versions_after: Results of repo_versions() from after the update.
345
346 @returns string containing a human friendly changes report.
347 """
348 result = []
349
Don Garrett35711212014-12-18 14:33:41 -0800350 if versions_after:
351 for project in sorted(set(versions_before.keys() + versions_after.keys())):
352 result.append('%s:' % project)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800353
Don Garrett35711212014-12-18 14:33:41 -0800354 _, before_hash = versions_before.get(project, (None, None))
355 after_dir, after_hash = versions_after.get(project, (None, None))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800356
Don Garrett35711212014-12-18 14:33:41 -0800357 if project not in versions_before:
358 result.append('Added.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800359
Don Garrett35711212014-12-18 14:33:41 -0800360 elif project not in versions_after:
361 result.append('Removed.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800362
Don Garrett35711212014-12-18 14:33:41 -0800363 elif before_hash == after_hash:
364 result.append('No Change.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800365
Don Garrett35711212014-12-18 14:33:41 -0800366 else:
367 hashes = '%s..%s' % (before_hash, after_hash)
368 cmd = ['git', 'log', hashes, '--oneline']
369 out = subprocess.check_output(cmd, cwd=after_dir,
370 stderr=subprocess.STDOUT)
371 result.append(out.strip())
Don Garrettfa2c1c42014-12-11 12:11:49 -0800372
Don Garrett35711212014-12-18 14:33:41 -0800373 result.append('')
374 else:
375 for project in sorted(versions_before.keys()):
376 _, before_hash = versions_before[project]
377 result.append('%s: %s' % (project, before_hash))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800378 result.append('')
379
380 return '\n'.join(result)
381
382
Don Garrett03432d62014-11-19 18:18:35 -0800383def parse_arguments(args):
384 """Parse command line arguments.
385
386 @param args: The command line arguments to parse. (ususally sys.argsv[1:])
387
Don Garrett40036362014-12-08 15:52:44 -0800388 @returns An argparse.Namespace populated with argument values.
Don Garrett03432d62014-11-19 18:18:35 -0800389 """
390 parser = argparse.ArgumentParser(
391 description='Command to update an autotest server.')
392 parser.add_argument('--skip-verify', action='store_false',
393 dest='verify', default=True,
394 help='Disable verification of a clean repository.')
395 parser.add_argument('--skip-update', action='store_false',
396 dest='update', default=True,
397 help='Skip the repository source code update.')
398 parser.add_argument('--skip-actions', action='store_false',
399 dest='actions', default=True,
400 help='Skip the post update actions.')
401 parser.add_argument('--skip-report', action='store_false',
402 dest='report', default=True,
403 help='Skip the git version report.')
Don Garrette3718912014-12-05 13:11:44 -0800404 parser.add_argument('--actions-only', action='store_true',
405 help='Run the post update actions (restart services).')
Don Garrett03432d62014-11-19 18:18:35 -0800406 parser.add_argument('--dryrun', action='store_true',
407 help='Don\'t actually run any commands, just log.')
Dan Shi57d4c732015-01-22 18:38:50 -0800408 parser.add_argument('--skip-service-status', action='store_true',
409 help='Skip checking the service status.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700410 parser.add_argument('--update_push_servers', action='store_true',
411 help='Indicate to update test_push server. If not '
412 'specify, then update server to production.')
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700413 parser.add_argument('--force_update', action='store_true',
414 help='Force to run the update commands for afe, tko '
415 'and build_externals')
Don Garrett03432d62014-11-19 18:18:35 -0800416
417 results = parser.parse_args(args)
418
Don Garrette3718912014-12-05 13:11:44 -0800419 if results.actions_only:
420 results.verify = False
421 results.update = False
422 results.report = False
423
Don Garrett03432d62014-11-19 18:18:35 -0800424 # TODO(dgarrett): Make these behaviors support dryrun.
425 if results.dryrun:
426 results.verify = False
427 results.update = False
428
429 return results
430
431
Allen Lie8c4ea42016-10-05 18:08:29 -0700432class ChangeDir(object):
433
434 """Context manager for changing to a directory temporarily."""
435
436 def __init__(self, dir):
437 self.new_dir = dir
438 self.old_dir = None
439
440 def __enter__(self):
441 self.old_dir = os.getcwd()
442 os.chdir(self.new_dir)
443
444 def __exit__(self, exc_type, exc_val, exc_tb):
445 os.chdir(self.old_dir)
446
447
448def update_chromeos_chromite():
449 """Update /usr/local/google/chromeos/chromite repo."""
450 print('Updating /usr/local/google/chromeos/chromite.')
451 with ChangeDir('/usr/local/google/chromeos/chromite'):
452 ret = subprocess.call(['repo', 'sync', '.'])
453 if ret != 0:
454 print('Update failed, exited with status: %d' % ret)
455
456
Don Garrett03432d62014-11-19 18:18:35 -0800457def main(args):
458 """Main method."""
459 os.chdir(common.autotest_dir)
460 global_config.global_config.parse_config_file()
461
462 behaviors = parse_arguments(args)
463
464 if behaviors.verify:
Don Garrettd0321722014-11-18 16:03:33 -0800465 try:
Don Garrett03432d62014-11-19 18:18:35 -0800466 print('Checking tree status:')
467 verify_repo_clean()
468 print('Clean.')
469 except DirtyTreeException as e:
470 print('Local tree is dirty, can\'t perform update safely.')
471 print()
472 print('repo status:')
Don Garrettd0321722014-11-18 16:03:33 -0800473 print(e.args[0])
474 return 1
Don Garrett8db752c2014-10-17 16:56:55 -0700475
Don Garrett35711212014-12-18 14:33:41 -0800476 versions_before = repo_versions()
477 versions_after = {}
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700478 cmd_versions_before = repo_versions_to_decide_whether_run_cmd_update()
479 cmd_versions_after = {}
Don Garrettfa2c1c42014-12-11 12:11:49 -0800480
Don Garrett03432d62014-11-19 18:18:35 -0800481 if behaviors.update:
Don Garrett03432d62014-11-19 18:18:35 -0800482 print('Updating Repo.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700483 repo_sync(behaviors.update_push_servers)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800484 versions_after = repo_versions()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700485 cmd_versions_after = repo_versions_to_decide_whether_run_cmd_update()
Don Garrett03432d62014-11-19 18:18:35 -0800486
Allen Lie8c4ea42016-10-05 18:08:29 -0700487 update_chromeos_chromite()
488
Don Garrett03432d62014-11-19 18:18:35 -0800489 if behaviors.actions:
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700490 # If the corresponding repo/file not change, no need to run the cmd.
491 cmds_skip = (set() if behaviors.force_update else
492 {t[0] for t in cmd_versions_before & cmd_versions_after})
Don Garrett03432d62014-11-19 18:18:35 -0800493 try:
Dan Shi57d4c732015-01-22 18:38:50 -0800494 run_deploy_actions(
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700495 cmds_skip, behaviors.dryrun, behaviors.skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800496 except UnstableServices as e:
497 print('The following services were not stable after '
498 'the update:')
499 print(e.args[0])
500 return 1
501
Don Garrett35711212014-12-18 14:33:41 -0800502 if behaviors.report:
Don Garrettfa2c1c42014-12-11 12:11:49 -0800503 print('Changes:')
504 print(report_changes(versions_before, versions_after))
Don Garrett8db752c2014-10-17 16:56:55 -0700505
506
507if __name__ == '__main__':
Don Garrett03432d62014-11-19 18:18:35 -0800508 sys.exit(main(sys.argv[1:]))