rudy 0.4.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +54 -30
- data/README.rdoc +100 -12
- data/Rakefile +103 -8
- data/Rudyfile +119 -0
- data/bin/ird +175 -0
- data/bin/rudy +259 -156
- data/bin/rudy-ec2 +228 -95
- data/bin/rudy-s3 +76 -0
- data/bin/rudy-sdb +67 -0
- data/lib/annoy.rb +270 -0
- data/lib/console.rb +30 -9
- data/lib/escape.rb +305 -0
- data/lib/rudy.rb +151 -182
- data/lib/rudy/aws.rb +56 -49
- data/lib/rudy/aws/ec2.rb +47 -292
- data/lib/rudy/aws/ec2/address.rb +157 -0
- data/lib/rudy/aws/ec2/group.rb +301 -0
- data/lib/rudy/aws/ec2/image.rb +168 -0
- data/lib/rudy/aws/ec2/instance.rb +434 -0
- data/lib/rudy/aws/ec2/keypair.rb +104 -0
- data/lib/rudy/aws/ec2/snapshot.rb +98 -0
- data/lib/rudy/aws/ec2/volume.rb +230 -0
- data/lib/rudy/aws/ec2/zone.rb +77 -0
- data/lib/rudy/aws/s3.rb +54 -0
- data/lib/rudy/aws/sdb.rb +298 -0
- data/lib/rudy/aws/sdb/error.rb +46 -0
- data/lib/rudy/{metadata/backup.rb → backup.rb} +26 -51
- data/lib/rudy/cli.rb +157 -0
- data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
- data/lib/rudy/cli/aws/ec2/candy.rb +208 -0
- data/lib/rudy/cli/aws/ec2/groups.rb +121 -0
- data/lib/rudy/cli/aws/ec2/images.rb +196 -0
- data/lib/rudy/cli/aws/ec2/instances.rb +194 -0
- data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
- data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
- data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
- data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
- data/lib/rudy/cli/aws/s3/buckets.rb +50 -0
- data/lib/rudy/cli/aws/s3/store.rb +22 -0
- data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
- data/lib/rudy/cli/candy.rb +8 -0
- data/lib/rudy/{command → cli}/config.rb +34 -24
- data/lib/rudy/cli/disks.rb +35 -0
- data/lib/rudy/cli/machines.rb +94 -0
- data/lib/rudy/cli/routines.rb +57 -0
- data/lib/rudy/config.rb +77 -72
- data/lib/rudy/config/objects.rb +29 -0
- data/lib/rudy/disks.rb +248 -0
- data/lib/rudy/global.rb +121 -0
- data/lib/rudy/huxtable.rb +340 -0
- data/lib/rudy/machines.rb +245 -0
- data/lib/rudy/metadata.rb +123 -13
- data/lib/rudy/routines.rb +47 -0
- data/lib/rudy/routines/helpers/diskhelper.rb +101 -0
- data/lib/rudy/routines/helpers/scripthelper.rb +91 -0
- data/lib/rudy/routines/release.rb +34 -0
- data/lib/rudy/routines/shutdown.rb +57 -0
- data/lib/rudy/routines/startup.rb +58 -0
- data/lib/rudy/scm/svn.rb +1 -1
- data/lib/rudy/utils.rb +322 -4
- data/lib/storable.rb +26 -17
- data/lib/sysinfo.rb +274 -0
- data/lib/tryouts.rb +6 -13
- data/rudy.gemspec +128 -42
- data/support/randomize-root-password +45 -0
- data/support/rudy-ec2-startup +9 -9
- data/support/update-ec2-ami-tools +20 -0
- data/test/05_config/00_setup_test.rb +20 -0
- data/test/05_config/30_machines_test.rb +69 -0
- data/test/20_sdb/00_setup_test.rb +16 -0
- data/test/20_sdb/10_domains_test.rb +115 -0
- data/test/25_ec2/00_setup_test.rb +29 -0
- data/test/25_ec2/10_keypairs_test.rb +41 -0
- data/test/25_ec2/20_groups_test.rb +131 -0
- data/test/25_ec2/30_addresses_test.rb +38 -0
- data/test/25_ec2/40_volumes_test.rb +49 -0
- data/test/25_ec2/50_snapshots_test.rb +74 -0
- data/test/26_ec2_instances/00_setup_test.rb +28 -0
- data/test/26_ec2_instances/10_instances_test.rb +83 -0
- data/test/26_ec2_instances/50_images_test.rb +13 -0
- data/test/30_sdb_metadata/00_setup_test.rb +21 -0
- data/test/30_sdb_metadata/10_disks_test.rb +109 -0
- data/test/30_sdb_metadata/20_backups_test.rb +102 -0
- data/test/coverage.txt +51 -0
- data/test/helper.rb +36 -0
- data/vendor/highline-1.5.1/CHANGELOG +222 -0
- data/vendor/highline-1.5.1/INSTALL +35 -0
- data/vendor/highline-1.5.1/LICENSE +7 -0
- data/vendor/highline-1.5.1/README +63 -0
- data/vendor/highline-1.5.1/Rakefile +82 -0
- data/vendor/highline-1.5.1/TODO +6 -0
- data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
- data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
- data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
- data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
- data/vendor/highline-1.5.1/examples/limit.rb +12 -0
- data/vendor/highline-1.5.1/examples/menus.rb +65 -0
- data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
- data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
- data/vendor/highline-1.5.1/examples/password.rb +7 -0
- data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
- data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
- data/vendor/highline-1.5.1/lib/highline.rb +758 -0
- data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
- data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
- data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
- data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
- data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
- data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
- data/vendor/highline-1.5.1/setup.rb +1360 -0
- data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
- data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
- data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
- data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
- data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
- metadata +141 -38
- data/lib/aws_sdb.rb +0 -3
- data/lib/aws_sdb/error.rb +0 -42
- data/lib/aws_sdb/service.rb +0 -215
- data/lib/rudy/aws/simpledb.rb +0 -53
- data/lib/rudy/command/addresses.rb +0 -46
- data/lib/rudy/command/backups.rb +0 -175
- data/lib/rudy/command/base.rb +0 -841
- data/lib/rudy/command/deploy.rb +0 -12
- data/lib/rudy/command/disks.rb +0 -213
- data/lib/rudy/command/environment.rb +0 -73
- data/lib/rudy/command/groups.rb +0 -61
- data/lib/rudy/command/images.rb +0 -91
- data/lib/rudy/command/instances.rb +0 -85
- data/lib/rudy/command/machines.rb +0 -161
- data/lib/rudy/command/metadata.rb +0 -41
- data/lib/rudy/command/release.rb +0 -174
- data/lib/rudy/command/volumes.rb +0 -66
- data/lib/rudy/metadata/disk.rb +0 -138
- data/tryouts/console_tryout.rb +0 -91
@@ -0,0 +1,434 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Rudy::AWS
|
5
|
+
class EC2::Instance < Storable
|
6
|
+
@@sformat = " -> %10s; %10s; %12s; %10s; groups: %s"
|
7
|
+
field :aki
|
8
|
+
field :ari
|
9
|
+
field :launch_index => Time
|
10
|
+
field :launch_time
|
11
|
+
field :keyname
|
12
|
+
field :size
|
13
|
+
field :ami
|
14
|
+
field :dns_private
|
15
|
+
field :dns_public
|
16
|
+
field :awsid
|
17
|
+
field :state
|
18
|
+
field :zone
|
19
|
+
field :reason
|
20
|
+
field :groups => Array
|
21
|
+
|
22
|
+
# Groups aren't returned when creating an instance so this
|
23
|
+
# method returns an empty Array if +@groups+ is not set.
|
24
|
+
def groups
|
25
|
+
@groups || []
|
26
|
+
end
|
27
|
+
|
28
|
+
def liner_note
|
29
|
+
info = self.running? ? self.dns_public : self.state
|
30
|
+
"%s %s" % [self.awsid.bright, info]
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s(with_title=false)
|
34
|
+
lines = []
|
35
|
+
lines << "%s (%s)" % [liner_note, @groups.join(', ')]
|
36
|
+
#if self.running?
|
37
|
+
# k, g = @keyname || 'no-keypair', self.groups.join(', ')
|
38
|
+
# lines << @@sformat % %w{zone size ami keyname groups} if with_title
|
39
|
+
# lines << @@sformat % [@zone, @size, @ami, k, g]
|
40
|
+
#end
|
41
|
+
lines.join($/)
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
lines = []
|
46
|
+
lines << liner_note
|
47
|
+
field_names.each do |key|
|
48
|
+
next unless self.respond_to?(key)
|
49
|
+
val = self.send(key)
|
50
|
+
lines << sprintf(" %22s: %s", key, (val.is_a?(Array) ? val.join(', ') : val))
|
51
|
+
end
|
52
|
+
lines.join($/)
|
53
|
+
end
|
54
|
+
|
55
|
+
def running?; self.state && self.state == 'running'; end
|
56
|
+
def pending?; self.state && self.state == 'pending'; end
|
57
|
+
def terminated?; self.state && self.state == 'terminated'; end
|
58
|
+
def degraded?; self.state && self.state == 'degraded'; end
|
59
|
+
def shutting_down?; self.state && self.state == 'shutting-down'; end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
module EC2
|
65
|
+
class Instances
|
66
|
+
include Rudy::AWS::ObjectBase
|
67
|
+
include Rudy::AWS::EC2::Base
|
68
|
+
|
69
|
+
unless defined?(KNOWN_STATES)
|
70
|
+
KNOWN_STATES = [:running, :pending, :shutting_down, :terminated, :degraded].freeze
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return an Array of Instance objects. Note: These objects will not have
|
74
|
+
# DNS data because they will still be in pending state. The DNS info becomes
|
75
|
+
# available once the instance enters the running state.
|
76
|
+
#
|
77
|
+
# +opts+ supports the following parameters:
|
78
|
+
#
|
79
|
+
# * +:ami+
|
80
|
+
# * +:group+
|
81
|
+
# * +:size+
|
82
|
+
# * +:keypair+
|
83
|
+
# * +:address+
|
84
|
+
# * +:private+ true or false (default)
|
85
|
+
# * +:machine_data+
|
86
|
+
# * +:min+ count
|
87
|
+
# * +:max+ count
|
88
|
+
#
|
89
|
+
def create(opts={}, &each_inst)
|
90
|
+
raise NoAMI unless opts[:ami]
|
91
|
+
raise NoGroup unless opts[:group]
|
92
|
+
|
93
|
+
opts = {
|
94
|
+
:size => 'm1.small',
|
95
|
+
:min => 1,
|
96
|
+
:max => nil
|
97
|
+
}.merge(opts)
|
98
|
+
|
99
|
+
old_opts = {
|
100
|
+
:image_id => opts[:ami].to_s,
|
101
|
+
:min_count => opts[:min],
|
102
|
+
:max_count => opts[:max] || opts[:min],
|
103
|
+
:key_name => (opts[:keypair] || '').to_s,
|
104
|
+
:group_id => [opts[:group]].flatten.compact,
|
105
|
+
#:user_data => opts[:machine_data], # Error: Invalid BASE64 encoding of user data ??
|
106
|
+
:availability_zone => opts[:zone].to_s,
|
107
|
+
:addressing_type => opts[:private] ? 'private' : 'public',
|
108
|
+
:instance_type => opts[:size].to_s,
|
109
|
+
:kernel_id => nil
|
110
|
+
}
|
111
|
+
#p opts[:machine_data]
|
112
|
+
#exit
|
113
|
+
|
114
|
+
response = execute_request({}) { @ec2.run_instances(old_opts) }
|
115
|
+
return nil unless response['instancesSet'].is_a?(Hash)
|
116
|
+
instances = response['instancesSet']['item'].collect do |inst|
|
117
|
+
self.class.from_hash(inst)
|
118
|
+
end
|
119
|
+
instances.each { |inst|
|
120
|
+
each_inst.call(inst)
|
121
|
+
} if each_inst
|
122
|
+
instances
|
123
|
+
end
|
124
|
+
|
125
|
+
def restart(inst_ids=[], &each_inst)
|
126
|
+
instances = list(:running, inst_ids, &each_inst) || []
|
127
|
+
raise NoRunningInstances if instances.empty?
|
128
|
+
inst_ids = objects_to_instance_ids(inst_ids)
|
129
|
+
response = execute_request({}) {
|
130
|
+
@ec2.reboot_instances(:instance_id => inst_ids)
|
131
|
+
}
|
132
|
+
response['return'] == 'true'
|
133
|
+
end
|
134
|
+
|
135
|
+
def destroy(inst_ids=[], &each_inst)
|
136
|
+
instances = list(:running, inst_ids, &each_inst) || []
|
137
|
+
raise NoRunningInstances if instances.empty?
|
138
|
+
|
139
|
+
inst_ids = objects_to_instance_ids(inst_ids)
|
140
|
+
|
141
|
+
response = execute_request({}) {
|
142
|
+
@ec2.terminate_instances(:instance_id => inst_ids)
|
143
|
+
}
|
144
|
+
|
145
|
+
#instancesSet:
|
146
|
+
# item:
|
147
|
+
# - instanceId: i-ebdcb882
|
148
|
+
# shutdownState:
|
149
|
+
# code: "48"
|
150
|
+
# name: terminated
|
151
|
+
# previousState:
|
152
|
+
# code: "48"
|
153
|
+
# name: terminated
|
154
|
+
|
155
|
+
raise MalformedResponse unless response['instancesSet'].is_a?(Hash)
|
156
|
+
instances_shutdown = []
|
157
|
+
response['instancesSet']['item'].collect do |inst|
|
158
|
+
next unless inst['shutdownState'].is_a?(Hash) && inst['shutdownState']['name'] == 'shutting-down'
|
159
|
+
instances_shutdown << inst['instanceId']
|
160
|
+
end
|
161
|
+
success = instances_shutdown.size == inst_ids.size
|
162
|
+
success
|
163
|
+
end
|
164
|
+
|
165
|
+
def restart_group(group, &each_inst)
|
166
|
+
instances = list_group(group, :running, &each_inst) || []
|
167
|
+
inst_ids = objects_to_instance_ids(instances)
|
168
|
+
restart(inst_ids, :skip_check)
|
169
|
+
end
|
170
|
+
|
171
|
+
def destroy_group(group, &each_inst)
|
172
|
+
instances = list_group(group, :running, &each_inst) || []
|
173
|
+
inst_ids = objects_to_instance_ids(instances)
|
174
|
+
destroy(inst_ids, :skip_check)
|
175
|
+
end
|
176
|
+
|
177
|
+
# * +state+ is an optional instance state. If specified, must be one of: running (default), pending, terminated.
|
178
|
+
# * +inst_ids+ is an Array of instance IDs.
|
179
|
+
# Returns an Array of Rudy::AWS::EC2::Instance objects.
|
180
|
+
def list(state=nil, inst_ids=[], &each_inst)
|
181
|
+
instances = list_as_hash(state, inst_ids, &each_inst)
|
182
|
+
instances &&= instances.values
|
183
|
+
instances = nil if instances && instances.empty? # Don't return an empty hash
|
184
|
+
instances
|
185
|
+
end
|
186
|
+
|
187
|
+
# * +group+ is a security group name.
|
188
|
+
# * +state+ is an optional instance state. If specified, must be one of: running (default), pending, terminated.
|
189
|
+
# * +inst_ids+ is an Array of instance IDs.
|
190
|
+
def list_group(group=nil, state=nil, inst_ids=[], &each_inst)
|
191
|
+
instances = list_group_as_hash(group, state, inst_ids, &each_inst)
|
192
|
+
instances &&= instances.values
|
193
|
+
instances = nil if instances && instances.empty? # Don't return an empty hash
|
194
|
+
instances
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
# * +group+ is a security group name.
|
199
|
+
# * +state+ is an optional instance state. If specified, must be one of: running (default), pending, terminated.
|
200
|
+
# * +inst_ids+ is an Array of instance IDs.
|
201
|
+
def list_group_as_hash(group=nil, state=nil, inst_ids=[], &each_inst)
|
202
|
+
instances = list_as_hash(state, inst_ids)
|
203
|
+
# Remove instances that are not in the specified group
|
204
|
+
if instances
|
205
|
+
instances = instances.reject { |id,inst| !inst.groups.member?(group) } if group
|
206
|
+
instances.each_value { |inst| each_inst.call(inst) } if each_inst
|
207
|
+
end
|
208
|
+
instances = nil if instances && instances.empty? # Don't return an empty hash
|
209
|
+
instances
|
210
|
+
end
|
211
|
+
|
212
|
+
# * +state+ is an optional instance state. If specified, must be
|
213
|
+
# one of: running (default), pending, terminated, any
|
214
|
+
# * +inst_ids+ is an Array of instance IDs or Rudy::AWS::EC2::Instance objects.
|
215
|
+
# Returns a Hash of Rudy::AWS::EC2::Instance objects. The key is the instance ID.
|
216
|
+
# * +each_inst+ a block to execute for every instance in the list.
|
217
|
+
def list_as_hash(state=nil, inst_ids=[], &each_inst)
|
218
|
+
state &&= state.to_sym
|
219
|
+
state = nil if state == :any
|
220
|
+
raise "Unknown state: #{state}" if state && !Instances.known_state?(state)
|
221
|
+
state = :'shutting-down' if state == :shutting_down # EC2 uses a dash
|
222
|
+
|
223
|
+
# If we got Instance objects, we want just the IDs.
|
224
|
+
# This method always returns an Array.
|
225
|
+
inst_ids = objects_to_instance_ids(inst_ids)
|
226
|
+
|
227
|
+
response = execute_request({}) {
|
228
|
+
@ec2.describe_instances(:instance_id => inst_ids)
|
229
|
+
}
|
230
|
+
|
231
|
+
# requestId: c16878ac-28e4-4859-9878-ef93af45789c
|
232
|
+
# reservationSet:
|
233
|
+
# item:
|
234
|
+
# - reservationId: r-e493148d
|
235
|
+
# groupSet:
|
236
|
+
# item:
|
237
|
+
# - groupId: default
|
238
|
+
# instancesSet:
|
239
|
+
# item:
|
240
|
+
return nil unless response['reservationSet'].is_a?(Hash) # No instances
|
241
|
+
|
242
|
+
resids = []
|
243
|
+
instances = {}
|
244
|
+
response['reservationSet']['item'].each do |res|
|
245
|
+
resids << res['reservationId']
|
246
|
+
groups = res['groupSet']['item'].collect { |g| g['groupId'] }
|
247
|
+
# And each reservation can have 1 or more instances
|
248
|
+
next unless res['instancesSet'].is_a?(Hash)
|
249
|
+
res['instancesSet']['item'].each do |props|
|
250
|
+
inst = Instances.from_hash(props)
|
251
|
+
next if state && inst.state != state.to_s
|
252
|
+
inst.groups = groups
|
253
|
+
#puts "STATE: #{inst.state} #{state}"
|
254
|
+
instances[inst.awsid] = inst
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
instances.each_value { |inst| each_inst.call(inst) } if each_inst
|
259
|
+
|
260
|
+
instances = nil if instances.empty? # Don't return an empty hash
|
261
|
+
instances
|
262
|
+
end
|
263
|
+
|
264
|
+
# System console output.
|
265
|
+
#
|
266
|
+
# * +inst_id+ instance ID (String) or Instance object.
|
267
|
+
#
|
268
|
+
# NOTE: Amazon sends the console outputs as a Base64 encoded string.
|
269
|
+
# This method DOES NOT decode in order to remain compliant with the
|
270
|
+
# data formats returned by Amazon.
|
271
|
+
#
|
272
|
+
# You can decode it like this:
|
273
|
+
#
|
274
|
+
# require 'base64'
|
275
|
+
# Base64.decode64(output)
|
276
|
+
#
|
277
|
+
def console(inst_id, &each_inst)
|
278
|
+
inst_ids = objects_to_instance_ids([inst_id])
|
279
|
+
response = execute_request({}) {
|
280
|
+
@ec2.get_console_output(:instance_id => inst_ids.first)
|
281
|
+
}
|
282
|
+
response['output']
|
283
|
+
end
|
284
|
+
|
285
|
+
def attached_volume?(id, device)
|
286
|
+
list = volumes(id)
|
287
|
+
list.each do |v|
|
288
|
+
return true if v.device == device
|
289
|
+
end
|
290
|
+
false
|
291
|
+
end
|
292
|
+
|
293
|
+
def volumes(id)
|
294
|
+
rvol = Rudy::AWS::EC2::Volumes.new
|
295
|
+
rvol.ec2 = @ec2
|
296
|
+
rvol.list || []
|
297
|
+
list.select { |v| v.attached? && v.instid === id }
|
298
|
+
end
|
299
|
+
|
300
|
+
def device_volume(id, device)
|
301
|
+
volumes(id).select { |v| v.device === device }
|
302
|
+
end
|
303
|
+
|
304
|
+
# +inst_id+ is an instance ID
|
305
|
+
# Returns an Instance object
|
306
|
+
def get(inst_id)
|
307
|
+
inst_id = inst_id.awsid if inst_id.is_a?(Rudy::AWS::EC2::Instance)
|
308
|
+
inst = list(:any, inst_id)
|
309
|
+
inst &&= inst.first
|
310
|
+
inst
|
311
|
+
end
|
312
|
+
|
313
|
+
def any?(state=:any, inst_ids=[])
|
314
|
+
!list(state, inst_ids).nil?
|
315
|
+
end
|
316
|
+
|
317
|
+
def exists?(inst_ids)
|
318
|
+
any?(:any, inst_ids)
|
319
|
+
end
|
320
|
+
|
321
|
+
def any_group?(group=nil, state=:any)
|
322
|
+
ret = list_group(group, state)
|
323
|
+
!ret.nil?
|
324
|
+
end
|
325
|
+
|
326
|
+
def running?(inst_ids)
|
327
|
+
compare_instance_lists(list(:running, inst_ids), inst_ids)
|
328
|
+
end
|
329
|
+
def pending?(inst_ids)
|
330
|
+
compare_instance_lists(list(:pending, inst_ids), inst_ids)
|
331
|
+
end
|
332
|
+
def terminated?(inst_ids)
|
333
|
+
compare_instance_lists(list(:terminated, inst_ids), inst_ids)
|
334
|
+
end
|
335
|
+
def shutting_down?(inst_ids)
|
336
|
+
compare_instance_lists(list(:shutting_down, inst_ids), inst_ids)
|
337
|
+
end
|
338
|
+
|
339
|
+
def unavailable?(inst_ids)
|
340
|
+
instances = list(:any, inst_ids) || []
|
341
|
+
instances.reject! { |inst|
|
342
|
+
(inst.state == "shutting-down" ||
|
343
|
+
inst.state == "pending" ||
|
344
|
+
inst.state == "terminated")
|
345
|
+
}
|
346
|
+
compare_instance_lists(instances, inst_ids)
|
347
|
+
end
|
348
|
+
|
349
|
+
#
|
350
|
+
# +h+ is a hash of instance properties in the format returned
|
351
|
+
# by EC2::Base#describe_instances:
|
352
|
+
#
|
353
|
+
# kernelId: aki-9b00e5f2
|
354
|
+
# amiLaunchIndex: "0"
|
355
|
+
# keyName: solutious-default
|
356
|
+
# launchTime: "2009-03-14T12:48:15.000Z"
|
357
|
+
# instanceType: m1.small
|
358
|
+
# imageId: ami-0734d36e
|
359
|
+
# privateDnsName:
|
360
|
+
# reason:
|
361
|
+
# placement:
|
362
|
+
# availabilityZone: us-east-1b
|
363
|
+
# dnsName:
|
364
|
+
# instanceId: i-cdaa34a4
|
365
|
+
# instanceState:
|
366
|
+
# name: pending
|
367
|
+
# code: "0"
|
368
|
+
#
|
369
|
+
# Returns an Instance object.
|
370
|
+
def self.from_hash(h)
|
371
|
+
inst = Rudy::AWS::EC2::Instance.new
|
372
|
+
inst.aki = h['kernelId']
|
373
|
+
inst.ami = h['imageId']
|
374
|
+
inst.launch_time = h['launchTime']
|
375
|
+
inst.keyname = h['keyName']
|
376
|
+
inst.launch_index = h['amiLaunchIndex']
|
377
|
+
inst.size = h['instanceType']
|
378
|
+
inst.dns_private = h['privateDnsName']
|
379
|
+
inst.dns_public = h['dnsName']
|
380
|
+
inst.reason = h['reason']
|
381
|
+
inst.zone = h['placement']['availabilityZone']
|
382
|
+
inst.awsid = h['instanceId']
|
383
|
+
inst.state = h['instanceState']['name']
|
384
|
+
inst
|
385
|
+
end
|
386
|
+
|
387
|
+
# Is +state+ a known EC2 machine instance state? See: KNOWN_STATES
|
388
|
+
def self.known_state?(state)
|
389
|
+
return false unless state
|
390
|
+
state &&= state.to_sym
|
391
|
+
state = :shutting_down if state == :'shutting-down'
|
392
|
+
KNOWN_STATES.member?(state)
|
393
|
+
end
|
394
|
+
|
395
|
+
private
|
396
|
+
|
397
|
+
|
398
|
+
# Find out whether two lists of instance IDs (or Rudy::AWS::EC2::Instance objects)
|
399
|
+
# contain the same instances regardless of order.
|
400
|
+
#
|
401
|
+
# *+listA+ An Array of instance IDs (Strings) or Rudy::AWS::EC2::Instance objects
|
402
|
+
# *+listB+ Another Array of instance IDs (Strings) or Rudy::AWS::EC2::Instance objects
|
403
|
+
# Returns true if:
|
404
|
+
# * both listA and listB are Arrays
|
405
|
+
# * listA and listB contain the same number of items
|
406
|
+
# * all items in listA are in listB
|
407
|
+
# * all items in listB are in listA
|
408
|
+
def compare_instance_lists(listA, listB)
|
409
|
+
listA = objects_to_instance_ids(listA)
|
410
|
+
listB = objects_to_instance_ids(listB)
|
411
|
+
return false if listA.empty? || listB.empty?
|
412
|
+
return false unless listA.size == listB.size
|
413
|
+
(listA - listB).empty? && (listB - listA).empty?
|
414
|
+
end
|
415
|
+
|
416
|
+
# * +inst_ids+ an Array of instance IDs (Strings) or Instance objects.
|
417
|
+
# Note: This method removes nil values and always returns an Array.
|
418
|
+
# Returns an Array of instances IDs.
|
419
|
+
def objects_to_instance_ids(inst_ids)
|
420
|
+
inst_ids = [inst_ids].flatten # Make sure it's an Array
|
421
|
+
inst_ids = inst_ids.collect do |inst|
|
422
|
+
next if inst.nil? || inst.to_s.empty?
|
423
|
+
if !inst.is_a?(Rudy::AWS::EC2::Instance) && !Rudy::Utils.is_id?(:instance, inst)
|
424
|
+
raise %Q("#{inst}" is not an instance ID or object)
|
425
|
+
end
|
426
|
+
inst.is_a?(Rudy::AWS::EC2::Instance) ? inst.awsid : inst
|
427
|
+
end
|
428
|
+
inst_ids
|
429
|
+
end
|
430
|
+
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
module Rudy::AWS
|
3
|
+
module EC2
|
4
|
+
|
5
|
+
class KeyPair < Storable
|
6
|
+
|
7
|
+
field :name
|
8
|
+
field :fingerprint
|
9
|
+
field :private_key
|
10
|
+
|
11
|
+
def liner_note
|
12
|
+
"%-20s %s" % [self.name.bright, self.fingerprint]
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s(titles=false)
|
16
|
+
str = titles ? "%-20s %s#{$/}" % ['name', 'fingerprint'] : ""
|
17
|
+
str << liner_note
|
18
|
+
end
|
19
|
+
|
20
|
+
def public_key
|
21
|
+
return unless @private_key
|
22
|
+
k = Rye::Key.new(@private_key)
|
23
|
+
k.public_key.to_ssh2
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class KeyPairs
|
29
|
+
include Rudy::AWS::ObjectBase
|
30
|
+
include Rudy::AWS::EC2::Base
|
31
|
+
|
32
|
+
def create(name)
|
33
|
+
raise "No name provided" unless name
|
34
|
+
ret = @ec2.create_keypair(:key_name => name)
|
35
|
+
self.class.from_hash(ret)
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy(name)
|
39
|
+
name = name.name if name.is_a?(Rudy::AWS::EC2::KeyPair)
|
40
|
+
raise "No name provided" unless name.is_a?(String)
|
41
|
+
ret = @ec2.delete_keypair(:key_name => name)
|
42
|
+
(ret && ret['return'] == 'true') # BUG? Always returns true
|
43
|
+
end
|
44
|
+
|
45
|
+
def list(*names)
|
46
|
+
keypairs = list_as_hash(names)
|
47
|
+
keypairs &&= keypairs.values
|
48
|
+
keypairs
|
49
|
+
end
|
50
|
+
|
51
|
+
def list_as_hash(*names)
|
52
|
+
names = names.flatten
|
53
|
+
klist = @ec2.describe_keypairs(:key_name => names)
|
54
|
+
return unless klist['keySet'].is_a?(Hash)
|
55
|
+
keypairs = {}
|
56
|
+
klist['keySet']['item'].each do |oldkp|
|
57
|
+
kp = self.class.from_hash(oldkp)
|
58
|
+
keypairs[kp.name] = kp
|
59
|
+
end
|
60
|
+
keypairs
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.from_hash(h)
|
64
|
+
# keyName: test-c5g4v3pe
|
65
|
+
# keyFingerprint: 65:d0:ce:e7:6a:b0:88:4a:9c:c7:2d:b8:33:0c:fd:3b:c8:0f:0a:3c
|
66
|
+
# keyMaterial: |-
|
67
|
+
# -----BEGIN RSA PRIVATE KEY-----
|
68
|
+
#
|
69
|
+
keypair = Rudy::AWS::EC2::KeyPair.new
|
70
|
+
keypair.fingerprint = h['keyFingerprint']
|
71
|
+
keypair.name = h['keyName']
|
72
|
+
keypair.private_key = h['keyMaterial']
|
73
|
+
keypair
|
74
|
+
end
|
75
|
+
|
76
|
+
def any?
|
77
|
+
keypairs = list || []
|
78
|
+
!keypairs.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
def get(name)
|
82
|
+
keypairs = list(name) || []
|
83
|
+
return if keypairs.empty?
|
84
|
+
keypairs.first
|
85
|
+
end
|
86
|
+
|
87
|
+
def exists?(name)
|
88
|
+
return false unless name
|
89
|
+
kp = get(name) rescue nil
|
90
|
+
!kp.nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
class Keypairs
|
96
|
+
def initialize(*args)
|
97
|
+
raise "Oops! The correct class uses a capital 'P': Rudy::AWS::EC2::KeyPairs"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|