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/rudy/command/disks.rb
CHANGED
@@ -4,6 +4,87 @@ module Rudy
|
|
4
4
|
module Command
|
5
5
|
class Disks < Rudy::Command::Base
|
6
6
|
|
7
|
+
def print_backups
|
8
|
+
criteria = [@zone]
|
9
|
+
criteria += [@environment, @role] unless @all
|
10
|
+
|
11
|
+
Rudy::MetaData::Backup.list(@sdb, *criteria).each do |backup|
|
12
|
+
puts "%s (%s)" % [backup.name, backup.awsid]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy_backup(name)
|
17
|
+
puts "Destroying #{name}"
|
18
|
+
begin
|
19
|
+
backup = Rudy::MetaData::Backup.get(@sdb, name)
|
20
|
+
rescue => ex
|
21
|
+
puts "Error deleteing backup: #{ex.message}"
|
22
|
+
end
|
23
|
+
|
24
|
+
return unless backup
|
25
|
+
|
26
|
+
begin
|
27
|
+
puts " -> deleting snapshot..."
|
28
|
+
@ec2.snapshots.destroy(backup.awsid)
|
29
|
+
rescue => ex
|
30
|
+
puts "Error deleting snapshot: #{ex.message}."
|
31
|
+
puts "Continuing..."
|
32
|
+
ensure
|
33
|
+
puts " -> deleting metadata..."
|
34
|
+
@sdb.destroy(RUDY_DOMAIN, name)
|
35
|
+
end
|
36
|
+
puts "Done."
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_backup
|
40
|
+
machine = find_current_machine
|
41
|
+
disks = Rudy::MetaData::Disk.list(@sdb, machine[:aws_availability_zone], @environment, @role, @position)
|
42
|
+
volumes = @ec2.instances.volumes(machine[:aws_instance_id])
|
43
|
+
|
44
|
+
puts "Machine: #{machine_name}"
|
45
|
+
|
46
|
+
raise "The machine #{machine_name} does not have any disk metadata" if disks.empty?
|
47
|
+
raise "The machine #{machine_name} does not have any volumes attached." if volumes.empty?
|
48
|
+
|
49
|
+
puts "#{disks.size} Disk(s) defined with #{volumes.size} Volume(s) running"
|
50
|
+
|
51
|
+
volumes.each do |volume|
|
52
|
+
print "Volume #{volume[:aws_id]}... "
|
53
|
+
disk = Rudy::MetaData::Disk.from_volume(@sdb, volume[:aws_id])
|
54
|
+
backup = Rudy::MetaData::Backup.new
|
55
|
+
|
56
|
+
# TODO: Look for the disk based on the machine
|
57
|
+
raise "No disk associated to volume #{volume[:aws_id]}" unless disk
|
58
|
+
|
59
|
+
backup.time_stamp
|
60
|
+
backup.volume = volume[:aws_id]
|
61
|
+
|
62
|
+
# Populate machine infos
|
63
|
+
[:zone, :environment, :role, :position].each do |n|
|
64
|
+
val = instance_variable_get("@#{n}")
|
65
|
+
backup.send("#{n}=", val) if val
|
66
|
+
end
|
67
|
+
|
68
|
+
# Populate disk infos
|
69
|
+
[:path, :size].each do |n|
|
70
|
+
backup.send("#{n}=", disk.send(n)) if disk.send(n)
|
71
|
+
end
|
72
|
+
|
73
|
+
snap = @ec2.snapshots.create(volume[:aws_id])
|
74
|
+
|
75
|
+
if !snap || !snap.is_a?(Hash)
|
76
|
+
puts "There was an unknown problem creating #{backup.name}. Continuing..."
|
77
|
+
next
|
78
|
+
end
|
79
|
+
|
80
|
+
backup.awsid = snap[:aws_id]
|
81
|
+
|
82
|
+
Rudy::MetaData::Backup.save(@sdb, backup)
|
83
|
+
|
84
|
+
puts backup.name
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
7
88
|
|
8
89
|
def create_disk
|
9
90
|
disk = Rudy::MetaData::Disk.new
|
@@ -26,17 +107,43 @@ module Rudy
|
|
26
107
|
criteria += [@environment, @role] unless @all
|
27
108
|
|
28
109
|
Rudy::MetaData::Disk.list(@sdb, *criteria).each do |disk|
|
29
|
-
|
110
|
+
backups = Rudy::MetaData::Backup.for_disk(@sdb, disk, 2)
|
111
|
+
print_disk(disk, backups)
|
112
|
+
|
30
113
|
end
|
31
114
|
|
32
115
|
end
|
33
116
|
|
117
|
+
def unattach_disk(name)
|
118
|
+
puts "Looking for #{name}"
|
119
|
+
disk = Rudy::MetaData::Disk.get(@sdb, name)
|
120
|
+
instances = @ec2.instances.list(machine_group)
|
121
|
+
@user = "root"
|
122
|
+
check_keys
|
123
|
+
raise "That is not a valid disk" unless disk
|
124
|
+
raise "There are no instances running in #{machine_group}" if !instances || instances.empty?
|
125
|
+
raise "The disk has no attached volume " unless disk.awsid
|
126
|
+
|
127
|
+
machine = instances.values.first
|
128
|
+
|
129
|
+
puts "Unmounting #{disk.path}..."
|
130
|
+
ssh machine[:dns_name], keypairpath, user, "umount #{disk.path}"
|
131
|
+
sleep 1
|
132
|
+
|
133
|
+
puts "Detaching #{disk.awsid}"
|
134
|
+
@ec2.volumes.detach(disk.awsid)
|
135
|
+
|
136
|
+
Rudy::MetaData::Backup.for_disk(@sdb, disk, 2)
|
137
|
+
|
138
|
+
puts "Done!"
|
139
|
+
end
|
140
|
+
|
34
141
|
def attach_disk(name)
|
35
142
|
puts "Looking for #{name}"
|
36
143
|
disk = Rudy::MetaData::Disk.get(@sdb, name)
|
37
144
|
instances = @ec2.instances.list(machine_group)
|
38
145
|
raise "There are no instances running in #{machine_group}" if !instances || instances.empty?
|
39
|
-
instance_id = instances.keys.first
|
146
|
+
instance_id = instances.keys.first # <--- TODO: This is bad!
|
40
147
|
machine = instances.values.first
|
41
148
|
|
42
149
|
do_dirty_disk_volume_deeds(disk, machine)
|
@@ -14,29 +14,21 @@ module Rudy
|
|
14
14
|
module Command
|
15
15
|
class Environment < Rudy::Command::Base
|
16
16
|
|
17
|
-
# Returns a hash of info for the requested machine. If the requested machine
|
18
|
-
# is not running, it will raise an exception.
|
19
|
-
def find_requested_machine
|
20
|
-
machine_list = @ec2.instances.list(machine_group)
|
21
|
-
machine = machine_list.values.first # NOTE: Only one machine per group, for now...
|
22
|
-
raise "There's no machine running in #{machine_group}" unless machine
|
23
|
-
raise "The primary machine in #{machine_group} is not in a running state" unless machine[:aws_state] == 'running'
|
24
17
|
|
25
|
-
machine
|
26
|
-
end
|
27
|
-
|
28
18
|
def connect
|
29
19
|
check_keys
|
30
|
-
machine =
|
20
|
+
machine = find_current_machine
|
31
21
|
ssh machine[:dns_name], keypairpath, user, false, false, false, @print
|
32
22
|
end
|
33
23
|
|
34
24
|
# +paths+ an array of paths to copy. The last element is the "to" path.
|
35
25
|
def copy(paths)
|
36
26
|
check_keys
|
37
|
-
machine =
|
27
|
+
machine = find_current_machine
|
28
|
+
|
38
29
|
paths = paths.flatten
|
39
30
|
to_path = paths.pop
|
31
|
+
|
40
32
|
scp machine[:dns_name], keypairpath, user, paths, to_path, @remote, false, @print
|
41
33
|
end
|
42
34
|
|
data/lib/rudy/command/images.rb
CHANGED
@@ -53,8 +53,21 @@ module Rudy
|
|
53
53
|
ssh machine[:dns_name], keypairpath, user, "ec2-bundle-vol -r i386 -p #{@image_name} -k /mnt/pk-*pem -c /mnt/cert*pem -u #{@account}"
|
54
54
|
ssh machine[:dns_name], keypairpath, user, "ec2-upload-bundle -b #{@bucket_name} -m /tmp/#{@image_name}.manifest.xml -a #{@access_key} -s #{@secret_key}"
|
55
55
|
|
56
|
-
#
|
57
|
-
|
56
|
+
@ec2.images.register("#{@bucket_name}/#{@image_name}.manifest.xml")
|
57
|
+
end
|
58
|
+
|
59
|
+
def deregister(ami)
|
60
|
+
raise "You must supply an AMI ID (ami-XXXXXXX)" unless ami
|
61
|
+
puts "Deregistering AMI: #{ami}"
|
62
|
+
|
63
|
+
are_you_sure?
|
64
|
+
|
65
|
+
if @ec2.images.deregister(ami)
|
66
|
+
puts "Done!"
|
67
|
+
else
|
68
|
+
puts "There was an unknown problem!"
|
69
|
+
end
|
70
|
+
|
58
71
|
end
|
59
72
|
|
60
73
|
end
|
data/lib/rudy/command/stage.rb
CHANGED
@@ -35,7 +35,7 @@ module Rudy
|
|
35
35
|
|
36
36
|
puts "Running #{basename}..."
|
37
37
|
scp machine[:dns_name], keypairpath, user, @rscripts[keypairname], "~/"
|
38
|
-
ssh machine[:dns_name], keypairpath, user, "chmod 755 ~/#{basename} && ~/#{basename}
|
38
|
+
ssh machine[:dns_name], keypairpath, user, "chmod 755 ~/#{basename} && ~/#{basename} #{@access_key} #{@secret_key}"
|
39
39
|
|
40
40
|
end
|
41
41
|
|
data/lib/rudy/command/volumes.rb
CHANGED
@@ -4,9 +4,46 @@ module Rudy
|
|
4
4
|
module Command
|
5
5
|
class Volumes < Rudy::Command::Base
|
6
6
|
|
7
|
+
def destroy_volume(id)
|
8
|
+
raise "No volume ID provided" unless id
|
9
|
+
raise "I will not help you destroy production!" if @environment == "prod"
|
10
|
+
raise "The volume #{id} doesn't exist!" unless @ec2.volumes.exists?(id)
|
11
|
+
disk = Rudy::MetaData::Disk.from_volume(@sdb, id)
|
12
|
+
|
13
|
+
puts "Destroying #{id}!"
|
14
|
+
@ec2.volumes.destroy id
|
15
|
+
|
16
|
+
if disk
|
17
|
+
disk.awsid = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
Rudy::MetaData::Disk.save(@sdb, disk)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
7
24
|
def print_volumes
|
8
|
-
|
25
|
+
machines = {}
|
26
|
+
volumes = @ec2.volumes.list
|
27
|
+
@ec2.volumes.list.each do |volume|
|
28
|
+
machine = @ec2.instances.get(volume[:aws_instance_id])
|
29
|
+
machines[ volume[:aws_instance_id] ] ||= {
|
30
|
+
:machine => machine,
|
31
|
+
:volumes => []
|
32
|
+
}
|
33
|
+
machines[ volume[:aws_instance_id] ][:volumes] << volume
|
34
|
+
end
|
35
|
+
|
36
|
+
machines.each_pair do |instance_id, hash|
|
37
|
+
machine = hash[:machine]
|
38
|
+
env = (machine[:aws_groups]) ? machine[:aws_groups] : "Not-attached"
|
39
|
+
puts "Environment: #{env}"
|
40
|
+
hash[:volumes].each do |vol|
|
41
|
+
disk = Rudy::MetaData::Disk.from_volume(@sdb, vol[:aws_id])
|
42
|
+
print_volume(vol, disk)
|
43
|
+
end
|
44
|
+
end
|
9
45
|
end
|
46
|
+
|
10
47
|
end
|
11
48
|
end
|
12
49
|
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Rudy
|
5
|
+
module MetaData
|
6
|
+
class Backup < Storable
|
7
|
+
include Rudy::MetaData::ObjectBase
|
8
|
+
extend Rudy::MetaData::ObjectBase
|
9
|
+
|
10
|
+
@@rtype = "back"
|
11
|
+
|
12
|
+
field :rtype
|
13
|
+
field :awsid
|
14
|
+
|
15
|
+
field :region
|
16
|
+
field :zone
|
17
|
+
field :environment
|
18
|
+
field :role
|
19
|
+
field :position
|
20
|
+
field :path
|
21
|
+
|
22
|
+
field :date
|
23
|
+
field :time
|
24
|
+
field :second
|
25
|
+
|
26
|
+
field :unixtime => Integer
|
27
|
+
|
28
|
+
field :size
|
29
|
+
field :volume
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@zone = DEFAULT_ZONE
|
33
|
+
@region = DEFAULT_REGION
|
34
|
+
@position = "01"
|
35
|
+
@rtype = @@rtype
|
36
|
+
end
|
37
|
+
|
38
|
+
def rtype
|
39
|
+
@@rtype
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.rtype
|
43
|
+
@@rtype
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def name
|
48
|
+
time = Time.at(@unixtime)
|
49
|
+
Backup.generate_name(@zone, @environment, @role, @position, @path, time)
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid?
|
53
|
+
@zone && @environment && @role && @position && @path && @date && @time && @sec
|
54
|
+
end
|
55
|
+
|
56
|
+
def time_stamp
|
57
|
+
#return [@date, @time] if @date && @time
|
58
|
+
now = Time.now.utc
|
59
|
+
datetime = Backup.format_timestamp(now).split(RUDY_DELIM)
|
60
|
+
@unixtime = now.to_i
|
61
|
+
@date, @time, @second = datetime
|
62
|
+
end
|
63
|
+
|
64
|
+
def nice_time
|
65
|
+
return "" unless @date && @time
|
66
|
+
t = @date.scan(/(\d\d\d\d)(\d\d)(\d\d)/).join('-')
|
67
|
+
t << " " << @time.scan(/(\d\d)(\d\d)/).join(':')
|
68
|
+
t
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_query(more=[], remove=[])
|
72
|
+
criteria = [:rtype, :zone, :environment, :role, :position, :path, :date, :time, :second, *more]
|
73
|
+
criteria -= [*remove].flatten
|
74
|
+
query = "select * from #{RUDY_DOMAIN} where unixtime > '0' "
|
75
|
+
criteria.each do |n|
|
76
|
+
query << "and #{n} = '#{self.send(n.to_sym)}'"
|
77
|
+
end
|
78
|
+
query << " order by unixtime desc"
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
str = ""
|
83
|
+
field_names.each do |key|
|
84
|
+
str << sprintf(" %22s: %s#{$/}", key, self.send(key.to_sym))
|
85
|
+
end
|
86
|
+
str
|
87
|
+
end
|
88
|
+
|
89
|
+
def disk
|
90
|
+
Disk.generate_name(@zone, @environment, @role, @position, @path)
|
91
|
+
end
|
92
|
+
|
93
|
+
# 20090224-1813-36
|
94
|
+
def Backup.format_timestamp(dat)
|
95
|
+
mon, day, hour, min, sec = [dat.mon, dat.day, dat.hour, dat.min, dat.sec].collect { |v| v.to_s.rjust(2, "0") }
|
96
|
+
[dat.year, mon, day, RUDY_DELIM, hour, min, RUDY_DELIM, sec].join
|
97
|
+
end
|
98
|
+
|
99
|
+
# Times are converted to UTC
|
100
|
+
# back-us-east-1b-stage-app-01-rilli-app-20090224-1813-36
|
101
|
+
def Backup.generate_name(zon, env, rol, pos, pat, dat, sep=File::SEPARATOR)
|
102
|
+
raise "The date you provided is not a Time object" unless dat.is_a?(Time)
|
103
|
+
pos = pos.to_s.rjust 2, '0'
|
104
|
+
dirs = pat.split sep if pat
|
105
|
+
dirs.shift while dirs && (dirs[0].nil? || dirs[0].empty?)
|
106
|
+
timestamp = Backup.format_timestamp(dat.utc)
|
107
|
+
[@@rtype, zon, env, rol, pos, dirs, timestamp].flatten.join(RUDY_DELIM)
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def Backup.for_disk(sdb, disk, max=50)
|
112
|
+
list = Backup.list(sdb, disk.zone, disk.environment, disk.role, disk.position, disk.path) || []
|
113
|
+
list[0..(max-1)]
|
114
|
+
end
|
115
|
+
|
116
|
+
def Backup.get(sdb, name)
|
117
|
+
object = sdb.get_attributes(RUDY_DOMAIN, name)
|
118
|
+
raise "Object #{name} does not exist!" unless object.has_key?(:attributes) && !object[:attributes].empty?
|
119
|
+
self.from_hash(object[:attributes])
|
120
|
+
end
|
121
|
+
|
122
|
+
def Backup.save(sdb, obj, replace = :replace)
|
123
|
+
sdb.store(RUDY_DOMAIN, obj.name, obj.to_hash, replace)
|
124
|
+
end
|
125
|
+
|
126
|
+
def Backup.list(sdb, zon, env=nil, rol=nil, pos=nil, path=nil, date=nil)
|
127
|
+
query = "select * from #{RUDY_DOMAIN} where "
|
128
|
+
query << "rtype = '#{rtype}' "
|
129
|
+
query << " and zone = '#{zon}'" if zon
|
130
|
+
query << " and environment = '#{env}'" if env
|
131
|
+
query << " and role = '#{rol}'" if rol
|
132
|
+
query << " and position = '#{pos}'" if pos
|
133
|
+
query << " and path = '#{path}'" if path
|
134
|
+
query << " and date = '#{date}'" if date
|
135
|
+
query << " and unixtime != '0' order by unixtime desc"
|
136
|
+
list = []
|
137
|
+
sdb.select(query).each do |obj|
|
138
|
+
list << self.from_hash(obj)
|
139
|
+
end
|
140
|
+
list
|
141
|
+
end
|
142
|
+
|
143
|
+
def Backup.destroy(sdb, name)
|
144
|
+
back = Backup.get(sdb, name) # get raises an exception if the disk doesn't exist
|
145
|
+
sdb.destroy(RUDY_DOMAIN, name)
|
146
|
+
true # wtf: RightAws::SimpleDB doesn't tell us whether it succeeds. We'll assume!
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
def Backup.is_defined?(sdb, backup)
|
152
|
+
query = backup.to_query()
|
153
|
+
puts query
|
154
|
+
!sdb.select(query).empty?
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
data/lib/rudy/metadata/disk.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
|
2
2
|
|
3
3
|
module Rudy
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initalize(sdb)
|
8
|
-
@sdb = sdb
|
9
|
-
end
|
10
|
-
|
11
|
-
end
|
12
|
-
class MetaData
|
4
|
+
|
5
|
+
module MetaData
|
13
6
|
class Disk < Storable
|
14
7
|
|
8
|
+
@@rtype = 'disk'
|
9
|
+
|
10
|
+
# This is a flag used internally to specify that a volume has been
|
11
|
+
# created for this disk, but not formated.
|
12
|
+
attr_accessor :raw_volume
|
13
|
+
|
14
|
+
field :rtype
|
15
15
|
field :awsid
|
16
16
|
|
17
17
|
field :environment
|
@@ -22,7 +22,7 @@ module Rudy
|
|
22
22
|
field :zone
|
23
23
|
field :region
|
24
24
|
field :device
|
25
|
-
field :backups => Array
|
25
|
+
#field :backups => Array
|
26
26
|
field :size
|
27
27
|
|
28
28
|
def initialize
|
@@ -30,7 +30,15 @@ module Rudy
|
|
30
30
|
@zone = DEFAULT_ZONE
|
31
31
|
@region = DEFAULT_REGION
|
32
32
|
@backups = []
|
33
|
-
|
33
|
+
@rtype = @@rtype.to_s
|
34
|
+
@raw_volume = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def rtype
|
38
|
+
@@rtype.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def rtype=(val)
|
34
42
|
end
|
35
43
|
|
36
44
|
def name
|
@@ -42,11 +50,11 @@ module Rudy
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def to_query(more=[], remove=[])
|
45
|
-
criteria = [:zone, :environment, :role, :position, :path, *more]
|
53
|
+
criteria = [:rtype, :zone, :environment, :role, :position, :path, *more]
|
46
54
|
criteria -= [*remove].flatten
|
47
55
|
query = []
|
48
56
|
criteria.each do |n|
|
49
|
-
query << "['#{n}' = '#{self.send(n.to_sym)}']"
|
57
|
+
query << "['#{n}' = '#{self.send(n.to_sym)}'] "
|
50
58
|
end
|
51
59
|
query.join(" intersection ")
|
52
60
|
end
|
@@ -68,6 +76,7 @@ module Rudy
|
|
68
76
|
|
69
77
|
def Disk.get(sdb, name)
|
70
78
|
disk = sdb.get_attributes(RUDY_DOMAIN, name)
|
79
|
+
|
71
80
|
raise "Disk #{name} does not exist!" unless disk && disk.has_key?(:attributes)
|
72
81
|
Rudy::MetaData::Disk.from_hash(disk[:attributes])
|
73
82
|
end
|
@@ -89,6 +98,16 @@ module Rudy
|
|
89
98
|
!sdb.query_with_attributes(RUDY_DOMAIN, query).empty?
|
90
99
|
end
|
91
100
|
|
101
|
+
def Disk.from_volume(sdb, vol_id)
|
102
|
+
query = "['awsid' = '#{vol_id}']"
|
103
|
+
res = sdb.query_with_attributes(RUDY_DOMAIN, query)
|
104
|
+
if res.empty?
|
105
|
+
nil
|
106
|
+
else
|
107
|
+
disk = Rudy::MetaData::Disk.from_hash(res.values.first)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
92
111
|
def Disk.update_volume(sdb, ec2, disk, machine)
|
93
112
|
|
94
113
|
disk = Disk.get(sdb, disk) if disk.is_a?(String)
|
@@ -103,19 +122,29 @@ module Rudy
|
|
103
122
|
unless disk.awsid
|
104
123
|
puts "No active EBS volume found for #{disk.name}"
|
105
124
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
ec2.
|
125
|
+
# TODO: pull actual backups
|
126
|
+
backups = Rudy::MetaData::Backup.for_disk(sdb, disk, 2)
|
127
|
+
|
128
|
+
if backups.is_a?(Array) && !backups.empty?
|
129
|
+
backup = backups.first
|
130
|
+
if ec2.snapshots.exists?(backup.awsid)
|
131
|
+
puts "We'll use the most recent backup (#{backup.awsid})..."
|
132
|
+
volume = ec2.volumes.create(disk.zone, disk.size, backup.awsid)
|
133
|
+
else
|
134
|
+
puts "The backup refers to a snapshot that doesn't exist."
|
135
|
+
puts backup.name, backup.awsid
|
136
|
+
puts "You need to delete this backup metadata before continuing."
|
137
|
+
exit 1
|
138
|
+
end
|
112
139
|
else
|
113
140
|
puts "We'll create one from scratch..."
|
114
141
|
volume = ec2.volumes.create(disk.zone, disk.size, nil)
|
115
|
-
disk.
|
116
|
-
puts "Saving disk metadata"
|
117
|
-
Disk.save(sdb, disk)
|
142
|
+
disk.raw_volume = true
|
118
143
|
end
|
144
|
+
|
145
|
+
puts "Saving disk metadata"
|
146
|
+
disk.awsid = volume[:aws_id]
|
147
|
+
Disk.save(sdb, disk)
|
119
148
|
puts ""
|
120
149
|
end
|
121
150
|
|
@@ -124,13 +153,15 @@ module Rudy
|
|
124
153
|
|
125
154
|
def Disk.list(sdb, zon, env=nil, rol=nil, pos=nil)
|
126
155
|
query = ''
|
127
|
-
query << "['
|
156
|
+
query << "['rtype' = '#{@@rtype}']" if zon
|
157
|
+
query << " intersection ['zone' = '#{zon}']" if zon
|
128
158
|
query << " intersection ['environment' = '#{env}']" if env
|
129
159
|
query << " intersection ['role' = '#{rol}']" if rol
|
130
160
|
query << " intersection ['position' = '#{pos}']" if pos
|
131
161
|
|
132
162
|
list = []
|
133
163
|
sdb.query_with_attributes(RUDY_DOMAIN, query).each_pair do |name, hash|
|
164
|
+
#puts "DISK: #{hash.to_yaml}"
|
134
165
|
list << Rudy::MetaData::Disk.from_hash(hash)
|
135
166
|
end
|
136
167
|
list
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
require 'aws_sdb'
|
3
|
+
|
4
|
+
module Rudy
|
5
|
+
module MetaData
|
6
|
+
attr_accessor :sdb
|
7
|
+
attr_accessor :ec2
|
8
|
+
|
9
|
+
def initalize(sdb, ec2)
|
10
|
+
@sdb = sdb
|
11
|
+
@ec2 = ec2
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module MetaData
|
17
|
+
|
18
|
+
module ObjectBase
|
19
|
+
extend self
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/rudy.rb
CHANGED
@@ -4,9 +4,11 @@ require 'stringio'
|
|
4
4
|
require 'ostruct'
|
5
5
|
require 'yaml'
|
6
6
|
|
7
|
+
require 'console'
|
7
8
|
require 'storable'
|
8
9
|
|
9
10
|
require 'rudy/aws'
|
11
|
+
require 'rudy/metadata'
|
10
12
|
require 'rudy/scm/svn'
|
11
13
|
require 'rudy/utils'
|
12
14
|
require 'rudy/command/base'
|
@@ -32,8 +34,8 @@ module Rudy #:nodoc:
|
|
32
34
|
|
33
35
|
module VERSION #:nodoc:
|
34
36
|
MAJOR = 0.freeze unless defined? MAJOR
|
35
|
-
MINOR =
|
36
|
-
TINY =
|
37
|
+
MINOR = 3.freeze unless defined? MINOR
|
38
|
+
TINY = 0.freeze unless defined? TINY
|
37
39
|
def self.to_s
|
38
40
|
[MAJOR, MINOR, TINY].join('.')
|
39
41
|
end
|
@@ -68,10 +70,34 @@ def capture(stream)
|
|
68
70
|
result
|
69
71
|
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
STDIN.
|
73
|
+
|
74
|
+
def are_you_sure?(len=3)
|
75
|
+
if STDIN.tty? # Only ask a question if there's a human
|
76
|
+
challenge = strand len
|
77
|
+
STDOUT.print "Are you sure? To continue type \"#{challenge}\": "
|
78
|
+
STDOUT.flush
|
79
|
+
if ((STDIN.gets || "").gsub(/["']/, '') =~ /^#{challenge}$/)
|
80
|
+
true
|
81
|
+
else
|
82
|
+
puts "Nothing changed"
|
83
|
+
exit 0
|
84
|
+
end
|
85
|
+
else
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Generates a string of random alphanumeric characters
|
92
|
+
# These are used as IDs throughout the system
|
93
|
+
def strand( len=8, safe=true )
|
94
|
+
chars = ("a".."z").to_a + ("0".."9").to_a
|
95
|
+
chars = [("a".."h").to_a, "j", "k", "m", "n", ("p".."z").to_a, ("2".."9").to_a].flatten if safe
|
96
|
+
newpass = ""
|
97
|
+
1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
|
98
|
+
newpass
|
74
99
|
end
|
100
|
+
|
75
101
|
def sh(command, chdir=false)
|
76
102
|
prevdir = Dir.pwd
|
77
103
|
Dir.chdir chdir if chdir
|
@@ -92,20 +118,25 @@ end
|
|
92
118
|
|
93
119
|
|
94
120
|
def scp(host, keypair, user, paths, to_path, to_local=false, verbose=false, printonly=false)
|
95
|
-
|
121
|
+
|
96
122
|
paths = [paths] unless paths.is_a?(Array)
|
97
123
|
from_paths = ""
|
98
124
|
if to_local
|
99
125
|
paths.each do |path|
|
100
126
|
from_paths << "#{user}@#{host}:#{path} "
|
101
|
-
end
|
127
|
+
end
|
128
|
+
puts "Copying FROM remote TO this machine", $/
|
129
|
+
|
102
130
|
else
|
103
131
|
to_path = "#{user}@#{host}:#{to_path}"
|
104
132
|
from_paths = paths.join(' ')
|
133
|
+
puts "Copying FROM this machine TO remote", $/
|
105
134
|
end
|
106
135
|
|
136
|
+
|
107
137
|
cmd = "scp -i #{keypair} #{from_paths} #{to_path}"
|
108
138
|
|
139
|
+
puts "CONNECTING TO #{host}..."
|
109
140
|
puts cmd if verbose
|
110
141
|
printonly ? (puts cmd) : system(cmd)
|
111
142
|
end
|