| #!/usr/bin/env python | |
| # Copyright 2015 The Chromium OS Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| """Tool to sync lab servers to the "Allowed Networks" of a CloudSQL instance. | |
| For a lab server to access CloudSQL instance, the server's IP must be added to | |
| the "Allowed Networks" list of the CloudSQL instance. This tool is to be used to | |
| read the list of lab servers from server database and update the list of | |
| "Allowed Networks" of a given CloudSQL instance. | |
| The tool also reads CLOUD/tko_access_servers from global config to add these | |
| servers to the "Allowed Networks" list of the CloudSQL instance. This allows | |
| servers that do not run Autotest code can access the CloudSQL instance. | |
| Note that running this tool will overwrite existing IPs in the "Allowed | |
| Networks" list. Therefore, manually editing that list from CloudSQL console | |
| should be prohibited. Instead, the servers should be added to | |
| CLOUD/tko_access_servers in shadow_config.ini. | |
| """ | |
| import argparse | |
| import socket | |
| import sys | |
| import common | |
| from autotest_lib.client.bin import utils | |
| from autotest_lib.client.common_lib import error | |
| from autotest_lib.client.common_lib import global_config | |
| from autotest_lib.server import frontend | |
| ROLES_REQUIRE_TKO_ACCESS = { | |
| 'afe', | |
| 'database', | |
| 'drone', | |
| 'scheduler', | |
| 'sentinel', | |
| 'shard', | |
| } | |
| def gcloud_login(project): | |
| """Login to Google Cloud service for gcloud command to run. | |
| @param project: Name of the Google Cloud project. | |
| """ | |
| # Login with user account. If the user hasn't log in yet, the script will | |
| # print a url and ask for a verification code. User should load the url in | |
| # browser, and copy the verification code from the web page. When private IP | |
| # can be supported to be added using non-corp account, the login can be done | |
| # through service account and key file, e.g., | |
| # gcloud auth activate-service-account --key-file ~/key.json | |
| utils.run('gcloud auth login', stdout_tee=sys.stdout, | |
| stderr_tee=sys.stderr, stdin=sys.stdin) | |
| def update_allowed_networks(project, instance, afe=None, extra_servers=None): | |
| """Update the "Allowed Networks" list of the given CloudSQL instance. | |
| @param project: Name of the Google Cloud project. | |
| @param instance: Name of the CloudSQL instance. | |
| @param afe: Server of the frontend RPC, default to None to use the server | |
| specified in global config. | |
| @param extra_servers: Extra servers to be included in the "Allowed Networks" | |
| list. Default is None. | |
| """ | |
| # Get the IP address of all servers need access to CloudSQL instance. | |
| rpc = frontend.AFE(server=afe) | |
| servers = [s['hostname'] for s in rpc.run('get_servers') | |
| if s['status'] != 'repair_required' and | |
| ROLES_REQUIRE_TKO_ACCESS.intersection(s['roles'])] | |
| if extra_servers: | |
| servers.extend(extra_servers.split(',')) | |
| # Extra servers can be listed in CLOUD/tko_access_servers shadow config. | |
| tko_servers = global_config.global_config.get_config_value( | |
| 'CLOUD', 'tko_access_servers', default='') | |
| if tko_servers: | |
| servers.extend(tko_servers.split(',')) | |
| print 'Adding servers %s to access list for projects %s' % (servers, | |
| instance) | |
| print 'Fetching their IP addresses...' | |
| ips = [] | |
| for name in servers: | |
| try: | |
| ips.append(socket.gethostbyname(name)) | |
| except socket.gaierror: | |
| print 'Failed to resolve IP address for name %s' % name | |
| raise | |
| print '...Done: %s' % ips | |
| cidr_ips = [str(ip) + '/32' for ip in ips] | |
| login = False | |
| while True: | |
| try: | |
| utils.run('gcloud config set project %s -q' % project) | |
| cmd = ('gcloud sql instances patch %s --authorized-networks %s ' | |
| '-q' % (instance, ','.join(cidr_ips))) | |
| print 'Running command to update whitelists: "%s"' % cmd | |
| utils.run(cmd, stdout_tee=sys.stdout, stderr_tee=sys.stderr) | |
| return | |
| except error.CmdError: | |
| if login: | |
| raise | |
| # Try to login and retry if the command failed. | |
| gcloud_login(project) | |
| login = True | |
| def main(): | |
| """main script.""" | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--project', type=str, dest='project', | |
| help='Name of the Google Cloud project.') | |
| parser.add_argument('--instance', type=str, dest='instance', | |
| help='Name of the CloudSQL instance.') | |
| parser.add_argument('--afe', type=str, dest='afe', | |
| help='Name of the RPC server to get server list.', | |
| default=None) | |
| parser.add_argument('--extra_servers', type=str, dest='extra_servers', | |
| help=('Extra servers to be included in the "Allowed ' | |
| 'Networks" list separated by comma.'), | |
| default=None) | |
| options = parser.parse_args() | |
| update_allowed_networks(options.project, options.instance, options.afe, | |
| options.extra_servers) | |
| if __name__ == '__main__': | |
| main() |