amazon-ec2 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +27 -5
- data/History.txt +0 -8
- data/Manifest.txt +7 -0
- data/README.txt +1 -1
- data/lib/EC2.rb +108 -497
- data/lib/EC2/image_attributes.rb +47 -0
- data/lib/EC2/images.rb +87 -0
- data/lib/EC2/instances.rb +95 -0
- data/lib/EC2/keypairs.rb +38 -0
- data/lib/EC2/responses.rb +340 -0
- data/lib/EC2/security_groups.rb +140 -0
- data/lib/EC2/version.rb +1 -1
- metadata +8 -2
data/CHANGELOG.txt
CHANGED
@@ -1,9 +1,31 @@
|
|
1
1
|
=CHANGELOG.txt : Amazon Elastic Compute Cloud (EC2) Ruby Gem
|
2
2
|
|
3
|
-
|
4
|
-
*
|
3
|
+
===0.0.3 (12/16/2006)
|
4
|
+
* API CHANGE : Changed method name 'authorize' to 'authorize_security_group_ingress' to ensure consistent
|
5
|
+
naming of Ruby library methods to match AWS EC2 API actions. Alias to 'authorize' for backwards compatibility.
|
6
|
+
* API CHANGE : Changed method name 'revoke' to 'revoke_security_group_ingress' to ensure consistent
|
7
|
+
naming of Ruby library methods to match AWS EC2 API actions. Alias to 'revoke' for backwards compatibility.
|
8
|
+
* API CHANGE : Changed method name 'delete_securitygroup' to 'delete_security_group' to ensure consistent
|
9
|
+
naming of Ruby library methods to match AWS EC2 API actions. Alias to 'delete_securitygroup' for backwards compatibility.
|
10
|
+
* API CHANGE : Changed method name 'describe_securitygroups' to 'describe_security_group' to ensure consistent
|
11
|
+
naming of Ruby library methods to match AWS EC2 API actions. Alias to 'describe_securitygroups' for backwards compatibility.
|
12
|
+
* API CHANGE : Changed method name 'create_securitygroup' to 'create_security_group' to ensure consistent
|
13
|
+
naming of Ruby library methods to match AWS EC2 API actions. Alias to 'create_securitygroup' for backwards compatibility.
|
14
|
+
* Added many API rdoc's, some method descriptions copied from Amazon Query API Developer Guide.
|
15
|
+
* Extracted some parts of the formerly monolithic EC2 library out into separate files for manageability.
|
16
|
+
* Changed the HTTP 'User-Agent' string used for each request so that we have our own user agent
|
17
|
+
to identify this library's calls. Now set the version # in the user agent string based on the
|
18
|
+
master version number for this library which is stored in lib/EC2/version.rb and should only
|
19
|
+
be defined in one place.
|
20
|
+
* Set @http.verify_mode = OpenSSL::SSL::VERIFY_NONE to avoid seeing SSL Cert warning
|
21
|
+
"warning: peer certificate won't be verified in this SSL session". File EC2.rb:96
|
22
|
+
* Make 'pathlist' utility method a private method (EC2.rb:111). No reason I can see for this to be exposed.
|
5
23
|
|
24
|
+
===0.0.2 (12/14/2006)
|
25
|
+
* Bugfix in run_instances method. Method works now. Patch submitted by Stephen Caudill on AWS forums. Thanks!
|
6
26
|
|
7
|
-
|
8
|
-
*
|
9
|
-
|
27
|
+
===0.0.1 (12/13/2006)
|
28
|
+
* Initial release of the Ruby Gem. This includes the version of the library exactly as provided by
|
29
|
+
Amazon Web Services as example code. No changes or enhancements to that code were made other than
|
30
|
+
packaging it as a Ruby Gem.
|
31
|
+
* RubyForge project created. http://amazon-ec2.rubyforge.org
|
data/History.txt
CHANGED
@@ -1,11 +1,3 @@
|
|
1
1
|
=History.txt : Amazon Elastic Compute Cloud (EC2) Ruby Gem
|
2
2
|
|
3
3
|
==Version History
|
4
|
-
|
5
|
-
===0.0.2 (12/14/2006)
|
6
|
-
* Bugfix in run_instances method. Method works now.
|
7
|
-
|
8
|
-
===0.0.1 (12/13/2006)
|
9
|
-
* Initial release of the Ruby Gem. This includes the version of the library exactly as provided by
|
10
|
-
Amazon Web Services as example code. No changes or enhancements to that code were made other than
|
11
|
-
packaging it as a Ruby Gem.
|
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
AWS EC2 is an interface library that can be used to interact
|
4
4
|
with the Amazon EC2 system. The library exposes one main interface class,
|
5
|
-
'AWSAuthConnection'. This class performs all the operations for using
|
5
|
+
'AWSAuthConnection'. This class performs all the operations for using
|
6
6
|
the EC2 service including header signing.
|
7
7
|
|
8
8
|
==Important note about this project:
|
data/lib/EC2.rb
CHANGED
@@ -1,11 +1,20 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
1
|
+
# Amazon Web Services EC2 Query API Ruby Library
|
2
|
+
# This library has been packaged as a Ruby Gem
|
3
|
+
# by Glenn Rempe ( glenn @nospam@ elasticworkbench.com ).
|
4
|
+
#
|
5
|
+
# Source code and gem hosted on RubyForge
|
6
|
+
# under the Ruby License as of 12/14/2006:
|
7
|
+
# http://amazon-ec2.rubyforge.org
|
8
|
+
|
9
|
+
# Original Amazon Web Services Notice
|
10
|
+
# This software code is made available "AS IS" without warranties of any
|
11
|
+
# kind. You may copy, display, modify and redistribute the software
|
12
|
+
# code either by itself or as incorporated into your code; provided that
|
13
|
+
# you do not remove any proprietary notices. Your use of this software
|
14
|
+
# code is at your own risk and you waive any claim against Amazon Web
|
15
|
+
# Services LLC or its affiliates with respect to your use of this software
|
16
|
+
# code. (c) 2006 Amazon Web Services LLC or its affiliates. All rights
|
17
|
+
# reserved.
|
9
18
|
|
10
19
|
require 'base64'
|
11
20
|
require 'cgi'
|
@@ -15,24 +24,40 @@ require 'net/https'
|
|
15
24
|
require 'rexml/document'
|
16
25
|
require 'time'
|
17
26
|
|
18
|
-
# Require any lib files that we have bundled with this Ruby Gem
|
19
|
-
Dir[File.join(File.dirname(__FILE__), 'EC2/**/*.rb')].sort.each { |lib| require lib }
|
20
|
-
|
21
27
|
include REXML
|
22
28
|
|
29
|
+
# Require any lib files that we have bundled with this Ruby Gem in the lib/EC2 directory.
|
30
|
+
# Parts of the EC2 module and AWSAuthConnection class are broken out into separate
|
31
|
+
# files for maintainability and are organized by the functional groupings defined
|
32
|
+
# in the EC2 API developers guide.
|
33
|
+
Dir[File.join(File.dirname(__FILE__), 'EC2/**/*.rb')].sort.each { |lib| require lib }
|
34
|
+
|
23
35
|
module EC2
|
36
|
+
|
37
|
+
# Which host FQDN will we connect to for all API calls to AWS?
|
24
38
|
DEFAULT_HOST = 'ec2.amazonaws.com'
|
39
|
+
|
40
|
+
# Define the ports to use for SSL(true) or Non-SSL(false) connections.
|
25
41
|
PORTS_BY_SECURITY = { true => 443, false => 80 }
|
42
|
+
|
43
|
+
# This is the version of the API as defined by Amazon Web Services
|
26
44
|
API_VERSION = '2006-10-01'
|
27
|
-
RELEASE_VERSION = "7813"
|
28
45
|
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
46
|
+
# This release version is passed in with each request as part
|
47
|
+
# of the HTTP 'User-Agent' header. Set this be the same value
|
48
|
+
# as what is stored in the lib/EC2/version.rb module constant instead.
|
49
|
+
# This way we keep it nice and DRY and only have to define the
|
50
|
+
# version number in a single place.
|
51
|
+
RELEASE_VERSION = EC2::VERSION::STRING
|
52
|
+
|
53
|
+
# Builds the canonical string for signing. This strips out all '&', '?', and '='
|
54
|
+
# from the query string to be signed.
|
55
|
+
# Note: The parameters in the path passed in must already be sorted in
|
56
|
+
# case-insensitive alphabetical order and must not be url encoded.
|
32
57
|
def EC2.canonical_string(path)
|
33
58
|
buf = path.gsub(/\&|\?|=/,"")
|
34
59
|
end
|
35
|
-
|
60
|
+
|
36
61
|
# Encodes the given string with the aws_secret_access_key, by taking the
|
37
62
|
# hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
|
38
63
|
# url encode the result of that to protect the string if it's going to
|
@@ -42,514 +67,100 @@ module EC2
|
|
42
67
|
b64_hmac =
|
43
68
|
Base64.encode64(
|
44
69
|
OpenSSL::HMAC.digest(digest, aws_secret_access_key, str)).strip
|
45
|
-
|
70
|
+
|
46
71
|
if urlencode
|
47
72
|
return CGI::escape(b64_hmac)
|
48
73
|
else
|
49
74
|
return b64_hmac
|
50
75
|
end
|
51
76
|
end
|
52
|
-
|
53
|
-
|
54
|
-
#
|
77
|
+
|
78
|
+
|
79
|
+
# The library exposes one main interface class, 'AWSAuthConnection'.
|
80
|
+
# This class performs all the operations for using the EC2 service
|
81
|
+
# including header signing. This class uses Net::HTTP to interface
|
82
|
+
# with EC2 Query API interface.
|
55
83
|
class AWSAuthConnection
|
56
|
-
|
84
|
+
|
85
|
+
# Allow viewing, or turning on and off, the verbose mode of the connection class.
|
86
|
+
# If 'true' some 'puts' are done to view variable contents.
|
57
87
|
attr_accessor :verbose
|
58
|
-
|
88
|
+
|
59
89
|
def initialize(aws_access_key_id, aws_secret_access_key, is_secure=true,
|
60
90
|
server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure])
|
91
|
+
|
61
92
|
@aws_access_key_id = aws_access_key_id
|
62
93
|
@aws_secret_access_key = aws_secret_access_key
|
63
94
|
@http = Net::HTTP.new(server, port)
|
64
95
|
@http.use_ssl = is_secure
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def pathlist(key, arr)
|
69
|
-
params = {}
|
70
|
-
arr.each_with_index do |value, i|
|
71
|
-
params["#{key}.#{i+1}"] = value
|
72
|
-
end
|
73
|
-
params
|
74
|
-
end
|
75
|
-
|
76
|
-
def register_image(imageLocation)
|
77
|
-
params = { "ImageLocation" => imageLocation }
|
78
|
-
RegisterImageResponse.new(make_request("RegisterImage", params))
|
79
|
-
end
|
80
|
-
|
81
|
-
def describe_images(imageIds=[], owners=[], executableBy=[])
|
82
|
-
params = pathlist("ImageId", imageIds)
|
83
|
-
params.merge!(pathlist("Owner", owners))
|
84
|
-
params.merge!(pathlist("ExecutableBy", executableBy))
|
85
|
-
DescribeImagesResponse.new(make_request("DescribeImages", params))
|
86
|
-
end
|
87
|
-
|
88
|
-
def deregister_image(imageId)
|
89
|
-
params = { "ImageId" => imageId }
|
90
|
-
DeregisterImageResponse.new(make_request("DeregisterImage", params))
|
91
|
-
end
|
92
|
-
|
93
|
-
def create_keypair(keyName)
|
94
|
-
params = { "KeyName" => keyName }
|
95
|
-
CreateKeyPairResponse.new(make_request("CreateKeyPair", params))
|
96
|
-
end
|
97
|
-
|
98
|
-
def describe_keypairs(keyNames=[])
|
99
|
-
params = pathlist("KeyName", keyNames)
|
100
|
-
DescribeKeyPairsResponse.new(make_request("DescribeKeyPairs", params))
|
101
|
-
end
|
102
|
-
|
103
|
-
def delete_keypair(keyName)
|
104
|
-
params = { "KeyName" => keyName }
|
105
|
-
DeleteKeyPairResponse.new(make_request("DeleteKeyPair", params))
|
106
|
-
end
|
107
|
-
|
108
|
-
def run_instances(imageId, kwargs={})
|
109
|
-
in_params = { :minCount=>1, :maxCount=>1, :keyname=>nil, :groupIds=>[], :userData=>nil, :base64Encoded=>false }
|
110
|
-
in_params.merge!(kwargs)
|
111
|
-
userData = Base64.encode64(userData) if userData and not base64Encoded
|
96
|
+
# Don't verify the SSL certificates. Avoids SSL Cert warning on every GET.
|
97
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
112
98
|
|
113
|
-
|
114
|
-
"ImageId" => imageId,
|
115
|
-
"MinCount" => in_params[:minCount].to_s,
|
116
|
-
"MaxCount" => in_params[:maxCount].to_s,
|
117
|
-
}.merge(pathlist("SecurityGroup", in_params[:groupIds]))
|
118
|
-
|
119
|
-
params["KeyName"] = in_params[:keyname] unless in_params[:keyname].nil?
|
120
|
-
|
121
|
-
RunInstancesResponse.new(make_request("RunInstances", params))
|
122
|
-
end
|
123
|
-
|
124
|
-
def describe_instances(instanceIds=[])
|
125
|
-
params = pathlist("InstanceId", instanceIds)
|
126
|
-
DescribeInstancesResponse.new(make_request("DescribeInstances", params))
|
127
|
-
end
|
128
|
-
|
129
|
-
def terminate_instances(instanceIds)
|
130
|
-
params = pathlist("InstanceId", instanceIds)
|
131
|
-
TerminateInstancesResponse.new(make_request("TerminateInstances", params))
|
132
|
-
end
|
133
|
-
|
134
|
-
def create_securitygroup(groupName, groupDescription)
|
135
|
-
params = {
|
136
|
-
"GroupName" => groupName,
|
137
|
-
"GroupDescription" => groupDescription
|
138
|
-
}
|
139
|
-
CreateSecurityGroupResponse.new(make_request("CreateSecurityGroup", params))
|
140
|
-
end
|
141
|
-
|
142
|
-
def describe_securitygroups(groupNames=[])
|
143
|
-
params = pathlist("GroupName", groupNames)
|
144
|
-
DescribeSecurityGroupsResponse.new(make_request("DescribeSecurityGroups", params))
|
145
|
-
end
|
146
|
-
|
147
|
-
def delete_securitygroup(groupName)
|
148
|
-
params = { "GroupName" => groupName }
|
149
|
-
DeleteSecurityGroupResponse.new(make_request("DeleteSecurityGroup", params))
|
150
|
-
end
|
151
|
-
|
152
|
-
def authorize(*args)
|
153
|
-
params = auth_revoke_impl(*args)
|
154
|
-
AuthorizeSecurityGroupIngressResponse.new(make_request("AuthorizeSecurityGroupIngress", params))
|
155
|
-
end
|
156
|
-
|
157
|
-
def revoke(*args)
|
158
|
-
params = auth_revoke_impl(*args)
|
159
|
-
RevokeSecurityGroupIngressResponse.new(make_request("RevokeSecurityGroupIngress", params))
|
160
|
-
end
|
161
|
-
|
162
|
-
def modify_image_attribute(imageId, attribute, operationType, attributeValueHash)
|
163
|
-
params = {
|
164
|
-
"ImageId" => imageId,
|
165
|
-
"Attribute" => attribute,
|
166
|
-
"OperationType" => operationType
|
167
|
-
}
|
168
|
-
if attribute == "launchPermission"
|
169
|
-
params.merge!(pathlist("UserGroup", attributeValueHash[:userGroups])) if attributeValueHash.has_key? :userGroups
|
170
|
-
params.merge!(pathlist("UserId", attributeValueHash[:userIds])) if attributeValueHash.has_key? :userIds
|
171
|
-
end
|
172
|
-
ModifyImageAttributeResponse.new(make_request("ModifyImageAttribute", params))
|
173
|
-
end
|
174
|
-
|
175
|
-
def reset_image_attribute(imageId, attribute)
|
176
|
-
params = { "ImageId" => imageId, "Attribute" => attribute }
|
177
|
-
ResetImageAttributeResponse.new(make_request("ResetImageAttribute", params))
|
178
|
-
end
|
179
|
-
|
180
|
-
def describe_image_attribute(imageId, attribute)
|
181
|
-
params = { "ImageId" => imageId, "Attribute" => attribute }
|
182
|
-
DescribeImageAttributeResponse.new(make_request("DescribeImageAttribute", params))
|
183
|
-
end
|
184
|
-
|
185
|
-
private
|
186
|
-
|
187
|
-
def auth_revoke_impl(groupName, kwargs={})
|
188
|
-
in_params = { :ipProtocol=>nil, :fromPort=>nil, :toPort=>nil, :cidrIp=>nil, :sourceSecurityGroupName=>nil,
|
189
|
-
:sourceSecurityGroupOwnerId=>nil}
|
190
|
-
in_params.merge! kwargs
|
99
|
+
@verbose = false
|
191
100
|
|
192
|
-
{ "GroupName" => in_params[:groupName] ,
|
193
|
-
"IpProtocol" => in_params[:ipProtocol],
|
194
|
-
"FromPort" => in_params[:fromPort].to_s,
|
195
|
-
"ToPort" => in_params[:toPort].to_s,
|
196
|
-
"CidrIp" => in_params[:cidrIp],
|
197
|
-
"SourceSecurityGroupName" => in_params[:sourceSecurityGroupName],
|
198
|
-
"SourceSecurityGroupOwnerId" => in_params[:sourceSecurityGroupOwnerId],
|
199
|
-
}.reject { |key, value| value.nil? or value.empty?}
|
200
|
-
|
201
101
|
end
|
202
102
|
|
203
|
-
private
|
204
|
-
|
205
|
-
|
206
|
-
def make_request(action, params, data='')
|
207
|
-
|
208
|
-
@http.start do
|
209
|
-
|
210
|
-
params.merge!( {"Action"=>action, "SignatureVersion"=>"1", "AWSAccessKeyId"=>@aws_access_key_id,
|
211
|
-
"Version"=>API_VERSION, "Timestamp"=>Time.now.getutc.iso8601,} )
|
212
|
-
p params if @verbose
|
213
|
-
|
214
|
-
sigpath = "?" + params.sort_by { |param| param[0].downcase }.collect { |param| param.join("=") }.join("&")
|
215
|
-
|
216
|
-
sig = get_aws_auth_param(sigpath, @aws_secret_access_key)
|
217
|
-
|
218
|
-
path = "?" + params.sort.collect do |param|
|
219
|
-
CGI::escape(param[0]) + "=" + CGI::escape(param[1])
|
220
|
-
end.join("&") + "&Signature=" + sig
|
221
|
-
|
222
|
-
puts path if @verbose
|
223
|
-
|
224
|
-
req = Net::HTTP::Get.new("/#{path}")
|
225
|
-
|
226
|
-
# ruby will automatically add a random content-type on some verbs, so
|
227
|
-
# here we add a dummy one to 'supress' it. change this logic if having
|
228
|
-
# an empty content-type header becomes semantically meaningful for any
|
229
|
-
# other verb.
|
230
|
-
req['Content-Type'] ||= ''
|
231
|
-
req['User-Agent'] = 'ec2-ruby-query 1.2-#{RELEASE_VERSION}'
|
232
|
-
|
233
|
-
data = nil unless req.request_body_permitted?
|
234
|
-
@http.request(req, data)
|
235
|
-
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# set the Authorization header using AWS signed header authentication
|
240
|
-
def get_aws_auth_param(path, aws_secret_access_key)
|
241
|
-
canonical_string = EC2.canonical_string(path)
|
242
|
-
encoded_canonical = EC2.encode(aws_secret_access_key, canonical_string)
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
class Response
|
247
|
-
attr_reader :http_response
|
248
|
-
attr_reader :http_xml
|
249
|
-
attr_reader :structure
|
250
|
-
|
251
|
-
ERROR_XPATH = "Response/Errors/Error"
|
252
103
|
|
253
|
-
|
254
|
-
@http_response = http_response
|
255
|
-
@http_xml = http_response.body
|
256
|
-
@is_error = false
|
257
|
-
if http_response.is_a? Net::HTTPSuccess
|
258
|
-
@structure = parse
|
259
|
-
else
|
260
|
-
@is_error = true
|
261
|
-
@structure = parse_error
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
def is_error?
|
266
|
-
@is_error
|
267
|
-
end
|
268
|
-
|
269
|
-
def parse_error
|
270
|
-
doc = Document.new(@http_xml)
|
271
|
-
element = XPath.first(doc, ERROR_XPATH)
|
272
|
-
|
273
|
-
errorCode = XPath.first(element, "Code").text
|
274
|
-
errorMessage = XPath.first(element, "Message").text
|
275
|
-
|
276
|
-
[["#{errorCode}: #{errorMessage}"]]
|
277
|
-
end
|
278
|
-
|
279
|
-
def parse
|
280
|
-
# Placeholder -- this method should be overridden in child classes.
|
281
|
-
nil
|
282
|
-
end
|
283
|
-
|
284
|
-
def to_s
|
285
|
-
@structure.collect do |line|
|
286
|
-
line.join("\t")
|
287
|
-
end.join("\n")
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
class DescribeImagesResponse < Response
|
292
|
-
ELEMENT_XPATH = "DescribeImagesResponse/imagesSet/item"
|
293
|
-
def parse
|
294
|
-
doc = Document.new(@http_xml)
|
295
|
-
lines = []
|
296
|
-
|
297
|
-
doc.elements.each(ELEMENT_XPATH) do |element|
|
298
|
-
imageId = XPath.first(element, "imageId").text
|
299
|
-
imageLocation = XPath.first(element, "imageLocation").text
|
300
|
-
imageOwnerId = XPath.first(element, "imageOwnerId").text
|
301
|
-
imageState = XPath.first(element, "imageState").text
|
302
|
-
isPublic = XPath.first(element, "isPublic").text
|
303
|
-
lines << ["IMAGE", imageId, imageLocation, imageOwnerId, imageState, isPublic]
|
304
|
-
end
|
305
|
-
lines
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
class RegisterImageResponse < Response
|
310
|
-
ELEMENT_XPATH = "RegisterImageResponse/imageId"
|
311
|
-
def parse
|
312
|
-
doc = Document.new(@http_xml)
|
313
|
-
lines = [["IMAGE", XPath.first(doc, ELEMENT_XPATH).text]]
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
class DeregisterImageResponse < Response
|
318
|
-
def parse
|
319
|
-
# If we don't get an error, the deregistration succeeded.
|
320
|
-
[["Image deregistered."]]
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
class CreateKeyPairResponse < Response
|
325
|
-
ELEMENT_XPATH = "CreateKeyPairResponse"
|
326
|
-
def parse
|
327
|
-
doc = Document.new(@http_xml)
|
328
|
-
element = XPath.first(doc, ELEMENT_XPATH)
|
329
|
-
|
330
|
-
keyName = XPath.first(element, "keyName").text
|
331
|
-
keyFingerprint = XPath.first(element, "keyFingerprint").text
|
332
|
-
keyMaterial = XPath.first(element, "keyMaterial").text
|
104
|
+
private
|
333
105
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
doc.elements.each(ELEMENT_XPATH) do |element|
|
345
|
-
keyName = XPath.first(element, "keyName").text
|
346
|
-
keyFingerprint = XPath.first(element, "keyFingerprint").text
|
347
|
-
lines << ["KEYPAIR", keyName, keyFingerprint]
|
348
|
-
end
|
349
|
-
lines
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
class DeleteKeyPairResponse < Response
|
354
|
-
def parse
|
355
|
-
# If we don't get an error, the deletion succeeded.
|
356
|
-
[["Keypair deleted."]]
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
class RunInstancesResponse < Response
|
361
|
-
ELEMENT_XPATH = "RunInstancesResponse"
|
362
|
-
def parse
|
363
|
-
doc = Document.new(@http_xml)
|
364
|
-
lines = []
|
365
|
-
|
366
|
-
rootelement = XPath.first(doc, ELEMENT_XPATH)
|
367
|
-
|
368
|
-
reservationId = XPath.first(rootelement, "reservationId").text
|
369
|
-
ownerId = XPath.first(rootelement, "ownerId").text
|
370
|
-
groups = nil
|
371
|
-
rootelement.elements.each("groupSet/item/groupId") do |element|
|
372
|
-
if not groups
|
373
|
-
groups = element.text
|
374
|
-
else
|
375
|
-
groups += "," + element.text
|
106
|
+
# pathlist is a utility method which takes a key string and and array as input.
|
107
|
+
# It converts the array into a Hash with the hash key being 'Key.n' where
|
108
|
+
# 'n' increments by 1 for each iteration. So if you pass in args
|
109
|
+
# ("ImageId", ["123", "456"]) you should get
|
110
|
+
# {"ImageId.1"=>"123", "ImageId.2"=>"456"} returned.
|
111
|
+
def pathlist(key, arr)
|
112
|
+
params = {}
|
113
|
+
arr.each_with_index do |value, i|
|
114
|
+
params["#{key}.#{i+1}"] = value
|
376
115
|
end
|
116
|
+
params
|
377
117
|
end
|
378
|
-
lines << ["RESERVATION", reservationId, ownerId, groups]
|
379
|
-
|
380
|
-
# rootelement = XPath.first(doc, ELEMENT_XPATH)
|
381
|
-
rootelement.elements.each("instancesSet/item") do |element|
|
382
|
-
instanceId = XPath.first(element, "instanceId").text
|
383
|
-
imageId = XPath.first(element, "imageId").text
|
384
|
-
instanceState = XPath.first(element, "instanceState/name").text
|
385
|
-
# Only for debug mode, which we don't support yet:
|
386
|
-
instanceStateCode = XPath.first(element, "instanceState/code").text
|
387
|
-
dnsName = XPath.first(element, "dnsName").text
|
388
|
-
# We don't return this, but still:
|
389
|
-
reason = XPath.first(element, "reason").text
|
390
|
-
lines << ["INSTANCE", instanceId, imageId, dnsName, instanceState]
|
391
|
-
end
|
392
|
-
lines
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
class DescribeInstancesResponse < Response
|
397
|
-
ELEMENT_XPATH = "DescribeInstancesResponse/reservationSet/item"
|
398
|
-
def parse
|
399
|
-
doc = Document.new(@http_xml)
|
400
|
-
lines = []
|
401
|
-
|
402
|
-
doc.elements.each(ELEMENT_XPATH) do |rootelement|
|
403
|
-
reservationId = XPath.first(rootelement, "reservationId").text
|
404
|
-
ownerId = XPath.first(rootelement, "ownerId").text
|
405
|
-
groups = nil
|
406
|
-
rootelement.elements.each("groupSet/item/groupId") do |element|
|
407
|
-
if not groups
|
408
|
-
groups = element.text
|
409
|
-
else
|
410
|
-
groups += "," + element.text
|
411
|
-
end
|
412
|
-
end
|
413
|
-
lines << ["RESERVATION", reservationId, ownerId, groups]
|
414
|
-
|
415
|
-
rootelement.elements.each("instancesSet/item") do |element|
|
416
|
-
instanceId = XPath.first(element, "instanceId").text
|
417
|
-
imageId = XPath.first(element, "imageId").text
|
418
|
-
instanceState = XPath.first(element, "instanceState/name").text
|
419
|
-
# Only for debug mode, which we don't support yet:
|
420
|
-
instanceStateCode = XPath.first(element, "instanceState/code").text
|
421
|
-
dnsName = XPath.first(element, "dnsName").text
|
422
|
-
# We don't return this, but still:
|
423
|
-
reason = XPath.first(element, "reason").text
|
424
|
-
lines << ["INSTANCE", instanceId, imageId, dnsName, instanceState]
|
425
|
-
end
|
426
|
-
end
|
427
|
-
lines
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
class TerminateInstancesResponse < Response
|
432
|
-
ELEMENT_XPATH = "TerminateInstancesResponse/instancesSet/item"
|
433
|
-
def parse
|
434
|
-
doc = Document.new(@http_xml)
|
435
|
-
lines = []
|
436
118
|
|
437
|
-
doc.elements.each(ELEMENT_XPATH) do |element|
|
438
|
-
instanceId = XPath.first(element, "instanceId").text
|
439
|
-
shutdownState = XPath.first(element, "shutdownState/name").text
|
440
|
-
# Only for debug mode, which we don't support yet:
|
441
|
-
shutdownStateCode = XPath.first(element, "shutdownState/code").text
|
442
|
-
previousState = XPath.first(element, "previousState/name").text
|
443
|
-
# Only for debug mode, which we don't support yet:
|
444
|
-
previousStateCode = XPath.first(element, "previousState/code").text
|
445
|
-
lines << ["INSTANCE", instanceId, previousState, shutdownState]
|
446
|
-
end
|
447
|
-
lines
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
class CreateSecurityGroupResponse < Response
|
452
|
-
def parse
|
453
|
-
# If we don't get an error, the creation succeeded.
|
454
|
-
[["Security Group created."]]
|
455
|
-
end
|
456
|
-
end
|
457
|
-
|
458
|
-
class DescribeSecurityGroupsResponse < Response
|
459
|
-
ELEMENT_XPATH = "DescribeSecurityGroupsResponse/securityGroupInfo/item"
|
460
|
-
def parse
|
461
|
-
doc = Document.new(@http_xml)
|
462
|
-
lines = []
|
463
119
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
120
|
+
# Make the connection to AWS EC2 passing in our request. This is generally called from
|
121
|
+
# within a 'Response' class object or one of its sub-classes so the response is interpreted
|
122
|
+
# in its proper context. See lib/EC2/responses.rb
|
123
|
+
def make_request(action, params, data='')
|
124
|
+
|
125
|
+
@http.start do
|
126
|
+
|
127
|
+
params.merge!( {"Action"=>action, "SignatureVersion"=>"1", "AWSAccessKeyId"=>@aws_access_key_id,
|
128
|
+
"Version"=>API_VERSION, "Timestamp"=>Time.now.getutc.iso8601,} )
|
129
|
+
p params if @verbose
|
130
|
+
|
131
|
+
sigpath = "?" + params.sort_by { |param| param[0].downcase }.collect { |param| param.join("=") }.join("&")
|
132
|
+
|
133
|
+
sig = get_aws_auth_param(sigpath, @aws_secret_access_key)
|
134
|
+
|
135
|
+
path = "?" + params.sort.collect do |param|
|
136
|
+
CGI::escape(param[0]) + "=" + CGI::escape(param[1])
|
137
|
+
end.join("&") + "&Signature=" + sig
|
138
|
+
|
139
|
+
puts path if @verbose
|
140
|
+
|
141
|
+
req = Net::HTTP::Get.new("/#{path}")
|
142
|
+
|
143
|
+
# Ruby will automatically add a random content-type on some verbs, so
|
144
|
+
# here we add a dummy one to 'supress' it. Change this logic if having
|
145
|
+
# an empty content-type header becomes semantically meaningful for any
|
146
|
+
# other verb.
|
147
|
+
req['Content-Type'] ||= ''
|
148
|
+
req['User-Agent'] = "rubyforge-amazon-ec2-ruby-gem-query-api v-#{RELEASE_VERSION}"
|
149
|
+
|
150
|
+
data = nil unless req.request_body_permitted?
|
151
|
+
@http.request(req, data)
|
152
|
+
|
492
153
|
end
|
154
|
+
|
493
155
|
end
|
494
|
-
lines
|
495
|
-
end
|
496
|
-
end
|
497
|
-
|
498
|
-
class DeleteSecurityGroupResponse < Response
|
499
|
-
def parse
|
500
|
-
# If we don't get an error, the deletion succeeded.
|
501
|
-
[["Security Group deleted."]]
|
502
|
-
end
|
503
|
-
end
|
504
|
-
|
505
|
-
class AuthorizeSecurityGroupIngressResponse < Response
|
506
|
-
def parse
|
507
|
-
# If we don't get an error, the authorization succeeded.
|
508
|
-
[["Ingress authorized."]]
|
509
|
-
end
|
510
|
-
end
|
511
|
-
|
512
|
-
class RevokeSecurityGroupIngressResponse < Response
|
513
|
-
def parse
|
514
|
-
# If we don't get an error, the revocation succeeded.
|
515
|
-
[["Ingress revoked."]]
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
class ModifyImageAttributeResponse < Response
|
520
|
-
def parse
|
521
|
-
# If we don't get an error, modification succeeded.
|
522
|
-
[["Image attribute modified."]]
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
class ResetImageAttributeResponse < Response
|
527
|
-
def parse
|
528
|
-
# If we don't get an error, reset succeeded.
|
529
|
-
[["Image attribute reset."]]
|
530
|
-
end
|
531
|
-
end
|
532
|
-
|
533
|
-
class DescribeImageAttributeResponse < Response
|
534
|
-
ELEMENT_XPATH = "DescribeImageAttributeResponse"
|
535
|
-
def parse
|
536
|
-
doc = Document.new(@http_xml)
|
537
|
-
lines = []
|
538
|
-
|
539
|
-
rootelement = XPath.first(doc, ELEMENT_XPATH)
|
540
|
-
imageId = XPath.first(rootelement, "imageId").text
|
541
156
|
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
element.name,
|
548
|
-
element.text
|
549
|
-
]
|
157
|
+
|
158
|
+
# Set the Authorization header using AWS signed header authentication
|
159
|
+
def get_aws_auth_param(path, aws_secret_access_key)
|
160
|
+
canonical_string = EC2.canonical_string(path)
|
161
|
+
encoded_canonical = EC2.encode(aws_secret_access_key, canonical_string)
|
550
162
|
end
|
551
|
-
|
552
|
-
end
|
163
|
+
|
553
164
|
end
|
554
|
-
|
165
|
+
|
555
166
|
end
|