1# Copyright (c) 2006-2012 Mitch Garnaat http://garnaat.org/
2# Copyright (c) 2012 Amazon.com, Inc. or its affiliates.
3# All Rights Reserved
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish, dis-
9# tribute, sublicense, and/or sell copies of the Software, and to permit
10# persons to whom the Software is furnished to do so, subject to the fol-
11# lowing conditions:
12#
13# The above copyright notice and this permission notice shall be included
14# in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
18# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22# IN THE SOFTWARE.
23#
24"""
25This module provides an interface to the Elastic Compute Cloud (EC2)
26load balancing service from AWS.
27"""
28from boto.connection import AWSQueryConnection
29from boto.ec2.instanceinfo import InstanceInfo
30from boto.ec2.elb.loadbalancer import LoadBalancer, LoadBalancerZones
31from boto.ec2.elb.instancestate import InstanceState
32from boto.ec2.elb.healthcheck import HealthCheck
33from boto.regioninfo import RegionInfo, get_regions, load_regions
34import boto
35from boto.compat import six
36
37RegionData = load_regions().get('elasticloadbalancing', {})
38
39
40def regions():
41    """
42    Get all available regions for the ELB service.
43
44    :rtype: list
45    :return: A list of :class:`boto.RegionInfo` instances
46    """
47    return get_regions('elasticloadbalancing', connection_cls=ELBConnection)
48
49
50def connect_to_region(region_name, **kw_params):
51    """
52    Given a valid region name, return a
53    :class:`boto.ec2.elb.ELBConnection`.
54
55    :param str region_name: The name of the region to connect to.
56
57    :rtype: :class:`boto.ec2.ELBConnection` or ``None``
58    :return: A connection to the given region, or None if an invalid region
59        name is given
60    """
61    for region in regions():
62        if region.name == region_name:
63            return region.connect(**kw_params)
64    return None
65
66
67class ELBConnection(AWSQueryConnection):
68
69    APIVersion = boto.config.get('Boto', 'elb_version', '2012-06-01')
70    DefaultRegionName = boto.config.get('Boto', 'elb_region_name', 'us-east-1')
71    DefaultRegionEndpoint = boto.config.get(
72        'Boto', 'elb_region_endpoint',
73        'elasticloadbalancing.us-east-1.amazonaws.com')
74
75    def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
76                 is_secure=True, port=None, proxy=None, proxy_port=None,
77                 proxy_user=None, proxy_pass=None, debug=0,
78                 https_connection_factory=None, region=None, path='/',
79                 security_token=None, validate_certs=True, profile_name=None):
80        """
81        Init method to create a new connection to EC2 Load Balancing Service.
82
83        .. note:: The region argument is overridden by the region specified in
84            the boto configuration file.
85        """
86        if not region:
87            region = RegionInfo(self, self.DefaultRegionName,
88                                self.DefaultRegionEndpoint)
89        self.region = region
90        super(ELBConnection, self).__init__(aws_access_key_id,
91                                            aws_secret_access_key,
92                                            is_secure, port, proxy, proxy_port,
93                                            proxy_user, proxy_pass,
94                                            self.region.endpoint, debug,
95                                            https_connection_factory, path,
96                                            security_token,
97                                            validate_certs=validate_certs,
98                                            profile_name=profile_name)
99
100    def _required_auth_capability(self):
101        return ['hmac-v4']
102
103    def build_list_params(self, params, items, label):
104        if isinstance(items, six.string_types):
105            items = [items]
106        for index, item in enumerate(items):
107            params[label % (index + 1)] = item
108
109    def get_all_load_balancers(self, load_balancer_names=None, marker=None):
110        """
111        Retrieve all load balancers associated with your account.
112
113        :type load_balancer_names: list
114        :keyword load_balancer_names: An optional list of load balancer names.
115
116        :type marker: string
117        :param marker: Use this only when paginating results and only
118            in follow-up request after you've received a response
119            where the results are truncated.  Set this to the value of
120            the Marker element in the response you just received.
121
122        :rtype: :py:class:`boto.resultset.ResultSet`
123        :return: A ResultSet containing instances of
124            :class:`boto.ec2.elb.loadbalancer.LoadBalancer`
125        """
126        params = {}
127        if load_balancer_names:
128            self.build_list_params(params, load_balancer_names,
129                                   'LoadBalancerNames.member.%d')
130
131        if marker:
132            params['Marker'] = marker
133
134        return self.get_list('DescribeLoadBalancers', params,
135                             [('member', LoadBalancer)])
136
137    def create_load_balancer(self, name, zones, listeners=None, subnets=None,
138                             security_groups=None, scheme='internet-facing',
139                             complex_listeners=None):
140        """
141        Create a new load balancer for your account. By default the load
142        balancer will be created in EC2. To create a load balancer inside a
143        VPC, parameter zones must be set to None and subnets must not be None.
144        The load balancer will be automatically created under the VPC that
145        contains the subnet(s) specified.
146
147        :type name: string
148        :param name: The mnemonic name associated with the new load balancer
149
150        :type zones: List of strings
151        :param zones: The names of the availability zone(s) to add.
152
153        :type listeners: List of tuples
154        :param listeners: Each tuple contains three or four values,
155            (LoadBalancerPortNumber, InstancePortNumber, Protocol,
156            [SSLCertificateId]) where LoadBalancerPortNumber and
157            InstancePortNumber are integer values between 1 and 65535,
158            Protocol is a string containing either 'TCP', 'SSL', HTTP', or
159            'HTTPS'; SSLCertificateID is the ARN of a AWS IAM
160            certificate, and must be specified when doing HTTPS.
161
162        :type subnets: list of strings
163        :param subnets: A list of subnet IDs in your VPC to attach to
164            your LoadBalancer.
165
166        :type security_groups: list of strings
167        :param security_groups: The security groups assigned to your
168            LoadBalancer within your VPC.
169
170        :type scheme: string
171        :param scheme: The type of a LoadBalancer.  By default, Elastic
172            Load Balancing creates an internet-facing LoadBalancer with
173            a publicly resolvable DNS name, which resolves to public IP
174            addresses.
175
176            Specify the value internal for this option to create an
177            internal LoadBalancer with a DNS name that resolves to
178            private IP addresses.
179
180            This option is only available for LoadBalancers attached
181            to an Amazon VPC.
182
183        :type complex_listeners: List of tuples
184        :param complex_listeners: Each tuple contains four or five values,
185            (LoadBalancerPortNumber, InstancePortNumber, Protocol,
186             InstanceProtocol, SSLCertificateId).
187
188            Where:
189                - LoadBalancerPortNumber and InstancePortNumber are integer
190                  values between 1 and 65535
191                - Protocol and InstanceProtocol is a string containing
192                  either 'TCP',
193                  'SSL', 'HTTP', or 'HTTPS'
194                - SSLCertificateId is the ARN of an SSL certificate loaded into
195                  AWS IAM
196
197        :rtype: :class:`boto.ec2.elb.loadbalancer.LoadBalancer`
198        :return: The newly created
199            :class:`boto.ec2.elb.loadbalancer.LoadBalancer`
200        """
201        if not listeners and not complex_listeners:
202            # Must specify one of the two options
203            return None
204
205        params = {'LoadBalancerName': name,
206                  'Scheme': scheme}
207
208        # Handle legacy listeners
209        if listeners:
210            for index, listener in enumerate(listeners):
211                i = index + 1
212                protocol = listener[2].upper()
213                params['Listeners.member.%d.LoadBalancerPort' % i] = listener[0]
214                params['Listeners.member.%d.InstancePort' % i] = listener[1]
215                params['Listeners.member.%d.Protocol' % i] = listener[2]
216                if protocol == 'HTTPS' or protocol == 'SSL':
217                    params['Listeners.member.%d.SSLCertificateId' % i] = listener[3]
218
219        # Handle the full listeners
220        if complex_listeners:
221            for index, listener in enumerate(complex_listeners):
222                i = index + 1
223                protocol = listener[2].upper()
224                InstanceProtocol = listener[3].upper()
225                params['Listeners.member.%d.LoadBalancerPort' % i] = listener[0]
226                params['Listeners.member.%d.InstancePort' % i] = listener[1]
227                params['Listeners.member.%d.Protocol' % i] = listener[2]
228                params['Listeners.member.%d.InstanceProtocol' % i] = listener[3]
229                if protocol == 'HTTPS' or protocol == 'SSL':
230                    params['Listeners.member.%d.SSLCertificateId' % i] = listener[4]
231
232        if zones:
233            self.build_list_params(params, zones, 'AvailabilityZones.member.%d')
234
235        if subnets:
236            self.build_list_params(params, subnets, 'Subnets.member.%d')
237
238        if security_groups:
239            self.build_list_params(params, security_groups,
240                                   'SecurityGroups.member.%d')
241
242        load_balancer = self.get_object('CreateLoadBalancer',
243                                        params, LoadBalancer)
244        load_balancer.name = name
245        load_balancer.listeners = listeners
246        load_balancer.availability_zones = zones
247        load_balancer.subnets = subnets
248        load_balancer.security_groups = security_groups
249        return load_balancer
250
251    def create_load_balancer_listeners(self, name, listeners=None,
252                                       complex_listeners=None):
253        """
254        Creates a Listener (or group of listeners) for an existing
255        Load Balancer
256
257        :type name: string
258        :param name: The name of the load balancer to create the listeners for
259
260        :type listeners: List of tuples
261        :param listeners: Each tuple contains three or four values,
262            (LoadBalancerPortNumber, InstancePortNumber, Protocol,
263            [SSLCertificateId]) where LoadBalancerPortNumber and
264            InstancePortNumber are integer values between 1 and 65535,
265            Protocol is a string containing either 'TCP', 'SSL', HTTP', or
266            'HTTPS'; SSLCertificateID is the ARN of a AWS IAM
267            certificate, and must be specified when doing HTTPS.
268
269        :type complex_listeners: List of tuples
270        :param complex_listeners: Each tuple contains four or five values,
271            (LoadBalancerPortNumber, InstancePortNumber, Protocol,
272             InstanceProtocol, SSLCertificateId).
273
274            Where:
275                - LoadBalancerPortNumber and InstancePortNumber are integer
276                  values between 1 and 65535
277                - Protocol and InstanceProtocol is a string containing
278                  either 'TCP',
279                  'SSL', 'HTTP', or 'HTTPS'
280                - SSLCertificateId is the ARN of an SSL certificate loaded into
281                  AWS IAM
282
283        :return: The status of the request
284        """
285        if not listeners and not complex_listeners:
286            # Must specify one of the two options
287            return None
288
289        params = {'LoadBalancerName': name}
290
291        # Handle the simple listeners
292        if listeners:
293            for index, listener in enumerate(listeners):
294                i = index + 1
295                protocol = listener[2].upper()
296                params['Listeners.member.%d.LoadBalancerPort' % i] = listener[0]
297                params['Listeners.member.%d.InstancePort' % i] = listener[1]
298                params['Listeners.member.%d.Protocol' % i] = listener[2]
299                if protocol == 'HTTPS' or protocol == 'SSL':
300                    params['Listeners.member.%d.SSLCertificateId' % i] = listener[3]
301
302        # Handle the full listeners
303        if complex_listeners:
304            for index, listener in enumerate(complex_listeners):
305                i = index + 1
306                protocol = listener[2].upper()
307                InstanceProtocol = listener[3].upper()
308                params['Listeners.member.%d.LoadBalancerPort' % i] = listener[0]
309                params['Listeners.member.%d.InstancePort' % i] = listener[1]
310                params['Listeners.member.%d.Protocol' % i] = listener[2]
311                params['Listeners.member.%d.InstanceProtocol' % i] = listener[3]
312                if protocol == 'HTTPS' or protocol == 'SSL':
313                    params['Listeners.member.%d.SSLCertificateId' % i] = listener[4]
314
315        return self.get_status('CreateLoadBalancerListeners', params)
316
317    def delete_load_balancer(self, name):
318        """
319        Delete a Load Balancer from your account.
320
321        :type name: string
322        :param name: The name of the Load Balancer to delete
323        """
324        params = {'LoadBalancerName': name}
325        return self.get_status('DeleteLoadBalancer', params)
326
327    def delete_load_balancer_listeners(self, name, ports):
328        """
329        Deletes a load balancer listener (or group of listeners)
330
331        :type name: string
332        :param name: The name of the load balancer to create the listeners for
333
334        :type ports: List int
335        :param ports: Each int represents the port on the ELB to be removed
336
337        :return: The status of the request
338        """
339        params = {'LoadBalancerName': name}
340        for index, port in enumerate(ports):
341            params['LoadBalancerPorts.member.%d' % (index + 1)] = port
342        return self.get_status('DeleteLoadBalancerListeners', params)
343
344    def enable_availability_zones(self, load_balancer_name, zones_to_add):
345        """
346        Add availability zones to an existing Load Balancer
347        All zones must be in the same region as the Load Balancer
348        Adding zones that are already registered with the Load Balancer
349        has no effect.
350
351        :type load_balancer_name: string
352        :param load_balancer_name: The name of the Load Balancer
353
354        :type zones: List of strings
355        :param zones: The name of the zone(s) to add.
356
357        :rtype: List of strings
358        :return: An updated list of zones for this Load Balancer.
359
360        """
361        params = {'LoadBalancerName': load_balancer_name}
362        self.build_list_params(params, zones_to_add,
363                               'AvailabilityZones.member.%d')
364        obj = self.get_object('EnableAvailabilityZonesForLoadBalancer',
365                              params, LoadBalancerZones)
366        return obj.zones
367
368    def disable_availability_zones(self, load_balancer_name, zones_to_remove):
369        """
370        Remove availability zones from an existing Load Balancer.
371        All zones must be in the same region as the Load Balancer.
372        Removing zones that are not registered with the Load Balancer
373        has no effect.
374        You cannot remove all zones from an Load Balancer.
375
376        :type load_balancer_name: string
377        :param load_balancer_name: The name of the Load Balancer
378
379        :type zones: List of strings
380        :param zones: The name of the zone(s) to remove.
381
382        :rtype: List of strings
383        :return: An updated list of zones for this Load Balancer.
384
385        """
386        params = {'LoadBalancerName': load_balancer_name}
387        self.build_list_params(params, zones_to_remove,
388                               'AvailabilityZones.member.%d')
389        obj = self.get_object('DisableAvailabilityZonesForLoadBalancer',
390                              params, LoadBalancerZones)
391        return obj.zones
392
393    def modify_lb_attribute(self, load_balancer_name, attribute, value):
394        """Changes an attribute of a Load Balancer
395
396        :type load_balancer_name: string
397        :param load_balancer_name: The name of the Load Balancer
398
399        :type attribute: string
400        :param attribute: The attribute you wish to change.
401
402        * crossZoneLoadBalancing - Boolean (true)
403        * connectingSettings - :py:class:`ConnectionSettingAttribute` instance
404        * accessLog - :py:class:`AccessLogAttribute` instance
405        * connectionDraining - :py:class:`ConnectionDrainingAttribute` instance
406
407        :type value: string
408        :param value: The new value for the attribute
409
410        :rtype: bool
411        :return: Whether the operation succeeded or not
412        """
413
414        bool_reqs = ('crosszoneloadbalancing',)
415        if attribute.lower() in bool_reqs:
416            if isinstance(value, bool):
417                if value:
418                    value = 'true'
419                else:
420                    value = 'false'
421
422        params = {'LoadBalancerName': load_balancer_name}
423        if attribute.lower() == 'crosszoneloadbalancing':
424            params['LoadBalancerAttributes.CrossZoneLoadBalancing.Enabled'
425                   ] = value
426        elif attribute.lower() == 'accesslog':
427            params['LoadBalancerAttributes.AccessLog.Enabled'] = \
428                value.enabled and 'true' or 'false'
429            params['LoadBalancerAttributes.AccessLog.S3BucketName'] = \
430                value.s3_bucket_name
431            params['LoadBalancerAttributes.AccessLog.S3BucketPrefix'] = \
432                value.s3_bucket_prefix
433            params['LoadBalancerAttributes.AccessLog.EmitInterval'] = \
434                value.emit_interval
435        elif attribute.lower() == 'connectiondraining':
436            params['LoadBalancerAttributes.ConnectionDraining.Enabled'] = \
437                value.enabled and 'true' or 'false'
438            params['LoadBalancerAttributes.ConnectionDraining.Timeout'] = \
439                value.timeout
440        elif attribute.lower() == 'connectingsettings':
441            params['LoadBalancerAttributes.ConnectionSettings.IdleTimeout'] = \
442                value.idle_timeout
443        else:
444            raise ValueError('InvalidAttribute', attribute)
445        return self.get_status('ModifyLoadBalancerAttributes', params,
446                               verb='GET')
447
448    def get_all_lb_attributes(self, load_balancer_name):
449        """Gets all Attributes of a Load Balancer
450
451        :type load_balancer_name: string
452        :param load_balancer_name: The name of the Load Balancer
453
454        :rtype: boto.ec2.elb.attribute.LbAttributes
455        :return: The attribute object of the ELB.
456        """
457        from boto.ec2.elb.attributes import LbAttributes
458        params = {'LoadBalancerName': load_balancer_name}
459        return self.get_object('DescribeLoadBalancerAttributes',
460                               params, LbAttributes)
461
462    def get_lb_attribute(self, load_balancer_name, attribute):
463        """Gets an attribute of a Load Balancer
464
465        This will make an EC2 call for each method call.
466
467        :type load_balancer_name: string
468        :param load_balancer_name: The name of the Load Balancer
469
470        :type attribute: string
471        :param attribute: The attribute you wish to see.
472
473          * accessLog - :py:class:`AccessLogAttribute` instance
474          * crossZoneLoadBalancing - Boolean
475          * connectingSettings - :py:class:`ConnectionSettingAttribute` instance
476          * connectionDraining - :py:class:`ConnectionDrainingAttribute`
477            instance
478
479        :rtype: Attribute dependent
480        :return: The new value for the attribute
481        """
482        attributes = self.get_all_lb_attributes(load_balancer_name)
483        if attribute.lower() == 'accesslog':
484            return attributes.access_log
485        if attribute.lower() == 'crosszoneloadbalancing':
486            return attributes.cross_zone_load_balancing.enabled
487        if attribute.lower() == 'connectiondraining':
488            return attributes.connection_draining
489        if attribute.lower() == 'connectingsettings':
490            return attributes.connecting_settings
491        return None
492
493    def register_instances(self, load_balancer_name, instances):
494        """
495        Add new Instances to an existing Load Balancer.
496
497        :type load_balancer_name: string
498        :param load_balancer_name: The name of the Load Balancer
499
500        :type instances: List of strings
501        :param instances: The instance ID's of the EC2 instances to add.
502
503        :rtype: List of strings
504        :return: An updated list of instances for this Load Balancer.
505
506        """
507        params = {'LoadBalancerName': load_balancer_name}
508        self.build_list_params(params, instances,
509                               'Instances.member.%d.InstanceId')
510        return self.get_list('RegisterInstancesWithLoadBalancer',
511                             params, [('member', InstanceInfo)])
512
513    def deregister_instances(self, load_balancer_name, instances):
514        """
515        Remove Instances from an existing Load Balancer.
516
517        :type load_balancer_name: string
518        :param load_balancer_name: The name of the Load Balancer
519
520        :type instances: List of strings
521        :param instances: The instance ID's of the EC2 instances to remove.
522
523        :rtype: List of strings
524        :return: An updated list of instances for this Load Balancer.
525
526        """
527        params = {'LoadBalancerName': load_balancer_name}
528        self.build_list_params(params, instances,
529                               'Instances.member.%d.InstanceId')
530        return self.get_list('DeregisterInstancesFromLoadBalancer',
531                             params, [('member', InstanceInfo)])
532
533    def describe_instance_health(self, load_balancer_name, instances=None):
534        """
535        Get current state of all Instances registered to an Load Balancer.
536
537        :type load_balancer_name: string
538        :param load_balancer_name: The name of the Load Balancer
539
540        :type instances: List of strings
541        :param instances: The instance ID's of the EC2 instances
542                          to return status for.  If not provided,
543                          the state of all instances will be returned.
544
545        :rtype: List of :class:`boto.ec2.elb.instancestate.InstanceState`
546        :return: list of state info for instances in this Load Balancer.
547
548        """
549        params = {'LoadBalancerName': load_balancer_name}
550        if instances:
551            self.build_list_params(params, instances,
552                                   'Instances.member.%d.InstanceId')
553        return self.get_list('DescribeInstanceHealth', params,
554                             [('member', InstanceState)])
555
556    def configure_health_check(self, name, health_check):
557        """
558        Define a health check for the EndPoints.
559
560        :type name: string
561        :param name: The mnemonic name associated with the load balancer
562
563        :type health_check: :class:`boto.ec2.elb.healthcheck.HealthCheck`
564        :param health_check: A HealthCheck object populated with the desired
565                             values.
566
567        :rtype: :class:`boto.ec2.elb.healthcheck.HealthCheck`
568        :return: The updated :class:`boto.ec2.elb.healthcheck.HealthCheck`
569        """
570        params = {'LoadBalancerName': name,
571                  'HealthCheck.Timeout': health_check.timeout,
572                  'HealthCheck.Target': health_check.target,
573                  'HealthCheck.Interval': health_check.interval,
574                  'HealthCheck.UnhealthyThreshold': health_check.unhealthy_threshold,
575                  'HealthCheck.HealthyThreshold': health_check.healthy_threshold}
576        return self.get_object('ConfigureHealthCheck', params, HealthCheck)
577
578    def set_lb_listener_SSL_certificate(self, lb_name, lb_port,
579                                        ssl_certificate_id):
580        """
581        Sets the certificate that terminates the specified listener's SSL
582        connections. The specified certificate replaces any prior certificate
583        that was used on the same LoadBalancer and port.
584        """
585        params = {'LoadBalancerName': lb_name,
586                  'LoadBalancerPort': lb_port,
587                  'SSLCertificateId': ssl_certificate_id}
588        return self.get_status('SetLoadBalancerListenerSSLCertificate', params)
589
590    def create_app_cookie_stickiness_policy(self, name, lb_name, policy_name):
591        """
592        Generates a stickiness policy with sticky session lifetimes that follow
593        that of an application-generated cookie. This policy can only be
594        associated with HTTP listeners.
595
596        This policy is similar to the policy created by
597        CreateLBCookieStickinessPolicy, except that the lifetime of the special
598        Elastic Load Balancing cookie follows the lifetime of the
599        application-generated cookie specified in the policy configuration. The
600        load balancer only inserts a new stickiness cookie when the application
601        response includes a new application cookie.
602
603        If the application cookie is explicitly removed or expires, the session
604        stops being sticky until a new application cookie is issued.
605        """
606        params = {'CookieName': name,
607                  'LoadBalancerName': lb_name,
608                  'PolicyName': policy_name}
609        return self.get_status('CreateAppCookieStickinessPolicy', params)
610
611    def create_lb_cookie_stickiness_policy(self, cookie_expiration_period,
612                                           lb_name, policy_name):
613        """
614        Generates a stickiness policy with sticky session lifetimes controlled
615        by the lifetime of the browser (user-agent) or a specified expiration
616        period. This policy can only be associated only with HTTP listeners.
617
618        When a load balancer implements this policy, the load balancer uses a
619        special cookie to track the backend server instance for each request.
620        When the load balancer receives a request, it first checks to see if
621        this cookie is present in the request. If so, the load balancer sends
622        the request to the application server specified in the cookie. If not,
623        the load balancer sends the request to a server that is chosen based on
624        the existing load balancing algorithm.
625
626        A cookie is inserted into the response for binding subsequent requests
627        from the same user to that server. The validity of the cookie is based
628        on the cookie expiration time, which is specified in the policy
629        configuration.
630
631        None may be passed for cookie_expiration_period.
632        """
633        params = {'LoadBalancerName': lb_name,
634                  'PolicyName': policy_name}
635        if cookie_expiration_period is not None:
636            params['CookieExpirationPeriod'] = cookie_expiration_period
637        return self.get_status('CreateLBCookieStickinessPolicy', params)
638
639    def create_lb_policy(self, lb_name, policy_name, policy_type,
640                         policy_attributes):
641        """
642        Creates a new policy that contains the necessary attributes
643        depending on the policy type. Policies are settings that are
644        saved for your load balancer and that can be applied to the
645        front-end listener, or the back-end application server.
646
647        """
648        params = {'LoadBalancerName': lb_name,
649                  'PolicyName': policy_name,
650                  'PolicyTypeName': policy_type}
651        for index, (name, value) in enumerate(six.iteritems(policy_attributes), 1):
652            params['PolicyAttributes.member.%d.AttributeName' % index] = name
653            params['PolicyAttributes.member.%d.AttributeValue' % index] = value
654        else:
655            params['PolicyAttributes'] = ''
656        return self.get_status('CreateLoadBalancerPolicy', params)
657
658    def delete_lb_policy(self, lb_name, policy_name):
659        """
660        Deletes a policy from the LoadBalancer. The specified policy must not
661        be enabled for any listeners.
662        """
663        params = {'LoadBalancerName': lb_name,
664                  'PolicyName': policy_name}
665        return self.get_status('DeleteLoadBalancerPolicy', params)
666
667    def set_lb_policies_of_listener(self, lb_name, lb_port, policies):
668        """
669        Associates, updates, or disables a policy with a listener on the load
670        balancer. Currently only zero (0) or one (1) policy can be associated
671        with a listener.
672        """
673        params = {'LoadBalancerName': lb_name,
674                  'LoadBalancerPort': lb_port}
675        if len(policies):
676            self.build_list_params(params, policies, 'PolicyNames.member.%d')
677        else:
678            params['PolicyNames'] = ''
679        return self.get_status('SetLoadBalancerPoliciesOfListener', params)
680
681    def set_lb_policies_of_backend_server(self, lb_name, instance_port,
682                                          policies):
683        """
684        Replaces the current set of policies associated with a port on which
685        the back-end server is listening with a new set of policies.
686        """
687        params = {'LoadBalancerName': lb_name,
688                  'InstancePort': instance_port}
689        if policies:
690            self.build_list_params(params, policies, 'PolicyNames.member.%d')
691        else:
692            params['PolicyNames'] = ''
693        return self.get_status('SetLoadBalancerPoliciesForBackendServer',
694                               params)
695
696    def apply_security_groups_to_lb(self, name, security_groups):
697        """
698        Associates one or more security groups with the load balancer.
699        The provided security groups will override any currently applied
700        security groups.
701
702        :type name: string
703        :param name: The name of the Load Balancer
704
705        :type security_groups: List of strings
706        :param security_groups: The name of the security group(s) to add.
707
708        :rtype: List of strings
709        :return: An updated list of security groups for this Load Balancer.
710
711        """
712        params = {'LoadBalancerName': name}
713        self.build_list_params(params, security_groups,
714                               'SecurityGroups.member.%d')
715        return self.get_list('ApplySecurityGroupsToLoadBalancer',
716                             params, None)
717
718    def attach_lb_to_subnets(self, name, subnets):
719        """
720        Attaches load balancer to one or more subnets.
721        Attaching subnets that are already registered with the
722        Load Balancer has no effect.
723
724        :type name: string
725        :param name: The name of the Load Balancer
726
727        :type subnets: List of strings
728        :param subnets: The name of the subnet(s) to add.
729
730        :rtype: List of strings
731        :return: An updated list of subnets for this Load Balancer.
732
733        """
734        params = {'LoadBalancerName': name}
735        self.build_list_params(params, subnets,
736                               'Subnets.member.%d')
737        return self.get_list('AttachLoadBalancerToSubnets',
738                             params, None)
739
740    def detach_lb_from_subnets(self, name, subnets):
741        """
742        Detaches load balancer from one or more subnets.
743
744        :type name: string
745        :param name: The name of the Load Balancer
746
747        :type subnets: List of strings
748        :param subnets: The name of the subnet(s) to detach.
749
750        :rtype: List of strings
751        :return: An updated list of subnets for this Load Balancer.
752
753        """
754        params = {'LoadBalancerName': name}
755        self.build_list_params(params, subnets,
756                               'Subnets.member.%d')
757        return self.get_list('DetachLoadBalancerFromSubnets',
758                             params, None)
759