maws 0.8.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/bin/maws +10 -0
- data/lib/maws/chunk_source.rb +41 -0
- data/lib/maws/command.rb +62 -0
- data/lib/maws/command_loader.rb +28 -0
- data/lib/maws/command_options_parser.rb +37 -0
- data/lib/maws/commands/configure.rb +287 -0
- data/lib/maws/commands/console.rb +38 -0
- data/lib/maws/commands/create.rb +25 -0
- data/lib/maws/commands/describe.rb +15 -0
- data/lib/maws/commands/destroy.rb +11 -0
- data/lib/maws/commands/elb-add.rb +17 -0
- data/lib/maws/commands/elb-describe.rb +23 -0
- data/lib/maws/commands/elb-disable-zones.rb +17 -0
- data/lib/maws/commands/elb-enable-zones.rb +18 -0
- data/lib/maws/commands/elb-remove.rb +16 -0
- data/lib/maws/commands/set-prefix.rb +24 -0
- data/lib/maws/commands/set-security-groups.rb +442 -0
- data/lib/maws/commands/start.rb +11 -0
- data/lib/maws/commands/status.rb +25 -0
- data/lib/maws/commands/stop.rb +11 -0
- data/lib/maws/commands/teardown.rb +11 -0
- data/lib/maws/commands/volumes-cleanup.rb +22 -0
- data/lib/maws/commands/volumes-status.rb +43 -0
- data/lib/maws/commands/wait.rb +61 -0
- data/lib/maws/connection.rb +121 -0
- data/lib/maws/core_ext/object.rb +5 -0
- data/lib/maws/description/ebs.rb +40 -0
- data/lib/maws/description/ec2.rb +72 -0
- data/lib/maws/description/elb.rb +52 -0
- data/lib/maws/description/rds.rb +47 -0
- data/lib/maws/description.rb +78 -0
- data/lib/maws/instance/ebs.rb +45 -0
- data/lib/maws/instance/ec2.rb +144 -0
- data/lib/maws/instance/elb.rb +92 -0
- data/lib/maws/instance/rds.rb +84 -0
- data/lib/maws/instance.rb +167 -0
- data/lib/maws/instance_collection.rb +98 -0
- data/lib/maws/instance_display.rb +84 -0
- data/lib/maws/instance_matcher.rb +27 -0
- data/lib/maws/loader.rb +173 -0
- data/lib/maws/logger.rb +66 -0
- data/lib/maws/mash.rb +9 -0
- data/lib/maws/maws.rb +102 -0
- data/lib/maws/profile_loader.rb +92 -0
- data/lib/maws/specification.rb +127 -0
- data/lib/maws/ssh.rb +7 -0
- data/lib/maws/trollop.rb +782 -0
- data/lib/maws/volumes_command.rb +29 -0
- data/lib/maws.rb +25 -0
- metadata +115 -0
@@ -0,0 +1,442 @@
|
|
1
|
+
require 'maws/command'
|
2
|
+
|
3
|
+
class SetSecurityGroups < Command
|
4
|
+
def description
|
5
|
+
"set-security-groups - create or update security groups from security rules configuration"
|
6
|
+
end
|
7
|
+
|
8
|
+
# override from command to ignore selection
|
9
|
+
def verify_options
|
10
|
+
if @config.command_line.region.blank?
|
11
|
+
Trollop::die "Region must be specified in command line options OR as 'default_region' in #{@config.config.paths.config}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def run!
|
16
|
+
rules = @config.security_rules
|
17
|
+
if rules.empty?
|
18
|
+
info "No security rules to create security groups from"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
@security_group_definitions = {}
|
23
|
+
rules.keys.map {|rules_set_name|
|
24
|
+
security_group_name = "#{@config.profile.name}-#{rules_set_name}"
|
25
|
+
service = if rules_set_name == 'ec2_default'
|
26
|
+
security_group_name = 'ec2_default'
|
27
|
+
:ec2
|
28
|
+
elsif rules_set_name == 'rds_default'
|
29
|
+
security_group_name = 'rds_default'
|
30
|
+
:rds
|
31
|
+
else
|
32
|
+
@config.combined[rules_set_name].service
|
33
|
+
end
|
34
|
+
|
35
|
+
@security_group_definitions[security_group_name] = {
|
36
|
+
:role => rules_set_name,
|
37
|
+
:rules => rules[rules_set_name],
|
38
|
+
:service => service.to_sym}
|
39
|
+
}
|
40
|
+
|
41
|
+
update_definitions_with_descriptions(@security_group_definitions)
|
42
|
+
|
43
|
+
|
44
|
+
@security_group_definitions.each { |name, definition|
|
45
|
+
create_security_group(name, definition) unless definition[:description]
|
46
|
+
}
|
47
|
+
|
48
|
+
update_definitions_with_descriptions(@security_group_definitions)
|
49
|
+
begin
|
50
|
+
update_definitions_rules_with_real_ids(@security_group_definitions)
|
51
|
+
rescue BadSecurityRule => err
|
52
|
+
info "Bad security rule: #{err.message}"
|
53
|
+
info "No AWS security rules will be updated."
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
@security_group_definitions.each { |name, definition|
|
58
|
+
info "#{definition[:service]} security group #{name}"
|
59
|
+
|
60
|
+
clear_out_security_group(name, definition)
|
61
|
+
|
62
|
+
# rds takes a while to propagate. this code handles errors with retries, but this sleep keeps the noise down
|
63
|
+
if definition[:service] == :rds
|
64
|
+
info "...waiting for revoke to take effect..."
|
65
|
+
sleep 20
|
66
|
+
end
|
67
|
+
|
68
|
+
set_security_group(name, definition)
|
69
|
+
|
70
|
+
info "done\n\n"
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
# sets :description, :group_id, :group_name and :owner_id for each definition
|
75
|
+
def update_definitions_with_descriptions(security_group_definitions)
|
76
|
+
ec2_sg_descriptions = connection.ec2.describe_security_groups
|
77
|
+
rds_sg_descriptions = connection.rds.describe_db_security_groups
|
78
|
+
|
79
|
+
# attach existing descriptions (if they exist) to sec group definition
|
80
|
+
security_group_definitions.each {|name, definition|
|
81
|
+
if definition[:service] == :ec2
|
82
|
+
definition[:description] = ec2_sg_descriptions.find {|description| description[:aws_group_name] == name }
|
83
|
+
if definition[:description]
|
84
|
+
definition[:group_id] = definition[:description][:group_id]
|
85
|
+
definition[:group_name] = definition[:description][:aws_group_name]
|
86
|
+
definition[:owner_id] = definition[:description][:aws_owner]
|
87
|
+
end
|
88
|
+
elsif definition[:service] == :rds
|
89
|
+
definition[:description] = rds_sg_descriptions.find {|description| description[:name] == name }
|
90
|
+
if definition[:description]
|
91
|
+
definition[:group_id] = definition[:description][:name]
|
92
|
+
definition[:group_name] = definition[:description][:name]
|
93
|
+
definition[:owner_id] = definition[:description][:owner_id]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
def create_security_group(name, definition)
|
102
|
+
info "CREATING #{name}"
|
103
|
+
|
104
|
+
if definition[:service] == :ec2
|
105
|
+
connection.ec2.create_security_group(name, name) # description same as name
|
106
|
+
elsif definition[:service] == :rds
|
107
|
+
connection.rds.create_db_security_group(name, name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear_out_security_group(name, definition)
|
112
|
+
info " clearing out #{name}"
|
113
|
+
description = definition[:description]
|
114
|
+
return unless description && !description.empty?
|
115
|
+
|
116
|
+
|
117
|
+
if definition[:service] == :ec2
|
118
|
+
clear_out_ec2_security_group(name, description)
|
119
|
+
elsif definition[:service] == :rds
|
120
|
+
clear_out_rds_security_group(name, description)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def set_security_group(name, definition)
|
125
|
+
info " adding security rules to #{name}"
|
126
|
+
service = definition[:service]
|
127
|
+
|
128
|
+
definition[:rules].each { |rule|
|
129
|
+
rule.port ||= 0
|
130
|
+
rule.port_from ||= (rule.port)
|
131
|
+
rule.port_to ||= (rule.port)
|
132
|
+
|
133
|
+
protocol_description = service == :ec2 ? "#{rule.protocol}:#{rule.port_from}-#{rule.port_to}" : ""
|
134
|
+
|
135
|
+
if rule.group
|
136
|
+
info " allow from #{rule.owner_id}/#{rule.group_id} (#{rule.group}) #{protocol_description}"
|
137
|
+
if service == :ec2
|
138
|
+
authorize_ec2_security_group_rule(definition[:group_id], rule.port_from, rule.port_to, rule.protocol,
|
139
|
+
:groups => {rule.owner_id => rule.group_id})
|
140
|
+
else
|
141
|
+
safe_authorize_rds_security_group_rule(definition[:group_id],
|
142
|
+
:ec2_security_group_owner => rule.owner_id,
|
143
|
+
:ec2_security_group_name => rule.group_name)
|
144
|
+
end
|
145
|
+
elsif rule.role
|
146
|
+
info " allow from #{rule.owner_id}/#{rule.group_id} (role '#{rule.role}') #{protocol_description}"
|
147
|
+
if service == :ec2
|
148
|
+
authorize_ec2_security_group_rule(definition[:group_id], rule.port_from, rule.port_to, rule.protocol,
|
149
|
+
:groups => {rule.owner_id => rule.group_id})
|
150
|
+
else
|
151
|
+
safe_authorize_rds_security_group_rule(definition[:group_id],
|
152
|
+
:ec2_security_group_owner => rule.owner_id,
|
153
|
+
:ec2_security_group_name => rule.group_id)
|
154
|
+
end
|
155
|
+
elsif rule.cidr
|
156
|
+
info " allow from #{rule.cidr.inspect} #{protocol_description}"
|
157
|
+
if service == :ec2
|
158
|
+
authorize_ec2_security_group_rule(definition[:group_id], rule.port_from, rule.port_to, rule.protocol,
|
159
|
+
:cidr_ips => [rule.cidr].flatten)
|
160
|
+
else
|
161
|
+
[rule.cidr].flatten.each { |cidr|
|
162
|
+
safe_authorize_rds_security_group_rule(definition[:group_id], :cidrip => cidr)
|
163
|
+
}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
### EC2 ###
|
170
|
+
|
171
|
+
def clear_out_ec2_security_group(name, description)
|
172
|
+
description[:aws_perms].each { |permission|
|
173
|
+
group_id = description[:group_id]
|
174
|
+
|
175
|
+
revoke_params = if permission[:group_id]
|
176
|
+
# this is a group rule
|
177
|
+
{:groups => {permission[:owner] => permission[:group_id]}}
|
178
|
+
else
|
179
|
+
{:cidr_ip => permission[:cidr_ips]}
|
180
|
+
end
|
181
|
+
|
182
|
+
revoke_params[:protocol] = permission[:protocol]
|
183
|
+
revoke_params[:from_port] = permission[:from_port]
|
184
|
+
revoke_params[:to_port] = permission[:to_port]
|
185
|
+
|
186
|
+
info " revoking #{revoke_params.inspect}"
|
187
|
+
connection.ec2.modify_security_group(:revoke, :ingress,
|
188
|
+
group_id, revoke_params);
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
def authorize_ec2_security_group_rule(group_id, from_port, to_port, protocol, rule)
|
193
|
+
params = {:from_port => from_port,
|
194
|
+
:to_port => to_port,
|
195
|
+
:protocol => protocol}.merge(rule)
|
196
|
+
connection.ec2.modify_security_group(:authorize, :ingress, group_id, params)
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
### RDS ###
|
202
|
+
|
203
|
+
def clear_out_rds_security_group(name, description)
|
204
|
+
description[:ec2_security_groups].each {|group_permission|
|
205
|
+
info " revoking from #{group_permission[:owner_id]}/#{group_permission[:name]}"
|
206
|
+
safe_revoke_rds_security_group(name,
|
207
|
+
:ec2_security_group_owner => group_permission[:owner_id],
|
208
|
+
:ec2_security_group_name => group_permission[:name])
|
209
|
+
}
|
210
|
+
|
211
|
+
description[:ip_ranges].each {|ip_permission|
|
212
|
+
info " revoking from #{ip_permission[:cidrip].inspect}"
|
213
|
+
safe_revoke_rds_security_group(name, :cidrip => ip_permission[:cidrip])
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
def update_definitions_rules_with_real_ids(definitions)
|
219
|
+
definitions.each { |name, definition|
|
220
|
+
definition[:rules].each { |rule|
|
221
|
+
next if rule.cidr
|
222
|
+
|
223
|
+
owner_id, group_id = if rule.role
|
224
|
+
resolve_owner_and_group_id_for_role_definition(rule.role)
|
225
|
+
elsif rule.group
|
226
|
+
resolve_owner_and_group_id_for_group_definition(rule.group)
|
227
|
+
end
|
228
|
+
|
229
|
+
unless owner_id && group_id
|
230
|
+
raise BadSecurityRule.new("Can't resolve security group id and owner for rule #{rule.inspect}}")
|
231
|
+
else
|
232
|
+
rule.owner_id = owner_id
|
233
|
+
rule.group_id = group_id
|
234
|
+
end
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
def resolve_owner_and_group_id_for_role_definition(role_name)
|
242
|
+
definition = @security_group_definitions.values.find {|definition| definition[:role] == role_name}
|
243
|
+
if definition
|
244
|
+
p [definition[:owner_id], definition[:group_id]]
|
245
|
+
[definition[:owner_id], definition[:group_id]]
|
246
|
+
else
|
247
|
+
security_group_name = "#{@config.profile.name}-#{role_name}"
|
248
|
+
raise BadSecurityRule.new("Can't find security group '#{security_group_name}' for role '#{role_name}'")
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def resolve_owner_and_group_id_for_group_definition(group_definition)
|
253
|
+
# 'owner_id/group_name' => ['owner_id', 'group_name'] OR 'group_name' => ['group_name']
|
254
|
+
owner_id_and_group = group_definition.split('/')
|
255
|
+
|
256
|
+
group = owner_id_and_group.pop
|
257
|
+
owner_id = owner_id_and_group.pop
|
258
|
+
|
259
|
+
group_id = if group =~ /sg-/
|
260
|
+
group
|
261
|
+
else
|
262
|
+
# look up name
|
263
|
+
definition = @security_group_definitions.values.find {|definition| definition[:group_name] == group}
|
264
|
+
|
265
|
+
definition[:group_id] if definition
|
266
|
+
end
|
267
|
+
|
268
|
+
raise BadSecurityRule.new("Can't find group id (sg-...) for group name #{group}") unless group_id
|
269
|
+
|
270
|
+
# use this account for owner id
|
271
|
+
owner_id = @security_group_definitions.values.first[:owner_id] unless owner_id
|
272
|
+
|
273
|
+
[owner_id, group_id]
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
def safe_revoke_rds_security_group(name, params)
|
278
|
+
rds_logger = connection.rds.logger
|
279
|
+
connection.rds.logger = NullLogger.new
|
280
|
+
|
281
|
+
tries = 4
|
282
|
+
|
283
|
+
loop do
|
284
|
+
if tries <= 0
|
285
|
+
info "!!!!!! FAILED TO REVOKE: #{name} #{params.inspect} !!!!!!"
|
286
|
+
return
|
287
|
+
end
|
288
|
+
|
289
|
+
begin
|
290
|
+
connection.rds.revoke_db_security_group_ingress(name, params)
|
291
|
+
info " (succesfully revoked)"
|
292
|
+
return
|
293
|
+
rescue Exception => e
|
294
|
+
if e.message =~ /AuthorizationNotFound/
|
295
|
+
info " (not authorized. nothing to do here)"
|
296
|
+
return
|
297
|
+
|
298
|
+
elsif e.message =~ /InvalidDBSecurityGroupState: Cannot revoke an authorization that is in the revoking state/
|
299
|
+
info " (already revoking - will be revoked shortly)"
|
300
|
+
sleep 10
|
301
|
+
tries -= 1
|
302
|
+
|
303
|
+
elsif e.message =~ /InvalidDBSecurityGroupState: Cannot revoke an authorization that is in the authorizing state/
|
304
|
+
info " (not yet finished authorizing. waiting and retrying...)"
|
305
|
+
sleep 10
|
306
|
+
tries -= 1
|
307
|
+
|
308
|
+
else
|
309
|
+
sleep 1
|
310
|
+
tries -= 1
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
ensure
|
315
|
+
connection.rds.logger = rds_logger
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
def safe_authorize_rds_security_group_rule(name, params)
|
320
|
+
rds_logger = connection.rds.logger
|
321
|
+
connection.rds.logger = NullLogger.new
|
322
|
+
|
323
|
+
tries = 4
|
324
|
+
|
325
|
+
loop do
|
326
|
+
if tries <= 0
|
327
|
+
info "!!!!!! FAILED TO AUTHORIZE: #{name} #{params.inspect} !!!!!!"
|
328
|
+
return
|
329
|
+
end
|
330
|
+
|
331
|
+
begin
|
332
|
+
connection.rds.authorize_db_security_group_ingress(name, params)
|
333
|
+
info " (succesfully authorized)"
|
334
|
+
return
|
335
|
+
rescue Exception => e
|
336
|
+
if e.message =~ /AuthorizationAlreadyExists/
|
337
|
+
info " (authorization already exists. probably means it is still being revoked. retrying...)"
|
338
|
+
sleep 10
|
339
|
+
tries -= 1
|
340
|
+
|
341
|
+
elsif e.message =~ /InvalidDBSecurityGroupState: Cannot authorize an authorization that is in the revoking state/
|
342
|
+
info " (currently revoking - will be revoked shortly. will retry authorizing then)"
|
343
|
+
sleep 10
|
344
|
+
tries -= 1
|
345
|
+
|
346
|
+
elsif e.message =~ /InvalidDBSecurityGroupState: Cannot authorize an authorization that is in the authorizing state/
|
347
|
+
info " (already authorizing. waiting and retrying to confirm...)"
|
348
|
+
sleep 10
|
349
|
+
tries -= 1
|
350
|
+
|
351
|
+
else
|
352
|
+
sleep 1
|
353
|
+
tries -= 1
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
ensure
|
358
|
+
connection.rds.logger = rds_logger
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
##### DESCRIPTIONS ######
|
363
|
+
|
364
|
+
### EC2 ###
|
365
|
+
|
366
|
+
# ec2.describe_security_groups #=>
|
367
|
+
# [{:aws_perms=>
|
368
|
+
# [{:owner=>"375390957666",
|
369
|
+
# :direction=>:ingress,
|
370
|
+
# :protocol=>"tcp",
|
371
|
+
# :group_id=>"sg-d7e9d9be",
|
372
|
+
# :from_port=>"22",
|
373
|
+
# :group_name=>"foo-e2e-queue",
|
374
|
+
# :to_port=>"22"},
|
375
|
+
|
376
|
+
# {:cidr_ips=>"208.240.243.170/32",
|
377
|
+
# :direction=>:ingress,
|
378
|
+
# :protocol=>"tcp",
|
379
|
+
# :from_port=>"22",
|
380
|
+
# :to_port=>"22"}],
|
381
|
+
|
382
|
+
# {:owner=>"375390957666",
|
383
|
+
# :direction=>:ingress,
|
384
|
+
# :protocol=>"icmp",
|
385
|
+
# :group_id=>"sg-9f5d5cf6",
|
386
|
+
# :from_port=>"15",
|
387
|
+
# :group_name=>"foo-e2e-service",
|
388
|
+
# :to_port=>"-1"}],
|
389
|
+
|
390
|
+
# :aws_owner=>"375390957666",
|
391
|
+
# :aws_description=>"something here",
|
392
|
+
# :group_id=>"sg-30fd1b58",
|
393
|
+
# :aws_group_name=>"test2"}]
|
394
|
+
|
395
|
+
# connection.ec2.modify_security_group(:grant, :ingress,
|
396
|
+
# 'sg-30fd1b58', {:protocol => 'tcp', :port => 22, :cidr_ip => '127.0.0.2/32'})
|
397
|
+
#
|
398
|
+
# connection.ec2.modify_security_group(:revoke, :ingress,
|
399
|
+
# 'sg-30fd1b58', {:protocol => :tcp, :port => 22, :groups => {'375390957666' => 'sg-d7e9d9be'} });
|
400
|
+
|
401
|
+
|
402
|
+
|
403
|
+
### RDS ###
|
404
|
+
|
405
|
+
# rds.describe_db_security_groups #=>
|
406
|
+
# [{:owner_id=>"375390957666",
|
407
|
+
# :description=>"Default",
|
408
|
+
# :ec2_security_groups=>[],
|
409
|
+
# :ip_ranges=>[],
|
410
|
+
# :name=>"Default"},
|
411
|
+
# {:owner_id=>"375390957666",
|
412
|
+
# :description=>"kd",
|
413
|
+
# :ec2_security_groups=>[],
|
414
|
+
# :ip_ranges=>[],
|
415
|
+
# :name=>"kd2"},
|
416
|
+
# {:owner_id=>"375390957666",
|
417
|
+
# :description=>"kd",
|
418
|
+
# :ec2_security_groups=>
|
419
|
+
# [{:status=>"Authorized", :owner_id=>"375390957666", :name=>"default"},
|
420
|
+
# {:status=>"Authorized", :owner_id=>"375390957666", :name=>"default1"},
|
421
|
+
# {:status=>"Authorized", :owner_id=>"375390957666", :name=>"default"},
|
422
|
+
# {:status=>"Authorized", :owner_id=>"375390957666", :name=>"default"},
|
423
|
+
# {:status=>"Authorized", :owner_id=>"375390957666", :name=>"default1"},
|
424
|
+
# {:status=>"Authorized", :owner_id=>"375390957666", :name=>"default22"}],
|
425
|
+
# :ip_ranges=>
|
426
|
+
# [{:status=>"Authorized", :cidrip=>"127.0.0.1/8"},
|
427
|
+
# {:status=>"Authorized", :cidrip=>"128.0.0.1/8"},
|
428
|
+
# {:status=>"Authorized", :cidrip=>"129.0.0.1/8"},
|
429
|
+
# {:status=>"Authorized", :cidrip=>"130.0.0.1/8"},
|
430
|
+
# {:status=>"Authorized", :cidrip=>"131.0.0.1/8"}],
|
431
|
+
# :name=>"kd3"}]
|
432
|
+
|
433
|
+
# rds.revoke_db_security_group_ingress('kd2', :ec2_security_group_owner => '375390957666',
|
434
|
+
# :ec2_security_group_name => 'default')
|
435
|
+
#
|
436
|
+
# rds.revoke_db_security_group_ingress('kd2', :cidrip=>"127.0.0.1/8")
|
437
|
+
#
|
438
|
+
# rds.authorize_db_security_group_ingress('kd3', :cidrip => '131.0.0.1/8')
|
439
|
+
end
|
440
|
+
|
441
|
+
class BadSecurityRule < Exception
|
442
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'maws/command'
|
2
|
+
require 'terminal-table/import'
|
3
|
+
|
4
|
+
class Status < Command
|
5
|
+
def description
|
6
|
+
"status - show information about specified instances"
|
7
|
+
end
|
8
|
+
|
9
|
+
def run!
|
10
|
+
instances.specified
|
11
|
+
if instances.specified.empty?
|
12
|
+
info "no instances specified"
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
roles_list = @config.available_roles
|
17
|
+
|
18
|
+
instances.specified.roles_in_order_of(roles_list).each { |role_name|
|
19
|
+
role_instances = instances.specified.with_role(role_name)
|
20
|
+
next if role_instances.empty?
|
21
|
+
InstanceDisplay.display_collection_for_role(role_name, role_instances)
|
22
|
+
}
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'maws/command'
|
2
|
+
require 'maws/volumes_command'
|
3
|
+
require 'terminal-table/import'
|
4
|
+
|
5
|
+
class VolumesCleanup < VolumesCommand
|
6
|
+
def description
|
7
|
+
"volumes-cleaned - delete unattached EBS volumes for specified roles"
|
8
|
+
end
|
9
|
+
|
10
|
+
def run!
|
11
|
+
super
|
12
|
+
|
13
|
+
unattached = instances.specified.ebs.matching(:attached? => false)
|
14
|
+
|
15
|
+
if unattached.empty?
|
16
|
+
info "no unattached volumes to clean up"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
unattached.each {|i| i.destroy}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'maws/command'
|
2
|
+
require 'maws/volumes_command'
|
3
|
+
require 'terminal-table/import'
|
4
|
+
|
5
|
+
class VolumesStatus < VolumesCommand
|
6
|
+
def description
|
7
|
+
"volumes-status - show brief status information for EBS volumes for specified roles"
|
8
|
+
end
|
9
|
+
|
10
|
+
def run!
|
11
|
+
super
|
12
|
+
attached = instances.specified.ebs.matching(:attached? => true)
|
13
|
+
unattached = instances.specified.ebs.matching(:attached? => false)
|
14
|
+
|
15
|
+
info "\n**** " + "ATTACHED EBS VOLUMES" + " *****************"
|
16
|
+
list_ebs_instances(attached)
|
17
|
+
|
18
|
+
info "\n\n**** " + "UNATTACHED EBS VOLUMES" + " *****************"
|
19
|
+
list_ebs_instances(unattached)
|
20
|
+
end
|
21
|
+
|
22
|
+
def list_ebs_instances(instances)
|
23
|
+
if instances.empty?
|
24
|
+
info "none available"
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
headers = instances.first.display.headers
|
29
|
+
table_rows = []
|
30
|
+
grouped_instances = instances.
|
31
|
+
group_by{|i| i.name}.
|
32
|
+
to_a.sort_by {|group| group[0]} # sort by name
|
33
|
+
|
34
|
+
grouped_instances.each_with_index do |(name, instances), i|
|
35
|
+
instances.each {|instance|
|
36
|
+
table_rows << instance.display.values
|
37
|
+
}
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
info table(headers, *table_rows)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'maws/command'
|
2
|
+
require 'maws/trollop'
|
3
|
+
|
4
|
+
class Wait < Command
|
5
|
+
def description
|
6
|
+
"wait - do nothing until specified instances enter specified state, then quite and notify"
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_specific_options(parser)
|
10
|
+
parser.opt :target_state, "State to wait for", :type => :string
|
11
|
+
parser.opt :wait, "Max wait (seconds)", :default => 60
|
12
|
+
parser.opt :count, "Minimum number of specified instances that must match the state", :default => 0
|
13
|
+
parser.opt :quiet, "Be quiet", :type => :flag, :default => false
|
14
|
+
parser.opt :growl, "Growl notify when done", :type => :flag, :default => false
|
15
|
+
end
|
16
|
+
|
17
|
+
def verify_options
|
18
|
+
super
|
19
|
+
state = @config.command_line.target_state
|
20
|
+
Trollop::die "Can't wait for blank state" if state.nil? || state.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def run!
|
24
|
+
at_exit do
|
25
|
+
if @config.command_line.growl
|
26
|
+
system("growlnotify -m 'waiting for AWS state #{@config.command_line.target_state} done'")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
state = @config.command_line.target_state
|
31
|
+
been_waiting = 0
|
32
|
+
wait_for_count = @config.command_line.count == 0 ? instances.specified.count : @config.command_line.count
|
33
|
+
wait_for_time = @config.command_line.wait
|
34
|
+
|
35
|
+
info "waiting #{wait_for_time} seconds or until #{wait_for_count} are #{state}..."
|
36
|
+
|
37
|
+
loop do
|
38
|
+
trap("INT") { info "...done (interrupted)"; return }
|
39
|
+
return if been_waiting >= wait_for_time
|
40
|
+
left_to_wait = wait_for_time - been_waiting
|
41
|
+
matching_count = instances.specified.with_approximate_status(state).count
|
42
|
+
|
43
|
+
if matching_count >= wait_for_count
|
44
|
+
info "...done (#{matching_count}/#{wait_for_count} are #{state})"
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
if @config.command_line.quiet
|
49
|
+
print "."
|
50
|
+
$stdout.flush
|
51
|
+
else
|
52
|
+
info "#{matching_count}/#{wait_for_count} are #{state} - wait #{left_to_wait} seconds or until #{wait_for_count}/#{wait_for_count} are #{state}..."
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
sleep 1
|
57
|
+
been_waiting += 1
|
58
|
+
@maws.resync_instances
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|