rudy 0.2.4 → 0.3.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 +6 -2
- data/README.rdoc +1 -1
- data/bin/rudy +65 -24
- data/lib/aws_sdb/error.rb +42 -0
- data/lib/aws_sdb/service.rb +215 -0
- data/lib/aws_sdb.rb +3 -0
- data/lib/console.rb +341 -0
- data/lib/rudy/aws/ec2.rb +45 -0
- data/lib/rudy/aws/simpledb.rb +5 -0
- data/lib/rudy/aws.rb +3 -0
- data/lib/rudy/command/base.rb +47 -23
- data/lib/rudy/command/disks.rb +109 -2
- data/lib/rudy/command/environment.rb +4 -12
- data/lib/rudy/command/images.rb +15 -2
- data/lib/rudy/command/stage.rb +1 -1
- data/lib/rudy/command/volumes.rb +38 -1
- data/lib/rudy/metadata/backup.rb +160 -0
- data/lib/rudy/metadata/disk.rb +54 -23
- data/lib/rudy/metadata/ec2startup.rb +2 -0
- data/lib/rudy/metadata.rb +26 -0
- data/lib/rudy.rb +38 -7
- data/lib/storable.rb +20 -15
- data/rudy.gemspec +8 -1
- metadata +9 -2
data/lib/console.rb
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
|
2
|
+
# Adapted from: http://github.com/oneup/ruby-console/tree/tput
|
3
|
+
|
4
|
+
if __FILE__ == $0
|
5
|
+
puts "This is a module, don't run it from the command line."
|
6
|
+
exit
|
7
|
+
end
|
8
|
+
|
9
|
+
class String
|
10
|
+
def color(*arg) # colorize a string
|
11
|
+
Console.color(arg) +
|
12
|
+
self +
|
13
|
+
Console.color(:default)
|
14
|
+
end
|
15
|
+
def printAt(*arg)
|
16
|
+
row = 0
|
17
|
+
col = 0
|
18
|
+
if not arg.is_a?(Array)
|
19
|
+
arg = [arg]
|
20
|
+
end
|
21
|
+
if arg.length > 0
|
22
|
+
row = arg[0]
|
23
|
+
end
|
24
|
+
if arg.length > 1
|
25
|
+
col = arg[1]
|
26
|
+
end
|
27
|
+
Cursor.save
|
28
|
+
Cursor.position = row, col
|
29
|
+
print self
|
30
|
+
Cursor.restore
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Console
|
35
|
+
def self.clear
|
36
|
+
system "tput clear"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.reset
|
40
|
+
system "tput reset"
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.color(*arg) # colorize a string
|
44
|
+
if arg[0].is_a?(Array)
|
45
|
+
arg = arg[0]
|
46
|
+
end
|
47
|
+
if arg.length == 0
|
48
|
+
arg = :default, :red, :bg_default
|
49
|
+
end
|
50
|
+
attribute = { # mapper for the attributes
|
51
|
+
:normal => 0,
|
52
|
+
:bright => 1,
|
53
|
+
:dim => 2,
|
54
|
+
:underscore => 4,
|
55
|
+
:blink => 5,
|
56
|
+
:reverse => 7,
|
57
|
+
:hidden => 8,
|
58
|
+
:default => 0
|
59
|
+
}
|
60
|
+
fg_color = { # mapper for the foreground color
|
61
|
+
:black => 30,
|
62
|
+
:red => 31,
|
63
|
+
:green => 32,
|
64
|
+
:yellow => 33,
|
65
|
+
:blue => 34,
|
66
|
+
:magenta => 35,
|
67
|
+
:cyan => 36,
|
68
|
+
:white => 37,
|
69
|
+
:default => 39
|
70
|
+
}
|
71
|
+
bg_color = { # mapper for the background color
|
72
|
+
:bg_black => 40,
|
73
|
+
:bg_red => 41,
|
74
|
+
:bg_green => 42,
|
75
|
+
:bg_yellow => 43,
|
76
|
+
:bg_blue => 44,
|
77
|
+
:bg_magenta => 45,
|
78
|
+
:bg_cyan => 46,
|
79
|
+
:bg_white => 47,
|
80
|
+
:bg_default => 49
|
81
|
+
}
|
82
|
+
if arg.length > 0 # turn symbols into numbers
|
83
|
+
arg[0] = attribute[arg[0]] # attributes
|
84
|
+
end
|
85
|
+
if arg.length > 1
|
86
|
+
arg[1] = fg_color[arg[1]] # foreground color
|
87
|
+
end
|
88
|
+
if arg.length > 2
|
89
|
+
arg[2] = bg_color[arg[2]] # background color
|
90
|
+
end
|
91
|
+
"\e[#{arg.join(";")}m" # magic ansi escape sequence
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.color=(arg)
|
95
|
+
if not arg.is_a?(Array)
|
96
|
+
arg = [arg]
|
97
|
+
end
|
98
|
+
if arg.length == 0
|
99
|
+
arg = :default, :red, :bg_default
|
100
|
+
end
|
101
|
+
print self.color(arg)
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.width
|
105
|
+
`tput cols`.chomp.to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.height
|
109
|
+
`tput lines`.chomp.to_i
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Cursor
|
114
|
+
def self.position
|
115
|
+
row = ""
|
116
|
+
col = ""
|
117
|
+
c = ""
|
118
|
+
|
119
|
+
termsettings = `stty -g`
|
120
|
+
system("stty raw -echo")
|
121
|
+
print "\e[6n"
|
122
|
+
while (c = STDIN.getc.chr) != ";"
|
123
|
+
if c == "\e" or c == "["
|
124
|
+
next
|
125
|
+
else
|
126
|
+
row += c
|
127
|
+
end
|
128
|
+
end
|
129
|
+
while (c = STDIN.getc.chr) != "R"
|
130
|
+
col += c
|
131
|
+
end
|
132
|
+
system("stty #{termsettings}")
|
133
|
+
|
134
|
+
[row, col]
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.position=(arg)
|
138
|
+
row = 0
|
139
|
+
col = 0
|
140
|
+
if not arg.is_a?(Array)
|
141
|
+
arg = [arg]
|
142
|
+
end
|
143
|
+
if arg.length > 0
|
144
|
+
row = arg[0]
|
145
|
+
end
|
146
|
+
if arg.length > 1
|
147
|
+
col = arg[1]
|
148
|
+
end
|
149
|
+
system "tput cup #{row} #{col}"
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.up(*arg)
|
153
|
+
if arg.length == 0
|
154
|
+
arg[0] == 1
|
155
|
+
end
|
156
|
+
print "\e[#{arg[0]}A"
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.down(*arg)
|
160
|
+
if arg.length == 0
|
161
|
+
arg[0] == 1
|
162
|
+
end
|
163
|
+
print "\e[#{arg[0]}B"
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.right(*arg)
|
167
|
+
if arg.length == 0
|
168
|
+
arg[0] == 1
|
169
|
+
end
|
170
|
+
system "tput cuf #{arg[0]}"
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.left(*arg)
|
174
|
+
if arg.length == 0
|
175
|
+
arg[0] == 1
|
176
|
+
end
|
177
|
+
system "tput cub #{arg[0]}"
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.save
|
181
|
+
system "tput sc"
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.restore
|
185
|
+
system "tput rc"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class Win
|
190
|
+
def initialize
|
191
|
+
@row = 1
|
192
|
+
@col = 1
|
193
|
+
@width = 10
|
194
|
+
@height = 5
|
195
|
+
@text = ""
|
196
|
+
@border = :single
|
197
|
+
@fg = :default
|
198
|
+
@bg = :bg_default
|
199
|
+
@bordercolor = :default
|
200
|
+
end
|
201
|
+
|
202
|
+
def width=(width)
|
203
|
+
@width = width
|
204
|
+
end
|
205
|
+
|
206
|
+
def width
|
207
|
+
@width
|
208
|
+
end
|
209
|
+
|
210
|
+
def height=(height)
|
211
|
+
@height = height
|
212
|
+
end
|
213
|
+
|
214
|
+
def height
|
215
|
+
@height
|
216
|
+
end
|
217
|
+
|
218
|
+
def text=(text)
|
219
|
+
@text = text
|
220
|
+
end
|
221
|
+
|
222
|
+
def text
|
223
|
+
@text
|
224
|
+
end
|
225
|
+
|
226
|
+
def border=(arg)
|
227
|
+
if not arg.is_a?(Array)
|
228
|
+
arg = [arg]
|
229
|
+
end
|
230
|
+
if arg.length > 0
|
231
|
+
@border = arg[0]
|
232
|
+
end
|
233
|
+
if arg.length > 1
|
234
|
+
@bordercolor = arg[1]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def border
|
239
|
+
[@border, @bordercolor]
|
240
|
+
end
|
241
|
+
|
242
|
+
def position=(arg)
|
243
|
+
if not arg.is_a?(Array)
|
244
|
+
arg = [arg]
|
245
|
+
end
|
246
|
+
if arg.length > 0
|
247
|
+
@row = arg[0]
|
248
|
+
end
|
249
|
+
if arg.length > 1
|
250
|
+
@col = arg[1]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def position
|
255
|
+
[@row, @col]
|
256
|
+
end
|
257
|
+
|
258
|
+
def fg=(fg)
|
259
|
+
@fg = fg
|
260
|
+
end
|
261
|
+
|
262
|
+
def fg
|
263
|
+
@fg
|
264
|
+
end
|
265
|
+
|
266
|
+
def bg=(bg)
|
267
|
+
@bg = bg
|
268
|
+
end
|
269
|
+
|
270
|
+
def bg
|
271
|
+
@bg
|
272
|
+
end
|
273
|
+
|
274
|
+
def drawBorder
|
275
|
+
single = {
|
276
|
+
:upper_left => "\u250C",
|
277
|
+
:upper_right => "\u2510",
|
278
|
+
:lower_left => "\u2514",
|
279
|
+
:lower_right => "\u2518",
|
280
|
+
:horizontal => "\u2500",
|
281
|
+
:vertical => "\u2502"
|
282
|
+
}
|
283
|
+
bold = {
|
284
|
+
:upper_left => "\u250D",
|
285
|
+
:upper_right => "\u2511",
|
286
|
+
:lower_left => "\u2515",
|
287
|
+
:lower_right => "\u2519",
|
288
|
+
:horizontal => "\u2501",
|
289
|
+
:vertical => "\u2503"
|
290
|
+
}
|
291
|
+
double = {
|
292
|
+
:upper_left => "\u2554",
|
293
|
+
:upper_right => "\u2557",
|
294
|
+
:lower_left => "\u255A",
|
295
|
+
:lower_right => "\u255D",
|
296
|
+
:horizontal => "\u2550",
|
297
|
+
:vertical => "\u2551"
|
298
|
+
}
|
299
|
+
round = {
|
300
|
+
:upper_left => "\u256D",
|
301
|
+
:upper_right => "\u256E",
|
302
|
+
:lower_left => "\u2570",
|
303
|
+
:lower_right => "\u256F",
|
304
|
+
:horizontal => "\u2500",
|
305
|
+
:vertical => "\u2502"
|
306
|
+
}
|
307
|
+
mapper = case @border
|
308
|
+
when :single then single
|
309
|
+
when :bold then bold
|
310
|
+
when :double then double
|
311
|
+
when :round then round
|
312
|
+
else single
|
313
|
+
end
|
314
|
+
(mapper[:upper_left] + mapper[:horizontal] * (@width - 2) + mapper[:upper_right]).color(:normal, @bordercolor, @bg).printAt @row, @col
|
315
|
+
(mapper[:lower_left] + mapper[:horizontal] * (@width - 2) + mapper[:lower_right]).color(:normal, @bordercolor, @bg).printAt @row + @height - 1, @col
|
316
|
+
0.upto(@height - 3) do |i|
|
317
|
+
(mapper[:vertical] + " " * (@width - 2) + mapper[:vertical]).color(:normal, @bordercolor, @bg).printAt @row + i + 1, @col
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def draw
|
322
|
+
text = @text.split("\n")
|
323
|
+
0.upto(text.length - 1) do |i|
|
324
|
+
if @border != :none
|
325
|
+
text[i] = text[i][0, @width - 2]
|
326
|
+
else
|
327
|
+
text[i] = text[i][0, @width]
|
328
|
+
end
|
329
|
+
end
|
330
|
+
if @border == :none
|
331
|
+
0.upto((text.length > @height ? @height : text.length) - 1) do |i|
|
332
|
+
text[i].color(:normal, @fg, @bg).printAt @row + i , @col
|
333
|
+
end
|
334
|
+
else
|
335
|
+
drawBorder
|
336
|
+
0.upto(@text.length > @height - 3 ? @height - 3 : text.length) do |i|
|
337
|
+
text[i].to_s.color(:normal, @fg, @bg).printAt @row + i + 1, @col + 1
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
data/lib/rudy/aws/ec2.rb
CHANGED
@@ -14,10 +14,46 @@ module Rudy::AWS
|
|
14
14
|
@aws.describe_images_by_owner('self') || []
|
15
15
|
end
|
16
16
|
|
17
|
+
# +id+ AMI ID to deregister (ami-XXXXXXX)
|
18
|
+
# Returns true when successful. Otherwise throws an exception.
|
19
|
+
def deregister(id)
|
20
|
+
@aws.deregister_image(id)
|
21
|
+
end
|
22
|
+
|
23
|
+
# +path+ the S3 path to the manifest (bucket/file.manifest.xml)
|
24
|
+
# Returns the AMI ID when successful, otherwise throws an exception.
|
25
|
+
def register(path)
|
26
|
+
@aws.register_image(path)
|
27
|
+
end
|
17
28
|
end
|
29
|
+
class Snapshots
|
30
|
+
include Rudy::AWS::ObjectBase
|
31
|
+
|
32
|
+
def list
|
33
|
+
@aws.describe_snapshots || []
|
34
|
+
end
|
35
|
+
|
36
|
+
def create(vol_id)
|
37
|
+
@aws.create_snapshot(vol_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy(snap_id)
|
41
|
+
@aws.delete_snapshot(snap_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
def exists?(id)
|
45
|
+
list.each do |v|
|
46
|
+
return true if v[:aws_id] === id
|
47
|
+
end
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
18
53
|
class Volumes
|
19
54
|
include Rudy::AWS::ObjectBase
|
20
55
|
|
56
|
+
|
21
57
|
def list
|
22
58
|
list = @aws.describe_volumes() || []
|
23
59
|
list.select { |v| v[:aws_status] != "deleting" }
|
@@ -27,10 +63,18 @@ module Rudy::AWS
|
|
27
63
|
@aws.attach_volume(vol_id, inst_id, device)
|
28
64
|
end
|
29
65
|
|
66
|
+
def detach(vol_id)
|
67
|
+
@aws.detach_volume(vol_id)
|
68
|
+
end
|
69
|
+
|
30
70
|
def create(zone, size, snapshot=nil)
|
31
71
|
@aws.create_volume(snapshot, size, zone)
|
32
72
|
end
|
33
73
|
|
74
|
+
def destroy(vol_id)
|
75
|
+
@aws.delete_volume(vol_id)
|
76
|
+
end
|
77
|
+
|
34
78
|
def exists?(id)
|
35
79
|
list.each do |v|
|
36
80
|
return true if v[:aws_id] === id
|
@@ -38,6 +82,7 @@ module Rudy::AWS
|
|
38
82
|
false
|
39
83
|
end
|
40
84
|
|
85
|
+
|
41
86
|
end
|
42
87
|
|
43
88
|
class Instances
|
data/lib/rudy/aws/simpledb.rb
CHANGED
data/lib/rudy/aws.rb
CHANGED
@@ -20,6 +20,7 @@ module Rudy
|
|
20
20
|
attr_reader :addresses
|
21
21
|
attr_reader :groups
|
22
22
|
attr_reader :volumes
|
23
|
+
attr_reader :snapshots
|
23
24
|
attr_reader :aws
|
24
25
|
|
25
26
|
def initialize(access_key, secret_key)
|
@@ -28,6 +29,7 @@ module Rudy
|
|
28
29
|
@images = Rudy::AWS::EC2::Images.new(@aws)
|
29
30
|
@groups = Rudy::AWS::EC2::Groups.new(@aws)
|
30
31
|
@addresses = Rudy::AWS::EC2::Addresses.new(@aws)
|
32
|
+
@snapshots = Rudy::AWS::EC2::Snapshots.new(@aws)
|
31
33
|
@volumes = Rudy::AWS::EC2::Volumes.new(@aws)
|
32
34
|
end
|
33
35
|
|
@@ -51,6 +53,7 @@ module Rudy
|
|
51
53
|
|
52
54
|
def initialize(access_key, secret_key)
|
53
55
|
@aws = RightAws::SdbInterface.new(access_key, secret_key, {:logger => Logger.new(@@logger)})
|
56
|
+
@aws2 = AwsSdb::Service.new(:access_key_id => access_key, :secret_access_key => secret_key, :logger => Logger.new(@@logger))
|
54
57
|
@domains = Rudy::AWS::SimpleDB::Domains.new(@aws)
|
55
58
|
end
|
56
59
|
|
data/lib/rudy/command/base.rb
CHANGED
@@ -28,7 +28,7 @@ module Rudy
|
|
28
28
|
attr_reader :scm
|
29
29
|
|
30
30
|
attr_reader :rscripts
|
31
|
-
|
31
|
+
attr_reader :domains
|
32
32
|
attr_reader :machine_images
|
33
33
|
|
34
34
|
def init
|
@@ -81,8 +81,9 @@ module Rudy
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
@sdb = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
|
85
84
|
@ec2 = Rudy::AWS::EC2.new(@access_key, @secret_key)
|
85
|
+
# RightAws::SdbInterface is on thin ice. No select support! Or sort!
|
86
|
+
@sdb = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
|
86
87
|
#@s3 = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
|
87
88
|
end
|
88
89
|
|
@@ -90,7 +91,7 @@ module Rudy
|
|
90
91
|
# not have a valid keypair configured. (See: EC2_KEYPAIR_*)
|
91
92
|
def check_keys
|
92
93
|
raise "No SSH key provided for #{keypairname}!" unless has_keypair?(keypairname)
|
93
|
-
raise "SSH key provided but cannot be found! (#{keypairpath})" unless File.exists?(keypairpath)
|
94
|
+
raise "SSH key provided but cannot be found! (#{keypairname}: #{keypairpath})" unless File.exists?(keypairpath)
|
94
95
|
end
|
95
96
|
|
96
97
|
def has_pem_keys?
|
@@ -157,10 +158,8 @@ module Rudy
|
|
157
158
|
raise "#{disk.device} is already in use on #{machine[:aws_instance_id]}! (Try umounting it)"
|
158
159
|
end
|
159
160
|
|
160
|
-
new_volume = false
|
161
161
|
if !disk.awsid || (disk.awsid && !@ec2.volumes.exists?(disk.awsid))
|
162
162
|
disk = Rudy::MetaData::Disk.update_volume(@sdb, @ec2, disk, machine)
|
163
|
-
new_volume = true
|
164
163
|
end
|
165
164
|
|
166
165
|
raise "Unknown error creating volume! #{disk.awsid}" unless disk && disk.awsid
|
@@ -171,11 +170,16 @@ module Rudy
|
|
171
170
|
|
172
171
|
@user = "root"
|
173
172
|
|
174
|
-
if
|
173
|
+
if disk.raw_volume
|
175
174
|
puts "Creating the filesystem (mkfs.ext3 -F #{disk.device})"
|
176
175
|
capture(:stdout) do
|
177
176
|
ssh machine[:dns_name], keypairpath, user, "mkfs.ext3 -F #{disk.device}"
|
178
177
|
end
|
178
|
+
|
179
|
+
puts "Saving disk metadata"
|
180
|
+
disk.raw_volume = false
|
181
|
+
Rudy::MetaData::Disk.save(@sdb, disk)
|
182
|
+
|
179
183
|
sleep 2
|
180
184
|
end
|
181
185
|
|
@@ -193,26 +197,25 @@ module Rudy
|
|
193
197
|
# +cmd+ is the name of the command current running.
|
194
198
|
def print_header(cmd=nil)
|
195
199
|
title = "RUDY v#{Rudy::VERSION}"
|
196
|
-
|
197
|
-
|
198
|
-
|
200
|
+
now_utc = Time.now.utc
|
201
|
+
criteria = []
|
202
|
+
[:zone, :environment, :role, :position].each do |n|
|
203
|
+
val = instance_variable_get("@#{n}")
|
204
|
+
criteria << "#{n.to_s.color :normal}:#{val.color :bright}"
|
205
|
+
end
|
206
|
+
puts '%s -- %s' % [title, criteria.join(", ")]
|
207
|
+
puts 'UTC: %s' % now_utc.strftime("%Y-%m-%d %H:%M:%S")
|
208
|
+
puts
|
209
|
+
|
199
210
|
if (@environment == "prod")
|
200
|
-
|
211
|
+
msg = %q(=======================================================
|
201
212
|
=======================================================
|
202
213
|
!!!!!!!!! YOU ARE PLAYING WITH PRODUCTION !!!!!!!!!
|
203
214
|
=======================================================
|
204
|
-
=======================================================
|
205
|
-
|
206
|
-
|
207
|
-
end
|
208
|
-
|
209
|
-
criteria = []
|
210
|
-
[:zone, :environment, :role, :position].each do |n|
|
211
|
-
val = instance_variable_get("@#{n}")
|
212
|
-
criteria << "[#{n} = #{val}]"
|
215
|
+
=======================================================)
|
216
|
+
puts msg.color(:bright, :red, :bg_white)
|
217
|
+
puts
|
213
218
|
end
|
214
|
-
puts criteria.join(" and ")
|
215
|
-
puts
|
216
219
|
|
217
220
|
end
|
218
221
|
|
@@ -220,6 +223,16 @@ module Rudy
|
|
220
223
|
|
221
224
|
end
|
222
225
|
|
226
|
+
# Returns a hash of info for the requested machine. If the requested machine
|
227
|
+
# is not running, it will raise an exception.
|
228
|
+
def find_current_machine
|
229
|
+
machine_list = @ec2.instances.list(machine_group)
|
230
|
+
machine = machine_list.values.first # NOTE: Only one machine per group, for now...
|
231
|
+
raise "There's no machine running in #{machine_group}" unless machine
|
232
|
+
raise "The primary machine in #{machine_group} is not in a running state" unless machine[:aws_state] == 'running'
|
233
|
+
|
234
|
+
machine
|
235
|
+
end
|
223
236
|
|
224
237
|
|
225
238
|
def group_metadata(env=@environment, role=@role)
|
@@ -242,17 +255,28 @@ module Rudy
|
|
242
255
|
|
243
256
|
def print_image(img)
|
244
257
|
puts '-'*60
|
245
|
-
puts "Image: #{img[:
|
258
|
+
puts "Image: #{img[:aws_id]}"
|
246
259
|
img.each_pair do |key, value|
|
247
260
|
printf(" %22s: %s#{$/}", key, value) if value
|
248
261
|
end
|
249
262
|
puts
|
250
263
|
end
|
251
264
|
|
252
|
-
def print_disk(disk)
|
265
|
+
def print_disk(disk, backups=[])
|
253
266
|
puts '-'*60
|
254
267
|
puts "Disk: #{disk.name}"
|
255
268
|
puts disk.to_s
|
269
|
+
puts "#{backups.size} most recent backups:", backups.collect { |back| "#{back.nice_time} (#{back.awsid})" }
|
270
|
+
puts
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
def print_volume(vol, disk)
|
275
|
+
puts '-'*60
|
276
|
+
puts "Volume: #{vol[:aws_id]} (disk: #{disk.name})"
|
277
|
+
vol.each_pair do |key, value|
|
278
|
+
printf(" %22s: %s#{$/}", key, value) if value
|
279
|
+
end
|
256
280
|
puts
|
257
281
|
end
|
258
282
|
|