1# Copyright (c) 2006-2012 Mitch Garnaat http://garnaat.org/ 2# Copyright (c) 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved 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# IN THE SOFTWARE. 22 23from boto.ec2.elb.healthcheck import HealthCheck 24from boto.ec2.elb.listener import Listener 25from boto.ec2.elb.listelement import ListElement 26from boto.ec2.elb.policies import Policies, OtherPolicy 27from boto.ec2.elb.securitygroup import SecurityGroup 28from boto.ec2.instanceinfo import InstanceInfo 29from boto.resultset import ResultSet 30from boto.compat import six 31 32 33class Backend(object): 34 """Backend server description""" 35 36 def __init__(self, connection=None): 37 self.connection = connection 38 self.instance_port = None 39 self.policies = None 40 41 def __repr__(self): 42 return 'Backend(%r:%r)' % (self.instance_port, self.policies) 43 44 def startElement(self, name, attrs, connection): 45 if name == 'PolicyNames': 46 self.policies = ResultSet([('member', OtherPolicy)]) 47 return self.policies 48 49 def endElement(self, name, value, connection): 50 if name == 'InstancePort': 51 self.instance_port = int(value) 52 return 53 54 55class LoadBalancerZones(object): 56 """ 57 Used to collect the zones for a Load Balancer when enable_zones 58 or disable_zones are called. 59 """ 60 def __init__(self, connection=None): 61 self.connection = connection 62 self.zones = ListElement() 63 64 def startElement(self, name, attrs, connection): 65 if name == 'AvailabilityZones': 66 return self.zones 67 68 def endElement(self, name, value, connection): 69 pass 70 71 72class LoadBalancer(object): 73 """ 74 Represents an EC2 Load Balancer. 75 """ 76 77 def __init__(self, connection=None, name=None, endpoints=None): 78 """ 79 :ivar boto.ec2.elb.ELBConnection connection: The connection this load 80 balancer was instance was instantiated from. 81 :ivar list listeners: A list of tuples in the form of 82 ``(<Inbound port>, <Outbound port>, <Protocol>)`` 83 :ivar boto.ec2.elb.healthcheck.HealthCheck health_check: The health 84 check policy for this load balancer. 85 :ivar boto.ec2.elb.policies.Policies policies: Cookie stickiness and 86 other policies. 87 :ivar str name: The name of the Load Balancer. 88 :ivar str dns_name: The external DNS name for the balancer. 89 :ivar str created_time: A date+time string showing when the 90 load balancer was created. 91 :ivar list instances: A list of :py:class:`boto.ec2.instanceinfo.InstanceInfo` 92 instances, representing the EC2 instances this load balancer is 93 distributing requests to. 94 :ivar list availability_zones: The availability zones this balancer 95 covers. 96 :ivar str canonical_hosted_zone_name: Current CNAME for the balancer. 97 :ivar str canonical_hosted_zone_name_id: The Route 53 hosted zone 98 ID of this balancer. Needed when creating an Alias record in a 99 Route 53 hosted zone. 100 :ivar boto.ec2.elb.securitygroup.SecurityGroup source_security_group: 101 The security group that you can use as part of your inbound rules 102 for your load balancer back-end instances to disallow traffic 103 from sources other than your load balancer. 104 :ivar list subnets: A list of subnets this balancer is on. 105 :ivar list security_groups: A list of additional security groups that 106 have been applied. 107 :ivar str vpc_id: The ID of the VPC that this ELB resides within. 108 :ivar list backends: A list of :py:class:`boto.ec2.elb.loadbalancer.Backend 109 back-end server descriptions. 110 """ 111 self.connection = connection 112 self.name = name 113 self.listeners = None 114 self.health_check = None 115 self.policies = None 116 self.dns_name = None 117 self.created_time = None 118 self.instances = None 119 self.availability_zones = ListElement() 120 self.canonical_hosted_zone_name = None 121 self.canonical_hosted_zone_name_id = None 122 self.source_security_group = None 123 self.subnets = ListElement() 124 self.security_groups = ListElement() 125 self.vpc_id = None 126 self.scheme = None 127 self.backends = None 128 self._attributes = None 129 130 def __repr__(self): 131 return 'LoadBalancer:%s' % self.name 132 133 def startElement(self, name, attrs, connection): 134 if name == 'HealthCheck': 135 self.health_check = HealthCheck(self) 136 return self.health_check 137 elif name == 'ListenerDescriptions': 138 self.listeners = ResultSet([('member', Listener)]) 139 return self.listeners 140 elif name == 'AvailabilityZones': 141 return self.availability_zones 142 elif name == 'Instances': 143 self.instances = ResultSet([('member', InstanceInfo)]) 144 return self.instances 145 elif name == 'Policies': 146 self.policies = Policies(self) 147 return self.policies 148 elif name == 'SourceSecurityGroup': 149 self.source_security_group = SecurityGroup() 150 return self.source_security_group 151 elif name == 'Subnets': 152 return self.subnets 153 elif name == 'SecurityGroups': 154 return self.security_groups 155 elif name == 'VPCId': 156 pass 157 elif name == "BackendServerDescriptions": 158 self.backends = ResultSet([('member', Backend)]) 159 return self.backends 160 else: 161 return None 162 163 def endElement(self, name, value, connection): 164 if name == 'LoadBalancerName': 165 self.name = value 166 elif name == 'DNSName': 167 self.dns_name = value 168 elif name == 'CreatedTime': 169 self.created_time = value 170 elif name == 'InstanceId': 171 self.instances.append(value) 172 elif name == 'CanonicalHostedZoneName': 173 self.canonical_hosted_zone_name = value 174 elif name == 'CanonicalHostedZoneNameID': 175 self.canonical_hosted_zone_name_id = value 176 elif name == 'VPCId': 177 self.vpc_id = value 178 elif name == 'Scheme': 179 self.scheme = value 180 else: 181 setattr(self, name, value) 182 183 def enable_zones(self, zones): 184 """ 185 Enable availability zones to this Access Point. 186 All zones must be in the same region as the Access Point. 187 188 :type zones: string or List of strings 189 :param zones: The name of the zone(s) to add. 190 191 """ 192 if isinstance(zones, six.string_types): 193 zones = [zones] 194 new_zones = self.connection.enable_availability_zones(self.name, zones) 195 self.availability_zones = new_zones 196 197 def disable_zones(self, zones): 198 """ 199 Disable availability zones from this Access Point. 200 201 :type zones: string or List of strings 202 :param zones: The name of the zone(s) to add. 203 204 """ 205 if isinstance(zones, six.string_types): 206 zones = [zones] 207 new_zones = self.connection.disable_availability_zones( 208 self.name, zones) 209 self.availability_zones = new_zones 210 211 def get_attributes(self, force=False): 212 """ 213 Gets the LbAttributes. The Attributes will be cached. 214 215 :type force: bool 216 :param force: Ignore cache value and reload. 217 218 :rtype: boto.ec2.elb.attributes.LbAttributes 219 :return: The LbAttribues object 220 """ 221 if not self._attributes or force: 222 self._attributes = self.connection.get_all_lb_attributes(self.name) 223 return self._attributes 224 225 def is_cross_zone_load_balancing(self, force=False): 226 """ 227 Identifies if the ELB is current configured to do CrossZone Balancing. 228 229 :type force: bool 230 :param force: Ignore cache value and reload. 231 232 :rtype: bool 233 :return: True if balancing is enabled, False if not. 234 """ 235 return self.get_attributes(force).cross_zone_load_balancing.enabled 236 237 def enable_cross_zone_load_balancing(self): 238 """ 239 Turns on CrossZone Load Balancing for this ELB. 240 241 :rtype: bool 242 :return: True if successful, False if not. 243 """ 244 success = self.connection.modify_lb_attribute( 245 self.name, 'crossZoneLoadBalancing', True) 246 if success and self._attributes: 247 self._attributes.cross_zone_load_balancing.enabled = True 248 return success 249 250 def disable_cross_zone_load_balancing(self): 251 """ 252 Turns off CrossZone Load Balancing for this ELB. 253 254 :rtype: bool 255 :return: True if successful, False if not. 256 """ 257 success = self.connection.modify_lb_attribute( 258 self.name, 'crossZoneLoadBalancing', False) 259 if success and self._attributes: 260 self._attributes.cross_zone_load_balancing.enabled = False 261 return success 262 263 def register_instances(self, instances): 264 """ 265 Adds instances to this load balancer. All instances must be in the same 266 region as the load balancer. Adding endpoints that are already 267 registered with the load balancer has no effect. 268 269 :param list instances: List of instance IDs (strings) that you'd like 270 to add to this load balancer. 271 272 """ 273 if isinstance(instances, six.string_types): 274 instances = [instances] 275 new_instances = self.connection.register_instances(self.name, 276 instances) 277 self.instances = new_instances 278 279 def deregister_instances(self, instances): 280 """ 281 Remove instances from this load balancer. Removing instances that are 282 not registered with the load balancer has no effect. 283 284 :param list instances: List of instance IDs (strings) that you'd like 285 to remove from this load balancer. 286 287 """ 288 if isinstance(instances, six.string_types): 289 instances = [instances] 290 new_instances = self.connection.deregister_instances(self.name, 291 instances) 292 self.instances = new_instances 293 294 def delete(self): 295 """ 296 Delete this load balancer. 297 """ 298 return self.connection.delete_load_balancer(self.name) 299 300 def configure_health_check(self, health_check): 301 """ 302 Configures the health check behavior for the instances behind this 303 load balancer. See :ref:`elb-configuring-a-health-check` for a 304 walkthrough. 305 306 :param boto.ec2.elb.healthcheck.HealthCheck health_check: A 307 HealthCheck instance that tells the load balancer how to check 308 its instances for health. 309 """ 310 return self.connection.configure_health_check(self.name, health_check) 311 312 def get_instance_health(self, instances=None): 313 """ 314 Returns a list of :py:class:`boto.ec2.elb.instancestate.InstanceState` 315 objects, which show the health of the instances attached to this 316 load balancer. 317 318 :rtype: list 319 :returns: A list of 320 :py:class:`InstanceState <boto.ec2.elb.instancestate.InstanceState>` 321 instances, representing the instances 322 attached to this load balancer. 323 """ 324 return self.connection.describe_instance_health(self.name, instances) 325 326 def create_listeners(self, listeners): 327 return self.connection.create_load_balancer_listeners(self.name, 328 listeners) 329 330 def create_listener(self, inPort, outPort=None, proto="tcp"): 331 if outPort is None: 332 outPort = inPort 333 return self.create_listeners([(inPort, outPort, proto)]) 334 335 def delete_listeners(self, listeners): 336 return self.connection.delete_load_balancer_listeners(self.name, 337 listeners) 338 339 def delete_listener(self, inPort): 340 return self.delete_listeners([inPort]) 341 342 def delete_policy(self, policy_name): 343 """ 344 Deletes a policy from the LoadBalancer. The specified policy must not 345 be enabled for any listeners. 346 """ 347 return self.connection.delete_lb_policy(self.name, policy_name) 348 349 def set_policies_of_listener(self, lb_port, policies): 350 return self.connection.set_lb_policies_of_listener(self.name, 351 lb_port, 352 policies) 353 354 def set_policies_of_backend_server(self, instance_port, policies): 355 return self.connection.set_lb_policies_of_backend_server( 356 self.name, instance_port, policies) 357 358 def create_cookie_stickiness_policy(self, cookie_expiration_period, 359 policy_name): 360 return self.connection.create_lb_cookie_stickiness_policy( 361 cookie_expiration_period, self.name, policy_name) 362 363 def create_app_cookie_stickiness_policy(self, name, policy_name): 364 return self.connection.create_app_cookie_stickiness_policy(name, 365 self.name, 366 policy_name) 367 368 def set_listener_SSL_certificate(self, lb_port, ssl_certificate_id): 369 return self.connection.set_lb_listener_SSL_certificate( 370 self.name, lb_port, ssl_certificate_id) 371 372 def create_lb_policy(self, policy_name, policy_type, policy_attribute): 373 return self.connection.create_lb_policy( 374 self.name, policy_name, policy_type, policy_attribute) 375 376 def attach_subnets(self, subnets): 377 """ 378 Attaches load balancer to one or more subnets. 379 Attaching subnets that are already registered with the 380 Load Balancer has no effect. 381 382 :type subnets: string or List of strings 383 :param subnets: The name of the subnet(s) to add. 384 385 """ 386 if isinstance(subnets, six.string_types): 387 subnets = [subnets] 388 new_subnets = self.connection.attach_lb_to_subnets(self.name, subnets) 389 self.subnets = new_subnets 390 391 def detach_subnets(self, subnets): 392 """ 393 Detaches load balancer from one or more subnets. 394 395 :type subnets: string or List of strings 396 :param subnets: The name of the subnet(s) to detach. 397 398 """ 399 if isinstance(subnets, six.string_types): 400 subnets = [subnets] 401 new_subnets = self.connection.detach_lb_from_subnets( 402 self.name, subnets) 403 self.subnets = new_subnets 404 405 def apply_security_groups(self, security_groups): 406 """ 407 Associates one or more security groups with the load balancer. 408 The provided security groups will override any currently applied 409 security groups. 410 411 :type security_groups: string or List of strings 412 :param security_groups: The name of the security group(s) to add. 413 414 """ 415 if isinstance(security_groups, six.string_types): 416 security_groups = [security_groups] 417 new_sgs = self.connection.apply_security_groups_to_lb( 418 self.name, security_groups) 419 self.security_groups = new_sgs 420