Frank Wierzbicki has proposed merging lp:~fwierzbicki/txaws/break-out-ec2-parser into lp:txaws.
Requested reviews: txAWS Developers (txaws-dev) txAWS Developers (txaws-dev) Related bugs: Bug #768071 in txAWS: "The parser should be extracted from EC2Client" https://bugs.launchpad.net/txaws/+bug/768071 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. -- 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)
_______________________________________________ Mailing list: https://launchpad.net/~txaws-dev Post to : txaws-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~txaws-dev More help : https://help.launchpad.net/ListHelp