1#!/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 33from autotest_lib.server import frontend 34 35 36ROLES_REQUIRE_TKO_ACCESS = {'scheduler', 'drone', 'shard', 'database', 'afe'} 37 38def gcloud_login(project): 39 """Login to Google Cloud service for gcloud command to run. 40 41 @param project: Name of the Google Cloud project. 42 """ 43 # Login with user account. If the user hasn't log in yet, the script will 44 # print a url and ask for a verification code. User should load the url in 45 # browser, and copy the verification code from the web page. When private IP 46 # can be supported to be added using non-corp account, the login can be done 47 # through service account and key file, e.g., 48 # gcloud auth activate-service-account --key-file ~/key.json 49 utils.run('gcloud auth login', stdout_tee=sys.stdout, 50 stderr_tee=sys.stderr, stdin=sys.stdin) 51 52 53def update_allowed_networks(project, instance, afe=None, extra_servers=None): 54 """Update the "Allowed Networks" list of the given CloudSQL instance. 55 56 @param project: Name of the Google Cloud project. 57 @param instance: Name of the CloudSQL instance. 58 @param afe: Server of the frontend RPC, default to None to use the server 59 specified in global config. 60 @param extra_servers: Extra servers to be included in the "Allowed Networks" 61 list. Default is None. 62 """ 63 # Get the IP address of all servers need access to CloudSQL instance. 64 rpc = frontend.AFE(server=afe) 65 servers = [s['hostname'] for s in rpc.run('get_servers') 66 if s['status'] != 'repair_required' and 67 ROLES_REQUIRE_TKO_ACCESS.intersection(s['roles'])] 68 if extra_servers: 69 servers.extend(extra_servers.split(',')) 70 # Extra servers can be listed in CLOUD/tko_access_servers shadow config. 71 tko_servers = global_config.global_config.get_config_value( 72 'CLOUD', 'tko_access_servers', default='') 73 if tko_servers: 74 servers.extend(tko_servers.split(',')) 75 print 'Adding servers %s to access list for projects %s' % (servers, 76 instance) 77 print 'Fetching their IP addresses...' 78 ips = [socket.gethostbyname(name) for name in servers] 79 print '...Done: %s' % ips 80 81 login = False 82 while True: 83 try: 84 utils.run('gcloud config set project %s -q' % project) 85 cmd = ('gcloud sql instances patch %s --authorized-networks %s ' 86 '-q' % (instance, ','.join(ips))) 87 print 'Running command to update whitelists: "%s"' % cmd 88 utils.run(cmd, stdout_tee=sys.stdout, stderr_tee=sys.stderr) 89 return 90 except error.CmdError: 91 if login: 92 raise 93 94 # Try to login and retry if the command failed. 95 gcloud_login(project) 96 login = True 97 98 99def main(): 100 """main script.""" 101 parser = argparse.ArgumentParser() 102 parser.add_argument('--project', type=str, dest='project', 103 help='Name of the Google Cloud project.') 104 parser.add_argument('--instance', type=str, dest='instance', 105 help='Name of the CloudSQL instance.') 106 parser.add_argument('--afe', type=str, dest='afe', 107 help='Name of the RPC server to get server list.', 108 default=None) 109 parser.add_argument('--extra_servers', type=str, dest='extra_servers', 110 help=('Extra servers to be included in the "Allowed ' 111 'Networks" list separated by comma.'), 112 default=None) 113 options = parser.parse_args() 114 115 update_allowed_networks(options.project, options.instance, options.afe, 116 options.extra_servers) 117 118 119if __name__ == '__main__': 120 main() 121