| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 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 # Elastic Load Balancer Tool | |
| 24 # | |
| 25 VERSION = "0.2" | |
| 26 usage = """%prog [options] [command] | |
| 27 Commands: | |
| 28 list|ls List all Elastic Load Balancers | |
| 29 delete <name> Delete ELB <name> | |
| 30 get <name> Get all instances associated with <name> | |
| 31 create <name> Create an ELB; -z and -l are required | |
| 32 add <name> <instances> Add <instances> in ELB <name> | |
| 33 remove|rm <name> <instances> Remove <instances> from ELB <name> | |
| 34 reap <name> Remove terminated instances from ELB <name> | |
| 35 enable|en <name> <zone> Enable Zone <zone> for ELB <name> | |
| 36 disable <name> <zone> Disable Zone <zone> for ELB <name> | |
| 37 addl <name> Add listeners (specified by -l) to the ELB | |
| 38 <name> | |
| 39 rml <name> <port> Remove Listener(s) specified by the port on | |
| 40 the ELB <name> | |
| 41 """ | |
| 42 | |
| 43 | |
| 44 def find_elb(elb, name): | |
| 45 try: | |
| 46 elbs = elb.get_all_load_balancers(name) | |
| 47 except boto.exception.BotoServerError as se: | |
| 48 if se.code == 'LoadBalancerNotFound': | |
| 49 elbs = [] | |
| 50 else: | |
| 51 raise | |
| 52 | |
| 53 if len(elbs) < 1: | |
| 54 print "No load balancer by the name of %s found" % name | |
| 55 return None | |
| 56 elif len(elbs) > 1: | |
| 57 print "More than one elb matches %s?" % name | |
| 58 return None | |
| 59 | |
| 60 # Should not happen | |
| 61 if name not in elbs[0].name: | |
| 62 print "No load balancer by the name of %s found" % name | |
| 63 return None | |
| 64 | |
| 65 return elbs[0] | |
| 66 | |
| 67 | |
| 68 def list(elb): | |
| 69 """List all ELBs""" | |
| 70 print "%-20s %s" % ("Name", "DNS Name") | |
| 71 print "-" * 80 | |
| 72 for b in elb.get_all_load_balancers(): | |
| 73 print "%-20s %s" % (b.name, b.dns_name) | |
| 74 | |
| 75 def check_valid_region(conn, region): | |
| 76 if conn is None: | |
| 77 print 'Invalid region (%s)' % region | |
| 78 sys.exit(1) | |
| 79 | |
| 80 def get(elb, name): | |
| 81 """Get details about ELB <name>""" | |
| 82 | |
| 83 b = find_elb(elb, name) | |
| 84 if b: | |
| 85 print "=" * 80 | |
| 86 print "Name: %s" % b.name | |
| 87 print "DNS Name: %s" % b.dns_name | |
| 88 if b.canonical_hosted_zone_name: | |
| 89 chzn = b.canonical_hosted_zone_name | |
| 90 print "Canonical hosted zone name: %s" % chzn | |
| 91 if b.canonical_hosted_zone_name_id: | |
| 92 chznid = b.canonical_hosted_zone_name_id | |
| 93 print "Canonical hosted zone name id: %s" % chznid | |
| 94 print | |
| 95 | |
| 96 print "Health Check: %s" % b.health_check | |
| 97 print | |
| 98 | |
| 99 print "Listeners" | |
| 100 print "---------" | |
| 101 print "%-8s %-8s %s" % ("IN", "OUT", "PROTO") | |
| 102 for l in b.listeners: | |
| 103 print "%-8s %-8s %s" % (l[0], l[1], l[2]) | |
| 104 | |
| 105 print | |
| 106 | |
| 107 print " Zones " | |
| 108 print "---------" | |
| 109 for z in b.availability_zones: | |
| 110 print z | |
| 111 | |
| 112 print | |
| 113 | |
| 114 # Make map of all instance Id's to Name tags | |
| 115 import boto | |
| 116 if not options.region: | |
| 117 ec2 = boto.connect_ec2() | |
| 118 else: | |
| 119 ec2 = boto.ec2.connect_to_region(options.region) | |
| 120 check_valid_region(ec2, options.region) | |
| 121 | |
| 122 instance_health = b.get_instance_health() | |
| 123 instances = [state.instance_id for state in instance_health] | |
| 124 | |
| 125 names = dict((k,'') for k in instances) | |
| 126 for i in ec2.get_only_instances(): | |
| 127 if i.id in instances: | |
| 128 names[i.id] = i.tags.get('Name', '') | |
| 129 | |
| 130 name_column_width = max([4] + [len(v) for k,v in names.iteritems()]) + 2 | |
| 131 | |
| 132 print "Instances" | |
| 133 print "---------" | |
| 134 print "%-12s %-15s %-*s %s" % ("ID", | |
| 135 "STATE", | |
| 136 name_column_width, "NAME", | |
| 137 "DESCRIPTION") | |
| 138 for state in instance_health: | |
| 139 print "%-12s %-15s %-*s %s" % (state.instance_id, | |
| 140 state.state, | |
| 141 name_column_width, names[state.instan
ce_id], | |
| 142 state.description) | |
| 143 | |
| 144 print | |
| 145 | |
| 146 | |
| 147 def create(elb, name, zones, listeners): | |
| 148 """Create an ELB named <name>""" | |
| 149 l_list = [] | |
| 150 for l in listeners: | |
| 151 l = l.split(",") | |
| 152 if l[2] == 'HTTPS': | |
| 153 l_list.append((int(l[0]), int(l[1]), l[2], l[3])) | |
| 154 else: | |
| 155 l_list.append((int(l[0]), int(l[1]), l[2])) | |
| 156 | |
| 157 b = elb.create_load_balancer(name, zones, l_list) | |
| 158 return get(elb, name) | |
| 159 | |
| 160 | |
| 161 def delete(elb, name): | |
| 162 """Delete this ELB""" | |
| 163 b = find_elb(elb, name) | |
| 164 if b: | |
| 165 b.delete() | |
| 166 print "Load Balancer %s deleted" % name | |
| 167 | |
| 168 | |
| 169 def add_instances(elb, name, instances): | |
| 170 """Add <instance> to ELB <name>""" | |
| 171 b = find_elb(elb, name) | |
| 172 if b: | |
| 173 b.register_instances(instances) | |
| 174 return get(elb, name) | |
| 175 | |
| 176 | |
| 177 def remove_instances(elb, name, instances): | |
| 178 """Remove instance from elb <name>""" | |
| 179 b = find_elb(elb, name) | |
| 180 if b: | |
| 181 b.deregister_instances(instances) | |
| 182 return get(elb, name) | |
| 183 | |
| 184 | |
| 185 def reap_instances(elb, name): | |
| 186 """Remove terminated instances from elb <name>""" | |
| 187 b = find_elb(elb, name) | |
| 188 if b: | |
| 189 for state in b.get_instance_health(): | |
| 190 if (state.state == 'OutOfService' and | |
| 191 state.description == 'Instance is in terminated state.'): | |
| 192 b.deregister_instances([state.instance_id]) | |
| 193 return get(elb, name) | |
| 194 | |
| 195 | |
| 196 def enable_zone(elb, name, zone): | |
| 197 """Enable <zone> for elb""" | |
| 198 b = find_elb(elb, name) | |
| 199 if b: | |
| 200 b.enable_zones([zone]) | |
| 201 return get(elb, name) | |
| 202 | |
| 203 | |
| 204 def disable_zone(elb, name, zone): | |
| 205 """Disable <zone> for elb""" | |
| 206 b = find_elb(elb, name) | |
| 207 if b: | |
| 208 b.disable_zones([zone]) | |
| 209 return get(elb, name) | |
| 210 | |
| 211 | |
| 212 def add_listener(elb, name, listeners): | |
| 213 """Add listeners to a given load balancer""" | |
| 214 l_list = [] | |
| 215 for l in listeners: | |
| 216 l = l.split(",") | |
| 217 l_list.append((int(l[0]), int(l[1]), l[2])) | |
| 218 b = find_elb(elb, name) | |
| 219 if b: | |
| 220 b.create_listeners(l_list) | |
| 221 return get(elb, name) | |
| 222 | |
| 223 | |
| 224 def rm_listener(elb, name, ports): | |
| 225 """Remove listeners from a given load balancer""" | |
| 226 b = find_elb(elb, name) | |
| 227 if b: | |
| 228 b.delete_listeners(ports) | |
| 229 return get(elb, name) | |
| 230 | |
| 231 | |
| 232 if __name__ == "__main__": | |
| 233 try: | |
| 234 import readline | |
| 235 except ImportError: | |
| 236 pass | |
| 237 import boto | |
| 238 import sys | |
| 239 from optparse import OptionParser | |
| 240 from boto.mashups.iobject import IObject | |
| 241 parser = OptionParser(version=VERSION, usage=usage) | |
| 242 parser.add_option("-z", "--zone", | |
| 243 help="Operate on zone", | |
| 244 action="append", default=[], dest="zones") | |
| 245 parser.add_option("-l", "--listener", | |
| 246 help="Specify Listener in,out,proto", | |
| 247 action="append", default=[], dest="listeners") | |
| 248 parser.add_option("-r", "--region", | |
| 249 help="Region to connect to", | |
| 250 action="store", dest="region") | |
| 251 | |
| 252 (options, args) = parser.parse_args() | |
| 253 | |
| 254 if len(args) < 1: | |
| 255 parser.print_help() | |
| 256 sys.exit(1) | |
| 257 | |
| 258 if not options.region: | |
| 259 elb = boto.connect_elb() | |
| 260 else: | |
| 261 import boto.ec2.elb | |
| 262 elb = boto.ec2.elb.connect_to_region(options.region) | |
| 263 check_valid_region(elb, options.region) | |
| 264 | |
| 265 print "%s" % (elb.region.endpoint) | |
| 266 | |
| 267 command = args[0].lower() | |
| 268 if command in ("ls", "list"): | |
| 269 list(elb) | |
| 270 elif command == "get": | |
| 271 get(elb, args[1]) | |
| 272 elif command == "create": | |
| 273 if not options.listeners: | |
| 274 print "-l option required for command create" | |
| 275 sys.exit(1) | |
| 276 if not options.zones: | |
| 277 print "-z option required for command create" | |
| 278 sys.exit(1) | |
| 279 create(elb, args[1], options.zones, options.listeners) | |
| 280 elif command == "delete": | |
| 281 delete(elb, args[1]) | |
| 282 elif command in ("add", "put"): | |
| 283 add_instances(elb, args[1], args[2:]) | |
| 284 elif command in ("rm", "remove"): | |
| 285 remove_instances(elb, args[1], args[2:]) | |
| 286 elif command == "reap": | |
| 287 reap_instances(elb, args[1]) | |
| 288 elif command in ("en", "enable"): | |
| 289 enable_zone(elb, args[1], args[2]) | |
| 290 elif command == "disable": | |
| 291 disable_zone(elb, args[1], args[2]) | |
| 292 elif command == "addl": | |
| 293 if not options.listeners: | |
| 294 print "-l option required for command addl" | |
| 295 sys.exit(1) | |
| 296 add_listener(elb, args[1], options.listeners) | |
| 297 elif command == "rml": | |
| 298 if not args[2:]: | |
| 299 print "port required" | |
| 300 sys.exit(2) | |
| 301 rm_listener(elb, args[1], args[2:]) | |
| OLD | NEW |