amazon-ec2 0.1.0 → 0.2.0
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/History.txt +166 -95
- data/License.txt +67 -20
- data/Manifest.txt +17 -6
- data/README.txt +112 -2
- data/Rakefile +15 -5
- data/bin/ec2-gem-example.rb +61 -0
- data/bin/ec2sh +73 -0
- data/bin/setup.rb +19 -0
- data/lib/EC2.rb +142 -61
- data/lib/EC2/console.rb +44 -0
- data/lib/EC2/exceptions.rb +136 -0
- data/lib/EC2/image_attributes.rb +137 -29
- data/lib/EC2/images.rb +120 -73
- data/lib/EC2/instances.rb +168 -98
- data/lib/EC2/keypairs.rb +79 -23
- data/lib/EC2/responses.rb +142 -321
- data/lib/EC2/security_groups.rb +209 -117
- data/lib/EC2/version.rb +11 -2
- data/test/test_EC2.rb +44 -13
- data/test/test_EC2_console.rb +54 -0
- data/test/test_EC2_image_attributes.rb +188 -0
- data/test/test_EC2_images.rb +191 -0
- data/test/test_EC2_instances.rb +303 -0
- data/test/test_EC2_keypairs.rb +123 -0
- data/test/test_EC2_responses.rb +102 -0
- data/test/test_EC2_security_groups.rb +205 -0
- data/test/test_EC2_version.rb +44 -0
- data/test/test_helper.rb +16 -8
- data/website/index.html +378 -86
- data/website/index.txt +339 -88
- data/website/stylesheets/screen.css +8 -8
- metadata +89 -16
- data/examples/ec2-example.rb +0 -48
- data/test/test_responses.rb +0 -17
data/lib/EC2/instances.rb
CHANGED
@@ -1,121 +1,191 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
1
|
+
#--
|
2
|
+
# Amazon Web Services EC2 Query API Ruby library
|
3
|
+
#
|
4
|
+
# Ruby Gem Name:: amazon-ec2
|
5
|
+
# Author:: Glenn Rempe (mailto:glenn@elasticworkbench.com)
|
6
|
+
# Copyright:: Copyright (c) 2007 Glenn Rempe
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
8
|
+
# Home:: http://amazon-ec2.rubyforge.org
|
9
|
+
#++
|
8
10
|
|
9
11
|
module EC2
|
10
12
|
|
11
|
-
class
|
13
|
+
class Base
|
12
14
|
|
13
|
-
#
|
15
|
+
#Amazon Developer Guide Docs:
|
14
16
|
#
|
15
|
-
#
|
16
|
-
# The Query version of RunInstances only allows instances of a
|
17
|
-
# single AMI to be launched in one call. This is different
|
18
|
-
# from the SOAP API call of the same name but similar to the
|
19
|
-
# ec2-run-instances command line tool.
|
17
|
+
# The RunInstances operation launches a specified number of instances.
|
20
18
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# request. All instances that are
|
32
|
-
# is provided will have access to the associated public key at boot
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
19
|
+
# Note : The Query version of RunInstances only allows instances of a single AMI to be launched in
|
20
|
+
# one call. This is different from the SOAP API call of the same name but similar to the
|
21
|
+
# ec2-run-instances command line tool.
|
22
|
+
#
|
23
|
+
# A call to RunInstances is guaranteed to start no fewer than the requested minimum. If there is
|
24
|
+
# insufficient capacity available then no instances will be started. Amazon EC2 will make a best effort
|
25
|
+
# attempt to satisfy the requested maximum values.
|
26
|
+
#
|
27
|
+
# Every instance is launched in a security group. This may be specified as part of the launch request. If a
|
28
|
+
# security group is not indicated then instances are started in a the default security group.
|
29
|
+
# An optional keypair ID may be provided for each image in the launch request. All instances that are
|
30
|
+
# created from images for which this is provided will have access to the associated public key at boot time
|
31
|
+
# (detailed below). This key may be used to provide secure access to an instance of an image on a
|
32
|
+
# per-instance basis. Amazon EC2 public images make use of this functionality to provide secure
|
33
|
+
# passwordless access to instances (and launching those images without a keypair ID will leave them
|
34
|
+
# inaccessible).
|
35
|
+
#
|
36
|
+
# The public key material is made available to the instance at boot time by placing it in a file named
|
37
|
+
# openssh_id.pub on a logical device that is exposed to the instance as /dev/sda2 (the ephemeral
|
38
|
+
# store). The format of this file is suitable for use as an entry within ~/.ssh/authorized_keys (the
|
39
|
+
# OpenSSH format). This can be done at boot time (as part of rclocal, for example) allowing for secure
|
40
|
+
# password-less access. As the need arises, other formats will also be considered.
|
41
|
+
#
|
42
|
+
#Required Arguments:
|
43
|
+
#
|
44
|
+
# :image_id => String (Default : "")
|
45
|
+
# :min_count => Integer (default : 1 )
|
46
|
+
# :max_count => Integer (default : 1 )
|
47
|
+
#
|
48
|
+
#Optional Arguments:
|
49
|
+
#
|
50
|
+
# :key_name => String (default : nil)
|
51
|
+
# :group_id => Array (default : [])
|
52
|
+
# :user_data => String (default : nil)
|
53
|
+
# :addressing_type => String (default : "public")
|
54
|
+
# :base64_encoded => Boolean (default : false)
|
55
|
+
#
|
56
|
+
def run_instances( options = {} )
|
57
|
+
|
58
|
+
options = { :image_id => "",
|
59
|
+
:min_count => 1,
|
60
|
+
:max_count => 1,
|
61
|
+
:key_name => nil,
|
62
|
+
:group_id => [],
|
63
|
+
:user_data => nil,
|
64
|
+
:addressing_type => "public",
|
65
|
+
:base64_encoded => false }.merge(options)
|
66
|
+
|
67
|
+
# Do some validation on the arguments provided
|
68
|
+
raise ArgumentError, ":image_id must be provided" if options[:image_id].nil? || options[:image_id].empty?
|
69
|
+
raise ArgumentError, ":min_count is not valid" unless options[:min_count].to_i > 0
|
70
|
+
raise ArgumentError, ":max_count is not valid" unless options[:max_count].to_i > 0
|
71
|
+
raise ArgumentError, ":addressing_type must be 'direct' or 'public'" unless options[:addressing_type] == "public" || options[:addressing_type] == "direct"
|
72
|
+
raise ArgumentError, ":base64_encoded must be 'true' or 'false'" unless options[:base64_encoded] == true || options[:base64_encoded] == false
|
73
|
+
|
74
|
+
# If :user_data is passed in then URL escape and Base64 encode it
|
56
75
|
# as needed. Need for URL Escape + Base64 encoding is determined
|
57
|
-
# by
|
58
|
-
if
|
59
|
-
if
|
60
|
-
|
76
|
+
# by :base64_encoded param.
|
77
|
+
if options[:user_data]
|
78
|
+
if options[:base64_encoded]
|
79
|
+
user_data = options[:user_data]
|
61
80
|
else
|
62
|
-
|
81
|
+
user_data = Base64.encode64(options[:user_data]).gsub(/\n/,"").strip()
|
63
82
|
end
|
64
83
|
else
|
65
|
-
|
84
|
+
user_data = nil
|
66
85
|
end
|
67
|
-
|
86
|
+
|
68
87
|
params = {
|
69
|
-
"ImageId" =>
|
70
|
-
"MinCount" =>
|
71
|
-
"MaxCount" =>
|
72
|
-
}.merge(pathlist("SecurityGroup",
|
88
|
+
"ImageId" => options[:image_id],
|
89
|
+
"MinCount" => options[:min_count].to_s,
|
90
|
+
"MaxCount" => options[:max_count].to_s,
|
91
|
+
}.merge(pathlist("SecurityGroup", options[:group_id]))
|
92
|
+
|
93
|
+
params["KeyName"] = options[:key_name] unless options[:key_name].nil?
|
94
|
+
params["UserData"] = user_data unless user_data.nil?
|
95
|
+
params["AddressingType"] = options[:addressing_type]
|
96
|
+
|
97
|
+
return response_generator(:action => "RunInstances", :params => params)
|
73
98
|
|
74
|
-
params["KeyName"] = in_params[:keyname] unless in_params[:keyname].nil?
|
75
|
-
params["UserData"] = userData unless userData.nil?
|
76
|
-
|
77
|
-
RunInstancesResponse.new(make_request("RunInstances", params))
|
78
99
|
end
|
79
100
|
|
80
|
-
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# instance
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# for a small interval subsequent to
|
92
|
-
# is typically of the order of one hour
|
93
|
-
|
94
|
-
|
95
|
-
|
101
|
+
|
102
|
+
#Amazon Developer Guide Docs:
|
103
|
+
#
|
104
|
+
# The DescribeInstances operation returns information about instances owned by the user
|
105
|
+
# making the request.
|
106
|
+
#
|
107
|
+
# An optional list of instance IDs may be provided to request information for those instances only. If no
|
108
|
+
# instance IDs are provided, information of all relevant instances information will be returned. If an
|
109
|
+
# instance is specified that does not exist a fault is returned. If an instance is specified that exists but is not
|
110
|
+
# owned by the user making the request, then that instance will not be included in the returned results.
|
111
|
+
#
|
112
|
+
# Recently terminated instances will be included in the returned results for a small interval subsequent to
|
113
|
+
# their termination. This interval is typically of the order of one hour
|
114
|
+
#
|
115
|
+
#Required Arguments:
|
116
|
+
#
|
117
|
+
# none
|
118
|
+
#
|
119
|
+
#Optional Arguments:
|
120
|
+
#
|
121
|
+
# :instance_id => Array (default : [])
|
122
|
+
#
|
123
|
+
def describe_instances( options = {} )
|
124
|
+
|
125
|
+
options = { :instance_id => [] }.merge(options)
|
126
|
+
|
127
|
+
params = pathlist("InstanceId", options[:instance_id])
|
128
|
+
|
129
|
+
return response_generator(:action => "DescribeInstances", :params => params)
|
130
|
+
|
96
131
|
end
|
97
132
|
|
98
|
-
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
|
133
|
+
|
134
|
+
#Amazon Developer Guide Docs:
|
135
|
+
#
|
136
|
+
# The RebootInstances operation requests a reboot of one or more instances. This operation is
|
137
|
+
# asynchronous; it only queues a request to reboot the specified instance(s). The operation will succeed
|
138
|
+
# provided the instances are valid and belong to the user. Terminated instances will be ignored.
|
139
|
+
#
|
140
|
+
#Required Arguments:
|
141
|
+
#
|
142
|
+
# :instance_id => Array (default : [])
|
143
|
+
#
|
144
|
+
#Optional Arguments:
|
145
|
+
#
|
146
|
+
# none
|
147
|
+
#
|
148
|
+
def reboot_instances( options = {} )
|
149
|
+
|
150
|
+
# defaults
|
151
|
+
options = { :instance_id => [] }.merge(options)
|
152
|
+
|
153
|
+
raise ArgumentError, "No instance IDs provided" if options[:instance_id].nil? || options[:instance_id].empty?
|
154
|
+
|
155
|
+
params = pathlist("InstanceId", options[:instance_id])
|
156
|
+
|
157
|
+
return response_generator(:action => "RebootInstances", :params => params)
|
158
|
+
|
108
159
|
end
|
109
160
|
|
110
|
-
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
161
|
+
|
162
|
+
#Amazon Developer Guide Docs:
|
163
|
+
#
|
164
|
+
# The TerminateInstances operation shuts down one or more instances. This operation is idempotent
|
165
|
+
# and terminating an instance that is in the process of shutting down (or already terminated) will succeed.
|
166
|
+
# Terminated instances remain visible for a short period of time (approximately one hour) after
|
167
|
+
# termination, after which their instance ID is invalidated.
|
168
|
+
#
|
169
|
+
#Required Arguments:
|
170
|
+
#
|
171
|
+
# :instance_id => Array (default : [])
|
172
|
+
#
|
173
|
+
#Optional Arguments:
|
174
|
+
#
|
175
|
+
# none
|
176
|
+
#
|
177
|
+
def terminate_instances( options = {} )
|
178
|
+
|
179
|
+
options = { :instance_id => [] }.merge(options)
|
180
|
+
|
181
|
+
raise ArgumentError, "No :instance_id provided" if options[:instance_id].nil? || options[:instance_id].empty?
|
182
|
+
|
183
|
+
params = pathlist("InstanceId", options[:instance_id])
|
184
|
+
|
185
|
+
return response_generator(:action => "TerminateInstances", :params => params)
|
186
|
+
|
118
187
|
end
|
188
|
+
|
119
189
|
end
|
120
190
|
|
121
191
|
end
|
data/lib/EC2/keypairs.rb
CHANGED
@@ -1,36 +1,92 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
1
|
+
#--
|
2
|
+
# Amazon Web Services EC2 Query API Ruby library
|
3
|
+
#
|
4
|
+
# Ruby Gem Name:: amazon-ec2
|
5
|
+
# Author:: Glenn Rempe (mailto:glenn@elasticworkbench.com)
|
6
|
+
# Copyright:: Copyright (c) 2007 Glenn Rempe
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
8
|
+
# Home:: http://amazon-ec2.rubyforge.org
|
9
|
+
#++
|
8
10
|
|
9
11
|
module EC2
|
10
12
|
|
11
|
-
class
|
13
|
+
class Base
|
12
14
|
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
|
16
|
+
#Amazon Developer Guide Docs:
|
17
|
+
#
|
18
|
+
# The CreateKeyPair operation creates a new 2048 bit RSA keypair and returns a unique ID that can be
|
19
|
+
# used to reference this keypair when launching new instances.
|
20
|
+
#
|
21
|
+
#Required Arguments:
|
22
|
+
#
|
23
|
+
# :key_name => String (default : "")
|
24
|
+
#
|
25
|
+
#Optional Arguments:
|
26
|
+
#
|
27
|
+
# none
|
28
|
+
#
|
29
|
+
def create_keypair( options = {} )
|
30
|
+
|
31
|
+
# defaults
|
32
|
+
options = { :key_name => "" }.merge(options)
|
33
|
+
|
34
|
+
raise ArgumentError, "No :key_name provided" if options[:key_name].nil? || options[:key_name].empty?
|
35
|
+
|
36
|
+
params = { "KeyName" => options[:key_name] }
|
37
|
+
|
38
|
+
return response_generator(:action => "CreateKeyPair", :params => params)
|
39
|
+
|
19
40
|
end
|
20
41
|
|
21
|
-
|
22
|
-
#
|
23
|
-
#
|
42
|
+
|
43
|
+
#Amazon Developer Guide Docs:
|
44
|
+
#
|
45
|
+
# The DescribeKeyPairs operation returns information about keypairs available for use by the user
|
46
|
+
# making the request. Selected keypairs may be specified or the list may be left empty if information for
|
24
47
|
# all registered keypairs is required.
|
25
|
-
|
26
|
-
|
27
|
-
|
48
|
+
#
|
49
|
+
#Required Arguments:
|
50
|
+
#
|
51
|
+
# :key_name => Array (default : [])
|
52
|
+
#
|
53
|
+
#Optional Arguments:
|
54
|
+
#
|
55
|
+
# none
|
56
|
+
#
|
57
|
+
def describe_keypairs( options = {} )
|
58
|
+
|
59
|
+
options = { :key_name => [] }.merge(options)
|
60
|
+
|
61
|
+
params = pathlist("KeyName", options[:key_name] )
|
62
|
+
|
63
|
+
return response_generator(:action => "DescribeKeyPairs", :params => params)
|
64
|
+
|
28
65
|
end
|
29
66
|
|
67
|
+
|
68
|
+
#Amazon Developer Guide Docs:
|
69
|
+
#
|
30
70
|
# The DeleteKeyPair operation deletes a keypair.
|
31
|
-
|
32
|
-
|
33
|
-
|
71
|
+
#
|
72
|
+
#Required Arguments:
|
73
|
+
#
|
74
|
+
# :key_name => String (default : "")
|
75
|
+
#
|
76
|
+
#Optional Arguments:
|
77
|
+
#
|
78
|
+
# none
|
79
|
+
#
|
80
|
+
def delete_keypair( options = {} )
|
81
|
+
|
82
|
+
options = { :key_name => "" }.merge(options)
|
83
|
+
|
84
|
+
raise ArgumentError, "No :key_name provided" if options[:key_name].nil? || options[:key_name].empty?
|
85
|
+
|
86
|
+
params = { "KeyName" => options[:key_name] }
|
87
|
+
|
88
|
+
return response_generator(:action => "DeleteKeyPair", :params => params)
|
89
|
+
|
34
90
|
end
|
35
91
|
|
36
92
|
end
|
data/lib/EC2/responses.rb
CHANGED
@@ -1,348 +1,169 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
1
|
+
#--
|
2
|
+
# Amazon Web Services EC2 Query API Ruby library
|
3
|
+
#
|
4
|
+
# Ruby Gem Name:: amazon-ec2
|
5
|
+
# Author:: Glenn Rempe (mailto:glenn@elasticworkbench.com)
|
6
|
+
# Copyright:: Copyright (c) 2007 Glenn Rempe
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
8
|
+
# Home:: http://amazon-ec2.rubyforge.org
|
9
|
+
#++
|
8
10
|
|
9
11
|
module EC2
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
# The make_request() and ec2_error? methods, which are shared by all, will raise any
|
14
|
+
# exceptions encountered along the way as it converses with EC2.
|
15
|
+
#
|
16
|
+
# Exception Handling: If for some reason an error occurrs when executing a method
|
17
|
+
# (e.g. its arguments were incorrect, or it simply failed) then an exception will
|
18
|
+
# be thrown. The exceptions are defined in exceptions.rb as individual classes and should
|
19
|
+
# match the exceptions that Amazon has defined for EC2. If the exception raised cannot be
|
20
|
+
# identified then a more generic exception class will be thrown.
|
21
|
+
#
|
22
|
+
# The implication of this is that you need to be prepared to handle any exceptions that
|
23
|
+
# may be raised by this library in YOUR code with a 'rescue' clauses. It is up to you
|
24
|
+
# how gracefully you want to handle these exceptions that are raised.
|
25
|
+
|
26
|
+
# Credits :
|
27
|
+
# I learned the magic of making an OpenStruct object able to respond as a fully Enumerable
|
28
|
+
# object (responds to .each, etc.) thanks to a great blog article on Struct and OpenStruct
|
29
|
+
# at http://errtheblog.com/post/30
|
30
|
+
#
|
31
|
+
# Thanks to Sean Knapp for the XmlSimple response patch which greatly simplified the response
|
32
|
+
# mechanism for the whole library while making it more accurate and much less brittle to boot!
|
33
|
+
#
|
34
|
+
|
35
|
+
require 'xmlsimple'
|
36
|
+
|
37
|
+
class Response < OpenStruct
|
15
38
|
|
16
|
-
|
39
|
+
include Enumerable
|
17
40
|
|
18
|
-
def initialize(http_response)
|
19
|
-
@http_response = http_response
|
20
|
-
@http_xml = http_response.body
|
21
|
-
@is_error = false
|
22
|
-
if http_response.is_a? Net::HTTPSuccess
|
23
|
-
@structure = parse
|
24
|
-
else
|
25
|
-
@is_error = true
|
26
|
-
@structure = parse_error
|
27
|
-
end
|
28
|
-
end
|
29
41
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
element = REXML::XPath.first(doc, ERROR_XPATH)
|
37
|
-
|
38
|
-
errorCode = REXML::XPath.first(element, "Code").text
|
39
|
-
errorMessage = REXML::XPath.first(element, "Message").text
|
42
|
+
def self.parse(options = {})
|
43
|
+
options = {
|
44
|
+
:xml => "",
|
45
|
+
:parse_options => { 'ForceArray' => ['item'], 'SuppressEmpty' => nil }
|
46
|
+
}.merge(options)
|
47
|
+
response = Response.new(XmlSimple.xml_in(options[:xml], options[:parse_options]))
|
40
48
|
|
41
|
-
|
49
|
+
# set the xml attribute of the response object to contain the original XML that was
|
50
|
+
# returned by amazon. This allows anyone to bypass our parsing if desired and just
|
51
|
+
# get right at the raw XML response.
|
52
|
+
response.xml = options[:xml]
|
53
|
+
return response
|
42
54
|
end
|
43
55
|
|
44
|
-
def parse
|
45
|
-
# Placeholder -- this method should be overridden in child classes.
|
46
|
-
nil
|
47
|
-
end
|
48
56
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
57
|
+
# Every member of an OpenStruct object has getters and setters, the latter of which
|
58
|
+
# has a method ending in "=". Find all of these methods, excluding those defined on
|
59
|
+
# parent classes.
|
60
|
+
def members
|
61
|
+
methods(false).sort.grep(/=/).map { |m| m[0...-1] }
|
53
62
|
end
|
54
63
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
doc = REXML::Document.new(@http_xml)
|
62
|
-
lines = []
|
63
|
-
|
64
|
-
doc.elements.each(ELEMENT_XPATH) do |element|
|
65
|
-
imageId = REXML::XPath.first(element, "imageId").text
|
66
|
-
imageLocation = REXML::XPath.first(element, "imageLocation").text
|
67
|
-
imageOwnerId = REXML::XPath.first(element, "imageOwnerId").text
|
68
|
-
imageState = REXML::XPath.first(element, "imageState").text
|
69
|
-
isPublic = REXML::XPath.first(element, "isPublic").text
|
70
|
-
lines << ["IMAGE", imageId, imageLocation, imageOwnerId, imageState, isPublic]
|
64
|
+
|
65
|
+
# Required by the Enumerable module. Iterate over each item in the members array
|
66
|
+
# and pass as a value the block passed to each using yield.
|
67
|
+
def each
|
68
|
+
members.each do |method|
|
69
|
+
yield send(method)
|
71
70
|
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
class RegisterImageResponse < Response
|
78
|
-
ELEMENT_XPATH = "RegisterImageResponse/imageId"
|
79
|
-
def parse
|
80
|
-
doc = REXML::Document.new(@http_xml)
|
81
|
-
lines = [["IMAGE", REXML::XPath.first(doc, ELEMENT_XPATH).text]]
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
|
86
|
-
class DeregisterImageResponse < Response
|
87
|
-
def parse
|
88
|
-
# If we don't get an error, the deregistration succeeded.
|
89
|
-
[["Image deregistered."]]
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
class CreateKeyPairResponse < Response
|
95
|
-
ELEMENT_XPATH = "CreateKeyPairResponse"
|
96
|
-
def parse
|
97
|
-
doc = REXML::Document.new(@http_xml)
|
98
|
-
element = REXML::XPath.first(doc, ELEMENT_XPATH)
|
99
|
-
|
100
|
-
keyName = REXML::XPath.first(element, "keyName").text
|
101
|
-
keyFingerprint = REXML::XPath.first(element, "keyFingerprint").text
|
102
|
-
keyMaterial = REXML::XPath.first(element, "keyMaterial").text
|
103
|
-
|
104
|
-
line = [["KEYPAIR", keyName, keyFingerprint], [keyMaterial]]
|
71
|
+
self
|
105
72
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
doc.elements.each(ELEMENT_XPATH) do |element|
|
116
|
-
keyName = REXML::XPath.first(element, "keyName").text
|
117
|
-
keyFingerprint = REXML::XPath.first(element, "keyFingerprint").text
|
118
|
-
lines << ["KEYPAIR", keyName, keyFingerprint]
|
73
|
+
|
74
|
+
|
75
|
+
# Same as the each method, but with both key and value.
|
76
|
+
#
|
77
|
+
#Sample Use:
|
78
|
+
# obj.each_pair { |k,v| puts "key: #{k}, value: #{v}" }
|
79
|
+
def each_pair
|
80
|
+
members.each do |method|
|
81
|
+
yield method, send(method)
|
119
82
|
end
|
120
|
-
|
83
|
+
self
|
121
84
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
# If we don't get an error, the deletion succeeded.
|
128
|
-
[["Keypair deleted."]]
|
85
|
+
|
86
|
+
|
87
|
+
# Alternative method for getting members.
|
88
|
+
def [](member)
|
89
|
+
send(member)
|
129
90
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
def parse
|
136
|
-
doc = REXML::Document.new(@http_xml)
|
137
|
-
lines = []
|
138
|
-
|
139
|
-
rootelement = REXML::XPath.first(doc, ELEMENT_XPATH)
|
140
|
-
|
141
|
-
reservationId = REXML::XPath.first(rootelement, "reservationId").text
|
142
|
-
ownerId = REXML::XPath.first(rootelement, "ownerId").text
|
143
|
-
groups = nil
|
144
|
-
rootelement.elements.each("groupSet/item/groupId") do |element|
|
145
|
-
if not groups
|
146
|
-
groups = element.text
|
147
|
-
else
|
148
|
-
groups += "," + element.text
|
149
|
-
end
|
150
|
-
end
|
151
|
-
lines << ["RESERVATION", reservationId, ownerId, groups]
|
152
|
-
|
153
|
-
# rootelement = REXML::XPath.first(doc, ELEMENT_XPATH)
|
154
|
-
rootelement.elements.each("instancesSet/item") do |element|
|
155
|
-
instanceId = REXML::XPath.first(element, "instanceId").text
|
156
|
-
imageId = REXML::XPath.first(element, "imageId").text
|
157
|
-
instanceState = REXML::XPath.first(element, "instanceState/name").text
|
158
|
-
# Only for debug mode, which we don't support yet:
|
159
|
-
instanceStateCode = REXML::XPath.first(element, "instanceState/code").text
|
160
|
-
dnsName = REXML::XPath.first(element, "dnsName").text
|
161
|
-
# We don't return this, but still:
|
162
|
-
reason = REXML::XPath.first(element, "reason").text
|
163
|
-
lines << ["INSTANCE", instanceId, imageId, dnsName, instanceState]
|
164
|
-
end
|
165
|
-
lines
|
91
|
+
|
92
|
+
|
93
|
+
# Alternative method for setting members.
|
94
|
+
def []=(member, value)
|
95
|
+
send("#{member}=", value)
|
166
96
|
end
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
else
|
184
|
-
groups += "," + element.text
|
97
|
+
|
98
|
+
|
99
|
+
# Helper for converting to string which support a long and short version
|
100
|
+
# to avoid recursion problems with parents.
|
101
|
+
def to_string(short=false)
|
102
|
+
s = "#<#{self.class}:0x#{(2 ** 32 + object_id).to_s(16).upcase}"
|
103
|
+
if (short)
|
104
|
+
s += " ..."
|
105
|
+
else
|
106
|
+
each_pair { |k,v|
|
107
|
+
if (v == self.parent && v.kind_of?(Response))
|
108
|
+
v = v.to_string(true)
|
109
|
+
elsif (v.kind_of?(String))
|
110
|
+
v = "\"#{v.gsub("\"", "\\\"")}\""
|
111
|
+
elsif (v.kind_of?(NilClass))
|
112
|
+
v = "nil"
|
185
113
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
rootelement.elements.each("instancesSet/item") do |element|
|
190
|
-
instanceId = REXML::XPath.first(element, "instanceId").text
|
191
|
-
imageId = REXML::XPath.first(element, "imageId").text
|
192
|
-
instanceState = REXML::XPath.first(element, "instanceState/name").text
|
193
|
-
# Only for debug mode, which we don't support yet:
|
194
|
-
instanceStateCode = REXML::XPath.first(element, "instanceState/code").text
|
195
|
-
dnsName = REXML::XPath.first(element, "dnsName").text
|
196
|
-
# We don't return this, but still:
|
197
|
-
reason = REXML::XPath.first(element, "reason").text
|
198
|
-
lines << ["INSTANCE", instanceId, imageId, dnsName, instanceState]
|
199
|
-
end
|
200
|
-
end
|
201
|
-
lines
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
class TerminateInstancesResponse < Response
|
207
|
-
ELEMENT_XPATH = "TerminateInstancesResponse/instancesSet/item"
|
208
|
-
def parse
|
209
|
-
doc = REXML::Document.new(@http_xml)
|
210
|
-
lines = []
|
211
|
-
|
212
|
-
doc.elements.each(ELEMENT_XPATH) do |element|
|
213
|
-
instanceId = REXML::XPath.first(element, "instanceId").text
|
214
|
-
shutdownState = REXML::XPath.first(element, "shutdownState/name").text
|
215
|
-
# Only for debug mode, which we don't support yet:
|
216
|
-
shutdownStateCode = REXML::XPath.first(element, "shutdownState/code").text
|
217
|
-
previousState = REXML::XPath.first(element, "previousState/name").text
|
218
|
-
# Only for debug mode, which we don't support yet:
|
219
|
-
previousStateCode = REXML::XPath.first(element, "previousState/code").text
|
220
|
-
lines << ["INSTANCE", instanceId, previousState, shutdownState]
|
114
|
+
s += " #{k}=#{v}"
|
115
|
+
}
|
221
116
|
end
|
222
|
-
|
117
|
+
s += ">"
|
118
|
+
return s
|
223
119
|
end
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
def
|
228
|
-
|
229
|
-
# Let's use the tag they're actually returning since it doesn't match the docs -- Kevin Clark 2/26/07
|
230
|
-
REXML::XPath.match( doc, "//return").first.text == "true" ? true : false
|
120
|
+
|
121
|
+
|
122
|
+
# Override of to string method.
|
123
|
+
def to_s
|
124
|
+
return to_string
|
231
125
|
end
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
126
|
+
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Initialize the object by passing data to the OpenStruct initialize method
|
131
|
+
# and then converting ourself to guarantee we have top-to-bottom data
|
132
|
+
# representation as a Response object.
|
133
|
+
def initialize(data, parent=nil)
|
134
|
+
super(data)
|
135
|
+
self.parent = parent
|
136
|
+
Response.convert(self, parent)
|
239
137
|
end
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
groupName = REXML::XPath.first(rootelement, "groupName").text
|
251
|
-
ownerId = REXML::XPath.first(rootelement, "ownerId").text
|
252
|
-
groupDescription = REXML::XPath.first(rootelement, "groupDescription").text
|
253
|
-
lines << ["GROUP", ownerId, groupName, groupDescription]
|
254
|
-
rootelement.elements.each("ipPermissions/item") do |element|
|
255
|
-
ipProtocol = REXML::XPath.first(element, "ipProtocol").text
|
256
|
-
fromPort = REXML::XPath.first(element, "fromPort").text
|
257
|
-
toPort = REXML::XPath.first(element, "toPort").text
|
258
|
-
permArr = [
|
259
|
-
"PERMISSION",
|
260
|
-
ownerId,
|
261
|
-
groupName,
|
262
|
-
"ALLOWS",
|
263
|
-
ipProtocol,
|
264
|
-
fromPort,
|
265
|
-
toPort,
|
266
|
-
"FROM"
|
267
|
-
]
|
268
|
-
element.elements.each("groups/item") do |subelement|
|
269
|
-
userId = REXML::XPath.first(subelement, "userId").text
|
270
|
-
targetGroupName = REXML::XPath.first(subelement, "groupName").text
|
271
|
-
lines << permArr + ["USER", userId, "GRPNAME", targetGroupName]
|
272
|
-
end
|
273
|
-
element.elements.each("ipRanges/item") do |subelement|
|
274
|
-
cidrIp = REXML::XPath.first(subelement, "cidrIp").text
|
275
|
-
lines << permArr + ["CIDR", cidrIp]
|
138
|
+
|
139
|
+
|
140
|
+
# The "brains" of our Response class. This method takes an arbitray object and
|
141
|
+
# depending on its class attempts to convert it.
|
142
|
+
def self.convert(obj, parent)
|
143
|
+
if (obj.kind_of?(Response))
|
144
|
+
# Recursively convert the object.
|
145
|
+
obj.each_pair { |k,v|
|
146
|
+
if (v != obj.parent)
|
147
|
+
obj[k] = convert(v, obj)
|
276
148
|
end
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
class AuthorizeSecurityGroupIngressResponse < Response
|
293
|
-
def parse
|
294
|
-
# If we don't get an error, the authorization succeeded.
|
295
|
-
[["Ingress authorized."]]
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
|
300
|
-
class RevokeSecurityGroupIngressResponse < Response
|
301
|
-
def parse
|
302
|
-
# If we don't get an error, the revocation succeeded.
|
303
|
-
[["Ingress revoked."]]
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
|
308
|
-
class ModifyImageAttributeResponse < Response
|
309
|
-
def parse
|
310
|
-
# If we don't get an error, modification succeeded.
|
311
|
-
[["Image attribute modified."]]
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
|
316
|
-
class ResetImageAttributeResponse < Response
|
317
|
-
def parse
|
318
|
-
# If we don't get an error, reset succeeded.
|
319
|
-
[["Image attribute reset."]]
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
|
324
|
-
class DescribeImageAttributeResponse < Response
|
325
|
-
ELEMENT_XPATH = "DescribeImageAttributeResponse"
|
326
|
-
def parse
|
327
|
-
doc = REXML::Document.new(@http_xml)
|
328
|
-
lines = []
|
329
|
-
|
330
|
-
rootelement = REXML::XPath.first(doc, ELEMENT_XPATH)
|
331
|
-
imageId = REXML::XPath.first(rootelement, "imageId").text
|
332
|
-
|
333
|
-
# Handle launchPermission attributes:
|
334
|
-
rootelement.elements.each("launchPermission/item/*") do |element|
|
335
|
-
lines << [
|
336
|
-
"launchPermission",
|
337
|
-
imageId,
|
338
|
-
element.name,
|
339
|
-
element.text
|
340
|
-
]
|
149
|
+
}
|
150
|
+
return obj
|
151
|
+
elsif (obj.kind_of?(Hash))
|
152
|
+
# Hashes make good Responses already thanks to OpenStruct.
|
153
|
+
return Response.new(obj, parent)
|
154
|
+
elsif (obj.kind_of?(Array))
|
155
|
+
# With arrays, make sure each element is appropriately converted.
|
156
|
+
new_arr = []
|
157
|
+
obj.each { |elem|
|
158
|
+
new_arr << convert(elem, parent)
|
159
|
+
}
|
160
|
+
return new_arr
|
161
|
+
else
|
162
|
+
# At this point we're out of ideas, so let's hope it is a string.
|
163
|
+
return obj
|
341
164
|
end
|
342
|
-
lines
|
343
165
|
end
|
344
|
-
end
|
345
|
-
|
346
|
-
# end EC2 Module
|
347
|
-
end
|
348
166
|
|
167
|
+
end # class Response < OpenStruct
|
168
|
+
|
169
|
+
end # module EC2
|