blob: aa59f88c941c2802fa985a3b65239d3dd73cd0fe [file] [log] [blame]
Alex Millerb0b2d252014-06-25 17:17:01 -07001#!/usr/bin/python
2
Don Garrett40036362014-12-08 15:52:44 -08003from __future__ import print_function
4
5import argparse
J. Richard Barnette868cf642014-07-21 16:34:38 -07006import subprocess
7import sys
Alex Millerb0b2d252014-06-25 17:17:01 -07008
Don Garrett40036362014-12-08 15:52:44 -08009import common
Don Garrett50713462015-01-07 18:04:05 -080010from autotest_lib.server import frontend
Alex Millerb0b2d252014-06-25 17:17:01 -070011from autotest_lib.site_utils.lib import infra
12
Alex Millerb0b2d252014-06-25 17:17:01 -070013
Don Garretteecbc132015-01-08 17:26:20 -080014def discover_servers(afe):
Don Garrett40036362014-12-08 15:52:44 -080015 """Discover the in-production servers to update.
Alex Millerb0b2d252014-06-25 17:17:01 -070016
Don Garretteecbc132015-01-08 17:26:20 -080017 @param afe: Server to contact with RPC requests.
18
Don Garrett40036362014-12-08 15:52:44 -080019 @returns A list of server host-names, in order for updates.
20 """
Don Garrett50713462015-01-07 18:04:05 -080021 # Example server details....
22 # {
23 # 'hostname': 'server1',
24 # 'status': 'backup',
25 # 'roles': ['drone', 'scheduler'],
26 # 'attributes': {'max_processes': 300}
27 # }
Don Garretteecbc132015-01-08 17:26:20 -080028 rpc = frontend.AFE(server=afe)
Don Garrett50713462015-01-07 18:04:05 -080029 servers = rpc.run('get_servers')
Don Garrett40036362014-12-08 15:52:44 -080030
Don Garrett50713462015-01-07 18:04:05 -080031 # Do not update servers that need repair.
32 servers = [s for s in servers if s['status'] != 'repair_required']
Don Garrett40036362014-12-08 15:52:44 -080033
Don Garrett50713462015-01-07 18:04:05 -080034 # Do not update devservers (not YET supported).
35 servers = [s for s in servers if 'devserver' not in s['roles']]
36
37 def update_order(s):
38 """Sort order for updating servers (lower first).
39
40 @param s: Server details for a single server.
41 """
42 if 'database' in s['roles']:
43 return 0
44 if 'scheduler' in s['roles']:
45 return 1
46 return 2
47
48 # Order in which servers are updated.
49 servers.sort(key=update_order)
50
51 return [s['hostname'] for s in servers]
Alex Millerb0b2d252014-06-25 17:17:01 -070052
J. Richard Barnettef533b182014-09-04 18:24:42 -070053
Don Garrett40036362014-12-08 15:52:44 -080054def parse_arguments(args):
55 """Parse command line arguments.
56
57 @param args: The command line arguments to parse. (usually sys.argv[1:])
58
59 @returns An argparse.Namespace populated with argument values.
60 """
61 parser = argparse.ArgumentParser(
Don Garrett3f2b6602014-12-16 18:19:16 -080062 formatter_class=argparse.RawDescriptionHelpFormatter,
63 description='Command to update an entire autotest installation.',
64 epilog=('Update all servers:\n'
65 ' deploy_production.py\n'
66 '\n'
67 'Update one server:\n'
68 ' deploy_production.py <server>\n'
69 '\n'
70 'Send arguments to remote deploy_production_local.py:\n'
71 ' deploy_production.py -- --dryrun\n'
72 '\n'
73 'See what arguments would be run on specified servers:\n'
74 ' deploy_production.py --dryrun <server_a> <server_b> --'
75 ' --skip-update\n'))
76
Don Garrett40036362014-12-08 15:52:44 -080077 parser.add_argument('--continue', action='store_true', dest='cont',
Don Garretteecbc132015-01-08 17:26:20 -080078 help='Continue to the next server on failure.')
79 parser.add_argument('--afe', default='cautotest',
80 help='What is the main server for this installation? (cautotest).')
Don Garrett40036362014-12-08 15:52:44 -080081 parser.add_argument('--dryrun', action='store_true',
Don Garretteecbc132015-01-08 17:26:20 -080082 help='Don\'t actually run remote commands.')
Don Garrett40036362014-12-08 15:52:44 -080083 parser.add_argument('args', nargs=argparse.REMAINDER,
Don Garretteecbc132015-01-08 17:26:20 -080084 help=('<server>, <server> ... -- <remote_arg>, <remote_arg> ...'))
Don Garrett40036362014-12-08 15:52:44 -080085
86 results = parser.parse_args(args)
87
Don Garrett3f2b6602014-12-16 18:19:16 -080088 # We take the args list and further split it down. Everything before --
89 # is a server name, and everything after it is an argument to pass along
90 # to deploy_production_local.py.
91 #
92 # This:
93 # server_a, server_b -- --dryrun --skip-report
94 #
95 # Becomes:
96 # args.servers['server_a', 'server_b']
97 # args.args['--dryrun', '--skip-report']
98 try:
99 local_args_index = results.args.index('--') + 1
100 except ValueError:
101 # If -- isn't present, they are all servers.
102 results.servers = results.args
103 results.args = []
104 else:
105 # Split arguments.
106 results.servers = results.args[:local_args_index-1]
107 results.args = results.args[local_args_index:]
Don Garrett40036362014-12-08 15:52:44 -0800108
109 return results
J. Richard Barnettef533b182014-09-04 18:24:42 -0700110
111
Don Garrett40036362014-12-08 15:52:44 -0800112def main(args):
113 """Main routine that drives all the real work.
Alex Millerb0b2d252014-06-25 17:17:01 -0700114
Don Garrett40036362014-12-08 15:52:44 -0800115 @param args: The command line arguments to parse. (usually sys.argv[1:])
J. Richard Barnette868cf642014-07-21 16:34:38 -0700116
Don Garrett40036362014-12-08 15:52:44 -0800117 @returns The system exit code.
118 """
119 options = parse_arguments(args)
Alex Millerb0b2d252014-06-25 17:17:01 -0700120
Don Garrett3f2b6602014-12-16 18:19:16 -0800121 if not options.servers:
122 print('Discover servers...')
Don Garretteecbc132015-01-08 17:26:20 -0800123 options.servers = discover_servers(options.afe)
Don Garrett3f2b6602014-12-16 18:19:16 -0800124 print()
Alex Millerb0b2d252014-06-25 17:17:01 -0700125
Don Garrett40036362014-12-08 15:52:44 -0800126 # Display what we plan to update.
127 print('Will update (in this order):')
Don Garrett3f2b6602014-12-16 18:19:16 -0800128 for server in options.servers:
Don Garrett40036362014-12-08 15:52:44 -0800129 print(' ', server)
130 print()
Alex Millerb0b2d252014-06-25 17:17:01 -0700131
Don Garrett40036362014-12-08 15:52:44 -0800132 # Do the updating.
Don Garrett3f2b6602014-12-16 18:19:16 -0800133 for server in options.servers:
Don Garrett40036362014-12-08 15:52:44 -0800134 cmd = ('/usr/local/autotest/contrib/deploy_production_local.py ' +
135 ' '.join(options.args))
136 print('%s: %s' % (server, cmd))
137 if not options.dryrun:
138 try:
Don Garrett699b4b32014-12-11 13:10:15 -0800139 out = infra.execute_command(server, cmd)
140 print(out)
Don Garrett40036362014-12-08 15:52:44 -0800141 print('Success')
142 print()
143 except subprocess.CalledProcessError as e:
144 print('Error:')
145 print(e.output)
146 if not options.cont:
147 return 1
J. Richard Barnettef533b182014-09-04 18:24:42 -0700148
149
150if __name__ == '__main__':
Don Garrett40036362014-12-08 15:52:44 -0800151 sys.exit(main(sys.argv[1:]))