You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2014/01/12 02:29:00 UTC

[1/4] git commit: docs: Update code coventions section.

Updated Branches:
  refs/heads/trunk 4fe570a7b -> 9548848cb


docs: Update code coventions section.


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/678f54ae
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/678f54ae
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/678f54ae

Branch: refs/heads/trunk
Commit: 678f54ae34b674851494ad177573ec77a8c13091
Parents: 4fe570a
Author: Tomaz Muraus <to...@apache.org>
Authored: Sat Jan 11 22:27:47 2014 +0100
Committer: Tomaz Muraus <to...@apache.org>
Committed: Sat Jan 11 22:27:47 2014 +0100

----------------------------------------------------------------------
 docs/development.rst | 48 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/678f54ae/docs/development.rst
----------------------------------------------------------------------
diff --git a/docs/development.rst b/docs/development.rst
index afe79d0..5d9b673 100644
--- a/docs/development.rst
+++ b/docs/development.rst
@@ -114,6 +114,54 @@ For example:
         def __eq__(self, other):
             return self.name == other.name
 
+Methods on a driver class should be organized in the following order:
+
+1. Methods which are part of the standard API
+2. Extension methods
+3. "Private" methods (methods prefixed with an underscore)
+4. "Internal" methods (methods prefixed and suffixed with a double underscore)
+
+Methods which perform a similar functionality should be grouped together and
+defined one after another.
+
+For example:
+
+.. sourcecode:: python
+
+    class MyDriver(object):
+        def __init__(self):
+            pass
+
+        def list_nodes(self):
+            pass
+
+        def list_images(self):
+            pass
+
+        def create_node(self):
+            pass
+
+        def reboot_node(self):
+            pass
+
+        def ex_create_image(self):
+            pass
+
+        def _to_nodes(self):
+            pass
+
+        def _to_node(self):
+            pass
+
+        def _to_images(self):
+            pass
+
+        def _to_image(self):
+            pass
+
+Methods should be ordered this way for the consistency reasons and to make
+reading and following the generated API documentation easier.
+
 3. Prefer keyword over regular arguments
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 


[2/4] Re-organize methods on the EC2 driver and move extension and "private" methods to the end.

Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/439adb30/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py
index e22180b..54459a1 100644
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@ -996,465 +996,6 @@ class BaseEC2NodeDriver(NodeDriver):
         'terminated': NodeState.TERMINATED
     }
 
-    def _pathlist(self, key, arr):
-        """
-        Converts a key and an array of values into AWS query param format.
-        """
-        params = {}
-        i = 0
-        for value in arr:
-            i += 1
-            params["%s.%s" % (key, i)] = value
-        return params
-
-    def _get_boolean(self, element):
-        tag = "{%s}%s" % (NAMESPACE, 'return')
-        return element.findtext(tag) == 'true'
-
-    def _get_state_boolean(self, element):
-        """
-        Checks for the instances's state
-        """
-        state = findall(element=element,
-                        xpath='instancesSet/item/currentState/name',
-                        namespace=NAMESPACE)[0].text
-
-        return state in ('stopping', 'pending', 'starting')
-
-    def _get_terminate_boolean(self, element):
-        status = element.findtext(".//{%s}%s" % (NAMESPACE, 'name'))
-        return any([term_status == status
-                    for term_status
-                    in ('shutting-down', 'terminated')])
-
-    def _to_reserved_nodes(self, object, xpath):
-        return [self._to_reserved_node(el)
-                for el in object.findall(fixxpath(xpath=xpath,
-                                                  namespace=NAMESPACE))]
-
-    def _to_reserved_node(self, element):
-        """
-        Build an EC2ReservedNode object using the reserved instance properties.
-        Information on these properties can be found at http://goo.gl/ulXCC7.
-        """
-
-        # Get our extra dictionary
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['reserved_node'])
-
-        try:
-            size = [size for size in self.list_sizes() if
-                    size.id == extra['instance_type']][0]
-        except IndexError:
-            size = None
-
-        return EC2ReservedNode(id=findtext(element=element,
-                                           xpath='reservedInstancesId',
-                                           namespace=NAMESPACE),
-                               state=findattr(element=element,
-                                              xpath='state',
-                                              namespace=NAMESPACE),
-                               driver=self,
-                               size=size,
-                               extra=extra)
-
-    def _to_nodes(self, object, xpath, groups=None):
-        return [self._to_node(el, groups=groups)
-                for el in object.findall(fixxpath(xpath=xpath,
-                                                  namespace=NAMESPACE))]
-
-    def _to_node(self, element, groups=None):
-        try:
-            state = self.NODE_STATE_MAP[findattr(element=element,
-                                                 xpath="instanceState/name",
-                                                 namespace=NAMESPACE)
-                                        ]
-        except KeyError:
-            state = NodeState.UNKNOWN
-
-        instance_id = findtext(element=element, xpath='instanceId',
-                               namespace=NAMESPACE)
-
-        # Get our tags
-        tags = self._get_resource_tags(element)
-
-        name = tags.get('Name', instance_id)
-
-        public_ip = findtext(element=element, xpath='ipAddress',
-                             namespace=NAMESPACE)
-        public_ips = [public_ip] if public_ip else []
-        private_ip = findtext(element=element, xpath='privateIpAddress',
-                              namespace=NAMESPACE)
-        private_ips = [private_ip] if private_ip else []
-
-        n = Node(
-            id=findtext(element=element, xpath='instanceId',
-                        namespace=NAMESPACE),
-            name=name,
-            state=state,
-            public_ips=public_ips,
-            private_ips=private_ips,
-            driver=self.connection.driver,
-            extra={
-                'dns_name': findattr(element=element, xpath="dnsName",
-                                     namespace=NAMESPACE),
-                'instanceId': findattr(element=element, xpath="instanceId",
-                                       namespace=NAMESPACE),
-                'imageId': findattr(element=element, xpath="imageId",
-                                    namespace=NAMESPACE),
-                'private_dns': findattr(element=element,
-                                        xpath="privateDnsName",
-                                        namespace=NAMESPACE),
-                'status': findattr(element=element, xpath="instanceState/name",
-                                   namespace=NAMESPACE),
-                'key_name': findattr(element=element, xpath="keyName",
-                                     namespace=NAMESPACE),
-                'launchindex': findattr(element=element,
-                                        xpath="amiLaunchIndex",
-                                        namespace=NAMESPACE),
-                'productcode': [
-                    p.text for p in findall(
-                        element=element,
-                        xpath="productCodesSet/item/productCode",
-                        namespace=NAMESPACE
-                    )],
-                'instancetype': findattr(element=element, xpath="instanceType",
-                                         namespace=NAMESPACE),
-                'launchdatetime': findattr(element=element, xpath="launchTime",
-                                           namespace=NAMESPACE),
-                'availability': findattr(element,
-                                         xpath="placement/availabilityZone",
-                                         namespace=NAMESPACE),
-                'kernelid': findattr(element=element, xpath="kernelId",
-                                     namespace=NAMESPACE),
-                'ramdiskid': findattr(element=element, xpath="ramdiskId",
-                                      namespace=NAMESPACE),
-                'clienttoken': findattr(element=element, xpath="clientToken",
-                                        namespace=NAMESPACE),
-                'groups': groups,
-                'tags': tags,
-                'iam_profile': findattr(element, xpath="iamInstanceProfile/id",
-                                        namespace=NAMESPACE)
-            }
-        )
-        return n
-
-    def _to_device_mappings(self, object):
-        return [self._to_device_mapping(el) for el in object.findall(
-            fixxpath(xpath='blockDeviceMapping/item', namespace=NAMESPACE))
-        ]
-
-    def _to_device_mapping(self, element):
-        """
-        Parse the XML element and return a dictionary of device properties.
-        Additional information can be found at http://goo.gl/GjWYBf.
-
-        @note: EBS volumes do not have a virtual name. Only ephemeral
-               disks use this property.
-        :rtype:     ``dict``
-        """
-        mapping = {}
-
-        mapping['device_name'] = findattr(element=element,
-                                          xpath='deviceName',
-                                          namespace=NAMESPACE)
-
-        mapping['virtual_name'] = findattr(element=element,
-                                           xpath='virtualName',
-                                           namespace=NAMESPACE)
-
-        # If virtual name does not exist then this is an EBS volume.
-        # Build the EBS dictionary leveraging the _get_extra_dict method.
-        if mapping['virtual_name'] is None:
-            mapping['ebs'] = self._get_extra_dict(
-                element, RESOURCE_EXTRA_ATTRIBUTES_MAP['ebs_volume'])
-
-        return mapping
-
-    def _to_images(self, object):
-        return [self._to_image(el) for el in object.findall(
-            fixxpath(xpath='imagesSet/item', namespace=NAMESPACE))
-        ]
-
-    def _to_image(self, element):
-
-        id = findtext(element=element, xpath='imageId', namespace=NAMESPACE)
-        name = findtext(element=element, xpath='name', namespace=NAMESPACE)
-
-        # Build block device mapping
-        block_device_mapping = self._to_device_mappings(element)
-
-        # Get our tags
-        tags = self._get_resource_tags(element)
-
-        # Get our extra dictionary
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['image'])
-
-        # Add our tags and block device mapping
-        extra['tags'] = tags
-        extra['block_device_mapping'] = block_device_mapping
-
-        return NodeImage(id=id, name=name, driver=self, extra=extra)
-
-    def _to_volume(self, element, name=None):
-        """
-        Parse the XML element and return a StorageVolume object.
-
-        :param      name: An optional name for the volume. If not provided
-                          then either tag with a key "Name" or volume ID
-                          will be used (which ever is available first in that
-                          order).
-        :type       name: ``str``
-
-        :rtype:     :class:`StorageVolume`
-        """
-        volId = findtext(element=element, xpath='volumeId',
-                         namespace=NAMESPACE)
-        size = findtext(element=element, xpath='size', namespace=NAMESPACE)
-
-        # Get our tags
-        tags = self._get_resource_tags(element)
-
-        # If name was not passed into the method then
-        # fall back then use the volume id
-        name = name if name else tags.get('Name', volId)
-
-        # Get our extra dictionary
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['volume'])
-
-        return StorageVolume(id=volId,
-                             name=name,
-                             size=int(size),
-                             driver=self,
-                             extra=extra)
-
-    def _to_snapshots(self, response):
-        return [self._to_snapshot(el) for el in response.findall(
-            fixxpath(xpath='snapshotSet/item', namespace=NAMESPACE))
-        ]
-
-    def _to_snapshot(self, element, name=None):
-        snapId = findtext(element=element, xpath='snapshotId',
-                          namespace=NAMESPACE)
-        size = findtext(element=element, xpath='volumeSize',
-                        namespace=NAMESPACE)
-
-        # Get our tags
-        tags = self._get_resource_tags(element)
-
-        # If name was not passed into the method then
-        # fall back then use the snapshot id
-        name = name if name else tags.get('Name', snapId)
-
-        # Get our extra dictionary
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['snapshot'])
-
-        # Add tags and name to the extra dict
-        extra['tags'] = tags
-        extra['name'] = name
-
-        return VolumeSnapshot(snapId, size=int(size),
-                              driver=self, extra=extra)
-
-    def _to_networks(self, response):
-        return [self._to_network(el) for el in response.findall(
-            fixxpath(xpath='vpcSet/item', namespace=NAMESPACE))
-        ]
-
-    def _to_network(self, element):
-        # Get the network id
-        vpc_id = findtext(element=element,
-                          xpath='vpcId',
-                          namespace=NAMESPACE)
-
-        # Get our tags
-        tags = self._get_resource_tags(element)
-
-        # Set our name if the Name key/value if available
-        # If we don't get anything back then use the vpc_id
-        name = tags.get('Name', vpc_id)
-
-        cidr_block = findtext(element=element,
-                              xpath='cidrBlock',
-                              namespace=NAMESPACE)
-
-        # Get our extra dictionary
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['network'])
-
-        # Add tags to the extra dict
-        extra['tags'] = tags
-
-        return EC2Network(vpc_id, name, cidr_block, extra=extra)
-
-    def _to_addresses(self, response, only_associated):
-        """
-        Builds a list of dictionaries containing elastic IP properties.
-
-        :param    only_associated: If true, return only those addresses
-                                   that are associated with an instance.
-                                   If false, return all addresses.
-        :type     only_associated: ``bool``
-
-        :rtype:   ``list`` of :class:`ElasticIP`
-        """
-        addresses = []
-        for el in response.findall(fixxpath(xpath='addressesSet/item',
-                                            namespace=NAMESPACE)):
-            addr = self._to_address(el, only_associated)
-            if addr is not None:
-                addresses.append(addr)
-
-        return addresses
-
-    def _to_address(self, element, only_associated):
-        instance_id = findtext(element=element, xpath='instanceId',
-                               namespace=NAMESPACE)
-
-        public_ip = findtext(element=element,
-                             xpath='publicIp',
-                             namespace=NAMESPACE)
-
-        domain = findtext(element=element,
-                          xpath='domain',
-                          namespace=NAMESPACE)
-
-        # Build our extra dict
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['elastic_ip'])
-
-        # Return NoneType if only associated IPs are requested
-        if only_associated and not instance_id:
-            return None
-
-        return ElasticIP(public_ip, domain, instance_id, extra=extra)
-
-    def _to_subnets(self, response):
-        return [self._to_subnet(el) for el in response.findall(
-            fixxpath(xpath='subnetSet/item', namespace=NAMESPACE))
-        ]
-
-    def _to_subnet(self, element):
-        # Get the subnet ID
-        subnet_id = findtext(element=element,
-                             xpath='subnetId',
-                             namespace=NAMESPACE)
-
-        # Get our tags
-        tags = self._get_resource_tags(element)
-
-        # If we don't get anything back then use the subnet_id
-        name = tags.get('Name', subnet_id)
-
-        state = findtext(element=element,
-                         xpath='state',
-                         namespace=NAMESPACE)
-
-        # Get our extra dictionary
-        extra = self._get_extra_dict(
-            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['subnet'])
-
-        # Also include our tags
-        extra['tags'] = tags
-
-        return EC2NetworkSubnet(subnet_id, name, state, extra=extra)
-
-    def _to_interfaces(self, response):
-        return [self._to_interface(el) for el in response.findall(
-            fixxpath(xpath='networkInterfaceSet/item', namespace=NAMESPACE))
-        ]
-
-    def _to_interface(self, element, name=None):
-        """
-        Parse the XML element and return a EC2NetworkInterface object.
-
-        :param      name: An optional name for the interface. If not provided
-                          then either tag with a key "Name" or the interface ID
-                          will be used (whichever is available first in that
-                          order).
-        :type       name: ``str``
-
-        :rtype:     :class: `EC2NetworkInterface`
-        """
-
-        interface_id = findtext(element=element,
-                                xpath='networkInterfaceId',
-                                namespace=NAMESPACE)
-
-        state = findtext(element=element,
-                         xpath='status',
-                         namespace=NAMESPACE)
-
-        # Get tags
-        tags = self._get_resource_tags(element)
-
-        name = name if name else tags.get('Name', interface_id)
-
-        # Build security groups
-        groups = []
-        for item in findall(element=element,
-                            xpath='groupSet/item',
-                            namespace=NAMESPACE):
-
-            groups.append({'group_id': findtext(element=item,
-                                                xpath='groupId',
-                                                namespace=NAMESPACE),
-                           'group_name': findtext(element=item,
-                                                  xpath='groupName',
-                                                  namespace=NAMESPACE)})
-
-        # Build private IPs
-        priv_ips = []
-        for item in findall(element=element,
-                            xpath='privateIpAddressesSet/item',
-                            namespace=NAMESPACE):
-
-            priv_ips.append({'private_ip': findtext(element=item,
-                                                    xpath='privateIpAddress',
-                                                    namespace=NAMESPACE),
-                            'private_dns': findtext(element=item,
-                                                    xpath='privateDnsName',
-                                                    namespace=NAMESPACE),
-                            'primary': findtext(element=item,
-                                                xpath='primary',
-                                                namespace=NAMESPACE)})
-
-        # Build our attachment dictionary which we will add into extra later
-        attributes_map = \
-            RESOURCE_EXTRA_ATTRIBUTES_MAP['network_interface_attachment']
-        attachment = self._get_extra_dict(element, attributes_map)
-
-        # Build our extra dict
-        attributes_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['network_interface']
-        extra = self._get_extra_dict(element, attributes_map)
-
-        # Include our previously built items as well
-        extra['tags'] = tags
-        extra['attachment'] = attachment
-        extra['private_ips'] = priv_ips
-        extra['groups'] = groups
-
-        return EC2NetworkInterface(interface_id, name, state, extra=extra)
-
-    def ex_list_reserved_nodes(self):
-        """
-        List all reserved instances/nodes which can be purchased from Amazon
-        for one or three year terms. Reservations are made at a region level
-        and reduce the hourly charge for instances.
-
-        More information can be found at http://goo.gl/ulXCC7.
-
-        :rtype: ``list`` of :class:`.EC2ReservedNode`
-        """
-        params = {'Action': 'DescribeReservedInstances'}
-
-        response = self.connection.request(self.path, params=params).object
-
-        return self._to_reserved_nodes(response, 'reservedInstancesSet/item')
-
     def list_nodes(self, ex_node_ids=None):
         """
         List all nodes
@@ -1550,39 +1091,6 @@ class BaseEC2NodeDriver(NodeDriver):
         )
         return images
 
-    def ex_get_limits(self):
-        """
-        Retrieve account resource limits.
-
-        :rtype: ``dict``
-        """
-        attributes = ['max-instances', 'max-elastic-ips',
-                      'vpc-max-elastic-ips']
-        params = {}
-        params['Action'] = 'DescribeAccountAttributes'
-
-        for index, attribute in enumerate(attributes):
-            params['AttributeName.%s' % (index)] = attribute
-
-        response = self.connection.request(self.path, params=params)
-        data = response.object
-
-        elems = data.findall(fixxpath(xpath='accountAttributeSet/item',
-                                      namespace=NAMESPACE))
-
-        result = {'resource': {}}
-
-        for elem in elems:
-            name = findtext(element=elem, xpath='attributeName',
-                            namespace=NAMESPACE)
-            value = findtext(element=elem,
-                             xpath='attributeValueSet/item/attributeValue',
-                             namespace=NAMESPACE)
-
-            result['resource'][name] = int(value)
-
-        return result
-
     def list_locations(self):
         locations = []
         for index, availability_zone in \
@@ -1608,6 +1116,153 @@ class BaseEC2NodeDriver(NodeDriver):
         ]
         return volumes
 
+    def create_node(self, **kwargs):
+        """
+        Create a new EC2 node.
+
+        Reference: http://bit.ly/8ZyPSy [docs.amazonwebservices.com]
+
+        @inherits: :class:`NodeDriver.create_node`
+
+        :keyword    ex_keyname: The name of the key pair
+        :type       ex_keyname: ``str``
+
+        :keyword    ex_userdata: User data
+        :type       ex_userdata: ``str``
+
+        :keyword    ex_security_groups: A list of names of security groups to
+                                        assign to the node.
+        :type       ex_security_groups:   ``list``
+
+        :keyword    ex_metadata: Key/Value metadata to associate with a node
+        :type       ex_metadata: ``dict``
+
+        :keyword    ex_mincount: Minimum number of instances to launch
+        :type       ex_mincount: ``int``
+
+        :keyword    ex_maxcount: Maximum number of instances to launch
+        :type       ex_maxcount: ``int``
+
+        :keyword    ex_clienttoken: Unique identifier to ensure idempotency
+        :type       ex_clienttoken: ``str``
+
+        :keyword    ex_blockdevicemappings: ``list`` of ``dict`` block device
+                    mappings.
+        :type       ex_blockdevicemappings: ``list`` of ``dict``
+
+        :keyword    ex_iamprofile: Name or ARN of IAM profile
+        :type       ex_iamprofile: ``str``
+        """
+        image = kwargs["image"]
+        size = kwargs["size"]
+        params = {
+            'Action': 'RunInstances',
+            'ImageId': image.id,
+            'MinCount': str(kwargs.get('ex_mincount', '1')),
+            'MaxCount': str(kwargs.get('ex_maxcount', '1')),
+            'InstanceType': size.id
+        }
+
+        if 'ex_security_groups' in kwargs and 'ex_securitygroup' in kwargs:
+            raise ValueError('You can only supply ex_security_groups or'
+                             ' ex_securitygroup')
+
+        # ex_securitygroup is here for backward compatibility
+        ex_security_groups = kwargs.get('ex_security_groups', None)
+        ex_securitygroup = kwargs.get('ex_securitygroup', None)
+        security_groups = ex_security_groups or ex_securitygroup
+
+        if security_groups:
+            if not isinstance(security_groups, (tuple, list)):
+                security_groups = [security_groups]
+
+            for sig in range(len(security_groups)):
+                params['SecurityGroup.%d' % (sig + 1,)] =\
+                    security_groups[sig]
+
+        if 'location' in kwargs:
+            availability_zone = getattr(kwargs['location'],
+                                        'availability_zone', None)
+            if 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 'auth' in kwargs and 'ex_keyname' in kwargs:
+            raise AttributeError('Cannot specify auth and ex_keyname together')
+
+        if 'auth' in kwargs:
+            auth = self._get_and_check_auth(kwargs['auth'])
+            params['KeyName'] = \
+                self.ex_find_or_import_keypair_by_key_material(auth.pubkey)
+
+        if 'ex_keyname' in kwargs:
+            params['KeyName'] = kwargs['ex_keyname']
+
+        if 'ex_userdata' in kwargs:
+            params['UserData'] = base64.b64encode(b(kwargs['ex_userdata']))\
+                .decode('utf-8')
+
+        if 'ex_clienttoken' in kwargs:
+            params['ClientToken'] = kwargs['ex_clienttoken']
+
+        if 'ex_blockdevicemappings' in kwargs:
+            if not isinstance(kwargs['ex_blockdevicemappings'], (list, tuple)):
+                raise AttributeError(
+                    'ex_blockdevicemappings not list or tuple')
+
+            for idx, mapping in enumerate(kwargs['ex_blockdevicemappings']):
+                idx += 1  # we want 1-based indexes
+                if not isinstance(mapping, dict):
+                    raise AttributeError(
+                        'mapping %s in ex_blockdevicemappings '
+                        'not a dict' % mapping)
+                for k, v in mapping.items():
+                    params['BlockDeviceMapping.%d.%s' % (idx, k)] = str(v)
+
+        if 'ex_iamprofile' in kwargs:
+            if not isinstance(kwargs['ex_iamprofile'], basestring):
+                raise AttributeError('ex_iamprofile not string')
+
+            if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'):
+                params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile']
+            else:
+                params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile']
+
+        object = self.connection.request(self.path, params=params).object
+        nodes = self._to_nodes(object, 'instancesSet/item')
+
+        for node in nodes:
+            tags = {'Name': kwargs['name']}
+            if 'ex_metadata' in kwargs:
+                tags.update(kwargs['ex_metadata'])
+
+            try:
+                self.ex_create_tags(resource=node, tags=tags)
+            except Exception:
+                continue
+
+            node.name = kwargs['name']
+            node.extra.update({'tags': tags})
+
+        if len(nodes) == 1:
+            return nodes[0]
+        else:
+            return nodes
+
+    def reboot_node(self, node):
+        params = {'Action': 'RebootInstances'}
+        params.update(self._pathlist('InstanceId', [node.id]))
+        res = self.connection.request(self.path, params=params).object
+        return self._get_boolean(res)
+
+    def destroy_node(self, node):
+        params = {'Action': 'TerminateInstances'}
+        params.update(self._pathlist('InstanceId', [node.id]))
+        res = self.connection.request(self.path, params=params).object
+        return self._get_terminate_boolean(res)
+
     def create_volume(self, size, name, location=None, snapshot=None):
         """
         :param location: Datacenter in which to create a volume in.
@@ -1626,13 +1281,6 @@ class BaseEC2NodeDriver(NodeDriver):
         self.ex_create_tags(volume, {'Name': name})
         return volume
 
-    def destroy_volume(self, volume):
-        params = {
-            'Action': 'DeleteVolume',
-            'VolumeId': volume.id}
-        response = self.connection.request(self.path, params=params).object
-        return self._get_boolean(response)
-
     def attach_volume(self, node, volume, device):
         params = {
             'Action': 'AttachVolume',
@@ -1651,6 +1299,13 @@ class BaseEC2NodeDriver(NodeDriver):
         self.connection.request(self.path, params=params)
         return True
 
+    def destroy_volume(self, volume):
+        params = {
+            'Action': 'DeleteVolume',
+            'VolumeId': volume.id}
+        response = self.connection.request(self.path, params=params).object
+        return self._get_boolean(response)
+
     def create_volume_snapshot(self, volume, name=None):
         """
         Create snapshot from volume
@@ -1718,23 +1373,7 @@ class BaseEC2NodeDriver(NodeDriver):
         response = self.connection.request(self.path, params=params).object
         return self._get_boolean(response)
 
-    def _to_key_pairs(self, elems):
-        key_pairs = [self._to_key_pair(elem=elem) for elem in elems]
-        return key_pairs
-
-    def _to_key_pair(self, elem):
-        name = findtext(element=elem, xpath='keyName', namespace=NAMESPACE)
-        fingerprint = findtext(element=elem, xpath='keyFingerprint',
-                               namespace=NAMESPACE).strip()
-        private_key = findtext(element=elem, xpath='keyMaterial',
-                               namespace=NAMESPACE)
-
-        key_pair = KeyPair(name=name,
-                           public_key=None,
-                           fingerprint=fingerprint,
-                           private_key=private_key,
-                           driver=self)
-        return key_pair
+    # Key pair management methods
 
     def list_key_pairs(self):
         params = {
@@ -1767,289 +1406,525 @@ class BaseEC2NodeDriver(NodeDriver):
             'KeyName': name
         }
 
-        response = self.connection.request(self.path, params=params)
-        elem = response.object
-        key_pair = self._to_key_pair(elem=elem)
-        return key_pair
-
-    def import_key_pair_from_string(self, name, key_material):
-        base64key = base64.b64encode(b(key_material))
+        response = self.connection.request(self.path, params=params)
+        elem = response.object
+        key_pair = self._to_key_pair(elem=elem)
+        return key_pair
+
+    def import_key_pair_from_string(self, name, key_material):
+        base64key = base64.b64encode(b(key_material))
+
+        params = {
+            'Action': 'ImportKeyPair',
+            'KeyName': name,
+            'PublicKeyMaterial': base64key
+        }
+
+        response = self.connection.request(self.path, params=params)
+        elem = response.object
+        key_pair = self._to_key_pair(elem=elem)
+        return key_pair
+
+    def delete_key_pair(self, key_pair):
+        params = {
+            'Action': 'DeleteKeyPair',
+            'KeyName': key_pair
+        }
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+        return element == 'true'
+
+    def ex_destroy_image(self, image):
+        params = {
+            'Action': 'DeregisterImage',
+            'ImageId': image.id
+        }
+        response = self.connection.request(self.path, params=params).object
+        return self._get_boolean(response)
+
+    def ex_list_networks(self):
+        """
+        Return a list of :class:`EC2Network` objects for the
+        current region.
+
+        :rtype:     ``list`` of :class:`EC2Network`
+        """
+        params = {'Action': 'DescribeVpcs'}
+
+        return self._to_networks(
+            self.connection.request(self.path, params=params).object
+        )
+
+    def ex_create_network(self, cidr_block, name=None,
+                          instance_tenancy='default'):
+        """
+        Create a network/VPC
+
+        :param      cidr_block: The CIDR block assigned to the network
+        :type       cidr_block: ``str``
+
+        :param      name: An optional name for the network
+        :type       name: ``str``
+
+        :param      instance_tenancy: The allowed tenancy of instances launched
+                                      into the VPC.
+                                      Valid values: default/dedicated
+        :type       instance_tenancy: ``str``
+
+        :return:    Dictionary of network properties
+        :rtype:     ``dict``
+        """
+        params = {'Action': 'CreateVpc',
+                  'CidrBlock': cidr_block,
+                  'InstanceTenancy':  instance_tenancy}
+
+        response = self.connection.request(self.path, params=params).object
+        element = response.findall(fixxpath(xpath='vpc',
+                                            namespace=NAMESPACE))[0]
+
+        network = self._to_network(element)
+
+        if name is not None:
+            self.ex_create_tags(network, {'Name': name})
+
+        return network
+
+    def ex_delete_network(self, vpc):
+        """
+        Deletes a network/VPC.
+
+        :param      vpc: VPC to delete.
+        :type       vpc: :class:`.EC2Network`
+
+        :rtype:     ``bool``
+        """
+        params = {'Action': 'DeleteVpc', 'VpcId': vpc.id}
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_list_subnets(self):
+        """
+        Return a list of :class:`EC2NetworkSubnet` objects for the
+        current region.
+
+        :rtype:     ``list`` of :class:`EC2NetworkSubnet`
+        """
+        params = {'Action': 'DescribeSubnets'}
+
+        return self._to_subnets(
+            self.connection.request(self.path, params=params).object
+        )
+
+    def ex_create_subnet(self, vpc_id, cidr_block,
+                         availability_zone, name=None):
+        """
+        Create a network subnet within a VPC
+
+        :param      vpc_id: The ID of the VPC that the subnet should be
+                            associated with
+        :type       vpc_id: ``str``
+
+        :param      cidr_block: The CIDR block assigned to the subnet
+        :type       cidr_block: ``str``
+
+        :param      availability_zone: The availability zone where the subnet
+                                       should reside
+        :type       availability_zone: ``str``
+
+        :param      name: An optional name for the network
+        :type       name: ``str``
+
+        :rtype:     :class: `EC2NetworkSubnet`
+        """
+        params = {'Action': 'CreateSubnet',
+                  'VpcId': vpc_id,
+                  'CidrBlock': cidr_block,
+                  'AvailabilityZone': availability_zone}
+
+        response = self.connection.request(self.path, params=params).object
+        element = response.findall(fixxpath(xpath='subnet',
+                                            namespace=NAMESPACE))[0]
+
+        subnet = self._to_subnet(element)
+
+        if name is not None:
+            self.ex_create_tags(subnet, {'Name': name})
+
+        return subnet
+
+    def ex_delete_subnet(self, subnet):
+        """
+        Deletes a VPC subnet.
+
+        :param      subnet: The subnet to delete
+        :type       subnet: :class:`.EC2NetworkSubnet`
+
+        :rtype:     ``bool``
+        """
+        params = {'Action': 'DeleteSubnet', 'SubnetId': subnet.id}
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result,
+                           xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_list_security_groups(self):
+        """
+        List existing Security Groups.
+
+        @note: This is a non-standard extension API, and only works for EC2.
+
+        :rtype: ``list`` of ``str``
+        """
+        params = {'Action': 'DescribeSecurityGroups'}
+        response = self.connection.request(self.path, params=params).object
+
+        groups = []
+        for group in findall(element=response, xpath='securityGroupInfo/item',
+                             namespace=NAMESPACE):
+            name = findtext(element=group, xpath='groupName',
+                            namespace=NAMESPACE)
+            groups.append(name)
+
+        return groups
+
+    def ex_create_security_group(self, name, description, vpc_id=None):
+        """
+        Creates a new Security Group in EC2-Classic or a targetted VPC.
+
+        :param      name:        The name of the security group to Create.
+                                 This must be unique.
+        :type       name:        ``str``
+
+        :param      description: Human readable description of a Security
+                                 Group.
+        :type       description: ``str``
+
+        :param      description: Optional identifier for VPC networks
+        :type       description: ``str``
+
+        :rtype: ``dict``
+        """
+        params = {'Action': 'CreateSecurityGroup',
+                  'GroupName': name,
+                  'GroupDescription': description}
+
+        if vpc_id is not None:
+            params['VpcId'] = vpc_id
+
+        response = self.connection.request(self.path, params=params).object
+        group_id = findattr(element=response, xpath='groupId',
+                            namespace=NAMESPACE)
+        return {
+            'group_id': group_id
+        }
+
+    def ex_delete_security_group_by_id(self, group_id):
+        """
+        Deletes a new Security Group using the group id.
 
-        params = {
-            'Action': 'ImportKeyPair',
-            'KeyName': name,
-            'PublicKeyMaterial': base64key
-        }
+        :param      group_id: The ID of the security group
+        :type       group_id: ``str``
 
-        response = self.connection.request(self.path, params=params)
-        elem = response.object
-        key_pair = self._to_key_pair(elem=elem)
-        return key_pair
+        :rtype: ``bool``
+        """
+        params = {'Action': 'DeleteSecurityGroup', 'GroupId': group_id}
 
-    def delete_key_pair(self, key_pair):
-        params = {
-            'Action': 'DeleteKeyPair',
-            'KeyName': key_pair
-        }
         result = self.connection.request(self.path, params=params).object
         element = findtext(element=result, xpath='return',
                            namespace=NAMESPACE)
+
         return element == 'true'
 
-    def ex_destroy_image(self, image):
-        params = {
-            'Action': 'DeregisterImage',
-            'ImageId': image.id
-        }
-        response = self.connection.request(self.path, params=params).object
-        return self._get_boolean(response)
+    def ex_delete_security_group_by_name(self, group_name):
+        """
+        Deletes a new Security Group using the group name.
 
-    def ex_create_keypair(self, name):
+        :param      group_name: The name of the security group
+        :type       group_name: ``str``
+
+        :rtype: ``bool``
         """
-        Creates a new keypair
+        params = {'Action': 'DeleteSecurityGroup', 'GroupName': group_name}
 
-        @note: This is a non-standard extension API, and only works for EC2.
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
 
-        :param      name: The name of the keypair to Create. This must be
-            unique, otherwise an InvalidKeyPair.Duplicate exception is raised.
+        return element == 'true'
+
+    def ex_delete_security_group(self, name):
+        """
+        Wrapper method which calls ex_delete_security_group_by_name.
+
+        :param      name: The name of the security group
         :type       name: ``str``
 
-        :rtype: ``dict``
+        :rtype: ``bool``
         """
-        warnings.warn('This method has been deprecated in favor of '
-                      'create_key_pair method')
+        return self.ex_delete_security_group_by_name(name)
 
-        key_pair = self.create_key_pair(name=name)
+    def ex_authorize_security_group(self, name, from_port, to_port, cidr_ip,
+                                    protocol='tcp'):
+        """
+        Edit a Security Group to allow specific traffic.
 
-        result = {
-            'keyMaterial': key_pair.private_key,
-            'keyFingerprint': key_pair.fingerprint
-        }
+        @note: This is a non-standard extension API, and only works for EC2.
 
-        return result
+        :param      name: The name of the security group to edit
+        :type       name: ``str``
 
-    def ex_delete_keypair(self, keypair):
-        """
-        Delete a key pair by name.
+        :param      from_port: The beginning of the port range to open
+        :type       from_port: ``str``
 
-        @note: This is a non-standard extension API, and only works with EC2.
+        :param      to_port: The end of the port range to open
+        :type       to_port: ``str``
 
-        :param      keypair: The name of the keypair to delete.
-        :type       keypair: ``str``
+        :param      cidr_ip: The ip to allow traffic for.
+        :type       cidr_ip: ``str``
+
+        :param      protocol: tcp/udp/icmp
+        :type       protocol: ``str``
 
         :rtype: ``bool``
         """
-        warnings.warn('This method has been deprecated in favor of '
-                      'delete_key_pair method')
 
-        return self.delete_key_pair(name=keypair)
+        params = {'Action': 'AuthorizeSecurityGroupIngress',
+                  'GroupName': name,
+                  'IpProtocol': protocol,
+                  'FromPort': str(from_port),
+                  'ToPort': str(to_port),
+                  'CidrIp': cidr_ip}
+        try:
+            resp = self.connection.request(
+                self.path, params=params.copy()).object
+            return bool(findtext(element=resp, xpath='return',
+                                 namespace=NAMESPACE))
+        except Exception:
+            e = sys.exc_info()[1]
+            if e.args[0].find('InvalidPermission.Duplicate') == -1:
+                raise e
 
-    def ex_import_keypair_from_string(self, name, key_material):
+    def ex_authorize_security_group_ingress(self, id, from_port, to_port,
+                                            cidr_ips=None, group_pairs=None,
+                                            protocol='tcp'):
         """
-        imports a new public key where the public key is passed in as a string
+        Edit a Security Group to allow specific ingress traffic using
+        CIDR blocks or either a group ID, group name or user ID (account).
 
-        @note: This is a non-standard extension API, and only works for EC2.
+        :param      id: The id of the security group to edit
+        :type       id: ``str``
 
-        :param      name: The name of the public key to import. This must be
-         unique, otherwise an InvalidKeyPair.Duplicate exception is raised.
-        :type       name: ``str``
+        :param      from_port: The beginning of the port range to open
+        :type       from_port: ``int``
 
-        :param     key_material: The contents of a public key file.
-        :type      key_material: ``str``
+        :param      to_port: The end of the port range to open
+        :type       to_port: ``int``
 
-        :rtype: ``dict``
-        """
-        warnings.warn('This method has been deprecated in favor of '
-                      'import_key_pair_from_string method')
+        :param      cidr_ips: The list of ip ranges to allow traffic for.
+        :type       cidr_ips: ``list``
 
-        key_pair = self.import_key_pair_from_string(name=name,
-                                                    key_material=key_material)
+        :param      group_pairs: Source user/group pairs to allow traffic for.
+                    More info can be found at http://goo.gl/stBHJF
 
-        result = {
-            'keyName': key_pair.name,
-            'keyFingerprint': key_pair.fingerprint
-        }
-        return result
+                    EC2 Classic Example: To allow access from any system
+                    associated with the default group on account 1234567890
 
-    def ex_import_keypair(self, name, keyfile):
-        """
-        imports a new public key where the public key is passed via a filename
+                    [{'group_name': 'default', 'user_id': '1234567890'}]
 
-        @note: This is a non-standard extension API, and only works for EC2.
+                    VPC Example: Allow access from any system associated with
+                    security group sg-47ad482e on your own account
 
-        :param      name: The name of the public key to import. This must be
-         unique, otherwise an InvalidKeyPair.Duplicate exception is raised.
-        :type       name: ``str``
+                    [{'group_id': ' sg-47ad482e'}]
+        :type       group_pairs: ``list`` of ``dict``
 
-        :param     keyfile: The filename with path of the public key to import.
-        :type      keyfile: ``str``
+        :param      protocol: tcp/udp/icmp
+        :type       protocol: ``str``
 
-        :rtype: ``dict``
+        :rtype: ``bool``
         """
-        warnings.warn('This method has been deprecated in favor of '
-                      'import_key_pair_from_file method')
 
-        key_pair = self.import_key_pair_from_file(name=name,
-                                                  key_file_path=keyfile)
+        params = self._get_common_security_group_params(id,
+                                                        protocol,
+                                                        from_port,
+                                                        to_port,
+                                                        cidr_ips,
+                                                        group_pairs)
 
-        result = {
-            'keyName': key_pair.name,
-            'keyFingerprint': key_pair.fingerprint
-        }
-        return result
+        params["Action"] = 'AuthorizeSecurityGroupIngress'
 
-    def ex_find_or_import_keypair_by_key_material(self, pubkey):
-        """
-        Given a public key, look it up in the EC2 KeyPair database. If it
-        exists, return any information we have about it. Otherwise, create it.
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
 
-        Keys that are created are named based on their comment and fingerprint.
+        return element == 'true'
 
-        :rtype: ``dict``
+    def ex_authorize_security_group_egress(self, id, from_port, to_port,
+                                           cidr_ips=None, group_pairs=None,
+                                           protocol='tcp'):
         """
-        key_fingerprint = get_pubkey_ssh2_fingerprint(pubkey)
-        key_comment = get_pubkey_comment(pubkey, default='unnamed')
-        key_name = '%s-%s' % (key_comment, key_fingerprint)
+        Edit a Security Group to allow specific egress traffic using
+        CIDR blocks or either a group ID, group name or user ID (account).
+        This call is not supported for EC2 classic and only works for VPC
+        groups.
 
-        key_pairs = self.list_key_pairs()
-        key_pairs = [key_pair for key_pair in key_pairs if
-                     key_pair.fingerprint == key_fingerprint]
+        :param      id: The id of the security group to edit
+        :type       id: ``str``
 
-        if len(key_pairs) >= 1:
-            key_pair = key_pairs[0]
-            result = {
-                'keyName': key_pair.name,
-                'keyFingerprint': key_pair.fingerprint
-            }
-        else:
-            result = self.ex_import_keypair_from_string(key_name, pubkey)
+        :param      from_port: The beginning of the port range to open
+        :type       from_port: ``int``
 
-        return result
+        :param      to_port: The end of the port range to open
+        :type       to_port: ``int``
 
-    def ex_list_keypairs(self):
+        :param      cidr_ips: The list of ip ranges to allow traffic for.
+        :type       cidr_ips: ``list``
+
+        :param      group_pairs: Source user/group pairs to allow traffic for.
+                    More info can be found at http://goo.gl/stBHJF
+
+                    EC2 Classic Example: To allow access from any system
+                    associated with the default group on account 1234567890
+
+                    [{'group_name': 'default', 'user_id': '1234567890'}]
+
+                    VPC Example: Allow access from any system associated with
+                    security group sg-47ad482e on your own account
+
+                    [{'group_id': ' sg-47ad482e'}]
+        :type       group_pairs: ``list`` of ``dict``
+
+        :param      protocol: tcp/udp/icmp
+        :type       protocol: ``str``
+
+        :rtype: ``bool``
         """
-        Lists all the keypair names and fingerprints.
 
-        :rtype: ``list`` of ``dict``
-        """
-        warnings.warn('This method has been deprecated in favor of '
-                      'list_key_pairs method')
+        params = self._get_common_security_group_params(id,
+                                                        protocol,
+                                                        from_port,
+                                                        to_port,
+                                                        cidr_ips,
+                                                        group_pairs)
+
+        params["Action"] = 'AuthorizeSecurityGroupEgress'
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
 
-        key_pairs = self.list_key_pairs()
+    def ex_revoke_security_group_ingress(self, id, from_port, to_port,
+                                         cidr_ips=None, group_pairs=None,
+                                         protocol='tcp'):
+        """
+        Edit a Security Group to revoke specific ingress traffic using
+        CIDR blocks or either a group ID, group name or user ID (account).
 
-        result = []
+        :param      id: The id of the security group to edit
+        :type       id: ``str``
 
-        for key_pair in key_pairs:
-            item = {
-                'keyName': key_pair.name,
-                'keyFingerprint': key_pair.fingerprint,
-            }
-            result.append(item)
+        :param      from_port: The beginning of the port range to open
+        :type       from_port: ``int``
 
-        return result
+        :param      to_port: The end of the port range to open
+        :type       to_port: ``int``
 
-    def ex_describe_all_keypairs(self):
-        """
-        Return names for all the available key pairs.
+        :param      cidr_ips: The list of ip ranges to allow traffic for.
+        :type       cidr_ips: ``list``
 
-        @note: This is a non-standard extension API, and only works for EC2.
+        :param      group_pairs: Source user/group pairs to allow traffic for.
+                    More info can be found at http://goo.gl/stBHJF
 
-        :rtype: ``list`` of ``str``
-        """
-        names = [key_pair.name for key_pair in self.list_key_pairs()]
-        return names
+                    EC2 Classic Example: To allow access from any system
+                    associated with the default group on account 1234567890
 
-    def ex_describe_keypairs(self, name):
-        """
-        Here for backward compatibility.
-        """
-        return self.ex_describe_keypair(name=name)
+                    [{'group_name': 'default', 'user_id': '1234567890'}]
 
-    def ex_describe_keypair(self, name):
-        """
-        Describes a keypair by name.
+                    VPC Example: Allow access from any system associated with
+                    security group sg-47ad482e on your own account
 
-        @note: This is a non-standard extension API, and only works for EC2.
+                    [{'group_id': ' sg-47ad482e'}]
+        :type       group_pairs: ``list`` of ``dict``
 
-        :param      name: The name of the keypair to describe.
-        :type       name: ``str``
+        :param      protocol: tcp/udp/icmp
+        :type       protocol: ``str``
 
-        :rtype: ``dict``
+        :rtype: ``bool``
         """
 
-        params = {
-            'Action': 'DescribeKeyPairs',
-            'KeyName.1': name
-        }
+        params = self._get_common_security_group_params(id,
+                                                        protocol,
+                                                        from_port,
+                                                        to_port,
+                                                        cidr_ips,
+                                                        group_pairs)
 
-        response = self.connection.request(self.path, params=params).object
-        key_name = findattr(element=response, xpath='keySet/item/keyName',
-                            namespace=NAMESPACE)
-        fingerprint = findattr(element=response,
-                               xpath='keySet/item/keyFingerprint',
-                               namespace=NAMESPACE).strip()
-        return {
-            'keyName': key_name,
-            'keyFingerprint': fingerprint
-        }
+        params["Action"] = 'RevokeSecurityGroupIngress'
 
-    def ex_list_networks(self):
-        """
-        Return a list of :class:`EC2Network` objects for the
-        current region.
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
 
-        :rtype:     ``list`` of :class:`EC2Network`
+        return element == 'true'
+
+    def ex_revoke_security_group_egress(self, id, from_port, to_port,
+                                        cidr_ips=None, group_pairs=None,
+                                        protocol='tcp'):
         """
-        params = {'Action': 'DescribeVpcs'}
+        Edit a Security Group to revoke specific egress traffic using
+        CIDR blocks or either a group ID, group name or user ID (account).
+        This call is not supported for EC2 classic and only works for
+        VPC groups.
 
-        return self._to_networks(
-            self.connection.request(self.path, params=params).object
-        )
+        :param      id: The id of the security group to edit
+        :type       id: ``str``
 
-    def ex_create_network(self, cidr_block, name=None,
-                          instance_tenancy='default'):
-        """
-        Create a network/VPC
+        :param      from_port: The beginning of the port range to open
+        :type       from_port: ``int``
 
-        :param      cidr_block: The CIDR block assigned to the network
-        :type       cidr_block: ``str``
+        :param      to_port: The end of the port range to open
+        :type       to_port: ``int``
 
-        :param      name: An optional name for the network
-        :type       name: ``str``
+        :param      cidr_ips: The list of ip ranges to allow traffic for.
+        :type       cidr_ips: ``list``
 
-        :param      instance_tenancy: The allowed tenancy of instances launched
-                                      into the VPC.
-                                      Valid values: default/dedicated
-        :type       instance_tenancy: ``str``
+        :param      group_pairs: Source user/group pairs to allow traffic for.
+                    More info can be found at http://goo.gl/stBHJF
 
-        :return:    Dictionary of network properties
-        :rtype:     ``dict``
-        """
-        params = {'Action': 'CreateVpc',
-                  'CidrBlock': cidr_block,
-                  'InstanceTenancy':  instance_tenancy}
+                    EC2 Classic Example: To allow access from any system
+                    associated with the default group on account 1234567890
 
-        response = self.connection.request(self.path, params=params).object
-        element = response.findall(fixxpath(xpath='vpc',
-                                            namespace=NAMESPACE))[0]
+                    [{'group_name': 'default', 'user_id': '1234567890'}]
 
-        network = self._to_network(element)
+                    VPC Example: Allow access from any system associated with
+                    security group sg-47ad482e on your own account
 
-        if name is not None:
-            self.ex_create_tags(network, {'Name': name})
+                    [{'group_id': ' sg-47ad482e'}]
+        :type       group_pairs: ``list`` of ``dict``
 
-        return network
+        :param      protocol: tcp/udp/icmp
+        :type       protocol: ``str``
 
-    def ex_delete_network(self, vpc):
+        :rtype: ``bool``
         """
-        Deletes a network/VPC.
 
-        :param      vpc: VPC to delete.
-        :type       vpc: :class:`.EC2Network`
+        params = self._get_common_security_group_params(id,
+                                                        protocol,
+                                                        from_port,
+                                                        to_port,
+                                                        cidr_ips,
+                                                        group_pairs)
 
-        :rtype:     ``bool``
-        """
-        params = {'Action': 'DeleteVpc', 'VpcId': vpc.id}
+        params['Action'] = 'RevokeSecurityGroupEgress'
 
         result = self.connection.request(self.path, params=params).object
         element = findtext(element=result, xpath='return',
@@ -2057,368 +1932,462 @@ class BaseEC2NodeDriver(NodeDriver):
 
         return element == 'true'
 
-    def ex_list_subnets(self):
+    def ex_authorize_security_group_permissive(self, name):
         """
-        Return a list of :class:`EC2NetworkSubnet` objects for the
-        current region.
+        Edit a Security Group to allow all traffic.
 
-        :rtype:     ``list`` of :class:`EC2NetworkSubnet`
-        """
-        params = {'Action': 'DescribeSubnets'}
+        @note: This is a non-standard extension API, and only works for EC2.
 
-        return self._to_subnets(
-            self.connection.request(self.path, params=params).object
-        )
+        :param      name: The name of the security group to edit
+        :type       name: ``str``
 
-    def ex_create_subnet(self, vpc_id, cidr_block,
-                         availability_zone, name=None):
+        :rtype: ``list`` of ``str``
         """
-        Create a network subnet within a VPC
 
-        :param      vpc_id: The ID of the VPC that the subnet should be
-                            associated with
-        :type       vpc_id: ``str``
+        results = []
+        params = {'Action': 'AuthorizeSecurityGroupIngress',
+                  'GroupName': name,
+                  'IpProtocol': 'tcp',
+                  'FromPort': '0',
+                  'ToPort': '65535',
+                  'CidrIp': '0.0.0.0/0'}
+        try:
+            results.append(
+                self.connection.request(self.path, params=params.copy()).object
+            )
+        except Exception:
+            e = sys.exc_info()[1]
+            if e.args[0].find("InvalidPermission.Duplicate") == -1:
+                raise e
+        params['IpProtocol'] = 'udp'
 
-        :param      cidr_block: The CIDR block assigned to the subnet
-        :type       cidr_block: ``str``
+        try:
+            results.append(
+                self.connection.request(self.path, params=params.copy()).object
+            )
+        except Exception:
+            e = sys.exc_info()[1]
+            if e.args[0].find("InvalidPermission.Duplicate") == -1:
+                raise e
 
-        :param      availability_zone: The availability zone where the subnet
-                                       should reside
-        :type       availability_zone: ``str``
+        params.update({'IpProtocol': 'icmp', 'FromPort': '-1', 'ToPort': '-1'})
 
-        :param      name: An optional name for the network
-        :type       name: ``str``
+        try:
+            results.append(
+                self.connection.request(self.path, params=params.copy()).object
+            )
+        except Exception:
+            e = sys.exc_info()[1]
 
-        :rtype:     :class: `EC2NetworkSubnet`
+            if e.args[0].find("InvalidPermission.Duplicate") == -1:
+                raise e
+        return results
+
+    def ex_list_availability_zones(self, only_available=True):
         """
-        params = {'Action': 'CreateSubnet',
-                  'VpcId': vpc_id,
-                  'CidrBlock': cidr_block,
-                  'AvailabilityZone': availability_zone}
+        Return a list of :class:`ExEC2AvailabilityZone` objects for the
+        current region.
 
-        response = self.connection.request(self.path, params=params).object
-        element = response.findall(fixxpath(xpath='subnet',
-                                            namespace=NAMESPACE))[0]
+        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: ``str``
+
+        :rtype: ``list`` of :class:`ExEC2AvailabilityZone`
+        """
+        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
 
-        subnet = self._to_subnet(element)
+        availability_zones = []
+        for element in findall(element=result,
+                               xpath='availabilityZoneInfo/item',
+                               namespace=NAMESPACE):
+            name = findtext(element=element, xpath='zoneName',
+                            namespace=NAMESPACE)
+            zone_state = findtext(element=element, xpath='zoneState',
+                                  namespace=NAMESPACE)
+            region_name = findtext(element=element, xpath='regionName',
+                                   namespace=NAMESPACE)
 
-        if name is not None:
-            self.ex_create_tags(subnet, {'Name': name})
+            availability_zone = ExEC2AvailabilityZone(
+                name=name,
+                zone_state=zone_state,
+                region_name=region_name
+            )
+            availability_zones.append(availability_zone)
 
-        return subnet
+        return availability_zones
 
-    def ex_delete_subnet(self, subnet):
+    def ex_describe_tags(self, resource):
         """
-        Deletes a VPC subnet.
+        Return a dictionary of tags for a resource (Node or StorageVolume).
 
-        :param      subnet: The subnet to delete
-        :type       subnet: :class:`.EC2NetworkSubnet`
+        :param  resource: resource which should be used
+        :type   resource: :class:`Node` or :class:`StorageVolume`
 
-        :rtype:     ``bool``
+        :return: dict Node tags
+        :rtype: ``dict``
         """
-        params = {'Action': 'DeleteSubnet', 'SubnetId': subnet.id}
+        params = {'Action': 'DescribeTags',
+                  'Filter.0.Name': 'resource-id',
+                  'Filter.0.Value.0': resource.id,
+                  'Filter.1.Name': 'resource-type',
+                  'Filter.1.Value.0': 'instance',
+                  }
 
         result = self.connection.request(self.path, params=params).object
-        element = findtext(element=result,
-                           xpath='return',
-                           namespace=NAMESPACE)
 
-        return element == 'true'
+        return self._get_resource_tags(result)
 
-    def ex_list_security_groups(self):
+    def ex_create_tags(self, resource, tags):
         """
-        List existing Security Groups.
+        Create tags for a resource (Node or StorageVolume).
 
-        @note: This is a non-standard extension API, and only works for EC2.
+        :param resource: Resource to be tagged
+        :type resource: :class:`Node` or :class:`StorageVolume`
 
-        :rtype: ``list`` of ``str``
+        :param tags: A dictionary or other mapping of strings to strings,
+                     associating tag names with tag values.
+        :type tags: ``dict``
+
+        :rtype: ``bool``
         """
-        params = {'Action': 'DescribeSecurityGroups'}
-        response = self.connection.request(self.path, params=params).object
+        if not tags:
+            return
 
-        groups = []
-        for group in findall(element=response, xpath='securityGroupInfo/item',
-                             namespace=NAMESPACE):
-            name = findtext(element=group, xpath='groupName',
-                            namespace=NAMESPACE)
-            groups.append(name)
+        params = {'Action': 'CreateTags',
+                  'ResourceId.0': resource.id}
+        for i, key in enumerate(tags):
+            params['Tag.%d.Key' % i] = key
+            params['Tag.%d.Value' % i] = tags[key]
 
-        return groups
+        result = self.connection.request(self.path,
+                                         params=params.copy()).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+        return element == 'true'
 
-    def ex_create_security_group(self, name, description, vpc_id=None):
+    def ex_delete_tags(self, resource, tags):
         """
-        Creates a new Security Group in EC2-Classic or a targetted VPC.
-
-        :param      name:        The name of the security group to Create.
-                                 This must be unique.
-        :type       name:        ``str``
+        Delete tags from a resource.
 
-        :param      description: Human readable description of a Security
-                                 Group.
-        :type       description: ``str``
+        :param resource: Resource to be tagged
+        :type resource: :class:`Node` or :class:`StorageVolume`
 
-        :param      description: Optional identifier for VPC networks
-        :type       description: ``str``
+        :param tags: A dictionary or other mapping of strings to strings,
+                     specifying the tag names and tag values to be deleted.
+        :type tags: ``dict``
 
-        :rtype: ``dict``
+        :rtype: ``bool``
         """
-        params = {'Action': 'CreateSecurityGroup',
-                  'GroupName': name,
-                  'GroupDescription': description}
+        if not tags:
+            return
 
-        if vpc_id is not None:
-            params['VpcId'] = vpc_id
+        params = {'Action': 'DeleteTags',
+                  'ResourceId.0': resource.id}
+        for i, key in enumerate(tags):
+            params['Tag.%d.Key' % i] = key
+            params['Tag.%d.Value' % i] = tags[key]
 
-        response = self.connection.request(self.path, params=params).object
-        group_id = findattr(element=response, xpath='groupId',
-                            namespace=NAMESPACE)
-        return {
-            'group_id': group_id
-        }
+        result = self.connection.request(self.path,
+                                         params=params.copy()).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+        return element == 'true'
 
-    def ex_delete_security_group_by_id(self, group_id):
+    def ex_get_metadata_for_node(self, node):
         """
-        Deletes a new Security Group using the group id.
+        Return the metadata associated with the node.
 
-        :param      group_id: The ID of the security group
-        :type       group_id: ``str``
+        :param      node: Node instance
+        :type       node: :class:`Node`
 
-        :rtype: ``bool``
+        :return: A dictionary or other mapping of strings to strings,
+                 associating tag names with tag values.
+        :rtype tags: ``dict``
         """
-        params = {'Action': 'DeleteSecurityGroup', 'GroupId': group_id}
+        return node.extra['tags']
 
-        result = self.connection.request(self.path, params=params).object
-        element = findtext(element=result, xpath='return',
-                           namespace=NAMESPACE)
+    def ex_allocate_address(self, domain='standard'):
+        """
+        Allocate a new Elastic IP address for EC2 classic or VPC
 
-        return element == 'true'
+        :param      domain: The domain to allocate the new address in
+                            (standard/vpc)
+        :type       domain: ``str``
 
-    def ex_delete_security_group_by_name(self, group_name):
+        :return:    Instance of ElasticIP
+        :rtype:     :class:`ElasticIP`
         """
-        Deletes a new Security Group using the group name.
+        params = {'Action': 'AllocateAddress'}
 
-        :param      group_name: The name of the security group
-        :type       group_name: ``str``
+        if domain == 'vpc':
+            params['Domain'] = domain
 
-        :rtype: ``bool``
+        response = self.connection.request(self.path, params=params).object
+
+        return self._to_address(response, only_associated=False)
+
+    def ex_release_address(self, elastic_ip, domain=None):
         """
-        params = {'Action': 'DeleteSecurityGroup', 'GroupName': group_name}
+        Release an Elastic IP address using the IP (EC2-Classic) or
+        using the allocation ID (VPC)
 
-        result = self.connection.request(self.path, params=params).object
-        element = findtext(element=result, xpath='return',
-                           namespace=NAMESPACE)
+        :param      elastic_ip: Elastic IP instance
+        :type       elastic_ip: :class:`ElasticIP`
 
-        return element == 'true'
+        :param      domain: The domain where the IP resides (vpc only)
+        :type       domain: ``str``
 
-    def ex_delete_security_group(self, name):
+        :return:    True on success, False otherwise.
+        :rtype:     ``bool``
         """
-        Wrapper method which calls ex_delete_security_group_by_name.
+        params = {'Action': 'ReleaseAddress'}
 
-        :param      name: The name of the security group
-        :type       name: ``str``
+        if domain is not None and domain != 'vpc':
+            raise AttributeError('Domain can only be set to vpc')
 
-        :rtype: ``bool``
+        if domain is None:
+            params['PublicIp'] = elastic_ip.ip
+        else:
+            params['AllocationId'] = elastic_ip.extra['allocation_id']
+
+        response = self.connection.request(self.path, params=params).object
+        return self._get_boolean(response)
+
+    def ex_describe_all_addresses(self, only_associated=False):
         """
-        return self.ex_delete_security_group_by_name(name)
+        Return all the Elastic IP addresses for this account
+        optionally, return only addresses associated with nodes
 
-    def ex_authorize_security_group(self, name, from_port, to_port, cidr_ip,
-                                    protocol='tcp'):
+        :param    only_associated: If true, return only those addresses
+                                   that are associated with an instance.
+        :type     only_associated: ``bool``
+
+        :return:  List of ElasticIP instances.
+        :rtype:   ``list`` of :class:`ElasticIP`
         """
-        Edit a Security Group to allow specific traffic.
+        params = {'Action': 'DescribeAddresses'}
 
-        @note: This is a non-standard extension API, and only works for EC2.
+        response = self.connection.request(self.path, params=params).object
 
-        :param      name: The name of the security group to edit
-        :type       name: ``str``
+        # We will send our only_associated boolean over to
+        # shape how the return data is sent back
+        return self._to_addresses(response, only_associated)
 
-        :param      from_port: The beginning of the port range to open
-        :type       from_port: ``str``
+    def ex_associate_address_with_node(self, node, elastic_ip, domain=None):
+        """
+        Associate an Elastic IP address with a particular node.
 
-        :param      to_port: The end of the port range to open
-        :type       to_port: ``str``
+        :param      node: Node instance
+        :type       node: :class:`Node`
 
-        :param      cidr_ip: The ip to allow traffic for.
-        :type       cidr_ip: ``str``
+        :param      elastic_ip: Elastic IP instance
+        :type       elastic_ip: :class:`ElasticIP`
 
-        :param      protocol: tcp/udp/icmp
-        :type       protocol: ``str``
+        :param      domain: The domain where the IP resides (vpc only)
+        :type       domain: ``str``
 
-        :rtype: ``bool``
+        :return:    A string representation of the association ID which is
+                    required for VPC disassociation. EC2/standard
+                    addresses return None
+        :rtype:     ``None`` or ``str``
         """
+        params = {'Action': 'AssociateAddress', 'InstanceId': node.id}
 
-        params = {'Action': 'AuthorizeSecurityGroupIngress',
-                  'GroupName': name,
-                  'IpProtocol': protocol,
-                  'FromPort': str(from_port),
-                  'ToPort': str(to_port),
-                  'CidrIp': cidr_ip}
-        try:
-            resp = self.connection.request(
-                self.path, params=params.copy()).object
-            return bool(findtext(element=resp, xpath='return',
-                                 namespace=NAMESPACE))
-        except Exception:
-            e = sys.exc_info()[1]
-            if e.args[0].find('InvalidPermission.Duplicate') == -1:
-                raise e
+        if domain is not None and domain != 'vpc':
+            raise AttributeError('Domain can only be set to vpc')
+
+        if domain is None:
+            params.update({'PublicIp': elastic_ip.ip})
+        else:
+            params.update({'AllocationId': elastic_ip.extra['allocation_id']})
+
+        response = self.connection.request(self.path, params=params).object
+        association_id = findtext(element=response,
+                                  xpath='associationId',
+                                  namespace=NAMESPACE)
+        return association_id
 
-    def ex_authorize_security_group_ingress(self, id, from_port, to_port,
-                                            cidr_ips=None, group_pairs=None,
-                                            protocol='tcp'):
+    def ex_associate_addresses(self, node, elastic_ip, domain=None):
+        """
+        Note: This method has been deprecated in favor of
+        the ex_associate_address_with_node method.
         """
-        Edit a Security Group to allow specific ingress traffic using
-        CIDR blocks or either a group ID, group name or user ID (account).
-
-        :param      id: The id of the security group to edit
-        :type       id: ``str``
 
-        :param      from_port: The beginning of the port range to open
-        :type       from_port: ``int``
+        return self.ex_associate_address_with_node(node=node,
+                                                   elastic_ip=elastic_ip,
+                                                   domain=domain)
 
-        :param      to_port: The end of the port range to open
-        :type       to_port: ``int``
+    def ex_disassociate_address(self, elastic_ip, domain=None):
+        """
+        Disassociate an Elastic IP address using the IP (EC2-Classic)
+        or the association ID (VPC)
 
-        :param      cidr_ips: The list of ip ranges to allow traffic for.
-        :type       cidr_ips: ``list``
+        :param      elastic_ip: ElasticIP instance
+        :type       elastic_ip: :class:`ElasticIP`
 
-        :param      group_pairs: Source user/group pairs to allow traffic for.
-                    More info can be found at http://goo.gl/stBHJF
+        :param      domain: The domain where the IP resides (vpc only)
+        :type       domain: ``str``
 
-                    EC2 Classic Example: To allow access from any system
-                    associated with the default group on account 1234567890
+        :return:    True on success, False otherwise.
+        :rtype:     ``bool``
+        """
+        params = {'Action': 'DisassociateAddress'}
 
-                    [{'group_name': 'default', 'user_id': '1234567890'}]
+        if domain is not None and domain != 'vpc':
+            raise AttributeError('Domain can only be set to vpc')
 
-                    VPC Example: Allow access from any system associated with
-                    security group sg-47ad482e on your own account
+        if domain is None:
+            params['PublicIp'] = elastic_ip.ip
 
-                    [{'group_id': ' sg-47ad482e'}]
-        :type       group_pairs: ``list`` of ``dict``
+        else:
+            params['AssociationId'] = elastic_ip.extra['association_id']
 
-        :param      protocol: tcp/udp/icmp
-        :type       protocol: ``str``
+        res = self.connection.request(self.path, params=params).object
+        return self._get_boolean(res)
 
-        :rtype: ``bool``
+    def ex_describe_addresses(self, nodes):
         """
+        Return Elastic IP addresses for all the nodes in the provided list.
 
-        params = self._get_common_security_group_params(id,
-                                                        protocol,
-                                                        from_port,
-                                                        to_port,
-                                                        cidr_ips,
-                                                        group_pairs)
+        :param      nodes: List of :class:`Node` instances
+        :type       nodes: ``list`` of :class:`Node`
 
-        params["Action"] = 'AuthorizeSecurityGroupIngress'
+        :return:    Dictionary where a key is a node ID and the value is a
+                    list with the Elastic IP addresses associated with
+                    this node.
+        :rtype:     ``dict``
+        """
+        if not nodes:
+            return {}
 
-        result = self.connection.request(self.path, params=params).object
-        element = findtext(element=result, xpath='return',
-                           namespace=NAMESPACE)
+        params = {'Action': 'DescribeAddresses'}
 
-        return element == 'true'
+        if len(nodes) == 1:
+            self._add_instance_filter(params, nodes[0])
 
-    def ex_authorize_security_group_egress(self, id, from_port, to_port,
-                                           cidr_ips=None, group_pairs=None,
-                                           protocol='tcp'):
-        """
-        Edit a Security Group to allow specific egress traffic using
-        CIDR blocks or either a group ID, group name or user ID (account).
-        This call is not supported for EC2 classic and only works for VPC
-        groups.
+        result = self.connection.request(self.path, params=params).object
 
-        :param      id: The id of the security group to edit
-        :type       id: ``str``
+        node_instance_ids = [node.id for node in nodes]
+        nodes_elastic_ip_mappings = {}
 
-        :param      from_port: The beginning of the port range to open
-        :type       from_port: ``int``
+        # We will set only_associated to True so that we only get back
+        # IPs which are associated with instances
+        only_associated = True
 
-        :param      to_port: The end of the port range to open
-        :type       to_port: ``int``
+        for node_id in node_instance_ids:
+            nodes_elastic_ip_mappings.setdefault(node_id, [])
+            for addr in self._to_addresses(result,
+                                           only_associated):
 
-        :param      cidr_ips: The list of ip ranges to allow traffic for.
-        :type       cidr_ips: ``list``
+                instance_id = addr.instance_id
 
-        :param      group_pairs: Source user/group pairs to allow traffic for.
-                    More info can be found at http://goo.gl/stBHJF
+                if node_id == instance_id:
+                    nodes_elastic_ip_mappings[instance_id].append(
+                        addr.ip)
 
-                    EC2 Classic Example: To allow access from any system
-                    associated with the default group on account 1234567890
+        return nodes_elastic_ip_mappings
 
-                    [{'group_name': 'default', 'user_id': '1234567890'}]
+    def ex_describe_addresses_for_node(self, node):
+        """
+        Return a list of Elastic IP addresses associated with this node.
 
-                    VPC Example: Allow access from any system associated with
-                    security group sg-47ad482e on your own account
+        :param      node: Node instance
+        :type       node: :class:`Node`
 
-                    [{'group_id': ' sg-47ad482e'}]
-        :type       group_pairs: ``list`` of ``dict``
+        :return: list Elastic IP addresses attached to this node.
+        :rtype: ``list`` of ``str``
+        """
+        node_elastic_ips = self.ex_describe_addresses([node])
+        return node_elastic_ips[node.id]
 
-        :param      protocol: tcp/udp/icmp
-        :type       protocol: ``str``
+    # Network interface management methods
 
-        :rtype: ``bool``
+    def ex_list_network_interfaces(self):
         """
+        Return all network interfaces
 
-        params = self._get_common_security_group_params(id,
-                                                        protocol,
-                                                        from_port,
-                                                        to_port,
-                                                        cidr_ips,
-                                                        group_pairs)
+        :return:    List of EC2NetworkInterface instances
+        :rtype:     ``list`` of :class `EC2NetworkInterface`
+        """
+        params = {'Action': 'DescribeNetworkInterfaces'}
 
-        params["Action"] = 'AuthorizeSecurityGroupEgress'
+        return self._to_interfaces(
+            self.connection.request(self.path, params=params).object
+        )
 
-        result = self.connection.request(self.path, params=params).object
-        element = findtext(element=result, xpath='return',
-                           namespace=NAMESPACE)
+    def ex_create_network_interface(self, subnet, name=None,
+                                    description=None,
+                                    private_ip_address=None):
+        """
+        Create a network interface within a VPC subnet.
 
-        return element == 'true'
+        :param      node: EC2NetworkSubnet instance
+        :type       node: :class:`EC2NetworkSubnet`
 
-    def ex_revoke_security_group_ingress(self, id, from_port, to_port,
-                                         cidr_ips=None, group_pairs=None,
-                                         protocol='tcp'):
-        """
-        Edit a Security Group to revoke specific ingress traffic using
-        CIDR blocks or either a group ID, group name or user ID (account).
+        :param      name:  Optional name of the interface
+        :type       name:  ``str``
 
-        :param      id: The id of the security group to edit
-        :type       id: ``str``
+        :param      description:  Optional description of the network interface
+        :type       description:  ``str``
 
-        :param      from_port: The beginning of the port range to open
-        :type       from_port: ``int``
+        :param      private_ip_address: Optional address to assign as the
+                                        primary private IP address of the
+                                        interface. If one is not provided then
+                                        Amazon will automatically auto-assign
+                                        an available IP. EC2 allows assignment
+                                        of multiple IPs, but this will be
+                                        the primary.
+        :type       private_ip_address: ``str``
 
-        :param      to_port: The end of the port range to open
-        :type       to_port: ``int``
+        :return:    EC2NetworkInterface instance
+        :rtype:     :class `EC2NetworkInterface`
+        """
+        params = {'Action': 'CreateNetworkInterface',
+                  'SubnetId': subnet.id}
 
-        :param      cidr_ips: The list of ip ranges to allow traffic for.
-        :type       cidr_ips: ``list``
+        if description:
+            params['Description'] = description
 
-        :param      group_pairs: Source user/group pairs to allow traffic for.
-                    More info can be found at http://goo.gl/stBHJF
+        if private_ip_address:
+            params['PrivateIpAddress'] = private_ip_address
 
-                    EC2 Classic Example: To allow access from any system
-                    associated with the default group on account 1234567890
+        response = self.connection.request(self.path, params=params).object
 
-                    [{'group_name': 'default', 'user_id': '1234567890'}]
+        element = response.findall(fixxpath(xpath='networkInterface',
+                                            namespace=NAMESPACE))[0]
 
-                    VPC Example: Allow access from any system associated with
-                    security group sg-47ad482e on your own account
+        interface = self._to_interface(element, name)
 
-                    [{'group_id': ' sg-47ad482e'}]
-        :type       group_pairs: ``list`` of ``dict``
+        if name is not None:
+            tags = {'Name': name}
+            self.ex_create_tags(resource=interface, tags=tags)
 
-        :param      protocol: tcp/udp/icmp
-        :type       protocol: ``str``
+        return interface
 
-        :rtype: ``bool``
+    def ex_delete_network_interface(self, network_interface):
         """
+        Deletes a network interface.
 
-        params = self._get_common_security_group_params(id,
-                                                        protocol,
-                                                        from_port,
-                                                        to_port,
-                                                        cidr_ips,
-                                                        group_pairs)
+        :param      network_interface: EC2NetworkInterface instance
+        :type       network_interface: :class:`EC2NetworkInterface`
 
-        params["Action"] = 'RevokeSecurityGroupIngress'
+        :rtype:     ``bool``
+        """
+        params = {'Action': 'DeleteNetworkInterface',
+                  'NetworkInterfaceId': network_interface.id}
 
         result = self.connection.request(self.path, params=params).object
         element = findtext(element=result, xpath='return',
@@ -2426,873 +2395,916 @@ class BaseEC2NodeDriver(NodeDriver):
 
         return element == 'true'
 
-    def ex_revoke_security_group_egress(self, id, from_port, to_port,
-                                        cidr_ips=None, group_pairs=None,
-                                        protocol='tcp'):
+    def ex_attach_network_interface_to_node(self, network_interface,
+                                            node, device_index):
         """
-        Edit a Security Group to revoke specific egress traffic using
-        CIDR blocks or either a group ID, group name or user ID (account).
-        This call is not supported for EC2 classic and only works for
-        VPC groups.
-
-        :param      id: The id of the security group to edit
-        :type       id: ``str``
+        Attatch a network interface to an instance.
 
-        :param      from_port: The beginning of the port range to open
-        :type       from_port: ``int``
+        :param      network_interface: EC2NetworkInterface instance
+        :type       network_interface: :class:`EC2NetworkInterface`
 
-        :param      to_port: The end of the port range to open
-        :type       to_port: ``int``
+        :param      node: Node instance
+        :type       node: :class:`Node`
 
-        :param      cidr_ips: The list of ip ranges to allow traffic for.
-        :type       cidr_ips: ``list``
+        :param      device_index: The interface device index
+        :type       device_index: ``int``
 
-        :param      group_pairs: Source user/group pairs to allow traffic for.
-                    More info can be found at http://goo.gl/stBHJF
+        :return:    String representation of the attachment id.
+                    This is required to detach the interface.
+        :rtype:     ``str``
+        """
+        params = {'Action': 'AttachNetworkInterface',
+                  'NetworkInterfaceId': network_interface.id,
+                  'InstanceId': node.id,
+                  'DeviceIndex': device_index}
 
-                    EC2 Classic Example: To allow access from any system
-                    associated with the default group on account 1234567890
+        response = self.connection.request(self.path, params=params).object
+        attachment_id = findattr(element=response, xpath='attachmentId',
+                                 namespace=NAMESPACE)
 
-                    [{'group_name': 'default', 'user_id': '1234567890'}]
+        return attachment_id
 
-       

<TRUNCATED>

[4/4] git commit: Add __all__ to ec2 module.

Posted by to...@apache.org.
Add __all__ to ec2 module.


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/9548848c
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/9548848c
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/9548848c

Branch: refs/heads/trunk
Commit: 9548848cba3a3a9b9ffe6cd5ec4ca1736fa87c06
Parents: 439adb3
Author: Tomaz Muraus <to...@apache.org>
Authored: Sun Jan 12 02:22:10 2014 +0100
Committer: Tomaz Muraus <to...@apache.org>
Committed: Sun Jan 12 02:22:10 2014 +0100

----------------------------------------------------------------------
 libcloud/compute/drivers/ec2.py | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/9548848c/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py
index 54459a1..fb7b5c9 100644
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@ -40,6 +40,27 @@ from libcloud.compute.base import NodeImage, StorageVolume, VolumeSnapshot
 from libcloud.compute.base import KeyPair
 from libcloud.compute.types import NodeState, KeyPairDoesNotExistError
 
+__all__ = [
+    'API_VERSION',
+    'NAMESPACE',
+    'INSTANCE_TYPES',
+
+    'EC2NodeDriver',
+    'BaseEC2NodeDriver',
+
+    'NimbusNodeDriver',
+    'EucNodeDriver',
+
+    'EC2NodeLocation',
+    'EC2ReservedNode',
+    'EC2Network',
+    'EC2NetworkSubnet',
+    'EC2NetworkInterface',
+    'ExEC2AvailabilityZone',
+
+    'IdempotentParamError'
+]
+
 API_VERSION = '2013-10-15'
 NAMESPACE = 'http://ec2.amazonaws.com/doc/%s/' % (API_VERSION)
 


[3/4] git commit: Re-organize methods on the EC2 driver and move extension and "private" methods to the end.

Posted by to...@apache.org.
Re-organize methods on the EC2 driver and move extension and "private"
methods to the end.


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/439adb30
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/439adb30
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/439adb30

Branch: refs/heads/trunk
Commit: 439adb308d6532e0c727af1bbc8dbc839be4edd5
Parents: 678f54a
Author: Tomaz Muraus <to...@apache.org>
Authored: Sun Jan 12 02:15:04 2014 +0100
Committer: Tomaz Muraus <to...@apache.org>
Committed: Sun Jan 12 02:15:04 2014 +0100

----------------------------------------------------------------------
 libcloud/compute/drivers/ec2.py | 3204 +++++++++++++++++-----------------
 1 file changed, 1608 insertions(+), 1596 deletions(-)
----------------------------------------------------------------------