libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r1429563 - in /libcloud/trunk: ./ libcloud/loadbalancer/ libcloud/loadbalancer/drivers/ libcloud/test/ libcloud/test/loadbalancer/ libcloud/test/loadbalancer/fixtures/elb/
Date Sun, 06 Jan 2013 18:05:23 GMT
Author: tomaz
Date: Sun Jan  6 18:05:23 2013
New Revision: 1429563

URL: http://svn.apache.org/viewvc?rev=1429563&view=rev
Log:
Add a new driver for AWS Elastic Load Balancing service.

Contributed by John Carr, part of LIBCLOUD-169.

Added:
    libcloud/trunk/libcloud/loadbalancer/drivers/elb.py
    libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/
    libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/create_load_balancer.xml
    libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/deregister_instances_from_load_balancer.xml
    libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/describe_load_balancers.xml
    libcloud/trunk/libcloud/test/loadbalancer/test_elb.py
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/loadbalancer/providers.py
    libcloud/trunk/libcloud/loadbalancer/types.py
    libcloud/trunk/libcloud/test/secrets.py-dist

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1429563&r1=1429562&r2=1429563&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Sun Jan  6 18:05:23 2013
@@ -136,6 +136,11 @@ Changes with Apache Libcloud in developm
     - Finish Amazon Route53 driver. (LIBCLOUD-132)
       [John Carr]
 
+  *) Load-Balancer
+
+    - Add new driver for AWS Elastic Load Balancing service. (LIBCLOUD-169)
+      [John Carr]
+
 Changes with Apache Libcloud 0.11.4:
 
   *) General

Added: libcloud/trunk/libcloud/loadbalancer/drivers/elb.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/loadbalancer/drivers/elb.py?rev=1429563&view=auto
==============================================================================
--- libcloud/trunk/libcloud/loadbalancer/drivers/elb.py (added)
+++ libcloud/trunk/libcloud/loadbalancer/drivers/elb.py Sun Jan  6 18:05:23 2013
@@ -0,0 +1,220 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__all__ = [
+    'ElasticLBDriver'
+]
+
+import base64
+import hmac
+import time
+
+from hashlib import sha256
+
+from libcloud.utils.py3 import httplib, urlquote, b
+from libcloud.utils.xml import findtext, findall
+from libcloud.loadbalancer.types import State
+from libcloud.loadbalancer.base import Driver, LoadBalancer, Member
+from libcloud.common.types import InvalidCredsError
+from libcloud.common.aws import AWSBaseResponse
+from libcloud.common.base import ConnectionUserAndKey
+
+
+VERSION = '2012-06-01'
+HOST = 'elasticloadbalancing.%s.amazonaws.com'
+ROOT = '/%s/' % (VERSION)
+NS = 'http://elasticloadbalancing.amazonaws.com/doc/%s/' % (VERSION, )
+
+
+class ELBResponse(AWSBaseResponse):
+    """
+    Amazon ELB response class.
+    """
+    def success(self):
+        return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
+
+    def parse_error(self):
+        status = int(self.status)
+
+        if status == httplib.FORBIDDEN:
+            if not self.body:
+                raise InvalidCredsError(str(self.status) + ': ' + self.error)
+            else:
+                raise InvalidCredsError(self.body)
+
+
+class ELBConnection(ConnectionUserAndKey):
+    host = HOST
+    responseCls = ELBResponse
+
+    def add_default_params(self, params):
+        params['SignatureVersion'] = '2'
+        params['SignatureMethod'] = 'HmacSHA256'
+        params['AWSAccessKeyId'] = self.user_id
+        params['Version'] = VERSION
+        params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
+                                            time.gmtime())
+        params['Signature'] = self._get_aws_auth_param(params, self.key,
+                                                       self.action)
+        return params
+
+    def _get_aws_auth_param(self, params, secret_key, path='/'):
+        """
+        Creates the signature required for AWS, per
+        http://bit.ly/aR7GaQ [docs.amazonwebservices.com]:
+
+        StringToSign = HTTPVerb + "\n" +
+                       ValueOfHostHeaderInLowercase + "\n" +
+                       HTTPRequestURI + "\n" +
+                       CanonicalizedQueryString <from the preceding step>
+        """
+        keys = list(params.keys())
+        keys.sort()
+        pairs = []
+        for key in keys:
+            pairs.append(urlquote(key, safe='') + '=' +
+                         urlquote(params[key], safe='-_~'))
+
+        qs = '&'.join(pairs)
+
+        hostname = self.host
+        if (self.secure and self.port != 443) or \
+           (not self.secure and self.port != 80):
+            hostname += ":" + str(self.port)
+
+        string_to_sign = '\n'.join(('GET', hostname, path, qs))
+
+        b64_hmac = base64.b64encode(
+            hmac.new(b(secret_key), b(string_to_sign),
+                     digestmod=sha256).digest()
+        )
+        return b64_hmac.decode('utf-8')
+
+
+class ElasticLBDriver(Driver):
+    name = 'ELB'
+    website = 'http://aws.amazon.com/elasticloadbalancing/'
+    connectionCls = ELBConnection
+
+    def __init__(self, access_id, secret, region):
+        super(ElasticLBDriver, self).__init__(access_id, secret)
+        self.region = region
+        self.connection.host = HOST % (region)
+
+    def list_protocols(self):
+        return ['tcp', 'ssl', 'http', 'https']
+
+    def list_balancers(self):
+        params = {'Action': 'DescribeLoadBalancers'}
+        data = self.connection.request(ROOT, params=params).object
+        return self._to_balancers(data)
+
+    def create_balancer(self, name, port, protocol, algorithm, members,
+                        ex_members_availability_zones=None):
+        if ex_members_availability_zones is None:
+            ex_members_availability_zones = ['a']
+
+        params = {
+            'Action': 'CreateLoadBalancer',
+            'LoadBalancerName': name,
+            'Listeners.member.1.InstancePort': str(port),
+            'Listeners.member.1.InstanceProtocol': protocol.upper(),
+            'Listeners.member.1.LoadBalancerPort': str(port),
+            'Listeners.member.1.Protocol': protocol.upper(),
+        }
+
+        for i, z in enumerate(ex_members_availability_zones, 1):
+            zone = '-'.join((self.region, z))
+            params['AvailabilityZones.member.%d' % i] = zone
+
+        data = self.connection.request(ROOT, params=params).object
+
+        balancer = LoadBalancer(
+            id=name,
+            name=name,
+            state=State.PENDING,
+            ip=findtext(element=data, xpath='DNSName', namespace=NS),
+            port=port,
+            driver=self.connection.driver
+        )
+        balancer._members = []
+        return balancer
+
+    def destroy_balancer(self, balancer):
+        params = {
+            'Action': 'DeleteLoadBalancer',
+            'LoadBalancerName': balancer.id
+        }
+        self.connection.request(ROOT, params=params)
+        return True
+
+    def get_balancer(self, balancer_id):
+        params = {
+            'Action': 'DescribeLoadBalancers',
+            'LoadBalancerNames.member.1': balancer_id
+        }
+        data = self.connection.request(ROOT, params=params).object
+        return self._to_balancers(data)[0]
+
+    def balancer_attach_compute_node(self, balancer, node):
+        params = {
+            'Action': 'RegisterInstancesWithLoadBalancer',
+            'LoadBalancerName': balancer.id,
+            'Instances.member.1.InstanceId': node.id
+        }
+        self.connection.request(ROOT, params=params)
+        balancer._members.append(Member(node.id, None, None, balancer=self))
+
+    def balancer_detach_member(self, balancer, member):
+        params = {
+            'Action': 'DeregisterInstancesFromLoadBalancer',
+            'LoadBalancerName': balancer.id,
+            'Instances.member.1.InstanceId': member.id
+        }
+        self.connection.request(ROOT, params=params)
+        balancer._members = [m for m in balancer._members if m.id != member.id]
+        return True
+
+    def balancer_list_members(self, balancer):
+        return balancer._members
+
+    def _to_balancers(self, data):
+        xpath = 'DescribeLoadBalancersResult/LoadBalancerDescriptions/member'
+        return [self._to_balancer(el)
+                for el in findall(element=data, xpath=xpath, namespace=NS)]
+
+    def _to_balancer(self, el):
+        name = findtext(element=el, xpath='LoadBalancerName', namespace=NS)
+        dns_name = findtext(el, xpath='DNSName', namespace=NS)
+        port = findtext(el, xpath='LoadBalancerPort', namespace=NS)
+
+        balancer = LoadBalancer(
+            id=name,
+            name=name,
+            state=State.UNKNOWN,
+            ip=dns_name,
+            port=port,
+            driver=self.connection.driver
+        )
+
+        xpath = 'Instances/member/InstanceId'
+        members = findall(element=el, xpath=xpath, namespace=NS)
+        balancer._members = []
+
+        for m in members:
+            balancer._members.append(Member(m.text, None, None,
+                                            balancer=balancer))
+
+        return balancer

Modified: libcloud/trunk/libcloud/loadbalancer/providers.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/loadbalancer/providers.py?rev=1429563&r1=1429562&r2=1429563&view=diff
==============================================================================
--- libcloud/trunk/libcloud/loadbalancer/providers.py (original)
+++ libcloud/trunk/libcloud/loadbalancer/providers.py Sun Jan  6 18:05:23 2013
@@ -33,7 +33,9 @@ DRIVERS = {
         Provider.NINEFOLD:
             ('libcloud.loadbalancer.drivers.ninefold', 'NinefoldLBDriver'),
         Provider.BRIGHTBOX:
-            ('libcloud.loadbalancer.drivers.brightbox', 'BrightboxLBDriver')
+            ('libcloud.loadbalancer.drivers.brightbox', 'BrightboxLBDriver'),
+        Provider.ELB:
+            ('libcloud.loadbalancer.drivers.elb', 'ElasticLBDriver')
 }
 
 

Modified: libcloud/trunk/libcloud/loadbalancer/types.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/loadbalancer/types.py?rev=1429563&r1=1429562&r2=1429563&view=diff
==============================================================================
--- libcloud/trunk/libcloud/loadbalancer/types.py (original)
+++ libcloud/trunk/libcloud/loadbalancer/types.py Sun Jan  6 18:05:23 2013
@@ -37,6 +37,7 @@ class Provider(object):
     NINEFOLD = 'ninefold'
     RACKSPACE_UK = 'rackspace_uk'
     BRIGHTBOX = 'brightbox'
+    ELB = 'elb'
 
 
 class State(object):

Added: libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/create_load_balancer.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/create_load_balancer.xml?rev=1429563&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/create_load_balancer.xml (added)
+++ libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/create_load_balancer.xml Sun Jan
 6 18:05:23 2013
@@ -0,0 +1,3 @@
+<CreateLoadBalancerResult xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+	<DNSName>tests.example.com</DNSName>
+</CreateLoadBalancerResult>

Added: libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/deregister_instances_from_load_balancer.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/deregister_instances_from_load_balancer.xml?rev=1429563&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/deregister_instances_from_load_balancer.xml
(added)
+++ libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/deregister_instances_from_load_balancer.xml
Sun Jan  6 18:05:23 2013
@@ -0,0 +1,4 @@
+<DeregisterInstancesFromLoadBalancer xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+	<Instances>
+	</Instances>
+</DeregisterInstancesFromLoadBalancer>
\ No newline at end of file

Added: libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/describe_load_balancers.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/describe_load_balancers.xml?rev=1429563&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/describe_load_balancers.xml (added)
+++ libcloud/trunk/libcloud/test/loadbalancer/fixtures/elb/describe_load_balancers.xml Sun
Jan  6 18:05:23 2013
@@ -0,0 +1,61 @@
+<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+  <DescribeLoadBalancersResult>
+    <LoadBalancerDescriptions>
+      <member>
+        <SecurityGroups>
+        </SecurityGroups>
+        <LoadBalancerName>tests</LoadBalancerName>
+        <CreatedTime>2013-01-01T00:00:00.19000Z</CreatedTime>
+        <HealthCheck>
+          <Interval>30</Interval>
+          <Target>TCP:22</Target>
+          <HealthyThreshold>10</HealthyThreshold>
+          <Timeout>5</Timeout>
+          <UnhealthyThreshold>2</UnhealthyThreshold>
+        </HealthCheck>
+        <VPCId>vpc-56e10e3d</VPCId>
+        <ListenerDescriptions>
+          <member>
+            <PolicyNames>
+              <member>AWSConsolePolicy-1</member>
+            </PolicyNames>
+            <Listener>
+              <Protocol>HTTP</Protocol>
+              <LoadBalancerPort>80</LoadBalancerPort>
+              <InstanceProtocol>HTTP</InstanceProtocol>
+              <InstancePort>80</InstancePort>
+            </Listener>
+          </member>
+        </ListenerDescriptions>
+        <Instances>
+          <member>
+            <InstanceId>i-64bd081c</InstanceId>
+          </member>
+        </Instances>
+        <Policies>
+          <AppCookieStickinessPolicies/>
+          <OtherPolicies/>
+          <LBCookieStickinessPolicies>
+            <member>
+              <PolicyName>AWSConsolePolicy-1</PolicyName>
+              <CookieExpirationPeriod>30</CookieExpirationPeriod>
+            </member>
+          </LBCookieStickinessPolicies>
+        </Policies>
+        <AvailabilityZones>
+          <member>us-east-1e</member>
+        </AvailabilityZones>
+        <CanonicalHostedZoneName>tests.us-east-1.elb.amazonaws.com</CanonicalHostedZoneName>
+        <CanonicalHostedZoneNameID>Z3ZONEID</CanonicalHostedZoneNameID>
+        <Scheme>internet-facing</Scheme>
+        <DNSName>tests.us-east-1.elb.amazonaws.com</DNSName>
+        <BackendServerDescriptions/>
+        <Subnets>
+        </Subnets>
+      </member>
+    </LoadBalancerDescriptions>
+  </DescribeLoadBalancersResult>
+  <ResponseMetadata>
+    <RequestId>f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c</RequestId>
+  </ResponseMetadata>
+</DescribeLoadBalancersResponse>

Added: libcloud/trunk/libcloud/test/loadbalancer/test_elb.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/loadbalancer/test_elb.py?rev=1429563&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/loadbalancer/test_elb.py (added)
+++ libcloud/trunk/libcloud/test/loadbalancer/test_elb.py Sun Jan  6 18:05:23 2013
@@ -0,0 +1,112 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+import unittest
+
+from libcloud.utils.py3 import httplib
+from libcloud.loadbalancer.base import Member, Algorithm
+from libcloud.loadbalancer.drivers.elb import ElasticLBDriver
+from libcloud.loadbalancer.types import State
+
+from libcloud.test import MockHttpTestCase
+from libcloud.test.secrets import LB_ELB_PARAMS
+from libcloud.test.file_fixtures import LoadBalancerFileFixtures
+
+
+class ElasticLBTests(unittest.TestCase):
+    def setUp(self):
+        ElasticLBMockHttp.test = self
+        ElasticLBDriver.connectionCls.conn_classes = (None,
+                                                      ElasticLBMockHttp)
+        ElasticLBMockHttp.type = None
+        ElasticLBMockHttp.use_param = 'Action'
+
+        self.driver = ElasticLBDriver(*LB_ELB_PARAMS)
+
+    def test_list_protocols(self):
+        protocols = self.driver.list_protocols()
+
+        self.assertEqual(len(protocols), 4)
+        self.assertTrue('tcp' in protocols)
+        self.assertTrue('http' in protocols)
+
+    def test_list_balancers(self):
+        balancers = self.driver.list_balancers()
+
+        self.assertEquals(len(balancers), 1)
+        self.assertEquals(balancers[0].id, 'tests')
+        self.assertEquals(balancers[0].name, 'tests')
+
+    def test_get_balancer(self):
+        balancer = self.driver.get_balancer(balancer_id='tests')
+
+        self.assertEquals(balancer.id, 'tests')
+        self.assertEquals(balancer.name, 'tests')
+        self.assertEquals(balancer.state, State.UNKNOWN)
+
+    def test_destroy_balancer(self):
+        balancer = self.driver.get_balancer(balancer_id='tests')
+
+        self.assertTrue(self.driver.destroy_balancer(balancer))
+
+    def test_create_balancer(self):
+        members = [Member('srv-lv426', None, None)]
+
+        balancer = self.driver.create_balancer(name='lb2', port=80,
+            protocol='http', algorithm=Algorithm.ROUND_ROBIN,
+            members=members)
+
+        self.assertEquals(balancer.name, 'lb2')
+        self.assertEquals(balancer.port, 80)
+        self.assertEquals(balancer.state, State.PENDING)
+
+    def test_balancer_list_members(self):
+        balancer = self.driver.get_balancer(balancer_id='tests')
+        members = balancer.list_members()
+
+        self.assertEquals(len(members), 1)
+        self.assertEquals(members[0].balancer, balancer)
+        self.assertEquals('i-64bd081c', members[0].id)
+
+    def test_balancer_detach_member(self):
+        balancer = self.driver.get_balancer(balancer_id='lba-1235f')
+        member = Member('i-64bd081c', None, None)
+
+        self.assertTrue(balancer.detach_member(member))
+
+
+class ElasticLBMockHttp(MockHttpTestCase):
+    fixtures = LoadBalancerFileFixtures('elb')
+
+    def _2012_06_01_DescribeLoadBalancers(self, method, url, body, headers):
+        body = self.fixtures.load('describe_load_balancers.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _2012_06_01_CreateLoadBalancer(self, method, url, body, headers):
+        body = self.fixtures.load('create_load_balancer.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _2012_06_01_DeregisterInstancesFromLoadBalancer(self, method, url, body, headers):
+        body = self.fixtures.load('deregister_instances_from_load_balancer.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _2012_06_01_DeleteLoadBalancer(self, method, url, body, headers):
+        body = ''
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())

Modified: libcloud/trunk/libcloud/test/secrets.py-dist
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/secrets.py-dist?rev=1429563&r1=1429562&r2=1429563&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/secrets.py-dist (original)
+++ libcloud/trunk/libcloud/test/secrets.py-dist Sun Jan  6 18:05:23 2013
@@ -46,6 +46,7 @@ STORAGE_GOOGLE_STORAGE_PARAMS = ('key', 
 
 # Loadbalancer
 LB_BRIGHTBOX_PARAMS = ('user', 'key')
+LB_ELB_PARAMS = ('access_id', 'secret', 'region')
 
 # DNS
 DNS_PARAMS_LINODE = ('user', 'key')



Mime
View raw message