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 |