rudy 0.4.0 → 0.6.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/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
|
+
|