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(-)
----------------------------------------------------------------------