Mercurial > repos > shellac > sam_consensus_v3
comparison env/bin/launch_instance @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
| author | shellac |
|---|---|
| date | Mon, 22 Mar 2021 18:12:50 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:4f3585e2f14b |
|---|---|
| 1 #!/Users/cmdms/OneDrive-UOB/Development/Projects/2021/sam-consensus-v3/env/bin/python3 | |
| 2 # Copyright (c) 2009 Chris Moyer http://coredumped.org/ | |
| 3 # | |
| 4 # Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 # copy of this software and associated documentation files (the | |
| 6 # "Software"), to deal in the Software without restriction, including | |
| 7 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
| 8 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
| 9 # persons to whom the Software is furnished to do so, subject to the fol- | |
| 10 # lowing conditions: | |
| 11 # | |
| 12 # The above copyright notice and this permission notice shall be included | |
| 13 # in all copies or substantial portions of the Software. | |
| 14 # | |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
| 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
| 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
| 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
| 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 21 | |
| 22 # | |
| 23 # Utility to launch an EC2 Instance | |
| 24 # | |
| 25 VERSION="0.2" | |
| 26 | |
| 27 | |
| 28 CLOUD_INIT_SCRIPT = """#!/usr/bin/env python | |
| 29 f = open("/etc/boto.cfg", "w") | |
| 30 f.write(\"\"\"%s\"\"\") | |
| 31 f.close() | |
| 32 """ | |
| 33 import boto.pyami.config | |
| 34 import boto.utils | |
| 35 import re, os | |
| 36 from boto.compat import ConfigParser | |
| 37 | |
| 38 class Config(boto.pyami.config.Config): | |
| 39 """A special config class that also adds import abilities | |
| 40 Directly in the config file. To have a config file import | |
| 41 another config file, simply use "#import <path>" where <path> | |
| 42 is either a relative path or a full URL to another config | |
| 43 """ | |
| 44 | |
| 45 def __init__(self): | |
| 46 ConfigParser.__init__(self, {'working_dir' : '/mnt/pyami', 'debug' : '0'}) | |
| 47 | |
| 48 def add_config(self, file_url): | |
| 49 """Add a config file to this configuration | |
| 50 :param file_url: URL for the file to add, or a local path | |
| 51 :type file_url: str | |
| 52 """ | |
| 53 if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", file_url): | |
| 54 if not file_url.startswith("/"): | |
| 55 file_url = os.path.join(os.getcwd(), file_url) | |
| 56 file_url = "file://%s" % file_url | |
| 57 (base_url, file_name) = file_url.rsplit("/", 1) | |
| 58 base_config = boto.utils.fetch_file(file_url) | |
| 59 base_config.seek(0) | |
| 60 for line in base_config.readlines(): | |
| 61 match = re.match("^#import[\s\t]*([^\s^\t]*)[\s\t]*$", line) | |
| 62 if match: | |
| 63 self.add_config("%s/%s" % (base_url, match.group(1))) | |
| 64 base_config.seek(0) | |
| 65 self.readfp(base_config) | |
| 66 | |
| 67 def add_creds(self, ec2): | |
| 68 """Add the credentials to this config if they don't already exist""" | |
| 69 if not self.has_section('Credentials'): | |
| 70 self.add_section('Credentials') | |
| 71 self.set('Credentials', 'aws_access_key_id', ec2.aws_access_key_id) | |
| 72 self.set('Credentials', 'aws_secret_access_key', ec2.aws_secret_access_key) | |
| 73 | |
| 74 | |
| 75 def __str__(self): | |
| 76 """Get config as string""" | |
| 77 from StringIO import StringIO | |
| 78 s = StringIO() | |
| 79 self.write(s) | |
| 80 return s.getvalue() | |
| 81 | |
| 82 SCRIPTS = [] | |
| 83 | |
| 84 def scripts_callback(option, opt, value, parser): | |
| 85 arg = value.split(',') | |
| 86 if len(arg) == 1: | |
| 87 SCRIPTS.append(arg[0]) | |
| 88 else: | |
| 89 SCRIPTS.extend(arg) | |
| 90 setattr(parser.values, option.dest, SCRIPTS) | |
| 91 | |
| 92 def add_script(scr_url): | |
| 93 """Read a script and any scripts that are added using #import""" | |
| 94 base_url = '/'.join(scr_url.split('/')[:-1]) + '/' | |
| 95 script_raw = boto.utils.fetch_file(scr_url) | |
| 96 script_content = '' | |
| 97 for line in script_raw.readlines(): | |
| 98 match = re.match("^#import[\s\t]*([^\s^\t]*)[\s\t]*$", line) | |
| 99 #if there is an import | |
| 100 if match: | |
| 101 #Read the other script and put it in that spot | |
| 102 script_content += add_script("%s/%s" % (base_url, match.group(1))) | |
| 103 else: | |
| 104 #Otherwise, add the line and move on | |
| 105 script_content += line | |
| 106 return script_content | |
| 107 | |
| 108 if __name__ == "__main__": | |
| 109 try: | |
| 110 import readline | |
| 111 except ImportError: | |
| 112 pass | |
| 113 import sys | |
| 114 import time | |
| 115 import boto | |
| 116 from boto.ec2 import regions | |
| 117 from optparse import OptionParser | |
| 118 from boto.mashups.iobject import IObject | |
| 119 parser = OptionParser(version=VERSION, usage="%prog [options] config_url") | |
| 120 parser.add_option("-c", "--max-count", help="Maximum number of this type of instance to launch", dest="max_count", default="1") | |
| 121 parser.add_option("--min-count", help="Minimum number of this type of instance to launch", dest="min_count", default="1") | |
| 122 parser.add_option("--cloud-init", help="Indicates that this is an instance that uses 'CloudInit', Ubuntu's cloud bootstrap process. This wraps the config in a shell script command instead of just passing it in directly", dest="cloud_init", default=False, action="store_true") | |
| 123 parser.add_option("-g", "--groups", help="Security Groups to add this instance to", action="append", dest="groups") | |
| 124 parser.add_option("-a", "--ami", help="AMI to launch", dest="ami_id") | |
| 125 parser.add_option("-t", "--type", help="Type of Instance (default m1.small)", dest="type", default="m1.small") | |
| 126 parser.add_option("-k", "--key", help="Keypair", dest="key_name") | |
| 127 parser.add_option("-z", "--zone", help="Zone (default us-east-1a)", dest="zone", default="us-east-1a") | |
| 128 parser.add_option("-r", "--region", help="Region (default us-east-1)", dest="region", default="us-east-1") | |
| 129 parser.add_option("-i", "--ip", help="Elastic IP", dest="elastic_ip") | |
| 130 parser.add_option("-n", "--no-add-cred", help="Don't add a credentials section", default=False, action="store_true", dest="nocred") | |
| 131 parser.add_option("--save-ebs", help="Save the EBS volume on shutdown, instead of deleting it", default=False, action="store_true", dest="save_ebs") | |
| 132 parser.add_option("-w", "--wait", help="Wait until instance is running", default=False, action="store_true", dest="wait") | |
| 133 parser.add_option("-d", "--dns", help="Returns public and private DNS (implicates --wait)", default=False, action="store_true", dest="dns") | |
| 134 parser.add_option("-T", "--tag", help="Set tag", default=None, action="append", dest="tags", metavar="key:value") | |
| 135 parser.add_option("-s", "--scripts", help="Pass in a script or a folder containing scripts to be run when the instance starts up, assumes cloud-init. Specify scripts in a list specified by commas. If multiple scripts are specified, they are run lexically (A good way to ensure they run in the order is to prefix filenames with numbers)", type='string', action="callback", callback=scripts_callback) | |
| 136 parser.add_option("--role", help="IAM Role to use, this implies --no-add-cred", dest="role") | |
| 137 | |
| 138 (options, args) = parser.parse_args() | |
| 139 | |
| 140 if len(args) < 1: | |
| 141 parser.print_help() | |
| 142 sys.exit(1) | |
| 143 file_url = os.path.expanduser(args[0]) | |
| 144 | |
| 145 cfg = Config() | |
| 146 cfg.add_config(file_url) | |
| 147 | |
| 148 for r in regions(): | |
| 149 if r.name == options.region: | |
| 150 region = r | |
| 151 break | |
| 152 else: | |
| 153 print("Region %s not found." % options.region) | |
| 154 sys.exit(1) | |
| 155 ec2 = boto.connect_ec2(region=region) | |
| 156 if not options.nocred and not options.role: | |
| 157 cfg.add_creds(ec2) | |
| 158 | |
| 159 iobj = IObject() | |
| 160 if options.ami_id: | |
| 161 ami = ec2.get_image(options.ami_id) | |
| 162 else: | |
| 163 ami_id = options.ami_id | |
| 164 l = [(a, a.id, a.location) for a in ec2.get_all_images()] | |
| 165 ami = iobj.choose_from_list(l, prompt='Choose AMI') | |
| 166 | |
| 167 if options.key_name: | |
| 168 key_name = options.key_name | |
| 169 else: | |
| 170 l = [(k, k.name, '') for k in ec2.get_all_key_pairs()] | |
| 171 key_name = iobj.choose_from_list(l, prompt='Choose Keypair').name | |
| 172 | |
| 173 if options.groups: | |
| 174 groups = options.groups | |
| 175 else: | |
| 176 groups = [] | |
| 177 l = [(g, g.name, g.description) for g in ec2.get_all_security_groups()] | |
| 178 g = iobj.choose_from_list(l, prompt='Choose Primary Security Group') | |
| 179 while g != None: | |
| 180 groups.append(g) | |
| 181 l.remove((g, g.name, g.description)) | |
| 182 g = iobj.choose_from_list(l, prompt='Choose Additional Security Group (0 to quit)') | |
| 183 | |
| 184 user_data = str(cfg) | |
| 185 # If it's a cloud init AMI, | |
| 186 # then we need to wrap the config in our | |
| 187 # little wrapper shell script | |
| 188 | |
| 189 if options.cloud_init: | |
| 190 user_data = CLOUD_INIT_SCRIPT % user_data | |
| 191 scriptuples = [] | |
| 192 if options.scripts: | |
| 193 scripts = options.scripts | |
| 194 scriptuples.append(('user_data', user_data)) | |
| 195 for scr in scripts: | |
| 196 scr_url = scr | |
| 197 if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", scr_url): | |
| 198 if not scr_url.startswith("/"): | |
| 199 scr_url = os.path.join(os.getcwd(), scr_url) | |
| 200 try: | |
| 201 newfiles = os.listdir(scr_url) | |
| 202 for f in newfiles: | |
| 203 #put the scripts in the folder in the array such that they run in the correct order | |
| 204 scripts.insert(scripts.index(scr) + 1, scr.split("/")[-1] + "/" + f) | |
| 205 except OSError: | |
| 206 scr_url = "file://%s" % scr_url | |
| 207 try: | |
| 208 scriptuples.append((scr, add_script(scr_url))) | |
| 209 except Exception as e: | |
| 210 pass | |
| 211 | |
| 212 user_data = boto.utils.write_mime_multipart(scriptuples, compress=True) | |
| 213 | |
| 214 shutdown_proc = "terminate" | |
| 215 if options.save_ebs: | |
| 216 shutdown_proc = "save" | |
| 217 | |
| 218 instance_profile_name = None | |
| 219 if options.role: | |
| 220 instance_profile_name = options.role | |
| 221 | |
| 222 r = ami.run(min_count=int(options.min_count), max_count=int(options.max_count), | |
| 223 key_name=key_name, user_data=user_data, | |
| 224 security_groups=groups, instance_type=options.type, | |
| 225 placement=options.zone, instance_initiated_shutdown_behavior=shutdown_proc, | |
| 226 instance_profile_name=instance_profile_name) | |
| 227 | |
| 228 instance = r.instances[0] | |
| 229 | |
| 230 if options.tags: | |
| 231 for tag_pair in options.tags: | |
| 232 name = tag_pair | |
| 233 value = '' | |
| 234 if ':' in tag_pair: | |
| 235 name, value = tag_pair.split(':', 1) | |
| 236 instance.add_tag(name, value) | |
| 237 | |
| 238 if options.dns: | |
| 239 options.wait = True | |
| 240 | |
| 241 if not options.wait: | |
| 242 sys.exit(0) | |
| 243 | |
| 244 while True: | |
| 245 instance.update() | |
| 246 if instance.state == 'running': | |
| 247 break | |
| 248 time.sleep(3) | |
| 249 | |
| 250 if options.dns: | |
| 251 print("Public DNS name: %s" % instance.public_dns_name) | |
| 252 print("Private DNS name: %s" % instance.private_dns_name) |
