txaws-dev team mailing list archive
-
txaws-dev team
-
Mailing list archive
-
Message #00029
[Merge] lp:~fwierzbicki/txaws/break-out-ec2-parser into lp:txaws
You have been requested to review the proposed merge of lp:~fwierzbicki/txaws/break-out-ec2-parser into lp:txaws.
For more details, see:
https://code.launchpad.net/~fwierzbicki/txaws/break-out-ec2-parser/+merge/58623
This branch breaks out the private parsing methods from EC2Client into a separate Parser class with public methods so that they can be safely overridden. It also adds docstrings to the parser functions that lacked them.
--
https://code.launchpad.net/~fwierzbicki/txaws/break-out-ec2-parser/+merge/58623
Your team txAWS Developers is requested to review the proposed merge of lp:~fwierzbicki/txaws/break-out-ec2-parser into lp:txaws.
=== modified file 'txaws/client/base.py'
--- txaws/client/base.py 2011-04-14 16:57:11 +0000
+++ txaws/client/base.py 2011-04-21 06:05:51 +0000
@@ -57,8 +57,10 @@
@param endpoint: The service endpoint URI.
@param query_factory: The class or function that produces a query
object for making requests to the EC2 service.
+ @param parser: A parser object for parsing responses from the EC2 service.
"""
- def __init__(self, creds=None, endpoint=None, query_factory=None):
+ def __init__(self, creds=None, endpoint=None, query_factory=None,
+ parser=None):
if creds is None:
creds = AWSCredentials()
if endpoint is None:
@@ -66,6 +68,7 @@
self.creds = creds
self.endpoint = endpoint
self.query_factory = query_factory
+ self.parser = parser
class BaseQuery(object):
=== modified file 'txaws/client/tests/test_client.py'
--- txaws/client/tests/test_client.py 2009-11-22 21:48:59 +0000
+++ txaws/client/tests/test_client.py 2011-04-21 06:05:51 +0000
@@ -53,10 +53,11 @@
class BaseClientTestCase(TXAWSTestCase):
def test_creation(self):
- client = BaseClient("creds", "endpoint", "query factory")
+ client = BaseClient("creds", "endpoint", "query factory", "parser")
self.assertEquals(client.creds, "creds")
self.assertEquals(client.endpoint, "endpoint")
self.assertEquals(client.query_factory, "query factory")
+ self.assertEquals(client.parser, "parser")
class BaseQueryTestCase(TXAWSTestCase):
=== modified file 'txaws/ec2/client.py'
--- txaws/ec2/client.py 2011-04-19 17:42:18 +0000
+++ txaws/ec2/client.py 2011-04-21 06:05:51 +0000
@@ -26,10 +26,13 @@
class EC2Client(BaseClient):
"""A client for EC2."""
- def __init__(self, creds=None, endpoint=None, query_factory=None):
+ def __init__(self, creds=None, endpoint=None, query_factory=None,
+ parser=None):
if query_factory is None:
query_factory = Query
- super(EC2Client, self).__init__(creds, endpoint, query_factory)
+ if parser is None:
+ parser = Parser()
+ super(EC2Client, self).__init__(creds, endpoint, query_factory, parser)
def describe_instances(self, *instance_ids):
"""Describe current instances."""
@@ -40,91 +43,7 @@
action="DescribeInstances", creds=self.creds,
endpoint=self.endpoint, other_params=instances)
d = query.submit()
- return d.addCallback(self._parse_describe_instances)
-
- def _parse_instances_set(self, root, reservation):
- """Parse instance data out of an XML payload.
-
- @param root: The root node of the XML payload.
- @param reservation: The L{Reservation} associated with the instances
- from the response.
- @return: A C{list} of L{Instance}s.
- """
- instances = []
- for instance_data in root.find("instancesSet"):
- instances.append(self._parse_instance(instance_data, reservation))
- return instances
-
- def _parse_instance(self, instance_data, reservation):
- """Parse instance data out of an XML payload.
-
- @param instance_data: An XML node containing instance data.
- @param reservation: The L{Reservation} associated with the instance.
- @return: An L{Instance}.
- """
- instance_id = instance_data.findtext("instanceId")
- instance_state = instance_data.find(
- "instanceState").findtext("name")
- instance_type = instance_data.findtext("instanceType")
- image_id = instance_data.findtext("imageId")
- private_dns_name = instance_data.findtext("privateDnsName")
- dns_name = instance_data.findtext("dnsName")
- key_name = instance_data.findtext("keyName")
- ami_launch_index = instance_data.findtext("amiLaunchIndex")
- launch_time = instance_data.findtext("launchTime")
- placement = instance_data.find("placement").findtext(
- "availabilityZone")
- products = []
- product_codes = instance_data.find("productCodes")
- if product_codes:
- for product_data in instance_data.find("productCodes"):
- products.append(product_data.text)
- kernel_id = instance_data.findtext("kernelId")
- ramdisk_id = instance_data.findtext("ramdiskId")
- instance = model.Instance(
- instance_id, instance_state, instance_type, image_id,
- private_dns_name, dns_name, key_name, ami_launch_index,
- launch_time, placement, products, kernel_id, ramdisk_id,
- reservation=reservation)
- return instance
-
- def _parse_describe_instances(self, xml_bytes):
- """
- Parse the reservations XML payload that is returned from an AWS
- describeInstances API call.
-
- Instead of returning the reservations as the "top-most" object, we
- return the object that most developers and their code will be
- interested in: the instances. In instances reservation is available on
- the instance object.
-
- The following instance attributes are optional:
- * ami_launch_index
- * key_name
- * kernel_id
- * product_codes
- * ramdisk_id
- * reason
- """
- root = XML(xml_bytes)
- results = []
- # May be a more elegant way to do this:
- for reservation_data in root.find("reservationSet"):
- # Get the security group information.
- groups = []
- for group_data in reservation_data.find("groupSet"):
- group_id = group_data.findtext("groupId")
- groups.append(group_id)
- # Create a reservation object with the parsed data.
- reservation = model.Reservation(
- reservation_id=reservation_data.findtext("reservationId"),
- owner_id=reservation_data.findtext("ownerId"),
- groups=groups)
- # Get the list of instances.
- instances = self._parse_instances_set(
- reservation_data, reservation)
- results.extend(instances)
- return results
+ return d.addCallback(self.parser.parse_describe_instances)
def run_instances(self, image_id, min_count, max_count,
security_groups=None, key_name=None, instance_type=None,
@@ -152,27 +71,7 @@
action="RunInstances", creds=self.creds, endpoint=self.endpoint,
other_params=params)
d = query.submit()
- return d.addCallback(self._parse_run_instances)
-
- def _parse_run_instances(self, xml_bytes):
- """
- Parse the reservations XML payload that is returned from an AWS
- RunInstances API call.
- """
- root = XML(xml_bytes)
- # Get the security group information.
- groups = []
- for group_data in root.find("groupSet"):
- group_id = group_data.findtext("groupId")
- groups.append(group_id)
- # Create a reservation object with the parsed data.
- reservation = model.Reservation(
- reservation_id=root.findtext("reservationId"),
- owner_id=root.findtext("ownerId"),
- groups=groups)
- # Get the list of instances.
- instances = self._parse_instances_set(root, reservation)
- return instances
+ return d.addCallback(self.parser.parse_run_instances)
def terminate_instances(self, *instance_ids):
"""Terminate some instances.
@@ -188,20 +87,7 @@
action="TerminateInstances", creds=self.creds,
endpoint=self.endpoint, other_params=instances)
d = query.submit()
- return d.addCallback(self._parse_terminate_instances)
-
- def _parse_terminate_instances(self, xml_bytes):
- root = XML(xml_bytes)
- result = []
- # May be a more elegant way to do this:
- for instance in root.find("instancesSet"):
- instanceId = instance.findtext("instanceId")
- previousState = instance.find("previousState").findtext(
- "name")
- shutdownState = instance.find("shutdownState").findtext(
- "name")
- result.append((instanceId, previousState, shutdownState))
- return result
+ return d.addCallback(self.parser.parse_terminate_instances)
def describe_security_groups(self, *names):
"""Describe security groups.
@@ -219,48 +105,7 @@
action="DescribeSecurityGroups", creds=self.creds,
endpoint=self.endpoint, other_params=group_names)
d = query.submit()
- return d.addCallback(self._parse_describe_security_groups)
-
- def _parse_describe_security_groups(self, xml_bytes):
- """Parse the XML returned by the C{DescribeSecurityGroups} function.
-
- @param xml_bytes: XML bytes with a C{DescribeSecurityGroupsResponse}
- root element.
- @return: A list of L{SecurityGroup} instances.
- """
- root = XML(xml_bytes)
- result = []
- for group_info in root.findall("securityGroupInfo/item"):
- name = group_info.findtext("groupName")
- description = group_info.findtext("groupDescription")
- owner_id = group_info.findtext("ownerId")
- allowed_groups = []
- allowed_ips = []
- ip_permissions = group_info.find("ipPermissions") or ()
- for ip_permission in ip_permissions:
- ip_protocol = ip_permission.findtext("ipProtocol")
- from_port = int(ip_permission.findtext("fromPort"))
- to_port = int(ip_permission.findtext("toPort"))
- for groups in ip_permission.findall("groups/item") or ():
- user_id = groups.findtext("userId")
- group_name = groups.findtext("groupName")
- if user_id and group_name:
- if (user_id, group_name) not in allowed_groups:
- allowed_groups.append((user_id, group_name))
- for ip_ranges in ip_permission.findall("ipRanges/item") or ():
- cidr_ip = ip_ranges.findtext("cidrIp")
- allowed_ips.append(
- model.IPPermission(
- ip_protocol, from_port, to_port, cidr_ip))
-
- allowed_groups = [model.UserIDGroupPair(user_id, group_name)
- for user_id, group_name in allowed_groups]
-
- security_group = model.SecurityGroup(
- name, description, owner_id=owner_id,
- groups=allowed_groups, ips=allowed_ips)
- result.append(security_group)
- return result
+ return d.addCallback(self.parser.parse_describe_security_groups)
def create_security_group(self, name, description):
"""Create security group.
@@ -275,11 +120,7 @@
action="CreateSecurityGroup", creds=self.creds,
endpoint=self.endpoint, other_params=parameters)
d = query.submit()
- return d.addCallback(self._parse_truth_return)
-
- def _parse_truth_return(self, xml_bytes):
- root = XML(xml_bytes)
- return root.findtext("return") == "true"
+ return d.addCallback(self.parser.parse_truth_return)
def delete_security_group(self, name):
"""
@@ -292,7 +133,7 @@
action="DeleteSecurityGroup", creds=self.creds,
endpoint=self.endpoint, other_params=parameter)
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def authorize_security_group(
self, group_name, source_group_name="", source_group_owner_id="",
@@ -349,7 +190,7 @@
action="AuthorizeSecurityGroupIngress", creds=self.creds,
endpoint=self.endpoint, other_params=parameters)
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def authorize_group_permission(
self, group_name, source_group_name, source_group_owner_id):
@@ -434,7 +275,7 @@
action="RevokeSecurityGroupIngress", creds=self.creds,
endpoint=self.endpoint, other_params=parameters)
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def revoke_group_permission(
self, group_name, source_group_name, source_group_owner_id):
@@ -473,35 +314,7 @@
action="DescribeVolumes", creds=self.creds, endpoint=self.endpoint,
other_params=volumeset)
d = query.submit()
- return d.addCallback(self._parse_describe_volumes)
-
- def _parse_describe_volumes(self, xml_bytes):
- root = XML(xml_bytes)
- result = []
- for volume_data in root.find("volumeSet"):
- volume_id = volume_data.findtext("volumeId")
- size = int(volume_data.findtext("size"))
- status = volume_data.findtext("status")
- availability_zone = volume_data.findtext("availabilityZone")
- snapshot_id = volume_data.findtext("snapshotId")
- create_time = volume_data.findtext("createTime")
- create_time = datetime.strptime(
- create_time[:19], "%Y-%m-%dT%H:%M:%S")
- volume = model.Volume(
- volume_id, size, status, create_time, availability_zone,
- snapshot_id)
- result.append(volume)
- for attachment_data in volume_data.find("attachmentSet"):
- instance_id = attachment_data.findtext("instanceId")
- status = attachment_data.findtext("status")
- device = attachment_data.findtext("device")
- attach_time = attachment_data.findtext("attachTime")
- attach_time = datetime.strptime(
- attach_time[:19], "%Y-%m-%dT%H:%M:%S")
- attachment = model.Attachment(
- instance_id, device, status, attach_time)
- volume.attachments.append(attachment)
- return result
+ return d.addCallback(self.parser.parse_describe_volumes)
def create_volume(self, availability_zone, size=None, snapshot_id=None):
"""Create a new volume."""
@@ -517,29 +330,14 @@
action="CreateVolume", creds=self.creds, endpoint=self.endpoint,
other_params=params)
d = query.submit()
- return d.addCallback(self._parse_create_volume)
-
- def _parse_create_volume(self, xml_bytes):
- root = XML(xml_bytes)
- volume_id = root.findtext("volumeId")
- size = int(root.findtext("size"))
- status = root.findtext("status")
- create_time = root.findtext("createTime")
- availability_zone = root.findtext("availabilityZone")
- snapshot_id = root.findtext("snapshotId")
- create_time = datetime.strptime(
- create_time[:19], "%Y-%m-%dT%H:%M:%S")
- volume = model.Volume(
- volume_id, size, status, create_time, availability_zone,
- snapshot_id)
- return volume
+ return d.addCallback(self.parser.parse_create_volume)
def delete_volume(self, volume_id):
query = self.query_factory(
action="DeleteVolume", creds=self.creds, endpoint=self.endpoint,
other_params={"VolumeId": volume_id})
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def describe_snapshots(self, *snapshot_ids):
"""Describe available snapshots."""
@@ -550,24 +348,7 @@
action="DescribeSnapshots", creds=self.creds,
endpoint=self.endpoint, other_params=snapshot_set)
d = query.submit()
- return d.addCallback(self._parse_snapshots)
-
- def _parse_snapshots(self, xml_bytes):
- root = XML(xml_bytes)
- result = []
- for snapshot_data in root.find("snapshotSet"):
- snapshot_id = snapshot_data.findtext("snapshotId")
- volume_id = snapshot_data.findtext("volumeId")
- status = snapshot_data.findtext("status")
- start_time = snapshot_data.findtext("startTime")
- start_time = datetime.strptime(
- start_time[:19], "%Y-%m-%dT%H:%M:%S")
- progress = snapshot_data.findtext("progress")[:-1]
- progress = float(progress or "0") / 100.
- snapshot = model.Snapshot(
- snapshot_id, volume_id, status, start_time, progress)
- result.append(snapshot)
- return result
+ return d.addCallback(self.parser.parse_snapshots)
def create_snapshot(self, volume_id):
"""Create a new snapshot of an existing volume."""
@@ -575,20 +356,7 @@
action="CreateSnapshot", creds=self.creds, endpoint=self.endpoint,
other_params={"VolumeId": volume_id})
d = query.submit()
- return d.addCallback(self._parse_create_snapshot)
-
- def _parse_create_snapshot(self, xml_bytes):
- root = XML(xml_bytes)
- snapshot_id = root.findtext("snapshotId")
- volume_id = root.findtext("volumeId")
- status = root.findtext("status")
- start_time = root.findtext("startTime")
- start_time = datetime.strptime(
- start_time[:19], "%Y-%m-%dT%H:%M:%S")
- progress = root.findtext("progress")[:-1]
- progress = float(progress or "0") / 100.
- return model.Snapshot(
- snapshot_id, volume_id, status, start_time, progress)
+ return d.addCallback(self.parser.parse_create_snapshot)
def delete_snapshot(self, snapshot_id):
"""Remove a previously created snapshot."""
@@ -596,7 +364,7 @@
action="DeleteSnapshot", creds=self.creds, endpoint=self.endpoint,
other_params={"SnapshotId": snapshot_id})
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def attach_volume(self, volume_id, instance_id, device):
"""Attach the given volume to the specified instance at C{device}."""
@@ -605,15 +373,7 @@
other_params={"VolumeId": volume_id, "InstanceId": instance_id,
"Device": device})
d = query.submit()
- return d.addCallback(self._parse_attach_volume)
-
- def _parse_attach_volume(self, xml_bytes):
- root = XML(xml_bytes)
- status = root.findtext("status")
- attach_time = root.findtext("attachTime")
- attach_time = datetime.strptime(
- attach_time[:19], "%Y-%m-%dT%H:%M:%S")
- return {"status": status, "attach_time": attach_time}
+ return d.addCallback(self.parser.parse_attach_volume)
def describe_keypairs(self, *keypair_names):
"""Returns information about key pairs available."""
@@ -624,19 +384,7 @@
action="DescribeKeyPairs", creds=self.creds,
endpoint=self.endpoint, other_params=keypairs)
d = query.submit()
- return d.addCallback(self._parse_describe_keypairs)
-
- def _parse_describe_keypairs(self, xml_bytes):
- results = []
- root = XML(xml_bytes)
- keypairs = root.find("keySet")
- if not keypairs:
- return results
- for keypair_data in keypairs:
- key_name = keypair_data.findtext("keyName")
- key_fingerprint = keypair_data.findtext("keyFingerprint")
- results.append(model.Keypair(key_name, key_fingerprint))
- return results
+ return d.addCallback(self.parser.parse_describe_keypairs)
def create_keypair(self, keypair_name):
"""
@@ -647,14 +395,7 @@
action="CreateKeyPair", creds=self.creds, endpoint=self.endpoint,
other_params={"KeyName": keypair_name})
d = query.submit()
- return d.addCallback(self._parse_create_keypair)
-
- def _parse_create_keypair(self, xml_bytes):
- keypair_data = XML(xml_bytes)
- key_name = keypair_data.findtext("keyName")
- key_fingerprint = keypair_data.findtext("keyFingerprint")
- key_material = keypair_data.findtext("keyMaterial")
- return model.Keypair(key_name, key_fingerprint, key_material)
+ return d.addCallback(self.parser.parse_create_keypair)
def delete_keypair(self, keypair_name):
"""Delete a given keypair."""
@@ -662,7 +403,7 @@
action="DeleteKeyPair", creds=self.creds, endpoint=self.endpoint,
other_params={"KeyName": keypair_name})
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def import_keypair(self, keypair_name, key_material):
"""
@@ -683,14 +424,7 @@
other_params={"KeyName": keypair_name,
"PublicKeyMaterial": b64encode(key_material)})
d = query.submit()
- return d.addCallback(self._parse_import_keypair, key_material)
-
- def _parse_import_keypair(self, xml_bytes, key_material):
- """Extract the key name and the fingerprint from the result."""
- keypair_data = XML(xml_bytes)
- key_name = keypair_data.findtext("keyName")
- key_fingerprint = keypair_data.findtext("keyFingerprint")
- return model.Keypair(key_name, key_fingerprint, key_material)
+ return d.addCallback(self.parser.parse_import_keypair, key_material)
def allocate_address(self):
"""
@@ -704,11 +438,7 @@
action="AllocateAddress", creds=self.creds, endpoint=self.endpoint,
other_params={})
d = query.submit()
- return d.addCallback(self._parse_allocate_address)
-
- def _parse_allocate_address(self, xml_bytes):
- address_data = XML(xml_bytes)
- return address_data.findtext("publicIp")
+ return d.addCallback(self.parser.parse_allocate_address)
def release_address(self, address):
"""
@@ -720,7 +450,7 @@
action="ReleaseAddress", creds=self.creds, endpoint=self.endpoint,
other_params={"PublicIp": address})
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def associate_address(self, instance_id, address):
"""
@@ -734,7 +464,7 @@
endpoint=self.endpoint,
other_params={"InstanceId": instance_id, "PublicIp": address})
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def disassociate_address(self, address):
"""
@@ -746,7 +476,7 @@
action="DisassociateAddress", creds=self.creds,
endpoint=self.endpoint, other_params={"PublicIp": address})
d = query.submit()
- return d.addCallback(self._parse_truth_return)
+ return d.addCallback(self.parser.parse_truth_return)
def describe_addresses(self, *addresses):
"""
@@ -764,16 +494,7 @@
action="DescribeAddresses", creds=self.creds,
endpoint=self.endpoint, other_params=address_set)
d = query.submit()
- return d.addCallback(self._parse_describe_addresses)
-
- def _parse_describe_addresses(self, xml_bytes):
- results = []
- root = XML(xml_bytes)
- for address_data in root.find("addressesSet"):
- address = address_data.findtext("publicIp")
- instance_id = address_data.findtext("instanceId")
- results.append((address, instance_id))
- return results
+ return d.addCallback(self.parser.parse_describe_addresses)
def describe_availability_zones(self, names=None):
zone_names = None
@@ -784,9 +505,373 @@
action="DescribeAvailabilityZones", creds=self.creds,
endpoint=self.endpoint, other_params=zone_names)
d = query.submit()
- return d.addCallback(self._parse_describe_availability_zones)
-
- def _parse_describe_availability_zones(self, xml_bytes):
+ return d.addCallback(self.parser.parse_describe_availability_zones)
+
+
+class Parser(object):
+ """A parser for EC2 responses"""
+
+ def __init__(self):
+ super(Parser, self).__init__()
+
+ def parse_instances_set(self, root, reservation):
+ """Parse instance data out of an XML payload.
+
+ @param root: The root node of the XML payload.
+ @param reservation: The L{Reservation} associated with the instances
+ from the response.
+ @return: A C{list} of L{Instance}s.
+ """
+ instances = []
+ for instance_data in root.find("instancesSet"):
+ instances.append(self.parse_instance(instance_data, reservation))
+ return instances
+
+ def parse_instance(self, instance_data, reservation):
+ """Parse instance data out of an XML payload.
+
+ @param instance_data: An XML node containing instance data.
+ @param reservation: The L{Reservation} associated with the instance.
+ @return: An L{Instance}.
+ """
+ instance_id = instance_data.findtext("instanceId")
+ instance_state = instance_data.find(
+ "instanceState").findtext("name")
+ instance_type = instance_data.findtext("instanceType")
+ image_id = instance_data.findtext("imageId")
+ private_dns_name = instance_data.findtext("privateDnsName")
+ dns_name = instance_data.findtext("dnsName")
+ key_name = instance_data.findtext("keyName")
+ ami_launch_index = instance_data.findtext("amiLaunchIndex")
+ launch_time = instance_data.findtext("launchTime")
+ placement = instance_data.find("placement").findtext(
+ "availabilityZone")
+ products = []
+ product_codes = instance_data.find("productCodes")
+ if product_codes:
+ for product_data in instance_data.find("productCodes"):
+ products.append(product_data.text)
+ kernel_id = instance_data.findtext("kernelId")
+ ramdisk_id = instance_data.findtext("ramdiskId")
+ instance = model.Instance(
+ instance_id, instance_state, instance_type, image_id,
+ private_dns_name, dns_name, key_name, ami_launch_index,
+ launch_time, placement, products, kernel_id, ramdisk_id,
+ reservation=reservation)
+ return instance
+
+ def parse_describe_instances(self, xml_bytes):
+ """
+ Parse the reservations XML payload that is returned from an AWS
+ describeInstances API call.
+
+ Instead of returning the reservations as the "top-most" object, we
+ return the object that most developers and their code will be
+ interested in: the instances. In instances reservation is available on
+ the instance object.
+
+ The following instance attributes are optional:
+ * ami_launch_index
+ * key_name
+ * kernel_id
+ * product_codes
+ * ramdisk_id
+ * reason
+
+ @param xml_bytes: raw XML payload from AWS.
+ """
+ root = XML(xml_bytes)
+ results = []
+ # May be a more elegant way to do this:
+ for reservation_data in root.find("reservationSet"):
+ # Get the security group information.
+ groups = []
+ for group_data in reservation_data.find("groupSet"):
+ group_id = group_data.findtext("groupId")
+ groups.append(group_id)
+ # Create a reservation object with the parsed data.
+ reservation = model.Reservation(
+ reservation_id=reservation_data.findtext("reservationId"),
+ owner_id=reservation_data.findtext("ownerId"),
+ groups=groups)
+ # Get the list of instances.
+ instances = self.parse_instances_set(
+ reservation_data, reservation)
+ results.extend(instances)
+ return results
+
+ def parse_run_instances(self, xml_bytes):
+ """
+ Parse the reservations XML payload that is returned from an AWS
+ RunInstances API call.
+
+ @param xml_bytes: raw XML payload from AWS.
+ """
+ root = XML(xml_bytes)
+ # Get the security group information.
+ groups = []
+ for group_data in root.find("groupSet"):
+ group_id = group_data.findtext("groupId")
+ groups.append(group_id)
+ # Create a reservation object with the parsed data.
+ reservation = model.Reservation(
+ reservation_id=root.findtext("reservationId"),
+ owner_id=root.findtext("ownerId"),
+ groups=groups)
+ # Get the list of instances.
+ instances = self.parse_instances_set(root, reservation)
+ return instances
+
+ def parse_terminate_instances(self, xml_bytes):
+ """Parse the XML returned by the C{TerminateInstances} function.
+
+ @param xml_bytes: XML bytes with a C{TerminateInstancesResponse} root
+ element.
+ @return: An iterable of C{tuple} of (instanceId, previousState,
+ shutdownState) for the ec2 instances that where terminated.
+ """
+ root = XML(xml_bytes)
+ result = []
+ # May be a more elegant way to do this:
+ for instance in root.find("instancesSet"):
+ instanceId = instance.findtext("instanceId")
+ previousState = instance.find("previousState").findtext(
+ "name")
+ shutdownState = instance.find("shutdownState").findtext(
+ "name")
+ result.append((instanceId, previousState, shutdownState))
+ return result
+
+ def parse_describe_security_groups(self, xml_bytes):
+ """Parse the XML returned by the C{DescribeSecurityGroups} function.
+
+ @param xml_bytes: XML bytes with a C{DescribeSecurityGroupsResponse}
+ root element.
+ @return: A list of L{SecurityGroup} instances.
+ """
+ root = XML(xml_bytes)
+ result = []
+ for group_info in root.findall("securityGroupInfo/item"):
+ name = group_info.findtext("groupName")
+ description = group_info.findtext("groupDescription")
+ owner_id = group_info.findtext("ownerId")
+ allowed_groups = []
+ allowed_ips = []
+ ip_permissions = group_info.find("ipPermissions") or ()
+ for ip_permission in ip_permissions:
+ ip_protocol = ip_permission.findtext("ipProtocol")
+ from_port = int(ip_permission.findtext("fromPort"))
+ to_port = int(ip_permission.findtext("toPort"))
+ for groups in ip_permission.findall("groups/item") or ():
+ user_id = groups.findtext("userId")
+ group_name = groups.findtext("groupName")
+ if user_id and group_name:
+ if (user_id, group_name) not in allowed_groups:
+ allowed_groups.append((user_id, group_name))
+ for ip_ranges in ip_permission.findall("ipRanges/item") or ():
+ cidr_ip = ip_ranges.findtext("cidrIp")
+ allowed_ips.append(
+ model.IPPermission(
+ ip_protocol, from_port, to_port, cidr_ip))
+
+ allowed_groups = [model.UserIDGroupPair(user_id, group_name)
+ for user_id, group_name in allowed_groups]
+
+ security_group = model.SecurityGroup(
+ name, description, owner_id=owner_id,
+ groups=allowed_groups, ips=allowed_ips)
+ result.append(security_group)
+ return result
+
+ def parse_truth_return(self, xml_bytes):
+ """Parse the XML for a truth value.
+
+ @param xml_bytes: XML bytes.
+ @return: True if the node contains "return" otherwise False.
+ """
+ root = XML(xml_bytes)
+ return root.findtext("return") == "true"
+
+ def parse_describe_volumes(self, xml_bytes):
+ """Parse the XML returned by the C{DescribeVolumes} function.
+
+ @param xml_bytes: XML bytes with a C{DescribeVolumesResponse} root
+ element.
+ @return: A list of L{Volume} instances.
+ """
+ root = XML(xml_bytes)
+ result = []
+ for volume_data in root.find("volumeSet"):
+ volume_id = volume_data.findtext("volumeId")
+ size = int(volume_data.findtext("size"))
+ status = volume_data.findtext("status")
+ availability_zone = volume_data.findtext("availabilityZone")
+ snapshot_id = volume_data.findtext("snapshotId")
+ create_time = volume_data.findtext("createTime")
+ create_time = datetime.strptime(
+ create_time[:19], "%Y-%m-%dT%H:%M:%S")
+ volume = model.Volume(
+ volume_id, size, status, create_time, availability_zone,
+ snapshot_id)
+ result.append(volume)
+ for attachment_data in volume_data.find("attachmentSet"):
+ instance_id = attachment_data.findtext("instanceId")
+ status = attachment_data.findtext("status")
+ device = attachment_data.findtext("device")
+ attach_time = attachment_data.findtext("attachTime")
+ attach_time = datetime.strptime(
+ attach_time[:19], "%Y-%m-%dT%H:%M:%S")
+ attachment = model.Attachment(
+ instance_id, device, status, attach_time)
+ volume.attachments.append(attachment)
+ return result
+
+ def parse_create_volume(self, xml_bytes):
+ """Parse the XML returned by the C{CreateVolume} function.
+
+ @param xml_bytes: XML bytes with a C{CreateVolumeResponse} root
+ element.
+ @return: The L{Volume} instance created.
+ """
+ root = XML(xml_bytes)
+ volume_id = root.findtext("volumeId")
+ size = int(root.findtext("size"))
+ status = root.findtext("status")
+ create_time = root.findtext("createTime")
+ availability_zone = root.findtext("availabilityZone")
+ snapshot_id = root.findtext("snapshotId")
+ create_time = datetime.strptime(
+ create_time[:19], "%Y-%m-%dT%H:%M:%S")
+ volume = model.Volume(
+ volume_id, size, status, create_time, availability_zone,
+ snapshot_id)
+ return volume
+
+ def parse_snapshots(self, xml_bytes):
+ """Parse the XML returned by the C{DescribeSnapshots} function.
+
+ @param xml_bytes: XML bytes with a C{DescribeSnapshotsResponse} root
+ element.
+ @return: A list of L{Snapshot} instances.
+ """
+ root = XML(xml_bytes)
+ result = []
+ for snapshot_data in root.find("snapshotSet"):
+ snapshot_id = snapshot_data.findtext("snapshotId")
+ volume_id = snapshot_data.findtext("volumeId")
+ status = snapshot_data.findtext("status")
+ start_time = snapshot_data.findtext("startTime")
+ start_time = datetime.strptime(
+ start_time[:19], "%Y-%m-%dT%H:%M:%S")
+ progress = snapshot_data.findtext("progress")[:-1]
+ progress = float(progress or "0") / 100.
+ snapshot = model.Snapshot(
+ snapshot_id, volume_id, status, start_time, progress)
+ result.append(snapshot)
+ return result
+
+ def parse_create_snapshot(self, xml_bytes):
+ """Parse the XML returned by the C{CreateSnapshot} function.
+
+ @param xml_bytes: XML bytes with a C{CreateSnapshotResponse} root
+ element.
+ @return: The L{Snapshot} instance created.
+ """
+ root = XML(xml_bytes)
+ snapshot_id = root.findtext("snapshotId")
+ volume_id = root.findtext("volumeId")
+ status = root.findtext("status")
+ start_time = root.findtext("startTime")
+ start_time = datetime.strptime(
+ start_time[:19], "%Y-%m-%dT%H:%M:%S")
+ progress = root.findtext("progress")[:-1]
+ progress = float(progress or "0") / 100.
+ return model.Snapshot(
+ snapshot_id, volume_id, status, start_time, progress)
+
+ def parse_attach_volume(self, xml_bytes):
+ """Parse the XML returned by the C{AttachVolume} function.
+
+ @param xml_bytes: XML bytes with a C{AttachVolumeResponse} root
+ element.
+ @return: a C{dict} with status and attach_time keys.
+ """
+ root = XML(xml_bytes)
+ status = root.findtext("status")
+ attach_time = root.findtext("attachTime")
+ attach_time = datetime.strptime(
+ attach_time[:19], "%Y-%m-%dT%H:%M:%S")
+ return {"status": status, "attach_time": attach_time}
+
+ def parse_describe_keypairs(self, xml_bytes):
+ """Parse the XML returned by the C{DescribeKeyPairs} function.
+
+ @param xml_bytes: XML bytes with a C{DescribeKeyPairsResponse} root
+ element.
+ @return: a C{list} of L{Keypair}.
+ """
+ results = []
+ root = XML(xml_bytes)
+ keypairs = root.find("keySet")
+ if not keypairs:
+ return results
+ for keypair_data in keypairs:
+ key_name = keypair_data.findtext("keyName")
+ key_fingerprint = keypair_data.findtext("keyFingerprint")
+ results.append(model.Keypair(key_name, key_fingerprint))
+ return results
+
+ def parse_create_keypair(self, xml_bytes):
+ """Parse the XML returned by the C{CreateKeyPair} function.
+
+ @param xml_bytes: XML bytes with a C{CreateKeyPairResponse} root
+ element.
+ @return: The L{Keypair} instance created.
+ """
+ keypair_data = XML(xml_bytes)
+ key_name = keypair_data.findtext("keyName")
+ key_fingerprint = keypair_data.findtext("keyFingerprint")
+ key_material = keypair_data.findtext("keyMaterial")
+ return model.Keypair(key_name, key_fingerprint, key_material)
+
+ def parse_import_keypair(self, xml_bytes, key_material):
+ """Extract the key name and the fingerprint from the result."""
+ keypair_data = XML(xml_bytes)
+ key_name = keypair_data.findtext("keyName")
+ key_fingerprint = keypair_data.findtext("keyFingerprint")
+ return model.Keypair(key_name, key_fingerprint, key_material)
+
+ def parse_allocate_address(self, xml_bytes):
+ """Parse the XML returned by the C{AllocateAddress} function.
+
+ @param xml_bytes: XML bytes with a C{AllocateAddress} root element.
+ @return: The public ip address as a string.
+ """
+ address_data = XML(xml_bytes)
+ return address_data.findtext("publicIp")
+
+ def parse_describe_addresses(self, xml_bytes):
+ """Parse the XML returned by the C{DescribeAddresses} function.
+
+ @param xml_bytes: XML bytes with a C{DescribeAddressesResponse} root
+ element.
+ @return: a C{list} of L{tuple} of (publicIp, instancId).
+ """
+ results = []
+ root = XML(xml_bytes)
+ for address_data in root.find("addressesSet"):
+ address = address_data.findtext("publicIp")
+ instance_id = address_data.findtext("instanceId")
+ results.append((address, instance_id))
+ return results
+
+ def parse_describe_availability_zones(self, xml_bytes):
+ """Parse the XML returned by the C{DescribeAvailibilityZones} function.
+
+ @param xml_bytes: XML bytes with a C{DescribeAvailibilityZonesResponse}
+ root element.
+ @return: a C{list} of L{AvailabilityZone}.
+ """
results = []
root = XML(xml_bytes)
for zone_data in root.find("availabilityZoneInfo"):
=== modified file 'txaws/ec2/tests/test_client.py'
--- txaws/ec2/tests/test_client.py 2011-04-19 17:42:18 +0000
+++ txaws/ec2/tests/test_client.py 2011-04-21 06:05:51 +0000
@@ -217,7 +217,7 @@
def test_parse_reservation(self):
creds = AWSCredentials("foo", "bar")
ec2 = client.EC2Client(creds=creds)
- results = ec2._parse_describe_instances(
+ results = ec2.parser.parse_describe_instances(
payload.sample_describe_instances_result)
self.check_parsed_instances(results)
References