solutious-rudy 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,66 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Volumes < Rudy::Command::Base
6
+
7
+ def destroy_volumes_valid?
8
+ id = @argv.first
9
+ raise "No volume ID provided" unless id
10
+ raise "I will not help you destroy production!" if @global.environment == "prod"
11
+ raise "The volume #{id} doesn't exist!" unless @ec2.volumes.exists?(id)
12
+ exit unless are_you_sure? 4
13
+ true
14
+ end
15
+
16
+ def destroy_volumes
17
+ id = @argv.first
18
+ disk = Rudy::MetaData::Disk.find_from_volume(@sdb, id)
19
+
20
+ begin
21
+ puts "Detaching #{id}"
22
+ @ec2.volumes.detach(id)
23
+ sleep 3
24
+
25
+ puts "Destroying #{id}"
26
+ @ec2.volumes.destroy(id)
27
+
28
+ if disk
29
+ puts "Deleteing metadata for #{disk.name}"
30
+ Rudy::MetaData::Disk.destroy(@sdb, disk)
31
+ end
32
+
33
+ rescue => ex
34
+ puts "Error while detaching volume #{id}: #{ex.message}"
35
+ end
36
+
37
+
38
+ end
39
+
40
+ def volumes
41
+ machines = {}
42
+ volumes = @ec2.volumes.list
43
+ @ec2.volumes.list.each do |volume|
44
+ machine = @ec2.instances.get(volume[:aws_instance_id])
45
+ machines[ volume[:aws_instance_id] ] ||= {
46
+ :machine => machine,
47
+ :volumes => []
48
+ }
49
+ machines[ volume[:aws_instance_id] ][:volumes] << volume
50
+ end
51
+
52
+ machines.each_pair do |instance_id, hash|
53
+ machine = hash[:machine]
54
+ env = (machine[:aws_groups]) ? machine[:aws_groups] : "Not-attached"
55
+ puts "Environment: #{env}"
56
+ hash[:volumes].each do |vol|
57
+ disk = Rudy::MetaData::Disk.find_from_volume(@sdb, vol[:aws_id])
58
+ print_volume(vol, disk)
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+
@@ -0,0 +1,93 @@
1
+
2
+
3
+ module Rudy
4
+ require 'caesars'
5
+
6
+ class AWSInfo < Caesars
7
+ def valid?
8
+ (!account.nil? && !accesskey.nil? && !secretkey.nil?) &&
9
+ (!account.empty? && !accesskey.empty? && !secretkey.empty?)
10
+ end
11
+ end
12
+
13
+ class Defaults < Caesars
14
+ end
15
+
16
+
17
+ class Routines < Caesars
18
+
19
+ def create(*args, &b)
20
+ hash_handler(:create, *args, &b)
21
+ end
22
+ def destroy(*args, &b)
23
+ hash_handler(:destroy, *args, &b)
24
+ end
25
+ def restore(*args, &b)
26
+ hash_handler(:restore, *args, &b)
27
+ end
28
+ def mount(*args, &b)
29
+ hash_handler(:mount, *args, &b)
30
+ end
31
+
32
+ #
33
+ # Force the specified keyword to always be treated as a hash.
34
+ # Example:
35
+ #
36
+ # startup do
37
+ # disks do
38
+ # create "/path/2" # Available as hash: [action][disks][create][/path/2] == {}
39
+ # create "/path/4" do # Available as hash: [action][disks][create][/path/4] == {size => 14}
40
+ # size 14
41
+ # end
42
+ # end
43
+ # end
44
+ #
45
+ def hash_handler(caesars_meth, *args, &b)
46
+ # TODO: Move to caesars
47
+ return @caesars_properties[caesars_meth] if @caesars_properties.has_key?(caesars_meth) && args.empty? && b.nil?
48
+ return nil if args.empty? && b.nil?
49
+ return method_missing(caesars_meth, *args, &b) if args.empty?
50
+
51
+ caesars_name = args.shift
52
+
53
+ prev = @caesars_pointer
54
+ @caesars_pointer[caesars_meth] ||= Caesars::Hash.new
55
+ hash = Caesars::Hash.new
56
+ @caesars_pointer = hash
57
+ b.call if b
58
+ @caesars_pointer = prev
59
+ @caesars_pointer[caesars_meth][caesars_name] = hash
60
+ @caesars_pointer = prev
61
+ end
62
+ end
63
+
64
+ class Machines < Caesars
65
+ end
66
+
67
+ class Config < Caesars::Config
68
+ dsl Rudy::AWSInfo::DSL
69
+ dsl Rudy::Defaults::DSL
70
+ dsl Rudy::Routines::DSL
71
+ dsl Rudy::Machines::DSL
72
+
73
+ def postprocess
74
+ # TODO: give caesar attributes setter methods
75
+ self.awsinfo.cert &&= File.expand_path(self.awsinfo.cert)
76
+ self.awsinfo.privatekey &&= File.expand_path(self.awsinfo.privatekey)
77
+
78
+ end
79
+
80
+ def look_and_load
81
+ cwd = Dir.pwd
82
+ # Rudy looks for configs in all these locations
83
+ @paths += Dir.glob(File.join('/etc', 'rudy', '*.rb')) || []
84
+ @paths += Dir.glob(File.join(cwd, 'config', 'rudy', '*.rb')) || []
85
+ @paths += Dir.glob(File.join(cwd, '.rudy', '*.rb')) || []
86
+ refresh
87
+ end
88
+
89
+
90
+ end
91
+ end
92
+
93
+
@@ -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
@@ -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 && @second
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
+
@@ -0,0 +1,138 @@
1
+
2
+
3
+ module Rudy
4
+
5
+ module MetaData
6
+ class Disk < Storable
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
+ field :awsid
16
+
17
+ field :environment
18
+ field :role
19
+ field :path
20
+ field :position
21
+
22
+ field :zone
23
+ field :region
24
+ field :device
25
+ #field :backups => Array
26
+ field :size
27
+
28
+ def initialize
29
+ @backups = []
30
+ @rtype = @@rtype.to_s
31
+ @raw_volume = false
32
+ end
33
+
34
+ def rtype
35
+ @@rtype.to_s
36
+ end
37
+
38
+ def rtype=(val)
39
+ end
40
+
41
+ def name
42
+ Disk.generate_name(@zone, @environment, @role, @position, @path)
43
+ end
44
+
45
+ def valid?
46
+ @zone && @environment && @role && @position && @path && @size && @device
47
+ end
48
+
49
+ def to_query(more=[], remove=[])
50
+ criteria = [:rtype, :zone, :environment, :role, :position, :path, *more]
51
+ criteria -= [*remove].flatten
52
+ query = []
53
+ criteria.each do |n|
54
+ query << "['#{n}' = '#{self.send(n.to_sym)}'] "
55
+ end
56
+ query.join(" intersection ")
57
+ end
58
+
59
+ def to_s
60
+ str = ""
61
+ field_names.each do |key|
62
+ str << sprintf(" %22s: %s#{$/}", key, self.send(key.to_sym))
63
+ end
64
+ str
65
+ end
66
+
67
+ def Disk.generate_name(zon, env, rol, pos, pat, sep=File::SEPARATOR)
68
+ pos = pos.to_s.rjust 2, '0'
69
+ dirs = pat.split sep if pat
70
+ dirs.shift while dirs && (dirs[0].nil? || dirs[0].empty?)
71
+ ["disk", zon, env, rol, pos, *dirs].join(RUDY_DELIM)
72
+ end
73
+
74
+ def Disk.get(sdb, name)
75
+ disk = sdb.get_attributes(RUDY_DOMAIN, name)
76
+ return nil unless disk && disk.has_key?(:attributes) && !disk[:attributes].empty?
77
+ # raise "Disk #{name} does not exist!" unless
78
+ Rudy::MetaData::Disk.from_hash(disk[:attributes])
79
+ end
80
+
81
+ def Disk.destroy(sdb, disk)
82
+ disk = Disk.get(sdb, disk) if disk.is_a?(String) # get raises an exception if the disk doesn't exist
83
+ sdb.destroy(RUDY_DOMAIN, disk.name)
84
+ true # wtf: RightAws::SimpleDB doesn't tell us whether it succeeds. We'll assume!
85
+ end
86
+
87
+ def Disk.save(sdb, disk)
88
+ sdb.store(RUDY_DOMAIN, disk.name, disk.to_hash, :replace)
89
+ end
90
+
91
+ def Disk.is_defined?(sdb, disk)
92
+ # We don't care about the path, but we do care about the device
93
+ # which is not part of the disk's name.
94
+ query = disk.to_query(:device, :path)
95
+ !sdb.query_with_attributes(RUDY_DOMAIN, query).empty?
96
+ end
97
+
98
+ def Disk.find_from_volume(sdb, vol_id)
99
+ query = "['awsid' = '#{vol_id}']"
100
+ res = sdb.query_with_attributes(RUDY_DOMAIN, query)
101
+ if res.empty?
102
+ nil
103
+ else
104
+ disk = Rudy::MetaData::Disk.from_hash(res.values.first)
105
+ end
106
+ end
107
+
108
+
109
+ def Disk.find_from_path(sdb, path)
110
+ query = "['path' = '#{path}']"
111
+ res = sdb.query_with_attributes(RUDY_DOMAIN, query)
112
+ if res.empty?
113
+ nil
114
+ else
115
+ disk = Rudy::MetaData::Disk.from_hash(res.values.first)
116
+ end
117
+ end
118
+
119
+ def Disk.list(sdb, zon, env=nil, rol=nil, pos=nil)
120
+ query = ''
121
+ query << "['rtype' = '#{@@rtype}']" if zon
122
+ query << " intersection ['zone' = '#{zon}']" if zon
123
+ query << " intersection ['environment' = '#{env}']" if env
124
+ query << " intersection ['role' = '#{rol}']" if rol
125
+ query << " intersection ['position' = '#{pos}']" if pos
126
+
127
+ list = []
128
+ sdb.query_with_attributes(RUDY_DOMAIN, query).each_pair do |name, hash|
129
+ #puts "DISK: #{hash.to_yaml}"
130
+ list << Rudy::MetaData::Disk.from_hash(hash)
131
+ end
132
+ list
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ end