Skip to content

Commit

Permalink
Merge pull request #3 from apache/trunk
Browse files Browse the repository at this point in the history
Pulling in latest changes
  • Loading branch information
cderamus committed Dec 23, 2013
2 parents e0872c1 + ab2b06f commit b5a105a
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 0 deletions.
9 changes: 9 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ Changes with Apache Libcloud in development
EC2 driver. (LIBCLOUD-466)
[Chris DeRamus]

- Add extension methods for deleting security groups
(ex_delete_security_group, ex_delete_security_group_by_id,
ex_delete_security_group_by_name) to the EC2 driver. (LIBCLOUD-464)
[Chris DeRamus]

- Add extension method for listing reserved instances
(ex_list_reserved_nodes) to the EC2 driver. (LIBCLOUD-469)
[Chris DeRamus]

*) Storage

- Allow user to specify 'Content-Disposition' header in the CloudFiles
Expand Down
116 changes: 116 additions & 0 deletions libcloud/compute/drivers/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,24 @@ def __repr__(self):
% (self.name, self.zone_state, self.region_name))


class EC2ReservedNode(Node):
"""
Class which stores information about EC2 reserved instances/nodes
Inherits from Node and passes in None for name and private/public IPs
Note: This class is EC2 specific.
"""

def __init__(self, id, state, driver, size=None, image=None, extra=None):
super(EC2ReservedNode, self).__init__(id=id, name=None, state=state,
public_ips=None,
private_ips=None,
driver=driver, extra=extra)

def __repr__(self):
return (('<EC2ReservedNode: id=%s>') % (self.id))


class BaseEC2NodeDriver(NodeDriver):
"""
Base Amazon EC2 node driver.
Expand Down Expand Up @@ -608,6 +626,88 @@ def _get_terminate_boolean(self, element):
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.
"""
# Build our extra attributes map
extra_attributes_map = {
'instance_type': {
'xpath': 'instanceType',
'type': str
},
'availability': {
'xpath': 'availabilityZone',
'type': str
},
'start': {
'xpath': 'start',
'type': str
},
'duration': {
'xpath': 'duration',
'type': int
},
'usage_price': {
'xpath': 'usagePrice',
'type': float
},
'fixed_price': {
'xpath': 'fixedPrice',
'type': float
},
'instance_count': {
'xpath': 'instanceCount',
'type': int
},
'description': {
'xpath': 'productDescription',
'type': str
},
'instance_tenancy': {
'xpath': 'instanceTenancy',
'type': str
},
'currency_code': {
'xpath': 'currencyCode',
'type': str
},
'offering_type': {
'xpath': 'offeringType',
'type': str
}
}

# Define and build our extra dictionary
extra = {}
for attribute, values in extra_attributes_map.items():
type_func = values['type']
value = findattr(element=element, xpath=values['xpath'],
namespace=NAMESPACE)
extra[attribute] = type_func(value)

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,
Expand Down Expand Up @@ -776,6 +876,22 @@ def _to_snapshot(self, element):
'description': description,
'state': state})

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
Expand Down
21 changes: 21 additions & 0 deletions libcloud/test/compute/fixtures/ec2/describe_reserved_instances.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<DescribeReservedInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>56d0fffa-8819-4658-bdd7-548f143a86d2</requestId>
<reservedInstancesSet>
<item>
<reservedInstancesId>93bbbca2-c500-49d0-9ede-9d8737400498</reservedInstancesId>
<instanceType>t1.micro</instanceType>
<availabilityZone>us-east-1b</availabilityZone>
<start>2013-06-18T12:07:53.161Z</start>
<duration>31536000</duration>
<fixedPrice>23.0</fixedPrice>
<usagePrice>0.012</usagePrice>
<instanceCount>1</instanceCount>
<productDescription>Linux/UNIX</productDescription>
<state>active</state>
<instanceTenancy>default</instanceTenancy>
<currencyCode>USD</currencyCode>
<offeringType>Light Utilization</offeringType>
<recurringCharges/>
</item>
</reservedInstancesSet>
</DescribeReservedInstancesResponse>
20 changes: 20 additions & 0 deletions libcloud/test/compute/test_ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,22 @@ def test_list_nodes(self):
'2009-08-07T05:47:04.000Z')
self.assertTrue('instancetype' in ret_node2.extra)

def test_ex_list_reserved_nodes(self):
node = self.driver.ex_list_reserved_nodes()[0]
self.assertEqual(node.id, '93bbbca2-c500-49d0-9ede-9d8737400498')
self.assertEqual(node.state, 'active')
self.assertEqual(node.extra['instance_type'], 't1.micro')
self.assertEqual(node.extra['availability'], 'us-east-1b')
self.assertEqual(node.extra['start'], '2013-06-18T12:07:53.161Z')
self.assertEqual(node.extra['duration'], 31536000)
self.assertEqual(node.extra['usage_price'], 0.012)
self.assertEqual(node.extra['fixed_price'], 23.0)
self.assertEqual(node.extra['instance_count'], 1)
self.assertEqual(node.extra['description'], 'Linux/UNIX')
self.assertEqual(node.extra['instance_tenancy'], 'default')
self.assertEqual(node.extra['currency_code'], 'USD')
self.assertEqual(node.extra['offering_type'], 'Light Utilization')

def test_list_nodes_with_name_tag(self):
EC2MockHttp.type = 'WITH_TAGS'
node = self.driver.list_nodes()[0]
Expand Down Expand Up @@ -843,6 +859,10 @@ def _WITH_TAGS_DescribeInstances(self, method, url, body, headers):
body = self.fixtures.load('describe_instances_with_tags.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])

def _DescribeReservedInstances(self, method, url, body, headers):
body = self.fixtures.load('describe_reserved_instances.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])

def _DescribeAvailabilityZones(self, method, url, body, headers):
body = self.fixtures.load('describe_availability_zones.xml')
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
Expand Down

0 comments on commit b5a105a

Please sign in to comment.