blob: bd4650b9788694d00e4dbbcff4416a2580ffdf64 [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
Don Garrett8db752c2014-10-17 16:56:55 -070019import subprocess
20import sys
21import time
22
23import common
24
25from autotest_lib.client.common_lib import global_config
Dan Shiac6fdbf2016-04-09 17:36:28 -070026from autotest_lib.server import utils as server_utils
Dan Shicf278042016-04-06 21:16:34 -070027from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
Don Garrett8db752c2014-10-17 16:56:55 -070028
Dan Shiac6fdbf2016-04-09 17:36:28 -070029
Don Garrettd0321722014-11-18 16:03:33 -080030# How long after restarting a service do we watch it to see if it's stable.
Prathmesh Prabhua62f92d2017-03-16 14:30:05 -070031SERVICE_STABILITY_TIMER = 60
Don Garrettd0321722014-11-18 16:03:33 -080032
Shuqian Zhaoa3438a52016-09-20 15:11:02 -070033# A dict to map update_commands defined in config file to repos or files that
34# decide whether need to update these commands. E.g. if no changes under
35# frontend repo, no need to update afe.
Richard Barnette1b72e7f2018-02-21 15:28:48 -080036COMMANDS_TO_REPOS_DICT = {'afe': 'frontend/client/',
37 'tko': 'frontend/client/'}
Shuqian Zhao697f7ed2016-10-21 14:58:28 -070038BUILD_EXTERNALS_COMMAND = 'build_externals'
Allen Lia2749cd2017-10-31 18:03:19 -070039
40_RESTART_SERVICES_FILE = os.path.join(os.environ['HOME'],
41 'push_restart_services')
Dan Shicf278042016-04-06 21:16:34 -070042
Dan Shiac6fdbf2016-04-09 17:36:28 -070043AFE = frontend_wrappers.RetryingAFE(
44 server=server_utils.get_global_afe_hostname(), timeout_min=5,
45 delay_sec=10)
Don Garrett8db752c2014-10-17 16:56:55 -070046
47class DirtyTreeException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080048 """Raised when the tree has been modified in an unexpected way."""
Don Garrett8db752c2014-10-17 16:56:55 -070049
50
51class UnknownCommandException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080052 """Raised when we try to run a command name with no associated command."""
Don Garrett8db752c2014-10-17 16:56:55 -070053
54
55class UnstableServices(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080056 """Raised if a service appears unstable after restart."""
Don Garrett8db752c2014-10-17 16:56:55 -070057
58
Don Garrett35711212014-12-18 14:33:41 -080059def strip_terminal_codes(text):
60 """This function removes all terminal formatting codes from a string.
61
62 @param text: String of text to cleanup.
63 @returns String with format codes removed.
64 """
65 ESC = '\x1b'
66 return re.sub(ESC+r'\[[^m]*m', '', text)
67
68
Richard Barnette488ac8b2017-09-01 09:28:55 -070069def _clean_pyc_files():
70 print('Removing .pyc files')
71 try:
72 subprocess.check_output([
73 'find', '.',
74 '(',
75 # These are ignored to reduce IO load (crbug.com/759780).
76 '-path', './site-packages',
77 '-o', '-path', './containers',
78 '-o', '-path', './logs',
79 '-o', '-path', './results',
80 ')',
81 '-prune',
82 '-o', '-name', '*.pyc',
83 '-exec', 'rm', '-f', '{}', '+'])
84 except Exception as e:
85 print('Warning: fail to remove .pyc! %s' % e)
86
87
Don Garrett8db752c2014-10-17 16:56:55 -070088def verify_repo_clean():
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070089 """This function cleans the current repo then verifies that it is valid.
Don Garrett8db752c2014-10-17 16:56:55 -070090
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070091 @raises DirtyTreeException if the repo is still not clean.
Don Garrett8db752c2014-10-17 16:56:55 -070092 @raises subprocess.CalledProcessError on a repo command failure.
93 """
Shuqian Zhao0647a212018-03-05 17:53:47 -080094 subprocess.check_output(['git', 'stash', '-u'])
95 subprocess.check_output(['git', 'stash', 'clear'])
Don Garrett8db752c2014-10-17 16:56:55 -070096 out = subprocess.check_output(['repo', 'status'], stderr=subprocess.STDOUT)
Don Garrett35711212014-12-18 14:33:41 -080097 out = strip_terminal_codes(out).strip()
Don Garrett699b4b32014-12-11 13:10:15 -080098
Shuqian Zhaoda985b62018-03-19 11:28:50 -070099 if not 'working directory clean' in out and not 'working tree clean' in out:
Dan Shicf278042016-04-06 21:16:34 -0700100 raise DirtyTreeException(out)
Don Garrett8db752c2014-10-17 16:56:55 -0700101
Don Garrett8db752c2014-10-17 16:56:55 -0700102
Aviv Keshetccbf6432017-10-19 12:03:34 -0700103def _clean_externals():
104 """Clean untracked files within ExternalSource and site-packages/
105
106 @raises subprocess.CalledProcessError on a git command failure.
107 """
108 dirs_to_clean = ['site-packages/', 'ExternalSource/']
109 cmd = ['git', 'clean', '-fxd'] + dirs_to_clean
110 subprocess.check_output(cmd)
111
112
Don Garrett8db752c2014-10-17 16:56:55 -0700113def repo_versions():
114 """This function collects the versions of all git repos in the general repo.
115
Don Garrettfa2c1c42014-12-11 12:11:49 -0800116 @returns A dictionary mapping project names to git hashes for HEAD.
Don Garrett8db752c2014-10-17 16:56:55 -0700117 @raises subprocess.CalledProcessError on a repo command failure.
118 """
Don Garrettfa2c1c42014-12-11 12:11:49 -0800119 cmd = ['repo', 'forall', '-p', '-c', 'pwd && git log -1 --format=%h']
Don Garrett35711212014-12-18 14:33:41 -0800120 output = strip_terminal_codes(subprocess.check_output(cmd))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800121
122 # The expected output format is:
123
124 # project chrome_build/
125 # /dir/holding/chrome_build
126 # 73dee9d
127 #
128 # project chrome_release/
129 # /dir/holding/chrome_release
130 # 9f3a5d8
131
132 lines = output.splitlines()
133
134 PROJECT_PREFIX = 'project '
135
136 project_heads = {}
137 for n in range(0, len(lines), 4):
138 project_line = lines[n]
139 project_dir = lines[n+1]
140 project_hash = lines[n+2]
141 # lines[n+3] is a blank line, but doesn't exist for the final block.
142
143 # Convert 'project chrome_build/' -> 'chrome_build'
144 assert project_line.startswith(PROJECT_PREFIX)
145 name = project_line[len(PROJECT_PREFIX):].rstrip('/')
146
147 project_heads[name] = (project_dir, project_hash)
148
149 return project_heads
Don Garrett8db752c2014-10-17 16:56:55 -0700150
151
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700152def repo_versions_to_decide_whether_run_cmd_update():
153 """Collect versions of repos/files defined in COMMANDS_TO_REPOS_DICT.
154
155 For the update_commands defined in config files, no need to run the command
156 every time. Only run it when the repos/files related to the commands have
157 been changed.
158
159 @returns A set of tuples: {(cmd, repo_version), ()...}
160 """
161 results = set()
162 for cmd, repo in COMMANDS_TO_REPOS_DICT.iteritems():
163 version = subprocess.check_output(
164 ['git', 'log', '-1', '--pretty=tformat:%h',
165 '%s/%s' % (common.autotest_dir, repo)])
166 results.add((cmd, version.strip()))
167 return results
168
169
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700170def repo_sync(update_push_servers=False):
Don Garrett8db752c2014-10-17 16:56:55 -0700171 """Perform a repo sync.
172
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700173 @param update_push_servers: If True, then update test_push servers to ToT.
174 Otherwise, update server to prod branch.
Don Garrett8db752c2014-10-17 16:56:55 -0700175 @raises subprocess.CalledProcessError on a repo command failure.
176 """
Don Garrettd0321722014-11-18 16:03:33 -0800177 subprocess.check_output(['repo', 'sync'])
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700178 if update_push_servers:
179 print('Updating push servers, checkout cros/master')
Shuqian Zhao9febd452017-01-31 15:36:40 -0800180 subprocess.check_output(['git', 'checkout', 'cros/master'],
181 stderr=subprocess.STDOUT)
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700182 else:
183 print('Updating server to prod branch')
Shuqian Zhao9febd452017-01-31 15:36:40 -0800184 subprocess.check_output(['git', 'checkout', 'cros/prod'],
185 stderr=subprocess.STDOUT)
Richard Barnette488ac8b2017-09-01 09:28:55 -0700186 _clean_pyc_files()
187
Don Garrett8db752c2014-10-17 16:56:55 -0700188
Don Garrettd0321722014-11-18 16:03:33 -0800189def discover_update_commands():
190 """Lookup the commands to run on this server.
Don Garrett8db752c2014-10-17 16:56:55 -0700191
Don Garrettd0321722014-11-18 16:03:33 -0800192 These commonly come from shadow_config.ini, since they vary by server type.
Don Garrett8db752c2014-10-17 16:56:55 -0700193
Don Garrettd0321722014-11-18 16:03:33 -0800194 @returns List of command names in string format.
Don Garrett8db752c2014-10-17 16:56:55 -0700195 """
Don Garrett8db752c2014-10-17 16:56:55 -0700196 try:
Don Garrettd0321722014-11-18 16:03:33 -0800197 return global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700198 'UPDATE', 'commands', type=list)
199
200 except (ConfigParser.NoSectionError, global_config.ConfigError):
Don Garrettd0321722014-11-18 16:03:33 -0800201 return []
Don Garrett8db752c2014-10-17 16:56:55 -0700202
Don Garrettd0321722014-11-18 16:03:33 -0800203
Allen Lia2749cd2017-10-31 18:03:19 -0700204def get_restart_services():
Don Garrettd0321722014-11-18 16:03:33 -0800205 """Find the services that need restarting on the current server.
206
207 These commonly come from shadow_config.ini, since they vary by server type.
208
Allen Lia2749cd2017-10-31 18:03:19 -0700209 @returns Iterable of service names in string format.
Don Garrettd0321722014-11-18 16:03:33 -0800210 """
Allen Lia2749cd2017-10-31 18:03:19 -0700211 with open(_RESTART_SERVICES_FILE) as f:
212 for line in f:
213 yield line.rstrip()
Don Garrett8db752c2014-10-17 16:56:55 -0700214
Don Garrett8db752c2014-10-17 16:56:55 -0700215
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700216def update_command(cmd_tag, dryrun=False, use_chromite_master=False):
Don Garrettd0321722014-11-18 16:03:33 -0800217 """Restart a command.
Don Garrett8db752c2014-10-17 16:56:55 -0700218
Don Garrettd0321722014-11-18 16:03:33 -0800219 The command name is looked up in global_config.ini to find the full command
220 to run, then it's executed.
Don Garrett8db752c2014-10-17 16:56:55 -0700221
Don Garrettd0321722014-11-18 16:03:33 -0800222 @param cmd_tag: Which command to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800223 @param dryrun: If true print the command that would have been run.
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700224 @param use_chromite_master: True if updating chromite to master, rather
225 than prod.
Don Garrett8db752c2014-10-17 16:56:55 -0700226
Don Garrettd0321722014-11-18 16:03:33 -0800227 @raises UnknownCommandException If cmd_tag can't be looked up.
228 @raises subprocess.CalledProcessError on a command failure.
229 """
230 # Lookup the list of commands to consider. They are intended to be
231 # in global_config.ini so that they can be shared everywhere.
232 cmds = dict(global_config.global_config.config.items(
233 'UPDATE_COMMANDS'))
Don Garrett8db752c2014-10-17 16:56:55 -0700234
Don Garrettd0321722014-11-18 16:03:33 -0800235 if cmd_tag not in cmds:
236 raise UnknownCommandException(cmd_tag, cmds)
Don Garrett8db752c2014-10-17 16:56:55 -0700237
Richard Barnette2e0a4232018-03-07 17:13:16 -0800238 command = cmds[cmd_tag]
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700239 # When updating push servers, pass an arg to build_externals to update
240 # chromite to master branch for testing
241 if use_chromite_master and cmd_tag == BUILD_EXTERNALS_COMMAND:
Richard Barnette2e0a4232018-03-07 17:13:16 -0800242 command += ' --use_chromite_master'
Don Garrett8db752c2014-10-17 16:56:55 -0700243
Richard Barnette2e0a4232018-03-07 17:13:16 -0800244 print('Running: %s: %s' % (cmd_tag, command))
Don Garrett03432d62014-11-19 18:18:35 -0800245 if dryrun:
Richard Barnette2e0a4232018-03-07 17:13:16 -0800246 print('Skip: %s' % command)
Don Garrett03432d62014-11-19 18:18:35 -0800247 else:
Don Garrett4769c902015-01-05 15:58:56 -0800248 try:
Richard Barnette2e0a4232018-03-07 17:13:16 -0800249 subprocess.check_output(command, shell=True,
250 cwd=common.autotest_dir,
Don Garrett4769c902015-01-05 15:58:56 -0800251 stderr=subprocess.STDOUT)
252 except subprocess.CalledProcessError as e:
253 print('FAILED:')
254 print(e.output)
255 raise
Don Garrett8db752c2014-10-17 16:56:55 -0700256
Don Garrett8db752c2014-10-17 16:56:55 -0700257
Don Garrett03432d62014-11-19 18:18:35 -0800258def restart_service(service_name, dryrun=False):
Don Garrettd0321722014-11-18 16:03:33 -0800259 """Restart a service.
260
261 Restarts the standard service with "service <name> restart".
262
263 @param service_name: The name of the service to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800264 @param dryrun: Don't really run anything, just print out the command.
Don Garrettd0321722014-11-18 16:03:33 -0800265
266 @raises subprocess.CalledProcessError on a command failure.
267 """
Don Garrett03432d62014-11-19 18:18:35 -0800268 cmd = ['sudo', 'service', service_name, 'restart']
Don Garrett699b4b32014-12-11 13:10:15 -0800269 print('Restarting: %s' % service_name)
Don Garrett03432d62014-11-19 18:18:35 -0800270 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800271 print('Skip: %s' % ' '.join(cmd))
Don Garrett03432d62014-11-19 18:18:35 -0800272 else:
Shuqian Zhao9febd452017-01-31 15:36:40 -0800273 subprocess.check_call(cmd, stderr=subprocess.STDOUT)
Don Garrettd0321722014-11-18 16:03:33 -0800274
275
276def service_status(service_name):
277 """Return the results "status <name>" for a given service.
278
279 This string is expected to contain the pid, and so to change is the service
280 is shutdown or restarted for any reason.
281
282 @param service_name: The name of the service to check on.
Don Garrett03432d62014-11-19 18:18:35 -0800283
Don Garrettd0321722014-11-18 16:03:33 -0800284 @returns The output of the external command.
285 Ex: autofs start/running, process 1931
286
287 @raises subprocess.CalledProcessError on a command failure.
288 """
Shuqian Zhaodcaa6162017-11-09 10:58:45 -0800289 return subprocess.check_output(['sudo', 'service', service_name, 'status'])
Don Garrettd0321722014-11-18 16:03:33 -0800290
291
Dan Shi57d4c732015-01-22 18:38:50 -0800292def restart_services(service_names, dryrun=False, skip_service_status=False):
Don Garrettd0321722014-11-18 16:03:33 -0800293 """Restart services as needed for the current server type.
294
295 Restart the listed set of services, and watch to see if they are stable for
296 at least SERVICE_STABILITY_TIMER. It restarts all services quickly,
297 waits for that delay, then verifies the status of all of them.
298
299 @param service_names: The list of service to restart and monitor.
Don Garrett03432d62014-11-19 18:18:35 -0800300 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800301 @param skip_service_status: Set to True to skip service status check.
302 Default is False.
Don Garrettd0321722014-11-18 16:03:33 -0800303
304 @raises subprocess.CalledProcessError on a command failure.
Don Garrett03432d62014-11-19 18:18:35 -0800305 @raises UnstableServices if any services are unstable after restart.
Don Garrettd0321722014-11-18 16:03:33 -0800306 """
307 service_statuses = {}
308
Don Garrett03432d62014-11-19 18:18:35 -0800309 if dryrun:
310 for name in service_names:
311 restart_service(name, dryrun=True)
312 return
313
Don Garrettd0321722014-11-18 16:03:33 -0800314 # Restart each, and record the status (including pid).
315 for name in service_names:
316 restart_service(name)
Don Garrettd0321722014-11-18 16:03:33 -0800317
Dan Shi57d4c732015-01-22 18:38:50 -0800318 # Skip service status check if --skip-service-status is specified. Used for
319 # servers in backup status.
320 if skip_service_status:
321 print('--skip-service-status is specified, skip checking services.')
322 return
323
Don Garrettd0321722014-11-18 16:03:33 -0800324 # Wait for a while to let the services settle.
325 time.sleep(SERVICE_STABILITY_TIMER)
Prathmesh Prabhua62f92d2017-03-16 14:30:05 -0700326 service_statuses = {name: service_status(name) for name in service_names}
327 time.sleep(SERVICE_STABILITY_TIMER)
Don Garrettd0321722014-11-18 16:03:33 -0800328 # Look for any services that changed status.
329 unstable_services = [n for n in service_names
330 if service_status(n) != service_statuses[n]]
331
332 # Report any services having issues.
333 if unstable_services:
334 raise UnstableServices(unstable_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700335
336
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700337def run_deploy_actions(cmds_skip=set(), dryrun=False,
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700338 skip_service_status=False, use_chromite_master=False):
Don Garrettfa2c1c42014-12-11 12:11:49 -0800339 """Run arbitrary update commands specified in global.ini.
Don Garrett8db752c2014-10-17 16:56:55 -0700340
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700341 @param cmds_skip: cmds no need to run since the corresponding repo/file
342 does not change.
Don Garrett03432d62014-11-19 18:18:35 -0800343 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800344 @param skip_service_status: Set to True to skip service status check.
345 Default is False.
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700346 @param use_chromite_master: True if updating chromite to master, rather
347 than prod.
Don Garrett8db752c2014-10-17 16:56:55 -0700348
Don Garrett03432d62014-11-19 18:18:35 -0800349 @raises subprocess.CalledProcessError on a command failure.
350 @raises UnstableServices if any services are unstable after restart.
351 """
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700352 defined_cmds = set(discover_update_commands())
353 cmds = defined_cmds - cmds_skip
Don Garrettd0321722014-11-18 16:03:33 -0800354 if cmds:
355 print('Running update commands:', ', '.join(cmds))
356 for cmd in cmds:
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700357 update_command(cmd, dryrun=dryrun,
358 use_chromite_master=use_chromite_master)
Don Garrettd0321722014-11-18 16:03:33 -0800359
Allen Lia2749cd2017-10-31 18:03:19 -0700360 services = list(get_restart_services())
Don Garrettd0321722014-11-18 16:03:33 -0800361 if services:
Don Garrett03432d62014-11-19 18:18:35 -0800362 print('Restarting Services:', ', '.join(services))
Dan Shi57d4c732015-01-22 18:38:50 -0800363 restart_services(services, dryrun=dryrun,
364 skip_service_status=skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800365
366
Don Garrettfa2c1c42014-12-11 12:11:49 -0800367def report_changes(versions_before, versions_after):
368 """Produce a report describing what changed in all repos.
369
370 @param versions_before: Results of repo_versions() from before the update.
371 @param versions_after: Results of repo_versions() from after the update.
372
373 @returns string containing a human friendly changes report.
374 """
375 result = []
376
Don Garrett35711212014-12-18 14:33:41 -0800377 if versions_after:
378 for project in sorted(set(versions_before.keys() + versions_after.keys())):
379 result.append('%s:' % project)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800380
Don Garrett35711212014-12-18 14:33:41 -0800381 _, before_hash = versions_before.get(project, (None, None))
382 after_dir, after_hash = versions_after.get(project, (None, None))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800383
Don Garrett35711212014-12-18 14:33:41 -0800384 if project not in versions_before:
385 result.append('Added.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800386
Don Garrett35711212014-12-18 14:33:41 -0800387 elif project not in versions_after:
388 result.append('Removed.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800389
Don Garrett35711212014-12-18 14:33:41 -0800390 elif before_hash == after_hash:
391 result.append('No Change.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800392
Don Garrett35711212014-12-18 14:33:41 -0800393 else:
394 hashes = '%s..%s' % (before_hash, after_hash)
395 cmd = ['git', 'log', hashes, '--oneline']
396 out = subprocess.check_output(cmd, cwd=after_dir,
397 stderr=subprocess.STDOUT)
398 result.append(out.strip())
Don Garrettfa2c1c42014-12-11 12:11:49 -0800399
Don Garrett35711212014-12-18 14:33:41 -0800400 result.append('')
401 else:
402 for project in sorted(versions_before.keys()):
403 _, before_hash = versions_before[project]
404 result.append('%s: %s' % (project, before_hash))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800405 result.append('')
406
407 return '\n'.join(result)
408
409
Don Garrett03432d62014-11-19 18:18:35 -0800410def parse_arguments(args):
411 """Parse command line arguments.
412
413 @param args: The command line arguments to parse. (ususally sys.argsv[1:])
414
Don Garrett40036362014-12-08 15:52:44 -0800415 @returns An argparse.Namespace populated with argument values.
Don Garrett03432d62014-11-19 18:18:35 -0800416 """
417 parser = argparse.ArgumentParser(
418 description='Command to update an autotest server.')
419 parser.add_argument('--skip-verify', action='store_false',
420 dest='verify', default=True,
421 help='Disable verification of a clean repository.')
422 parser.add_argument('--skip-update', action='store_false',
423 dest='update', default=True,
424 help='Skip the repository source code update.')
425 parser.add_argument('--skip-actions', action='store_false',
426 dest='actions', default=True,
427 help='Skip the post update actions.')
428 parser.add_argument('--skip-report', action='store_false',
429 dest='report', default=True,
430 help='Skip the git version report.')
Don Garrette3718912014-12-05 13:11:44 -0800431 parser.add_argument('--actions-only', action='store_true',
432 help='Run the post update actions (restart services).')
Don Garrett03432d62014-11-19 18:18:35 -0800433 parser.add_argument('--dryrun', action='store_true',
434 help='Don\'t actually run any commands, just log.')
Dan Shi57d4c732015-01-22 18:38:50 -0800435 parser.add_argument('--skip-service-status', action='store_true',
436 help='Skip checking the service status.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700437 parser.add_argument('--update_push_servers', action='store_true',
438 help='Indicate to update test_push server. If not '
439 'specify, then update server to production.')
Aviv Keshetccbf6432017-10-19 12:03:34 -0700440 parser.add_argument('--force-clean-externals', action='store_true',
441 default=False,
442 help='Force a cleanup of all untracked files within '
443 'site-packages/ and ExternalSource/, so that '
444 'build_externals will build from scratch.')
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700445 parser.add_argument('--force_update', action='store_true',
446 help='Force to run the update commands for afe, tko '
447 'and build_externals')
Don Garrett03432d62014-11-19 18:18:35 -0800448
449 results = parser.parse_args(args)
450
Don Garrette3718912014-12-05 13:11:44 -0800451 if results.actions_only:
452 results.verify = False
453 results.update = False
454 results.report = False
455
Don Garrett03432d62014-11-19 18:18:35 -0800456 # TODO(dgarrett): Make these behaviors support dryrun.
457 if results.dryrun:
458 results.verify = False
459 results.update = False
Aviv Keshetccbf6432017-10-19 12:03:34 -0700460 results.force_clean_externals = False
Don Garrett03432d62014-11-19 18:18:35 -0800461
Shuqian Zhao87a4d6a2017-12-04 17:36:28 -0800462 if not results.update_push_servers:
463 print('Will skip service check for pushing servers in prod.')
464 results.skip_service_status = True
Don Garrett03432d62014-11-19 18:18:35 -0800465 return results
466
467
Allen Lie8c4ea42016-10-05 18:08:29 -0700468class ChangeDir(object):
469
470 """Context manager for changing to a directory temporarily."""
471
472 def __init__(self, dir):
473 self.new_dir = dir
474 self.old_dir = None
475
476 def __enter__(self):
477 self.old_dir = os.getcwd()
478 os.chdir(self.new_dir)
479
480 def __exit__(self, exc_type, exc_val, exc_tb):
481 os.chdir(self.old_dir)
482
483
Allen Li8802a5e2017-01-17 15:04:15 -0800484def _sync_chromiumos_repo():
485 """Update ~chromeos-test/chromiumos repo."""
486 print('Updating ~chromeos-test/chromiumos')
Allen Li19c48eb2017-01-25 15:03:10 -0800487 with ChangeDir(os.path.expanduser('~chromeos-test/chromiumos')):
Shuqian Zhao9febd452017-01-31 15:36:40 -0800488 ret = subprocess.call(['repo', 'sync'], stderr=subprocess.STDOUT)
Richard Barnette488ac8b2017-09-01 09:28:55 -0700489 _clean_pyc_files()
Allen Lie8c4ea42016-10-05 18:08:29 -0700490 if ret != 0:
491 print('Update failed, exited with status: %d' % ret)
492
493
Don Garrett03432d62014-11-19 18:18:35 -0800494def main(args):
495 """Main method."""
Richard Barnette2e0a4232018-03-07 17:13:16 -0800496 # Be careful before you change this call to `os.chdir()`:
497 # We make several calls to `subprocess.check_output()` and
498 # friends that depend on this directory, most notably calls to
499 # the 'repo' command from `verify_repo_clean()`.
Don Garrett03432d62014-11-19 18:18:35 -0800500 os.chdir(common.autotest_dir)
501 global_config.global_config.parse_config_file()
502
503 behaviors = parse_arguments(args)
504
505 if behaviors.verify:
Prathmesh Prabhu32f6c222017-03-16 14:53:12 -0700506 print('Checking tree status:')
507 verify_repo_clean()
508 print('Tree status: clean')
Don Garrett8db752c2014-10-17 16:56:55 -0700509
Aviv Keshetccbf6432017-10-19 12:03:34 -0700510 if behaviors.force_clean_externals:
511 print('Cleaning all external packages and their cache...')
512 _clean_externals()
513 print('...done.')
514
Don Garrett35711212014-12-18 14:33:41 -0800515 versions_before = repo_versions()
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700516 versions_after = set()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700517 cmd_versions_before = repo_versions_to_decide_whether_run_cmd_update()
Shuqian Zhao697f7ed2016-10-21 14:58:28 -0700518 cmd_versions_after = set()
Don Garrettfa2c1c42014-12-11 12:11:49 -0800519
Don Garrett03432d62014-11-19 18:18:35 -0800520 if behaviors.update:
Don Garrett03432d62014-11-19 18:18:35 -0800521 print('Updating Repo.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700522 repo_sync(behaviors.update_push_servers)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800523 versions_after = repo_versions()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700524 cmd_versions_after = repo_versions_to_decide_whether_run_cmd_update()
Allen Li8802a5e2017-01-17 15:04:15 -0800525 _sync_chromiumos_repo()
Allen Lie8c4ea42016-10-05 18:08:29 -0700526
Don Garrett03432d62014-11-19 18:18:35 -0800527 if behaviors.actions:
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700528 # If the corresponding repo/file not change, no need to run the cmd.
529 cmds_skip = (set() if behaviors.force_update else
530 {t[0] for t in cmd_versions_before & cmd_versions_after})
Prathmesh Prabhu32f6c222017-03-16 14:53:12 -0700531 run_deploy_actions(
532 cmds_skip, behaviors.dryrun, behaviors.skip_service_status,
533 use_chromite_master=behaviors.update_push_servers)
Don Garrett03432d62014-11-19 18:18:35 -0800534
Don Garrett35711212014-12-18 14:33:41 -0800535 if behaviors.report:
Don Garrettfa2c1c42014-12-11 12:11:49 -0800536 print('Changes:')
537 print(report_changes(versions_before, versions_after))
Don Garrett8db752c2014-10-17 16:56:55 -0700538
539
540if __name__ == '__main__':
Don Garrett03432d62014-11-19 18:18:35 -0800541 sys.exit(main(sys.argv[1:]))