amazon-ec2 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|