solutious-rudy 0.4.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.
@@ -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