blob: b9762b35d0c4bd8bac2f751617f2557180646a32 [file] [log] [blame]
Dan Shia2fa8d12015-07-22 11:49:38 -07001#!/usr/bin/env python
2
3# Copyright 2015 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Tool to sync lab servers to the "Allowed Networks" of a CloudSQL instance.
8
9For a lab server to access CloudSQL instance, the server's IP must be added to
10the "Allowed Networks" list of the CloudSQL instance. This tool is to be used to
11read the list of lab servers from server database and update the list of
12"Allowed Networks" of a given CloudSQL instance.
13
14The tool also reads CLOUD/tko_access_servers from global config to add these
15servers to the "Allowed Networks" list of the CloudSQL instance. This allows
16servers that do not run Autotest code can access the CloudSQL instance.
17
18Note that running this tool will overwrite existing IPs in the "Allowed
19Networks" list. Therefore, manually editing that list from CloudSQL console
20should be prohibited. Instead, the servers should be added to
21CLOUD/tko_access_servers in shadow_config.ini.
22
23"""
24
25import argparse
26import socket
27import sys
28
29import common
30from autotest_lib.client.bin import utils
31from autotest_lib.client.common_lib import error
32from autotest_lib.client.common_lib import global_config
Ningning Xiaaeb8b632018-05-03 12:14:58 -070033from autotest_lib.client.common_lib.cros import retry
Dan Shia2fa8d12015-07-22 11:49:38 -070034from autotest_lib.server import frontend
35
36
Prathmesh Prabhuaaa55862018-01-31 10:06:22 -080037ROLES_REQUIRE_TKO_ACCESS = {
38 'afe',
39 'database',
40 'drone',
41 'scheduler',
42 'sentinel',
43 'shard',
44}
Dan Shia2fa8d12015-07-22 11:49:38 -070045
Ningning Xiaaeb8b632018-05-03 12:14:58 -070046
Dan Shia2fa8d12015-07-22 11:49:38 -070047def gcloud_login(project):
48 """Login to Google Cloud service for gcloud command to run.
49
50 @param project: Name of the Google Cloud project.
51 """
52 # Login with user account. If the user hasn't log in yet, the script will
53 # print a url and ask for a verification code. User should load the url in
54 # browser, and copy the verification code from the web page. When private IP
55 # can be supported to be added using non-corp account, the login can be done
56 # through service account and key file, e.g.,
57 # gcloud auth activate-service-account --key-file ~/key.json
58 utils.run('gcloud auth login', stdout_tee=sys.stdout,
59 stderr_tee=sys.stderr, stdin=sys.stdin)
60
61
Ningning Xiaaeb8b632018-05-03 12:14:58 -070062@retry.retry(error.CmdError, timeout_min=3)
63def _fetch_external_ip(server_name):
64 return utils.run('ssh %s curl -s ifconfig.me' % server_name).stdout.rstrip()
65
66
67def update_allowed_networks(project, instance, afe=None, extra_servers=None,
68 dryrun=False):
Dan Shia2fa8d12015-07-22 11:49:38 -070069 """Update the "Allowed Networks" list of the given CloudSQL instance.
70
71 @param project: Name of the Google Cloud project.
72 @param instance: Name of the CloudSQL instance.
73 @param afe: Server of the frontend RPC, default to None to use the server
74 specified in global config.
75 @param extra_servers: Extra servers to be included in the "Allowed Networks"
76 list. Default is None.
Ningning Xiaaeb8b632018-05-03 12:14:58 -070077 @param dryrun: Boolean indicating whether this is a dryrun.
Dan Shia2fa8d12015-07-22 11:49:38 -070078 """
79 # Get the IP address of all servers need access to CloudSQL instance.
80 rpc = frontend.AFE(server=afe)
81 servers = [s['hostname'] for s in rpc.run('get_servers')
82 if s['status'] != 'repair_required' and
83 ROLES_REQUIRE_TKO_ACCESS.intersection(s['roles'])]
84 if extra_servers:
85 servers.extend(extra_servers.split(','))
86 # Extra servers can be listed in CLOUD/tko_access_servers shadow config.
87 tko_servers = global_config.global_config.get_config_value(
88 'CLOUD', 'tko_access_servers', default='')
89 if tko_servers:
90 servers.extend(tko_servers.split(','))
Aviv Keshet2eea6b32017-05-01 18:21:04 -070091 print 'Adding servers %s to access list for projects %s' % (servers,
92 instance)
93 print 'Fetching their IP addresses...'
Aviv Keshet4eb2a892018-02-06 16:51:18 -080094 ips = []
95 for name in servers:
96 try:
Ningning Xiaaeb8b632018-05-03 12:14:58 -070097 # collect internal ips
Aviv Keshet4eb2a892018-02-06 16:51:18 -080098 ips.append(socket.gethostbyname(name))
Ningning Xiaaeb8b632018-05-03 12:14:58 -070099 # collect external ips
100 ips.append(_fetch_external_ip(name))
Aviv Keshet4eb2a892018-02-06 16:51:18 -0800101 except socket.gaierror:
Ningning Xiaaeb8b632018-05-03 12:14:58 -0700102 print 'Failed to resolve internal IP address for name %s' % name
Aviv Keshet4eb2a892018-02-06 16:51:18 -0800103 raise
Ningning Xiaaeb8b632018-05-03 12:14:58 -0700104 except error.TimeoutException:
105 print 'Failed to resolve external IP address for %s' % name
106 raise
107
Aviv Keshet2eea6b32017-05-01 18:21:04 -0700108 print '...Done: %s' % ips
Dan Shia2fa8d12015-07-22 11:49:38 -0700109
Aviv Keshetfd793bd2018-02-12 15:29:35 -0800110 cidr_ips = [str(ip) + '/32' for ip in ips]
111
Ningning Xiaaeb8b632018-05-03 12:14:58 -0700112 if dryrun:
113 print 'This is a dryrun: skip updating glcoud sql whitelists.'
114 return
115
Dan Shia2fa8d12015-07-22 11:49:38 -0700116 login = False
117 while True:
118 try:
Aviv Keshet0495e152017-05-01 19:31:44 -0700119 utils.run('gcloud config set project %s -q' % project)
120 cmd = ('gcloud sql instances patch %s --authorized-networks %s '
Aviv Keshetfd793bd2018-02-12 15:29:35 -0800121 '-q' % (instance, ','.join(cidr_ips)))
Aviv Keshet2eea6b32017-05-01 18:21:04 -0700122 print 'Running command to update whitelists: "%s"' % cmd
123 utils.run(cmd, stdout_tee=sys.stdout, stderr_tee=sys.stderr)
Dan Shia2fa8d12015-07-22 11:49:38 -0700124 return
125 except error.CmdError:
126 if login:
127 raise
128
129 # Try to login and retry if the command failed.
130 gcloud_login(project)
131 login = True
132
133
134def main():
135 """main script."""
136 parser = argparse.ArgumentParser()
137 parser.add_argument('--project', type=str, dest='project',
138 help='Name of the Google Cloud project.')
139 parser.add_argument('--instance', type=str, dest='instance',
140 help='Name of the CloudSQL instance.')
141 parser.add_argument('--afe', type=str, dest='afe',
142 help='Name of the RPC server to get server list.',
143 default=None)
144 parser.add_argument('--extra_servers', type=str, dest='extra_servers',
145 help=('Extra servers to be included in the "Allowed '
146 'Networks" list separated by comma.'),
147 default=None)
Ningning Xiaaeb8b632018-05-03 12:14:58 -0700148 parser.add_argument('--dryrun', dest='dryrun', action='store_true',
149 default=False,
150 help='Fetch IPs without updating whitelists in gcloud.')
Dan Shia2fa8d12015-07-22 11:49:38 -0700151 options = parser.parse_args()
152
153 update_allowed_networks(options.project, options.instance, options.afe,
Ningning Xiaaeb8b632018-05-03 12:14:58 -0700154 options.extra_servers, options.dryrun)
Dan Shia2fa8d12015-07-22 11:49:38 -0700155
156
157if __name__ == '__main__':
Aviv Keshet2eea6b32017-05-01 18:21:04 -0700158 main()