| OLD | NEW | 
| (Empty) |  | 
 |    1 #!/usr/bin/env python | 
 |    2 # Copyright (c) 2011 Joel Barciauskas http://joel.barciausk.as/ | 
 |    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 # Auto Scaling Groups Tool | 
 |   24 # | 
 |   25 VERSION="0.1" | 
 |   26 usage = """%prog [options] [command] | 
 |   27 Commands: | 
 |   28     list|ls                           List all Auto Scaling Groups | 
 |   29     list-lc|ls-lc                     List all Launch Configurations | 
 |   30     delete    <name>                  Delete ASG <name> | 
 |   31     delete-lc <name>                  Delete Launch Configuration <name> | 
 |   32     get       <name>                  Get details of ASG <name> | 
 |   33     create    <name>                  Create an ASG | 
 |   34     create-lc <name>                  Create a Launch Configuration | 
 |   35     update    <name> <prop> <value>   Update a property of an ASG | 
 |   36     update-image <asg-name> <lc-name> Update image ID for ASG by creating a new 
     LC | 
 |   37     migrate-instances <name>          Shut down current instances one by one and
      wait for ASG to start up a new instance with the current AMI (useful in conjunc
     tion with update-image) | 
 |   38  | 
 |   39 Examples: | 
 |   40  | 
 |   41     1) Create launch configuration | 
 |   42         bin/asadmin create-lc my-lc-1 -i ami-1234abcd -t c1.xlarge -k my-key -s 
     web-group -m | 
 |   43  | 
 |   44     2) Create auto scaling group in us-east-1a and us-east-1c with a load balanc
     er and min size of 2 and max size of 6 | 
 |   45         bin/asadmin create my-asg -z us-east-1a -z us-east-1c -l my-lc-1 -b my-l
     b -H ELB -p 180 -x 2 -X 6 | 
 |   46 """ | 
 |   47  | 
 |   48 def get_group(autoscale, name): | 
 |   49     g = autoscale.get_all_groups(names=[name]) | 
 |   50     if len(g) < 1: | 
 |   51         print "No auto scaling groups by the name of %s found" % name | 
 |   52         return sys.exit(1) | 
 |   53     return g[0] | 
 |   54  | 
 |   55 def get_lc(autoscale, name): | 
 |   56     l = autoscale.get_all_launch_configurations(names=[name]) | 
 |   57     if len(l) < 1: | 
 |   58         print "No launch configurations by the name of %s found" % name | 
 |   59         sys.exit(1) | 
 |   60     return l[0] | 
 |   61  | 
 |   62 def list(autoscale): | 
 |   63     """List all ASGs""" | 
 |   64     print "%-20s %s" %  ("Name", "LC Name") | 
 |   65     print "-"*80 | 
 |   66     groups = autoscale.get_all_groups() | 
 |   67     for g in groups: | 
 |   68         print "%-20s %s" % (g.name, g.launch_config_name) | 
 |   69  | 
 |   70 def list_lc(autoscale): | 
 |   71     """List all LCs""" | 
 |   72     print "%-30s %-20s %s" %  ("Name", "Image ID", "Instance Type") | 
 |   73     print "-"*80 | 
 |   74     for l in autoscale.get_all_launch_configurations(): | 
 |   75         print "%-30s %-20s %s" % (l.name, l.image_id, l.instance_type) | 
 |   76  | 
 |   77 def get(autoscale, name): | 
 |   78     """Get details about ASG <name>""" | 
 |   79     g = get_group(autoscale, name) | 
 |   80     print "="*80 | 
 |   81     print "%-30s %s" % ('Name:', g.name) | 
 |   82     print "%-30s %s" % ('Launch configuration:', g.launch_config_name) | 
 |   83     print "%-30s %s" % ('Minimum size:', g.min_size) | 
 |   84     print "%-30s %s" % ('Maximum size:', g.max_size) | 
 |   85     print "%-30s %s" % ('Desired capacity:', g.desired_capacity) | 
 |   86     print "%-30s %s" % ('Load balancers:', ','.join(g.load_balancers)) | 
 |   87  | 
 |   88     print | 
 |   89  | 
 |   90     print "Instances" | 
 |   91     print "---------" | 
 |   92     print "%-20s %-20s %-20s %s" % ("ID", "Status", "Health", "AZ") | 
 |   93     for i in g.instances: | 
 |   94         print "%-20s %-20s %-20s %s" % \ | 
 |   95         (i.instance_id, i.lifecycle_state, i.health_status, i.availability_zone) | 
 |   96  | 
 |   97     print | 
 |   98  | 
 |   99 def create(autoscale, name, zones, lc_name, load_balancers, hc_type, hc_period, | 
 |  100         min_size, max_size, cooldown, capacity): | 
 |  101     """Create an ASG named <name>""" | 
 |  102     g = AutoScalingGroup(name=name, launch_config=lc_name, | 
 |  103             availability_zones=zones, load_balancers=load_balancers, | 
 |  104             default_cooldown=cooldown, health_check_type=hc_type, | 
 |  105             health_check_period=hc_period, desired_capacity=capacity, | 
 |  106             min_size=min_size, max_size=max_size) | 
 |  107     g = autoscale.create_auto_scaling_group(g) | 
 |  108     return list(autoscale) | 
 |  109  | 
 |  110 def create_lc(autoscale, name, image_id, instance_type, key_name, | 
 |  111         security_groups, instance_monitoring): | 
 |  112     l = LaunchConfiguration(name=name, image_id=image_id, | 
 |  113             instance_type=instance_type,key_name=key_name, | 
 |  114             security_groups=security_groups, | 
 |  115             instance_monitoring=instance_monitoring) | 
 |  116     l = autoscale.create_launch_configuration(l) | 
 |  117     return list_lc(autoscale) | 
 |  118  | 
 |  119 def update(autoscale, name, prop, value): | 
 |  120     g = get_group(autoscale, name) | 
 |  121     setattr(g, prop, value) | 
 |  122     g.update() | 
 |  123     return get(autoscale, name) | 
 |  124  | 
 |  125 def delete(autoscale, name, force_delete=False): | 
 |  126     """Delete this ASG""" | 
 |  127     g = get_group(autoscale, name) | 
 |  128     autoscale.delete_auto_scaling_group(g.name, force_delete) | 
 |  129     print "Auto scaling group %s deleted" % name | 
 |  130     return list(autoscale) | 
 |  131  | 
 |  132 def delete_lc(autoscale, name): | 
 |  133     """Delete this LC""" | 
 |  134     l = get_lc(autoscale, name) | 
 |  135     autoscale.delete_launch_configuration(name) | 
 |  136     print "Launch configuration %s deleted" % name | 
 |  137     return list_lc(autoscale) | 
 |  138  | 
 |  139 def update_image(autoscale, name, lc_name, image_id, is_migrate_instances=False)
     : | 
 |  140     """ Get the current launch config, | 
 |  141         Update its name and image id | 
 |  142         Re-create it as a new launch config | 
 |  143         Update the ASG with the new LC | 
 |  144         Delete the old LC """ | 
 |  145  | 
 |  146     g = get_group(autoscale, name) | 
 |  147     l = get_lc(autoscale, g.launch_config_name) | 
 |  148  | 
 |  149     old_lc_name = l.name | 
 |  150     l.name = lc_name | 
 |  151     l.image_id = image_id | 
 |  152     autoscale.create_launch_configuration(l) | 
 |  153     g.launch_config_name = l.name | 
 |  154     g.update() | 
 |  155  | 
 |  156     if(is_migrate_instances): | 
 |  157         migrate_instances(autoscale, name) | 
 |  158     else: | 
 |  159         return get(autoscale, name) | 
 |  160  | 
 |  161 def migrate_instances(autoscale, name): | 
 |  162     """ Shut down instances of the old image type one by one | 
 |  163         and let the ASG start up instances with the new image """ | 
 |  164     g = get_group(autoscale, name) | 
 |  165  | 
 |  166     old_instances = g.instances | 
 |  167     ec2 = boto.connect_ec2() | 
 |  168     for old_instance in old_instances: | 
 |  169         print "Terminating instance " + old_instance.instance_id | 
 |  170         ec2.terminate_instances([old_instance.instance_id]) | 
 |  171         while True: | 
 |  172             g = get_group(autoscale, name) | 
 |  173             new_instances = g.instances | 
 |  174             for new_instance in new_instances: | 
 |  175                 hasOldInstance = False | 
 |  176                 instancesReady = True | 
 |  177                 if(old_instance.instance_id == new_instance.instance_id): | 
 |  178                     hasOldInstance = True | 
 |  179                     print "Waiting for old instance to shut down..." | 
 |  180                     break | 
 |  181                 elif(new_instance.lifecycle_state != 'InService'): | 
 |  182                     instancesReady = False | 
 |  183                     print "Waiting for instances to be ready...." | 
 |  184                     break | 
 |  185             if(not hasOldInstance and instancesReady): | 
 |  186                 break | 
 |  187             else: | 
 |  188                 time.sleep(20) | 
 |  189     return get(autoscale, name) | 
 |  190  | 
 |  191 if __name__ == "__main__": | 
 |  192     try: | 
 |  193         import readline | 
 |  194     except ImportError: | 
 |  195         pass | 
 |  196     import boto | 
 |  197     import sys | 
 |  198     import time | 
 |  199     from optparse import OptionParser | 
 |  200     from boto.mashups.iobject import IObject | 
 |  201     from boto.ec2.autoscale import AutoScalingGroup | 
 |  202     from boto.ec2.autoscale import LaunchConfiguration | 
 |  203     parser = OptionParser(version=VERSION, usage=usage) | 
 |  204     """ Create launch config options """ | 
 |  205     parser.add_option("-i", "--image-id", | 
 |  206             help="Image (AMI) ID", action="store", | 
 |  207             type="string", default=None, dest="image_id") | 
 |  208     parser.add_option("-t", "--instance-type", | 
 |  209             help="EC2 Instance Type (e.g., m1.large, c1.xlarge), default is m1.l
     arge", | 
 |  210             action="store", type="string", default="m1.large", dest="instance_ty
     pe") | 
 |  211     parser.add_option("-k", "--key-name", | 
 |  212             help="EC2 Key Name", | 
 |  213             action="store", type="string", dest="key_name") | 
 |  214     parser.add_option("-s", "--security-group", | 
 |  215             help="EC2 Security Group", | 
 |  216             action="append", default=[], dest="security_groups") | 
 |  217     parser.add_option("-m", "--monitoring", | 
 |  218             help="Enable instance monitoring", | 
 |  219             action="store_true", default=False, dest="instance_monitoring") | 
 |  220  | 
 |  221     """ Create auto scaling group options """ | 
 |  222     parser.add_option("-z", "--zone", help="Add availability zone", action="appe
     nd", default=[], dest="zones") | 
 |  223     parser.add_option("-l", "--lc-name", | 
 |  224             help="Launch configuration name", | 
 |  225             action="store", default=None, type="string", dest="lc_name") | 
 |  226     parser.add_option("-b", "--load-balancer", | 
 |  227             help="Load balancer name", | 
 |  228             action="append", default=[], dest="load_balancers") | 
 |  229     parser.add_option("-H", "--health-check-type", | 
 |  230             help="Health check type (EC2 or ELB)", | 
 |  231             action="store", default="EC2", type="string", dest="hc_type") | 
 |  232     parser.add_option("-p", "--health-check-period", | 
 |  233             help="Health check period in seconds (default 300s)", | 
 |  234             action="store", default=300, type="int", dest="hc_period") | 
 |  235     parser.add_option("-X", "--max-size", | 
 |  236             help="Max size of ASG (default 10)", | 
 |  237             action="store", default=10, type="int", dest="max_size") | 
 |  238     parser.add_option("-x", "--min-size", | 
 |  239             help="Min size of ASG (default 2)", | 
 |  240             action="store", default=2, type="int", dest="min_size") | 
 |  241     parser.add_option("-c", "--cooldown", | 
 |  242             help="Cooldown time after a scaling activity in seconds (default 300
     s)", | 
 |  243             action="store", default=300, type="int", dest="cooldown") | 
 |  244     parser.add_option("-C", "--desired-capacity", | 
 |  245             help="Desired capacity of the ASG", | 
 |  246             action="store", default=None, type="int", dest="capacity") | 
 |  247     parser.add_option("-f", "--force", | 
 |  248             help="Force delete ASG", | 
 |  249             action="store_true", default=False, dest="force") | 
 |  250     parser.add_option("-y", "--migrate-instances", | 
 |  251             help="Automatically migrate instances to new image when running upda
     te-image", | 
 |  252             action="store_true", default=False, dest="migrate_instances") | 
 |  253  | 
 |  254     (options, args) = parser.parse_args() | 
 |  255  | 
 |  256     if len(args) < 1: | 
 |  257         parser.print_help() | 
 |  258         sys.exit(1) | 
 |  259  | 
 |  260     autoscale = boto.connect_autoscale() | 
 |  261  | 
 |  262     print "%s" % (autoscale.region.endpoint) | 
 |  263  | 
 |  264     command = args[0].lower() | 
 |  265     if command in ("ls", "list"): | 
 |  266         list(autoscale) | 
 |  267     elif command in ("ls-lc", "list-lc"): | 
 |  268         list_lc(autoscale) | 
 |  269     elif command == "get": | 
 |  270         get(autoscale, args[1]) | 
 |  271     elif command == "create": | 
 |  272         create(autoscale, args[1], options.zones, options.lc_name, | 
 |  273                 options.load_balancers, options.hc_type, | 
 |  274                 options.hc_period, options.min_size, options.max_size, | 
 |  275                 options.cooldown, options.capacity) | 
 |  276     elif command == "create-lc": | 
 |  277         create_lc(autoscale, args[1], options.image_id, options.instance_type, | 
 |  278                 options.key_name, options.security_groups, | 
 |  279                 options.instance_monitoring) | 
 |  280     elif command == "update": | 
 |  281         update(autoscale, args[1], args[2], args[3]) | 
 |  282     elif command == "delete": | 
 |  283         delete(autoscale, args[1], options.force) | 
 |  284     elif command == "delete-lc": | 
 |  285         delete_lc(autoscale, args[1]) | 
 |  286     elif command == "update-image": | 
 |  287         update_image(autoscale, args[1], args[2], | 
 |  288                 options.image_id, options.migrate_instances) | 
 |  289     elif command == "migrate-instances": | 
 |  290         migrate_instances(autoscale, args[1]) | 
| OLD | NEW |