You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by je...@apache.org on 2011/01/03 06:14:25 UTC

svn commit: r1054527 - in /incubator/libcloud/trunk: CHANGES libcloud/drivers/ec2.py test/test_ec2.py

Author: jerry
Date: Mon Jan  3 05:14:24 2011
New Revision: 1054527

URL: http://svn.apache.org/viewvc?rev=1054527&view=rev
Log:
Add support for availability zones to EC2 driver,
via ex_list_availability_zones; LIBCLOUD-67

Submitted By: Tomaž Muraus <ka...@k5-storitve.net>

Modified:
    incubator/libcloud/trunk/CHANGES
    incubator/libcloud/trunk/libcloud/drivers/ec2.py
    incubator/libcloud/trunk/test/test_ec2.py

Modified: incubator/libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/CHANGES?rev=1054527&r1=1054526&r2=1054527&view=diff
==============================================================================
--- incubator/libcloud/trunk/CHANGES (original)
+++ incubator/libcloud/trunk/CHANGES Mon Jan  3 05:14:24 2011
@@ -2,6 +2,10 @@
 
 Changes with Apache Libcloud 0.4.1 [In Development]
 
+    *) EC2 Driver availability zones, via ex_list_availability_zones;
+       list_locations rewrite to include availablity zones
+       [Tomaž Muraus]
+
     *) EC2 Driver Idempotency capability in create_node; LIBCLOUD-69
        [David LaBissoniere]
 

Modified: incubator/libcloud/trunk/libcloud/drivers/ec2.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/ec2.py?rev=1054527&r1=1054526&r2=1054527&view=diff
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/ec2.py (original)
+++ incubator/libcloud/trunk/libcloud/drivers/ec2.py Mon Jan  3 05:14:24 2011
@@ -149,6 +149,18 @@ EC2_EU_WEST_INSTANCE_TYPES['m2.4xlarge']
 # prices are the same
 EC2_AP_SOUTHEAST_INSTANCE_TYPES = dict(EC2_EU_WEST_INSTANCE_TYPES)
 
+
+class EC2NodeLocation(NodeLocation):
+    def __init__(self, id, name, country, driver, availability_zone):
+        super(EC2NodeLocation, self).__init__(id, name, country, driver)
+        self.availability_zone = availability_zone
+
+    def __repr__(self):
+        return (('<EC2NodeLocation: id=%s, name=%s, country=%s, '
+                 'availability_zone=%s driver=%s>')
+                % (self.id, self.name, self.country,
+                   self.availability_zone.name, self.driver.name))
+
 class EC2Response(Response):
     """
     EC2 specific response parsing and error handling.
@@ -231,6 +243,22 @@ class EC2Connection(ConnectionUserAndKey
         )
         return b64_hmac
 
+class ExEC2AvailabilityZone(object):
+    """
+    Extension class which stores information about an EC2 availability zone.
+
+    Note: This class is EC2 specific.
+    """
+    def __init__(self, name, zone_state, region_name):
+        self.name = name
+        self.zone_state = zone_state
+        self.region_name = region_name
+
+    def __repr__(self):
+        return (('<ExEC2AvailabilityZone: name=%s, zone_state=%s, '
+                 'region_name=%s>')
+                % (self.name, self.zone_state, self.region_name))
+
 class EC2NodeDriver(NodeDriver):
     """
     Amazon EC2 node driver
@@ -239,6 +267,9 @@ class EC2NodeDriver(NodeDriver):
     connectionCls = EC2Connection
     type = Provider.EC2
     name = 'Amazon EC2 (us-east-1)'
+    friendly_name = 'Amazon US N. Virginia'
+    country = 'US'
+    region_name = 'us-east-1'
     path = '/'
 
     _instance_types = EC2_US_EAST_INSTANCE_TYPES
@@ -360,24 +391,37 @@ class EC2NodeDriver(NodeDriver):
         )
         return images
 
+    def list_locations(self):
+        locations = []
+        for index, availability_zone in enumerate(self.ex_list_availability_zones()):
+            locations.append(EC2NodeLocation(index,
+                                             self.friendly_name,
+                                             self.country,
+                                             self,
+                                             availability_zone))
+        return locations
+
     def ex_create_keypair(self, name):
         """Creates a new keypair
 
-        @note: This is a non-standard extension API, and only works for EC2.
+        @note: This is a non-standard extension API, and
+               only works for EC2.
 
         @type name: C{str}
-        @param name: The name of the keypair to Create. This must be unique,
-                     otherwise an InvalidKeyPair.Duplicate exception is raised.
+        @param name: The name of the keypair to Create. This must be
+                     unique, otherwise an InvalidKeyPair.Duplicate
+                     exception is raised.
         """
-        params = {'Action': 'CreateKeyPair',
-                  'KeyName': name,
+        params = {
+            'Action': 'CreateKeyPair',
+            'KeyName': name,
         }
         response = self.connection.request(self.path, params=params).object
         key_material = self._findtext(response, 'keyMaterial')
         key_fingerprint = self._findtext(response, 'keyFingerprint')
         return {
-                'keyMaterial': key_material,
-                'keyFingerprint': key_fingerprint,
+            'keyMaterial': key_material,
+            'keyFingerprint': key_fingerprint,
         }
 
     def ex_import_keypair(self, name, keyfile):
@@ -489,6 +533,45 @@ class EC2NodeDriver(NodeDriver):
                 raise e
         return results
 
+    def ex_list_availability_zones(self, only_available=True):
+        """
+        Return a list of L{ExEC2AvailabilityZone} objects for the
+        current region.
+
+        Note: This is an extension method and is only available for EC2
+        driver.
+
+        @keyword  only_available: If true, return only availability zones
+                                  with state 'available'
+        @type     only_available: C{string}
+        """
+        params = {'Action': 'DescribeAvailabilityZones'}
+
+        if only_available:
+            params.update({'Filter.0.Name': 'state'})
+            params.update({'Filter.0.Value.0': 'available'})
+
+        params.update({'Filter.1.Name': 'region-name'})
+        params.update({'Filter.1.Value.0': self.region_name})
+
+        result = self.connection.request(self.path,
+                                         params=params.copy()).object
+
+        availability_zones = []
+        for element in self._findall(result, 'availabilityZoneInfo/item'):
+            name = self._findtext(element, 'zoneName')
+            zone_state = self._findtext(element, 'zoneState')
+            region_name = self._findtext(element, 'regionName')
+
+            availability_zone = ExEC2AvailabilityZone(
+                name=name,
+                zone_state=zone_state,
+                region_name=region_name
+            )
+            availability_zones.append(availability_zone)
+
+        return availability_zones
+
     def create_node(self, **kwargs):
         """Create a new EC2 node
 
@@ -529,6 +612,13 @@ class EC2NodeDriver(NodeDriver):
             for sig in range(len(kwargs['ex_securitygroup'])):
                 params['SecurityGroup.%d' % (sig+1,)]  = kwargs['ex_securitygroup'][sig]
 
+        if 'location' in kwargs:
+            availability_zone = kwargs['location'].availability_zone
+            if availability_zone.region_name != self.region_name:
+                raise AttributeError('Invalid availability zone: %s'
+                                     % (availability_zone.name))
+            params['Placement.AvailabilityZone'] = availability_zone.name
+
         if 'ex_keyname' in kwargs:
             params['KeyName'] = kwargs['ex_keyname']
 
@@ -564,8 +654,6 @@ class EC2NodeDriver(NodeDriver):
         res = self.connection.request(self.path, params=params).object
         return self._get_terminate_boolean(res)
 
-    def list_locations(self):
-        return [NodeLocation(0, 'Amazon US N. Virginia', 'US', self)]
 
 class EC2EUConnection(EC2Connection):
     """
@@ -579,10 +667,11 @@ class EC2EUNodeDriver(EC2NodeDriver):
     """
 
     name = 'Amazon EC2 (eu-west-1)'
+    friendly_name = 'Amazon Europe Ireland'
+    country = 'IE'
+    region_name = 'eu-west-1'
     connectionCls = EC2EUConnection
     _instance_types = EC2_EU_WEST_INSTANCE_TYPES
-    def list_locations(self):
-        return [NodeLocation(0, 'Amazon Europe Ireland', 'IE', self)]
 
 class EC2USWestConnection(EC2Connection):
     """
@@ -597,10 +686,11 @@ class EC2USWestNodeDriver(EC2NodeDriver)
     """
 
     name = 'Amazon EC2 (us-west-1)'
+    friendly_name = 'Amazon US N. California'
+    country = 'US'
+    region_name = 'us-west-1'
     connectionCls = EC2USWestConnection
     _instance_types = EC2_US_WEST_INSTANCE_TYPES
-    def list_locations(self):
-        return [NodeLocation(0, 'Amazon US N. California', 'US', self)]
 
 class EC2APSEConnection(EC2Connection):
     """
@@ -615,10 +705,11 @@ class EC2APSENodeDriver(EC2NodeDriver):
     """
 
     name = 'Amazon EC2 (ap-southeast-1)'
+    friendly_name = 'Amazon Asia-Pacific Singapore'
+    country = 'SG'
+    region_name = 'ap-southeast-1'
     connectionCls = EC2APSEConnection
     _instance_types = EC2_AP_SOUTHEAST_INSTANCE_TYPES
-    def list_locations(self):
-        return [NodeLocation(0, 'Amazon Asia-Pacific Singapore', 'SG', self)]
 
 class EucConnection(EC2Connection):
     """

Modified: incubator/libcloud/trunk/test/test_ec2.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/test_ec2.py?rev=1054527&r1=1054526&r2=1054527&view=diff
==============================================================================
--- incubator/libcloud/trunk/test/test_ec2.py (original)
+++ incubator/libcloud/trunk/test/test_ec2.py Mon Jan  3 05:14:24 2011
@@ -46,6 +46,11 @@ class EC2Tests(unittest.TestCase, TestCa
         node = self.driver.list_nodes()[0]
         self.assertEqual(node.id, 'i-4382922a')
 
+    def test_list_location(self):
+        locations = self.driver.list_locations()
+        self.assertTrue(len(locations) > 0)
+        self.assertTrue(locations[0].availability_zone != None)
+
     def test_reboot_node(self):
         node = Node('i-4382922a', None, None, None, None, self.driver)
         ret = self.driver.reboot_node(node)
@@ -78,6 +83,14 @@ class EC2Tests(unittest.TestCase, TestCa
         self.assertEqual(image.name, 'ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml')
         self.assertEqual(image.id, 'ami-be3adfd7')
 
+    def test_ex_list_availability_zones(self):
+        availability_zones = self.driver.ex_list_availability_zones()
+        availability_zone = availability_zones[0]
+        self.assertTrue(len(availability_zones) > 0)
+        self.assertEqual(availability_zone.name, 'eu-west-1a')
+        self.assertEqual(availability_zone.zone_state, 'available')
+        self.assertEqual(availability_zone.region_name, 'eu-west-1')
+
 class EC2MockHttp(MockHttp):
 
     fixtures = FileFixtures('ec2')
@@ -86,6 +99,10 @@ class EC2MockHttp(MockHttp):
         body = self.fixtures.load('describe_instances.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+    def _DescribeAvailabilityZones(self, method, url, body, headers):
+        body = self.fixtures.load('describe_availability_zones.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
     def _RebootInstances(self, method, url, body, headers):
         body = self.fixtures.load('reboot_instances.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])