| OLD | NEW | 
|    1 #!/usr/bin/env python |    1 #!/usr/bin/env python | 
|    2 # Copyright (c) 2009 Chris Moyer http://coredumped.org/ |    2 # Copyright (c) 2009 Chris Moyer http://coredumped.org/ | 
|    3 # |    3 # | 
|    4 # Permission is hereby granted, free of charge, to any person obtaining a |    4 # Permission is hereby granted, free of charge, to any person obtaining a | 
|    5 # copy of this software and associated documentation files (the |    5 # copy of this software and associated documentation files (the | 
|    6 # "Software"), to deal in the Software without restriction, including |    6 # "Software"), to deal in the Software without restriction, including | 
|    7 # without limitation the rights to use, copy, modify, merge, publish, dis- |    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 |    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- |    9 # persons to whom the Software is furnished to do so, subject to the fol- | 
|   10 # lowing conditions: |   10 # lowing conditions: | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
|   24 # |   24 # | 
|   25 VERSION="0.2" |   25 VERSION="0.2" | 
|   26  |   26  | 
|   27  |   27  | 
|   28 CLOUD_INIT_SCRIPT = """#!/usr/bin/env python |   28 CLOUD_INIT_SCRIPT = """#!/usr/bin/env python | 
|   29 f = open("/etc/boto.cfg", "w") |   29 f = open("/etc/boto.cfg", "w") | 
|   30 f.write(\"\"\"%s\"\"\") |   30 f.write(\"\"\"%s\"\"\") | 
|   31 f.close() |   31 f.close() | 
|   32 """ |   32 """ | 
|   33 import boto.pyami.config |   33 import boto.pyami.config | 
|   34 from boto.utils import fetch_file |   34 import boto.utils | 
|   35 import re, os |   35 import re, os | 
|   36 import ConfigParser |   36 import ConfigParser | 
|   37  |   37  | 
|   38 class Config(boto.pyami.config.Config): |   38 class Config(boto.pyami.config.Config): | 
|   39     """A special config class that also adds import abilities |   39     """A special config class that also adds import abilities | 
|   40     Directly in the config file. To have a config file import |   40     Directly in the config file. To have a config file import | 
|   41     another config file, simply use "#import <path>" where <path> |   41     another config file, simply use "#import <path>" where <path> | 
|   42     is either a relative path or a full URL to another config |   42     is either a relative path or a full URL to another config | 
|   43     """ |   43     """ | 
|   44  |   44  | 
|   45     def __init__(self): |   45     def __init__(self): | 
|   46         ConfigParser.SafeConfigParser.__init__(self, {'working_dir' : '/mnt/pyam
     i', 'debug' : '0'}) |   46         ConfigParser.SafeConfigParser.__init__(self, {'working_dir' : '/mnt/pyam
     i', 'debug' : '0'}) | 
|   47  |   47  | 
|   48     def add_config(self, file_url): |   48     def add_config(self, file_url): | 
|   49         """Add a config file to this configuration |   49         """Add a config file to this configuration | 
|   50         :param file_url: URL for the file to add, or a local path |   50         :param file_url: URL for the file to add, or a local path | 
|   51         :type file_url: str |   51         :type file_url: str | 
|   52         """ |   52         """ | 
|   53         if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", file_url): |   53         if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", file_url): | 
|   54             if not file_url.startswith("/"): |   54             if not file_url.startswith("/"): | 
|   55                 file_url = os.path.join(os.getcwd(), file_url) |   55                 file_url = os.path.join(os.getcwd(), file_url) | 
|   56             file_url = "file://%s" % file_url |   56             file_url = "file://%s" % file_url | 
|   57         (base_url, file_name) = file_url.rsplit("/", 1) |   57         (base_url, file_name) = file_url.rsplit("/", 1) | 
|   58         base_config = fetch_file(file_url) |   58         base_config = boto.utils.fetch_file(file_url) | 
|   59         base_config.seek(0) |   59         base_config.seek(0) | 
|   60         for line in base_config.readlines(): |   60         for line in base_config.readlines(): | 
|   61             match = re.match("^#import[\s\t]*([^\s^\t]*)[\s\t]*$", line) |   61             match = re.match("^#import[\s\t]*([^\s^\t]*)[\s\t]*$", line) | 
|   62             if match: |   62             if match: | 
|   63                 self.add_config("%s/%s" % (base_url, match.group(1))) |   63                 self.add_config("%s/%s" % (base_url, match.group(1))) | 
|   64         base_config.seek(0) |   64         base_config.seek(0) | 
|   65         self.readfp(base_config) |   65         self.readfp(base_config) | 
|   66  |   66  | 
|   67     def add_creds(self, ec2): |   67     def add_creds(self, ec2): | 
|   68         """Add the credentials to this config if they don't already exist""" |   68         """Add the credentials to this config if they don't already exist""" | 
|   69         if not self.has_section('Credentials'): |   69         if not self.has_section('Credentials'): | 
|   70             self.add_section('Credentials') |   70             self.add_section('Credentials') | 
|   71             self.set('Credentials', 'aws_access_key_id', ec2.aws_access_key_id) |   71             self.set('Credentials', 'aws_access_key_id', ec2.aws_access_key_id) | 
|   72             self.set('Credentials', 'aws_secret_access_key', ec2.aws_secret_acce
     ss_key) |   72             self.set('Credentials', 'aws_secret_access_key', ec2.aws_secret_acce
     ss_key) | 
|   73  |   73  | 
|   74      |   74  | 
|   75     def __str__(self): |   75     def __str__(self): | 
|   76         """Get config as string""" |   76         """Get config as string""" | 
|   77         from StringIO import StringIO |   77         from StringIO import StringIO | 
|   78         s = StringIO() |   78         s = StringIO() | 
|   79         self.write(s) |   79         self.write(s) | 
|   80         return s.getvalue() |   80         return s.getvalue() | 
|   81  |   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 | 
|   82  |  107  | 
|   83 if __name__ == "__main__": |  108 if __name__ == "__main__": | 
|   84     try: |  109     try: | 
|   85         import readline |  110         import readline | 
|   86     except ImportError: |  111     except ImportError: | 
|   87         pass |  112         pass | 
|   88     import sys |  113     import sys | 
|   89     import time |  114     import time | 
|   90     import boto |  115     import boto | 
|   91     from boto.ec2 import regions |  116     from boto.ec2 import regions | 
|   92     from optparse import OptionParser |  117     from optparse import OptionParser | 
|   93     from boto.mashups.iobject import IObject |  118     from boto.mashups.iobject import IObject | 
|   94     parser = OptionParser(version=VERSION, usage="%prog [options] config_url") |  119     parser = OptionParser(version=VERSION, usage="%prog [options] config_url") | 
|   95     parser.add_option("-c", "--max-count", help="Maximum number of this type of 
     instance to launch", dest="max_count", default="1") |  120     parser.add_option("-c", "--max-count", help="Maximum number of this type of 
     instance to launch", dest="max_count", default="1") | 
|   96     parser.add_option("--min-count", help="Minimum number of this type of instan
     ce to launch", dest="min_count", default="1") |  121     parser.add_option("--min-count", help="Minimum number of this type of instan
     ce to launch", dest="min_count", default="1") | 
|   97     parser.add_option("--cloud-init", help="Indicates that this is an instance t
     hat 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_ini
     t", default=False, action="store_true") |  122     parser.add_option("--cloud-init", help="Indicates that this is an instance t
     hat 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_ini
     t", default=False, action="store_true") | 
|   98     parser.add_option("-g", "--groups", help="Security Groups to add this instan
     ce to",  action="append", dest="groups") |  123     parser.add_option("-g", "--groups", help="Security Groups to add this instan
     ce to",  action="append", dest="groups") | 
|   99     parser.add_option("-a", "--ami", help="AMI to launch", dest="ami_id") |  124     parser.add_option("-a", "--ami", help="AMI to launch", dest="ami_id") | 
|  100     parser.add_option("-t", "--type", help="Type of Instance (default m1.small)"
     , dest="type", default="m1.small") |  125     parser.add_option("-t", "--type", help="Type of Instance (default m1.small)"
     , dest="type", default="m1.small") | 
|  101     parser.add_option("-k", "--key", help="Keypair", dest="key_name") |  126     parser.add_option("-k", "--key", help="Keypair", dest="key_name") | 
|  102     parser.add_option("-z", "--zone", help="Zone (default us-east-1a)", dest="zo
     ne", default="us-east-1a") |  127     parser.add_option("-z", "--zone", help="Zone (default us-east-1a)", dest="zo
     ne", default="us-east-1a") | 
|  103     parser.add_option("-r", "--region", help="Region (default us-east-1)", dest=
     "region", default="us-east-1") |  128     parser.add_option("-r", "--region", help="Region (default us-east-1)", dest=
     "region", default="us-east-1") | 
|  104     parser.add_option("-i", "--ip", help="Elastic IP", dest="elastic_ip") |  129     parser.add_option("-i", "--ip", help="Elastic IP", dest="elastic_ip") | 
|  105     parser.add_option("-n", "--no-add-cred", help="Don't add a credentials secti
     on", default=False, action="store_true", dest="nocred") |  130     parser.add_option("-n", "--no-add-cred", help="Don't add a credentials secti
     on", default=False, action="store_true", dest="nocred") | 
|  106     parser.add_option("--save-ebs", help="Save the EBS volume on shutdown, inste
     ad of deleting it", default=False, action="store_true", dest="save_ebs") |  131     parser.add_option("--save-ebs", help="Save the EBS volume on shutdown, inste
     ad of deleting it", default=False, action="store_true", dest="save_ebs") | 
|  107     parser.add_option("-w", "--wait", help="Wait until instance is running", def
     ault=False, action="store_true", dest="wait") |  132     parser.add_option("-w", "--wait", help="Wait until instance is running", def
     ault=False, action="store_true", dest="wait") | 
|  108     parser.add_option("-d", "--dns", help="Returns public and private DNS (impli
     cates --wait)", default=False, action="store_true", dest="dns") |  133     parser.add_option("-d", "--dns", help="Returns public and private DNS (impli
     cates --wait)", default=False, action="store_true", dest="dns") | 
|  109     parser.add_option("-T", "--tag", help="Set tag", default=None, action="appen
     d", dest="tags", metavar="key:value") |  134     parser.add_option("-T", "--tag", help="Set tag", default=None, action="appen
     d", dest="tags", metavar="key:value") | 
 |  135     parser.add_option("-s", "--scripts", help="Pass in a script or a folder cont
     aining scripts to be run when the instance starts up, assumes cloud-init. Specif
     y 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 file
     names with numbers)", type='string', action="callback", callback=scripts_callbac
     k) | 
|  110  |  136  | 
|  111     (options, args) = parser.parse_args() |  137     (options, args) = parser.parse_args() | 
|  112  |  138  | 
|  113     if len(args) < 1: |  139     if len(args) < 1: | 
|  114         parser.print_help() |  140         parser.print_help() | 
|  115         sys.exit(1) |  141         sys.exit(1) | 
|  116     file_url = os.path.expanduser(args[0]) |  142     file_url = os.path.expanduser(args[0]) | 
|  117  |  143  | 
|  118     cfg = Config() |  144     cfg = Config() | 
|  119     cfg.add_config(file_url) |  145     cfg.add_config(file_url) | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  151         g = iobj.choose_from_list(l, prompt='Choose Primary Security Group') |  177         g = iobj.choose_from_list(l, prompt='Choose Primary Security Group') | 
|  152         while g != None: |  178         while g != None: | 
|  153             groups.append(g) |  179             groups.append(g) | 
|  154             l.remove((g, g.name, g.description)) |  180             l.remove((g, g.name, g.description)) | 
|  155             g = iobj.choose_from_list(l, prompt='Choose Additional Security Grou
     p (0 to quit)') |  181             g = iobj.choose_from_list(l, prompt='Choose Additional Security Grou
     p (0 to quit)') | 
|  156  |  182  | 
|  157     user_data = str(cfg) |  183     user_data = str(cfg) | 
|  158     # If it's a cloud init AMI, |  184     # If it's a cloud init AMI, | 
|  159     # then we need to wrap the config in our |  185     # then we need to wrap the config in our | 
|  160     # little wrapper shell script |  186     # little wrapper shell script | 
 |  187  | 
|  161     if options.cloud_init: |  188     if options.cloud_init: | 
|  162         user_data = CLOUD_INIT_SCRIPT % user_data |  189         user_data = CLOUD_INIT_SCRIPT % user_data | 
 |  190         scriptuples = [] | 
 |  191         if options.scripts: | 
 |  192             scripts = options.scripts | 
 |  193             scriptuples.append(('user_data', user_data)) | 
 |  194             for scr in scripts: | 
 |  195                 scr_url = scr | 
 |  196                 if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", scr_url): | 
 |  197                     if not scr_url.startswith("/"): | 
 |  198                         scr_url = os.path.join(os.getcwd(), scr_url) | 
 |  199                     try: | 
 |  200                         newfiles = os.listdir(scr_url) | 
 |  201                         for f in newfiles: | 
 |  202                             #put the scripts in the folder in the array such tha
     t they run in the correct order | 
 |  203                             scripts.insert(scripts.index(scr) + 1, scr.split("/"
     )[-1] + "/" + f) | 
 |  204                     except OSError: | 
 |  205                         scr_url = "file://%s" % scr_url | 
 |  206                 try: | 
 |  207                     scriptuples.append((scr, add_script(scr_url))) | 
 |  208                 except Exception, e: | 
 |  209                     pass | 
 |  210  | 
 |  211             user_data = boto.utils.write_mime_multipart(scriptuples, compress=Tr
     ue) | 
 |  212  | 
|  163     shutdown_proc = "terminate" |  213     shutdown_proc = "terminate" | 
|  164     if options.save_ebs: |  214     if options.save_ebs: | 
|  165         shutdown_proc = "save" |  215         shutdown_proc = "save" | 
|  166  |  216  | 
|  167     r = ami.run(min_count=int(options.min_count), max_count=int(options.max_coun
     t), |  217     r = ami.run(min_count=int(options.min_count), max_count=int(options.max_coun
     t), | 
|  168             key_name=key_name, user_data=user_data, |  218             key_name=key_name, user_data=user_data, | 
|  169             security_groups=groups, instance_type=options.type, |  219             security_groups=groups, instance_type=options.type, | 
|  170             placement=options.zone, instance_initiated_shutdown_behavior=shutdow
     n_proc) |  220             placement=options.zone, instance_initiated_shutdown_behavior=shutdow
     n_proc) | 
|  171  |  221  | 
|  172     instance = r.instances[0] |  222     instance = r.instances[0] | 
| (...skipping 14 matching lines...) Expand all  Loading... | 
|  187  |  237  | 
|  188     while True: |  238     while True: | 
|  189         instance.update() |  239         instance.update() | 
|  190         if instance.state == 'running': |  240         if instance.state == 'running': | 
|  191             break |  241             break | 
|  192         time.sleep(3) |  242         time.sleep(3) | 
|  193  |  243  | 
|  194     if options.dns: |  244     if options.dns: | 
|  195         print "Public DNS name: %s" % instance.public_dns_name |  245         print "Public DNS name: %s" % instance.public_dns_name | 
|  196         print "Private DNS name: %s" % instance.private_dns_name |  246         print "Private DNS name: %s" % instance.private_dns_name | 
|  197  |  | 
| OLD | NEW |