Merge lp:~therve/txaws/run-instances into lp:txaws

Proposed by Thomas Herve
Status: Merged
Merged at revision: not available
Proposed branch: lp:~therve/txaws/run-instances
Merge into: lp:txaws
Diff against target: 307 lines
4 files modified
txaws/ec2/client.py (+81/-29)
txaws/ec2/tests/test_client.py (+51/-0)
txaws/testing/ec2.py (+6/-0)
txaws/testing/payload.py (+66/-0)
To merge this branch: bzr merge lp:~therve/txaws/run-instances
Reviewer Review Type Date Requested Status
Duncan McGreggor Approve
Review via email: mp+13271@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Thomas Herve (therve) wrote :

This add support for the RunInstances operation. Using the code from describe_instances, it's a relatively small change.

Revision history for this message
Duncan McGreggor (oubiwann) wrote :

Looks great!

[1] I see you imported b64encode; is there are reason you didn't use .encode("base64")?

review: Approve
Revision history for this message
Thomas Herve (therve) wrote :

> Looks great!
>
> [1] I see you imported b64encode; is there are reason you didn't use
> .encode("base64")?

It's not really equivalent, .encode("base64") has some quirks.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'txaws/ec2/client.py'
2--- txaws/ec2/client.py 2009-10-12 17:02:33 +0000
3+++ txaws/ec2/client.py 2009-10-13 09:15:23 +0000
4@@ -7,6 +7,7 @@
5
6 from datetime import datetime
7 from urllib import quote
8+from base64 import b64encode
9
10 from twisted.internet import reactor, ssl
11 from twisted.web.client import HTTPClientFactory
12@@ -63,6 +64,36 @@
13 d = q.submit()
14 return d.addCallback(self._parse_describe_instances)
15
16+ def _parse_instances_set(self, root, reservation):
17+ instances = []
18+ for instance_data in root.find("instancesSet"):
19+ instance_id = instance_data.findtext("instanceId")
20+ instance_state = instance_data.find(
21+ "instanceState").findtext("name")
22+ instance_type = instance_data.findtext("instanceType")
23+ image_id = instance_data.findtext("imageId")
24+ private_dns_name = instance_data.findtext("privateDnsName")
25+ dns_name = instance_data.findtext("dnsName")
26+ key_name = instance_data.findtext("keyName")
27+ ami_launch_index = instance_data.findtext("amiLaunchIndex")
28+ launch_time = instance_data.findtext("launchTime")
29+ placement = instance_data.find("placement").findtext(
30+ "availabilityZone")
31+ products = []
32+ product_codes = instance_data.find("productCodes")
33+ if product_codes:
34+ for product_data in instance_data.find("productCodes"):
35+ products.append(product_data.text)
36+ kernel_id = instance_data.findtext("kernelId")
37+ ramdisk_id = instance_data.findtext("ramdiskId")
38+ instance = model.Instance(
39+ instance_id, instance_state, instance_type, image_id,
40+ private_dns_name, dns_name, key_name, ami_launch_index,
41+ launch_time, placement, products, kernel_id, ramdisk_id,
42+ reservation=reservation)
43+ instances.append(instance)
44+ return instances
45+
46 def _parse_describe_instances(self, xml_bytes):
47 """
48 Parse the reservations XML payload that is returned from an AWS
49@@ -96,36 +127,58 @@
50 owner_id=reservation_data.findtext("ownerId"),
51 groups=groups)
52 # Get the list of instances.
53- instances = []
54- for instance_data in reservation_data.find("instancesSet"):
55- instance_id = instance_data.findtext("instanceId")
56- instance_state = instance_data.find(
57- "instanceState").findtext("name")
58- instance_type = instance_data.findtext("instanceType")
59- image_id = instance_data.findtext("imageId")
60- private_dns_name = instance_data.findtext("privateDnsName")
61- dns_name = instance_data.findtext("dnsName")
62- key_name = instance_data.findtext("keyName")
63- ami_launch_index = instance_data.findtext("amiLaunchIndex")
64- launch_time = instance_data.findtext("launchTime")
65- placement = instance_data.find("placement").findtext(
66- "availabilityZone")
67- products = []
68- product_codes = instance_data.find("productCodes")
69- if product_codes:
70- for product_data in instance_data.find("productCodes"):
71- products.append(product_data.text)
72- kernel_id = instance_data.findtext("kernelId")
73- ramdisk_id = instance_data.findtext("ramdiskId")
74- instance = model.Instance(
75- instance_id, instance_state, instance_type, image_id,
76- private_dns_name, dns_name, key_name, ami_launch_index,
77- launch_time, placement, products, kernel_id, ramdisk_id,
78- reservation=reservation)
79- instances.append(instance)
80+ instances = self._parse_instances_set(
81+ reservation_data, reservation)
82 results.extend(instances)
83 return results
84
85+ def run_instances(self, image_id, min_count, max_count,
86+ security_groups=None, key_name=None, instance_type=None,
87+ user_data=None, availability_zone=None, kernel_id=None,
88+ ramdisk_id=None):
89+ """Run new instances."""
90+ params = {"ImageId": image_id, "MinCount": min_count,
91+ "MaxCount": max_count}
92+ if security_groups is not None:
93+ for i, name in enumerate(security_groups):
94+ params["SecurityGroup.%d" % (i+1)] = name
95+ if key_name is not None:
96+ params["KeyName"] = key_name
97+ if user_data is not None:
98+ params["UserData"] = b64encode(user_data)
99+ if instance_type is not None:
100+ params["InstanceType"] = instance_type
101+ if availability_zone is not None:
102+ params["Placement.AvailabilityZone"] = availability_zone
103+ if kernel_id is not None:
104+ params["KernelId"] = kernel_id
105+ if ramdisk_id is not None:
106+ params["RamdiskId"] = ramdisk_id
107+ q = self.query_factory(
108+ "RunInstances", self.creds, self.endpoint, params)
109+ d = q.submit()
110+ return d.addCallback(self._parse_run_instances)
111+
112+ def _parse_run_instances(self, xml_bytes):
113+ """
114+ Parse the reservations XML payload that is returned from an AWS
115+ RunInstances API call.
116+ """
117+ root = XML(xml_bytes)
118+ # Get the security group information.
119+ groups = []
120+ for group_data in root.find("groupSet"):
121+ group_id = group_data.findtext("groupId")
122+ groups.append(group_id)
123+ # Create a reservation object with the parsed data.
124+ reservation = model.Reservation(
125+ reservation_id=root.findtext("reservationId"),
126+ owner_id=root.findtext("ownerId"),
127+ groups=groups)
128+ # Get the list of instances.
129+ instances = self._parse_instances_set(root, reservation)
130+ return instances
131+
132 def terminate_instances(self, *instance_ids):
133 """Terminate some instances.
134
135@@ -219,7 +272,6 @@
136 @return: A C{Deferred} that will fire with a truth value for the
137 success of the operation.
138 """
139- group_names = None
140 parameters = {"GroupName": name, "GroupDescription": description}
141 query = self.query_factory("CreateSecurityGroup", self.creds,
142 self.endpoint, parameters)
143@@ -690,7 +742,7 @@
144 self.endpoint, zone_names)
145 d = query.submit()
146 return d.addCallback(self._parse_describe_availability_zones)
147-
148+
149 def _parse_describe_availability_zones(self, xml_bytes):
150 results = []
151 root = XML(xml_bytes)
152
153=== modified file 'txaws/ec2/tests/test_client.py'
154--- txaws/ec2/tests/test_client.py 2009-10-12 17:02:33 +0000
155+++ txaws/ec2/tests/test_client.py 2009-10-13 09:15:23 +0000
156@@ -260,6 +260,57 @@
157 d.addCallback(check_transition)
158 return d
159
160+ def check_parsed_run_instances(self, results):
161+ instance = results[0]
162+ # check reservations
163+ reservation = instance.reservation
164+ self.assertEquals(reservation.reservation_id, "r-47a5402e")
165+ self.assertEquals(reservation.owner_id, "495219933132")
166+ # check groups
167+ group = reservation.groups[0]
168+ self.assertEquals(group, "default")
169+ # check instance
170+ self.assertEquals(instance.instance_id, "i-2ba64342")
171+ self.assertEquals(instance.instance_state, "pending")
172+ self.assertEquals(instance.instance_type, "m1.small")
173+ self.assertEquals(instance.placement, "us-east-1b")
174+ instance = results[1]
175+ self.assertEquals(instance.instance_id, "i-2bc64242")
176+ self.assertEquals(instance.instance_state, "pending")
177+ self.assertEquals(instance.instance_type, "m1.small")
178+ self.assertEquals(instance.placement, "us-east-1b")
179+ instance = results[2]
180+ self.assertEquals(instance.instance_id, "i-2be64332")
181+ self.assertEquals(instance.instance_state, "pending")
182+ self.assertEquals(instance.instance_type, "m1.small")
183+ self.assertEquals(instance.placement, "us-east-1b")
184+
185+ def test_run_instances(self):
186+
187+ class StubQuery(object):
188+ def __init__(stub, action, creds, endpoint, params):
189+ self.assertEqual(action, "RunInstances")
190+ self.assertEqual(creds.access_key, "foo")
191+ self.assertEqual(creds.secret_key, "bar")
192+ self.assertEquals(
193+ params,
194+ {"ImageId": "ami-1234", "MaxCount": 2, "MinCount": 1,
195+ "SecurityGroup.1": u"group1", "KeyName": u"default",
196+ "UserData": "Zm9v", "InstanceType": u"m1.small",
197+ "Placement.AvailabilityZone": u"us-east-1b",
198+ "KernelId": u"k-1234", "RamdiskId": u"r-1234"})
199+ def submit(self):
200+ return succeed(
201+ payload.sample_run_instances_result)
202+
203+ creds = AWSCredentials("foo", "bar")
204+ ec2 = client.EC2Client(creds, query_factory=StubQuery)
205+ d = ec2.run_instances("ami-1234", 1, 2, security_groups=[u"group1"],
206+ key_name=u"default", user_data=u"foo", instance_type=u"m1.small",
207+ availability_zone=u"us-east-1b", kernel_id=u"k-1234",
208+ ramdisk_id=u"r-1234")
209+ d.addCallback(self.check_parsed_run_instances)
210+
211
212 class EC2ClientSecurityGroupsTestCase(TXAWSTestCase):
213
214
215=== modified file 'txaws/testing/ec2.py'
216--- txaws/testing/ec2.py 2009-10-13 07:14:42 +0000
217+++ txaws/testing/ec2.py 2009-10-13 09:15:23 +0000
218@@ -33,6 +33,12 @@
219 def describe_instances(self, *instances):
220 return succeed(self.instances)
221
222+ def run_instances(self, image_id, min_count, max_count,
223+ security_groups=None, key_name=None, instance_type=None,
224+ user_data=None, availability_zone=None, kernel_id=None,
225+ ramdisk_id=None):
226+ return succeed(self.instances)
227+
228 def describe_keypairs(self):
229 return succeed(self.keypairs)
230
231
232=== modified file 'txaws/testing/payload.py'
233--- txaws/testing/payload.py 2009-10-05 23:50:35 +0000
234+++ txaws/testing/payload.py 2009-10-13 09:15:23 +0000
235@@ -81,6 +81,72 @@
236 """ % (aws_api,)
237
238
239+sample_run_instances_result = """\
240+<?xml version="1.0"?>
241+<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/%s/">
242+ <reservationId>r-47a5402e</reservationId>
243+ <ownerId>495219933132</ownerId>
244+ <groupSet>
245+ <item>
246+ <groupId>default</groupId>
247+ </item>
248+ </groupSet>
249+ <instancesSet>
250+ <item>
251+ <instanceId>i-2ba64342</instanceId>
252+ <imageId>ami-60a54009</imageId>
253+ <instanceState>
254+ <code>0</code>
255+ <name>pending</name>
256+ </instanceState>
257+ <privateDnsName></privateDnsName>
258+ <dnsName></dnsName>
259+ <keyName>example-key-name</keyName>
260+ <amiLaunchIndex>0</amiLaunchIndex>
261+ <instanceType>m1.small</instanceType>
262+ <launchTime>2007-08-07T11:51:50.000Z</launchTime>
263+ <placement>
264+ <availabilityZone>us-east-1b</availabilityZone>
265+ </placement>
266+ </item>
267+ <item>
268+ <instanceId>i-2bc64242</instanceId>
269+ <imageId>ami-60a54009</imageId>
270+ <instanceState>
271+ <code>0</code>
272+ <name>pending</name>
273+ </instanceState>
274+ <privateDnsName></privateDnsName>
275+ <dnsName></dnsName>
276+ <keyName>example-key-name</keyName>
277+ <amiLaunchIndex>1</amiLaunchIndex>
278+ <instanceType>m1.small</instanceType>
279+ <launchTime>2007-08-07T11:51:50.000Z</launchTime>
280+ <placement>
281+ <availabilityZone>us-east-1b</availabilityZone>
282+ </placement>
283+ </item>
284+ <item>
285+ <instanceId>i-2be64332</instanceId>
286+ <imageId>ami-60a54009</imageId>
287+ <instanceState>
288+ <code>0</code>
289+ <name>pending</name>
290+ </instanceState>
291+ <privateDnsName></privateDnsName>
292+ <dnsName></dnsName>
293+ <keyName>example-key-name</keyName>
294+ <amiLaunchIndex>2</amiLaunchIndex>
295+ <instanceType>m1.small</instanceType>
296+ <launchTime>2007-08-07T11:51:50.000Z</launchTime>
297+ <placement>
298+ <availabilityZone>us-east-1b</availabilityZone>
299+ </placement>
300+ </item>
301+ </instancesSet>
302+</RunInstancesResponse>
303+""" % (aws_api,)
304+
305 sample_terminate_instances_result = """\
306 <?xml version="1.0"?>
307 <TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/%s/">

Subscribers

People subscribed via source and target branches

to all changes: