rudy 0.8.5 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/CHANGES.txt +110 -18
  2. data/README.rdoc +40 -44
  3. data/Rudyfile +35 -50
  4. data/bin/rudy +88 -57
  5. data/bin/rudy-ec2 +2 -16
  6. data/bin/rudy-s3 +0 -10
  7. data/bin/rudy-sdb +11 -12
  8. data/lib/rudy.rb +59 -91
  9. data/lib/rudy/aws.rb +4 -45
  10. data/lib/rudy/aws/ec2.rb +57 -20
  11. data/lib/rudy/aws/ec2/address.rb +10 -11
  12. data/lib/rudy/aws/ec2/group.rb +10 -9
  13. data/lib/rudy/aws/ec2/image.rb +8 -8
  14. data/lib/rudy/aws/ec2/instance.rb +18 -19
  15. data/lib/rudy/aws/ec2/keypair.rb +14 -19
  16. data/lib/rudy/aws/ec2/snapshot.rb +16 -9
  17. data/lib/rudy/aws/ec2/volume.rb +39 -26
  18. data/lib/rudy/aws/ec2/zone.rb +5 -4
  19. data/lib/rudy/aws/s3.rb +2 -1
  20. data/lib/rudy/aws/sdb.rb +35 -86
  21. data/lib/rudy/backups.rb +24 -0
  22. data/lib/rudy/cli.rb +5 -131
  23. data/lib/rudy/cli/aws/ec2/addresses.rb +19 -27
  24. data/lib/rudy/cli/aws/ec2/candy.rb +45 -20
  25. data/lib/rudy/cli/aws/ec2/groups.rb +9 -13
  26. data/lib/rudy/cli/aws/ec2/images.rb +5 -133
  27. data/lib/rudy/cli/aws/ec2/instances.rb +25 -25
  28. data/lib/rudy/cli/aws/ec2/keypairs.rb +7 -11
  29. data/lib/rudy/cli/aws/ec2/snapshots.rb +5 -9
  30. data/lib/rudy/cli/aws/ec2/volumes.rb +22 -23
  31. data/lib/rudy/cli/aws/ec2/zones.rb +2 -3
  32. data/lib/rudy/cli/aws/sdb/domains.rb +5 -6
  33. data/lib/rudy/cli/aws/sdb/objects.rb +33 -0
  34. data/lib/rudy/cli/aws/sdb/select.rb +23 -0
  35. data/lib/rudy/cli/backups.rb +38 -0
  36. data/lib/rudy/cli/base.rb +104 -0
  37. data/lib/rudy/cli/candy.rb +1 -2
  38. data/lib/rudy/cli/config.rb +20 -7
  39. data/lib/rudy/cli/disks.rb +7 -9
  40. data/lib/rudy/cli/execbase.rb +56 -0
  41. data/lib/rudy/cli/machines.rb +242 -45
  42. data/lib/rudy/cli/metadata.rb +24 -10
  43. data/lib/rudy/cli/networks.rb +34 -0
  44. data/lib/rudy/cli/routines.rb +32 -6
  45. data/lib/rudy/cli/status.rb +60 -0
  46. data/lib/rudy/config.rb +55 -32
  47. data/lib/rudy/config/objects.rb +44 -30
  48. data/lib/rudy/disks.rb +25 -0
  49. data/lib/rudy/exceptions.rb +99 -0
  50. data/lib/rudy/global.rb +67 -28
  51. data/lib/rudy/guidelines.rb +3 -2
  52. data/lib/rudy/huxtable.rb +67 -58
  53. data/lib/rudy/machines.rb +41 -263
  54. data/lib/rudy/metadata.rb +212 -38
  55. data/lib/rudy/metadata/backup.rb +123 -78
  56. data/lib/rudy/metadata/disk.rb +153 -170
  57. data/lib/rudy/metadata/machine.rb +179 -0
  58. data/lib/rudy/mixins.rb +2 -1
  59. data/lib/rudy/mixins/hash.rb +3 -1
  60. data/lib/rudy/mixins/symbol.rb +8 -0
  61. data/lib/rudy/routines.rb +127 -344
  62. data/lib/rudy/routines/base.rb +229 -0
  63. data/lib/rudy/routines/handlers/base.rb +48 -0
  64. data/lib/rudy/routines/handlers/depends.rb +49 -0
  65. data/lib/rudy/routines/handlers/disks.rb +249 -0
  66. data/lib/rudy/routines/handlers/group.rb +44 -0
  67. data/lib/rudy/routines/handlers/host.rb +70 -0
  68. data/lib/rudy/routines/handlers/keypair.rb +70 -0
  69. data/lib/rudy/routines/handlers/machines.rb +15 -0
  70. data/lib/rudy/routines/handlers/script.rb +85 -0
  71. data/lib/rudy/routines/handlers/user.rb +45 -0
  72. data/lib/rudy/routines/passthrough.rb +19 -23
  73. data/lib/rudy/routines/reboot.rb +98 -50
  74. data/lib/rudy/routines/shutdown.rb +65 -14
  75. data/lib/rudy/routines/startup.rb +112 -17
  76. data/lib/rudy/utils.rb +35 -68
  77. data/rudy.gemspec +82 -25
  78. data/tryouts/01_mixins/01_hash_tryouts.rb +20 -0
  79. data/tryouts/10_require_time/10_rudy_tryouts.rb +33 -0
  80. data/tryouts/10_require_time/15_global_tryouts.rb +58 -0
  81. data/tryouts/12_config/10_load_config_tryouts.rb +43 -0
  82. data/tryouts/12_config/20_defaults_tryouts.rb +16 -0
  83. data/tryouts/12_config/30_accounts_tryouts.rb +17 -0
  84. data/tryouts/12_config/40_machines_tryouts.rb +53 -0
  85. data/tryouts/12_config/50_commands_tryouts.rb +17 -0
  86. data/tryouts/12_config/60_routines_tryouts.rb +16 -0
  87. data/tryouts/15_huxtable/10_huxtable_tryouts.rb +47 -0
  88. data/tryouts/15_huxtable/20_user_tryouts.rb +47 -0
  89. data/tryouts/20_simpledb/10_domains_tryouts.rb +36 -0
  90. data/tryouts/20_simpledb/20_objects_tryouts.rb +56 -0
  91. data/tryouts/25_ec2/10_keypairs_tryouts.rb +54 -0
  92. data/tryouts/25_ec2/20_groups_tryouts.rb +56 -0
  93. data/tryouts/25_ec2/21_groups_authorize_address_tryouts.rb +53 -0
  94. data/tryouts/25_ec2/22_groups_authorize_account_tryouts.rb +54 -0
  95. data/tryouts/25_ec2/30_addresses_tryouts.rb +42 -0
  96. data/tryouts/25_ec2/40_volumes_tryouts.rb +53 -0
  97. data/tryouts/25_ec2/50_snapshots_tryouts.rb +75 -0
  98. data/tryouts/26_ec2_instances/10_instance_tryouts.rb +107 -0
  99. data/tryouts/26_ec2_instances/50_images_tryouts.rb +7 -0
  100. data/tryouts/30_metadata/10_include_tryouts.rb +45 -0
  101. data/tryouts/30_metadata/13_object_tryouts.rb +19 -0
  102. data/tryouts/30_metadata/50_disk_tryouts.rb +115 -0
  103. data/tryouts/30_metadata/51_disk_digest_tryouts.rb +24 -0
  104. data/tryouts/30_metadata/53_disk_list_tryouts.rb +35 -0
  105. data/tryouts/30_metadata/56_disk_volume_tryouts.rb +68 -0
  106. data/tryouts/30_metadata/60_backup_tryouts.rb +101 -0
  107. data/tryouts/30_metadata/63_backup_list_tryouts.rb +38 -0
  108. data/tryouts/30_metadata/64_backup_disk_tryouts.rb +65 -0
  109. data/tryouts/30_metadata/66_backup_snapshot_tryouts.rb +76 -0
  110. data/tryouts/30_metadata/70_machine_tryouts.rb +85 -0
  111. data/tryouts/30_metadata/73_machine_list_tryouts.rb +58 -0
  112. data/tryouts/30_metadata/76_machine_instance_tryouts.rb +64 -0
  113. data/tryouts/30_metadata/77_machines_tryouts.rb +45 -0
  114. data/tryouts/40_routines/10_keypair_handler_tryouts.rb +52 -0
  115. data/tryouts/40_routines/11_group_handler_tryouts.rb +36 -0
  116. data/tryouts/80_cli/10_rudyec2_tryouts.rb +8 -0
  117. data/tryouts/80_cli/60_rudy_tryouts.rb +41 -0
  118. data/tryouts/exploration/console.rb +91 -0
  119. data/tryouts/exploration/machine.rb +23 -0
  120. data/tryouts/failer +6 -0
  121. metadata +116 -32
  122. data/bin/ird +0 -153
  123. data/lib/rudy/metadata/backups.rb +0 -67
  124. data/lib/rudy/metadata/debug.rb +0 -38
  125. data/lib/rudy/metadata/disks.rb +0 -67
  126. data/lib/rudy/metadata/objectbase.rb +0 -108
  127. data/lib/rudy/routines/helper.rb +0 -76
  128. data/lib/rudy/routines/helpers/dependshelper.rb +0 -34
  129. data/lib/rudy/routines/helpers/diskhelper.rb +0 -403
  130. data/lib/rudy/routines/helpers/scripthelper.rb +0 -197
  131. data/lib/rudy/routines/helpers/userhelper.rb +0 -37
  132. data/support/rudy-ec2-startup +0 -200
data/lib/rudy/machines.rb CHANGED
@@ -1,293 +1,71 @@
1
-
2
1
 
3
2
 
4
3
  module Rudy
5
- class Machine < Storable
6
- include Rudy::MetaData::ObjectBase
7
4
 
8
- field :rtype
9
- field :awsid
10
-
11
- field :region
12
- field :zone
13
- field :environment
14
- field :role
15
- field :position
16
-
17
- field :created => Time
18
- field :started => Time
19
-
20
- field :dns_public
21
- field :dns_private
22
- field :state
23
-
24
- field :os
25
-
26
- attr_reader :instance
27
-
28
- def init
29
- #@created =
30
- @rtype = 'm'
31
- @region = @@global.region
32
- @zone = @@global.zone
33
- @environment = @@global.environment
34
- @role = @@global.role
35
- @position = find_next_position || '01'
36
- @state = 'no-instance'
37
- @os = 'unknown'
38
- end
5
+ module Machines
6
+ RTYPE = 'm'.freeze
39
7
 
40
- def liner_note
41
- update #if !dns_public? && @awsid
42
- info = !@dns_public.nil? && !@dns_public.empty? ? @dns_public : "#{@awsid}:#{@state}"
43
- "%s %s" % [self.name.bright, info]
44
- end
8
+ extend self
9
+ extend Rudy::Metadata::ClassMethods
10
+ extend Rudy::Huxtable
45
11
 
46
- def to_s(with_title=false)
47
- lines = []
48
- lines << liner_note
49
- #if self.running?
50
- # k, g = @keyname || 'no-keypair', self.groups.join(', ')
51
- # lines << @@sformat % %w{zone size ami keyname groups} if with_title
52
- # lines << @@sformat % [@zone, @size, @ami, k, g]
53
- #end
54
- lines.join($/)
12
+ def get(position)
13
+ tmp = Rudy::Machine.new position
14
+ record = Rudy::Metadata.get tmp.name
15
+ return nil unless record.is_a?(Hash)
16
+ tmp.from_hash record
55
17
  end
56
18
 
57
- def inspect
58
- update #if !dns_public? && @awsid
59
- lines = []
60
- lines << liner_note
61
- field_names.each do |key|
62
- next unless self.respond_to?(key)
63
- val = self.send(key)
64
- lines << sprintf(" %22s: %s", key, (val.is_a?(Array) ? val.join(', ') : val))
65
- end
66
- lines.join($/)
67
- end
68
-
69
19
  def find_next_position
70
- list = @sdb.select(self.to_select(nil, [:position])) || []
20
+ raise "reimplement by looking at position values"
21
+ list = Rudy::Machines.list({}, [:position]) || []
71
22
  pos = list.size + 1
72
23
  pos.to_s.rjust(2, '0')
73
24
  end
74
25
 
75
- def name
76
- Machine.generate_name(@zone, @environment, @role, @position)
77
- end
78
-
79
- def Machine.generate_name(zon, env, rol, pos)
80
- pos = pos.to_s.rjust 2, '0'
81
- ["m", zon, env, rol, pos].join(Rudy::DELIM)
26
+ # Returns true if any machine metadata exists for this group
27
+ def exists?(pos=nil)
28
+ machines = pos.nil? ? list : get(pos)
29
+ !machines.nil?
82
30
  end
83
31
 
84
- def get_instance
85
- @ec2inst.get(@awsid) rescue nil
32
+ # Returns true if all machines in the group are running instances
33
+ def running?(pos=nil)
34
+ group = pos.nil? ? list : [get(pos)].compact
35
+ return false if group.nil? || group.empty?
36
+ group.collect! { |m| m.instance_running? }
37
+ !group.member?(false)
86
38
  end
87
39
 
88
- def update
89
- return false unless @awsid
90
- @instance = get_instance
91
- if @instance.is_a?(Rudy::AWS::EC2::Instance)
92
- @dns_public = @instance.dns_public
93
- @dns_private = @instance.dns_private
94
- @state = @instance.state
95
- save
96
- elsif @instance.nil?
97
- @awsid = @dns_public = @dns_private = nil
98
- @state = 'rogue'
99
- # Don't save it b/c it's possible the EC2 server is just down.
100
- end
101
- end
102
-
103
- def dns_public?
104
- !@dns_public.nil? && !@dns_public.empty?
105
- end
106
- def dns_private?
107
- !@dns_private.nil? && !@dns_private.empty?
108
- end
109
-
110
- def start(opts={})
111
- raise "#{name} is already running" if running?
112
-
113
- opts = {
114
- :min => 1,
115
- :size => current_machine_size,
116
- :ami => current_machine_image,
117
- :group => current_group_name,
118
- :keypair => root_keypairname,
119
- :zone => @@global.zone.to_s,
120
- :machine_data => Machine.generate_machine_data.to_yaml
121
- }.merge(opts)
122
-
123
- @os = current_machine_os
124
-
125
- @ec2inst.create(opts) do |inst|
126
- @awsid = inst.awsid
127
- @created = @starts = Time.now
128
- @state = inst.state
129
- # We need to be safe when creating machines because if an exception is
130
- # raised, instances will have been creating but the calling class won't know.
131
- begin
132
- address = current_machine_address(@position)
133
- # Assign IP address only if we have one for that position
134
- if address
135
- # Make sure the address is associated to the current account
136
- if @radd.exists?(address)
137
- puts "Associating #{address} to #{inst.awsid}"
138
- @radd.associate(address, inst.awsid)
139
- else
140
- STDERR.puts "Unknown address: #{address}"
141
- end
142
- end
143
- rescue => ex
144
- STDERR.puts "Error: #{ex.message}"
145
- STDERR.puts ex.backtrace if Rudy.debug?
40
+ # Returns an Array of newly created Rudy::Machine objects
41
+ def create(size=nil)
42
+ if Rudy::Huxtable.global.position.nil?
43
+ size ||= current_machine_count.to_i || 1
44
+ group = Array.new(size) do |i|
45
+ m = Rudy::Machine.new(i + 1)
46
+ m.create
47
+ m
146
48
  end
147
- end
148
-
149
- self.save
150
-
151
- self
152
- end
153
-
154
- def destroy
155
- @ec2inst.destroy(@awsid) if running?
156
- super
157
- end
158
-
159
- def restart
160
- @ec2inst.restart(@awsid) if running?
161
- end
162
-
163
- def Machine.generate_machine_data
164
- data = { # Give the machine an identity
165
- :region => @@global.region.to_s,
166
- :zone => @@global.zone.to_s,
167
- :environment => @@global.environment.to_s,
168
- :role => @@global.role.to_s,
169
- :position => @@global.position.to_s,
170
- :hosts => { # Add hosts to the /etc/hosts file
171
- :dbmaster => "127.0.0.1",
172
- }
173
- }
174
- data
175
- end
176
-
177
- def running?
178
- return false if @awsid.nil? || @awsid.empty?
179
- @ec2inst.running?(@awsid) rescue nil
180
- end
181
-
182
- end
183
-
184
-
185
- class Machines
186
- include Rudy::MetaData
187
-
188
- def create(&each_mach)
189
- raise MachineGroupAlreadyRunning, current_machine_group if running?
190
- raise MachineGroupNotDefined, current_machine_group unless known_machine_group?
191
-
192
- unless (1..MAX_INSTANCES).member?(current_machine_count)
193
- raise "Instance count must be more than 0, less than #{MAX_INSTANCES}"
194
- end
195
-
196
- unless @rgrp.exists?(current_group_name)
197
- puts "Creating group: #{current_group_name}"
198
- @rgrp.create(current_group_name)
199
- end
200
-
201
- unless @rkey.exists?(root_keypairname)
202
- kp_file = File.join(Rudy::CONFIG_DIR, root_keypairname)
203
- raise PrivateKeyFileExists, kp_file if File.exists?(kp_file)
204
- puts "Creating keypair: #{root_keypairname}"
205
- kp = @rkey.create(root_keypairname)
206
- puts "Saving #{kp_file}"
207
- Rudy::Utils.write_to_file(kp_file, kp.private_key, 'w', 0600)
208
49
  else
209
- kp_file = root_keypairpath
210
- # This means no keypair file can be found
211
- raise PrivateKeyNotFound, root_keypairname if kp_file.nil?
212
- # This means we found a keypair in the config but we cannot find the private key file.
213
- raise PrivateKeyNotFound, kp_file if !File.exists?(kp_file)
214
- end
215
-
216
- machines = []
217
- current_machine_count.times do |i|
218
- machine = Rudy::Machine.new
219
-
220
- #puts "Starting %s" % machine.name
221
-
222
- machine.start
223
- machines << machine
50
+ m = Rudy::Machine.new(Rudy::Huxtable.global.position)
51
+ m.create
52
+ [m]
224
53
  end
225
- machines.each { |m| each_mach.call(m) } if each_mach
226
- machines
227
54
  end
228
55
 
229
-
230
- def destroy(&each_mach)
231
- raise MachineGroupNotDefined, current_machine_group unless known_machine_group?
232
- raise MachineGroupNotRunning, current_machine_group unless running?
233
- list.each { |m| each_mach.call(m); } if each_mach
234
- list do |mach|
235
- #puts "Destroying #{mach.name}"
236
- mach.destroy
237
- end
238
- end
239
-
240
-
241
- def restart(&each_mach)
242
- raise MachineGroupNotDefined, current_machine_group unless known_machine_group?
243
- raise MachineGroupNotRunning, current_machine_group unless running?
244
- machines = list
245
- machines.each do |mach|
246
- each_mach.call(mach) if each_mach
247
- puts "Restarting #{mach.name}"
248
- mach.restart
249
- end
250
- machines
251
- end
252
-
253
- def list(more=[], less=[], &each_mach)
254
- machines = list_as_hash(more, less, &each_mach)
255
- machines &&= machines.values
256
- machines
257
- end
258
-
259
- def list_as_hash(more=[], less=[], &each_mach)
260
- query = to_select([:rtype, 'm'], less)
261
- list = @sdb.select(query) || {}
262
- machines = {}
263
- list.each_pair do |n,m|
264
- machines[n] = Rudy::Machine.from_hash(m)
56
+ def restart
57
+ group = list
58
+ raise MachineGroupNotRunning, current_group_name if group.nil?
59
+ group.each do |inst|
60
+ inst.restart
265
61
  end
266
- machines.each_pair { |n,mach| each_mach.call(mach) } if each_mach
267
- machines = nil if machines.empty?
268
- machines
62
+ group
269
63
  end
270
64
 
271
- def get(rname=nil)
272
- Rudy::Machine.from_hash(@sdb.get(Rudy::DOMAIN, rname)) # Returns nil if empty
65
+ def from_hash(h)
66
+ Rudy::Machine.from_hash h
273
67
  end
274
68
 
275
-
276
- def running?
277
- !list.nil?
278
- # TODO: add logic that checks whether the instances are running.
279
- end
280
-
281
- end
282
-
283
-
284
- class Machines::Offline
285
- def list(more=[], less=[], &each_mach)
286
- m = Rudy::Machine.new
287
- m.dns_public = 'localhost'
288
- each_mach.call(m) if each_mach
289
- [m]
290
- end
291
69
  end
292
70
 
293
71
  end
data/lib/rudy/metadata.rb CHANGED
@@ -1,62 +1,236 @@
1
1
 
2
+
2
3
  module Rudy
3
- module MetaData
4
+ module Metadata
4
5
  include Rudy::Huxtable
5
6
 
6
- attr_reader :sdb_domain
7
+ COMMON_FIELDS = [:region, :zone, :environment, :role].freeze
8
+
9
+ @@rsdb = nil
10
+ @@domain = Rudy::DOMAIN
7
11
 
8
- def initialize(*args)
9
- a, s, r = @@global.accesskey, @@global.secretkey, @@global.region
10
- @sdb = Rudy::AWS::SDB.new(a, s, r)
11
- @radd = Rudy::AWS::EC2::Addresses.new(a, s, r)
12
- @rinst = Rudy::AWS::EC2::Instances.new(a, s, r)
13
- @rgrp = Rudy::AWS::EC2::Groups.new(a, s, r)
14
- @rkey = Rudy::AWS::EC2::KeyPairs.new(a, s, r)
15
- @sdb_domain = Rudy::DOMAIN
16
- init(*args)
12
+ #
13
+ def self.get_rclass(rtype)
14
+ case rtype
15
+ when Rudy::Machines::RTYPE
16
+ Rudy::Machines
17
+ when Rudy::Disks::RTYPE
18
+ Rudy::Disks
19
+ when Rudy::Backups::RTYPE
20
+ Rudy::Backups
21
+ else
22
+ raise UnknownRecordType, rtype
23
+ end
17
24
  end
18
25
 
19
- def init(*args)
26
+ # Creates instances of the following and stores to class variables:
27
+ # * Rudy::AWS::SDB
28
+ # * Rudy::AWS::EC2::Volumes
29
+ def self.connect(accesskey, secretkey, region, reconnect=false)
30
+ return @@rsdb unless reconnect || @@rsdb.nil?
31
+ @@rsdb = Rudy::AWS::SDB.new accesskey, secretkey, region
32
+ true
33
+ end
34
+ def self.domain(name=nil)
35
+ return @@domain if name.nil?
36
+ @@domain = name
37
+ end
38
+ # An alias for Rudy::Metadata.domain
39
+ def self.domain=(*args)
40
+ domain *args
20
41
  end
21
42
 
22
- # 20090224-1813-36
23
- def format_timestamp(dat)
24
- mon, day, hour, min, sec = [dat.mon, dat.day, dat.hour, dat.min, dat.sec].collect { |v| v.to_s.rjust(2, "0") }
25
- [dat.year, mon, day, Rudy::DELIM, hour, min, Rudy::DELIM, sec].join
43
+ # Creates a SimpleDB domain named +n+ and updates +@@domain+ if successful
44
+ def self.create_domain(n)
45
+ @@domain = n if @@rsdb.create_domain n
26
46
  end
27
47
 
28
- def destroy
29
- @sdb.destroy(@sdb_domain, name)
48
+ # Destroys a SimpleDB domain named +n+ and sets +@@domain+ to Rudy::DOMAIN
49
+ def self.destroy_domain(n)
50
+ Rudy::Huxtable.ld "DESTROY: #{n}" if Rudy.debug?
51
+ @@rsdb.destroy_domain n
52
+ @@domain = Rudy::DOMAIN
30
53
  true
31
54
  end
32
55
 
33
- private
34
-
35
- # Returns a generic zipped Array of metadata
36
- # (There is region, zone, environment, role, but no rtype)
37
- # NOTE: I've encoded some mega bullshit in here. more is actually an Array
38
- # of name values [n1, v1, n2, v2] (wtf!) which makes local unnecessary.
39
- def build_criteria(more=[], less=[], local={})
40
- # TODO: This build_criteria treats "more" differently than the
41
- # ObjectBase one. Sort it out! (This way is better)
42
- names = [:region, :zone, :environment, :role].compact
43
- names -= [*less].flatten.uniq.compact
44
- values = names.collect do |n|
45
- local[n.to_sym] || @@global.send(n.to_sym)
56
+ # Get a record from SimpleDB with the key +n+
57
+ def self.get(n)
58
+ Rudy::Huxtable.ld "GET: #{n}" if Rudy.debug?
59
+ @@rsdb.get @@domain, n
60
+ end
61
+
62
+ def self.exists?(n)
63
+ !get(n).nil?
64
+ end
65
+
66
+ def self.put(n, o, replace=false)
67
+ Rudy::Huxtable.ld "PUT: #{n}" if Rudy.debug?
68
+ @@rsdb.put @@domain, n, o, replace
69
+ end
70
+
71
+ def self.destroy(n)
72
+ Rudy::Huxtable.ld "DESTROY: #{n}" if Rudy.debug?
73
+ @@rsdb.destroy @@domain, n
74
+ end
75
+
76
+ # Generates and executes a SimpleDB select query based on
77
+ # the specified +fields+ Hash. See self.build_criteria.
78
+ #
79
+ # Returns a Hash. keys are SimpleDB object IDs and values
80
+ # are the object attributes.
81
+ def self.select(fields={})
82
+ squery = Rudy::AWS::SDB.generate_select @@domain, fields
83
+ Rudy::Huxtable.ld "SELECT: #{squery}" if Rudy.debug?
84
+ @@rsdb.select squery
85
+ end
86
+
87
+
88
+ # Generates a default criteria for all metadata based on
89
+ # region, zone, environment, and role. If a position has
90
+ # been specified in the globals it will also be included.
91
+ # * +rtype+ is the record type. One of: m, disk, or back.
92
+ # * +fields+ replaces and adds values to this criteria
93
+ # * +less+ removes keys from the default criteria.
94
+ #
95
+ # Returns a Hash.
96
+ def self.build_criteria(rtype, fields={}, less=[])
97
+ fields ||= {}
98
+ fields[:rtype] = rtype
99
+ fields[:position] = @@global.position unless @@global.position.nil?
100
+ names = Rudy::Metadata::COMMON_FIELDS
101
+ values = names.collect { |n| @@global.send(n.to_sym) }
102
+ mixer = names.zip(values).flatten
103
+ criteria = Hash[*mixer].merge(fields)
104
+ criteria.reject! { |n,v| less.member?(n) }
105
+ Rudy::Huxtable.ld "CRITERIA: #{criteria.inspect}"
106
+ criteria
107
+ end
108
+
109
+ module ClassMethods
110
+ extend self
111
+ extend Rudy::Huxtable
112
+
113
+ # TODO: MOVE TO Rudy:Disks etc...
114
+ def list(fields={}, less=[], &block)
115
+ fields = Rudy::Metadata.build_criteria self::RTYPE, fields, less
116
+ records_raw, records = Rudy::Metadata.select(fields), []
117
+ return nil if records_raw.nil? || records_raw.empty?
118
+ records_raw.each_pair do |p, r|
119
+ obj = self.from_hash r
120
+ records << obj
121
+ end
122
+ records.sort { |a,b| a.name <=> b.name }
123
+ end
124
+
125
+ def list_as_hash(fields={}, less=[], &block)
126
+ fields = Rudy::Metadata.build_criteria self::RTYPE, fields, less
127
+ records_raw, records = Rudy::Metadata.select(fields), {}
128
+ return nil if records_raw.nil? || records_raw.empty?
129
+ records_raw.each_pair do |p, r|
130
+ obj = self.from_hash r
131
+ records[p] = obj
132
+ end
133
+ records
46
134
  end
47
- names.zip(values) + more
135
+
136
+ def any?(fields={}, less=[])
137
+ !list(fields, less).nil?
138
+ end
139
+
48
140
  end
49
141
 
50
- def to_query(more=[], less=[], local={})
51
- Rudy::AWS::SDB.generate_query build_criteria(more, less, local)
142
+ # All classes which include Rudy::Metadata must reimplement
143
+ # the method stubs in this module. These methods only raise
144
+ # exceptions.
145
+ module InstanceMethods
146
+ class << self
147
+ def valid?; raise "implement valid?"; end
148
+ def name; raise "implement name"; end
149
+ def postprocess; raise "implement postprocess"; end
150
+ end
52
151
  end
53
-
54
- def to_select(more=[], less=[], local={})
55
- Rudy::AWS::SDB.generate_select ['*'], @sdb_domain, build_criteria(more, less, local)
152
+
153
+ def self.included(obj)
154
+ obj.send :include, Rudy::Metadata::InstanceMethods
155
+
156
+ # Add common storable fields.
157
+ [COMMON_FIELDS, :position].flatten.each do |n|
158
+ obj.field n
159
+ end
160
+
161
+ end
162
+
163
+ def initialize(rtype, opts={})
164
+ @rtype = rtype
165
+ @position = position || @@global.position || '01'
166
+
167
+ COMMON_FIELDS.each { |n|
168
+ ld "SETTING: #{n}: #{@@global.send(n)}" if @@global.verbose > 3
169
+ instance_variable_set("@#{n}", @@global.send(n))
170
+ }
171
+
172
+ opts.each_pair do |n,v|
173
+ raise "Unknown attribute for #{self.class}: #{n}" if !self.has_field? n
174
+ next if v.nil?
175
+ ld "RESETTING: #{n}: #{v}" if @@global.verbose > 3
176
+ self.send("#{n}=", v)
177
+ end
178
+
179
+ end
180
+
181
+ def name(*other)
182
+ parts = [@rtype, @zone, @environment, @role, @position, *other].flatten
183
+ parts.join Rudy::DELIM
184
+ end
185
+
186
+ def save(replace=false)
187
+ raise DuplicateRecord, self.name unless replace || !self.exists?
188
+ Rudy::Metadata.put self.name, self.to_hash, replace
189
+ true
190
+ end
191
+
192
+ def destroy(force=false)
193
+ raise UnknownObject, self.name unless self.exists?
194
+ Rudy::Metadata.destroy self.name
195
+ true
196
+ end
197
+
198
+ def descriptors(*additional)
199
+ criteria = {
200
+ :region => @region, :zone => @zone,
201
+ :environment => @environment, :role => @role
202
+ }
203
+ additional.each do |att|
204
+ criteria[att] = self.send(att)
205
+ end
206
+ ld "DESCRIPTORS: #{criteria.inspect} (#{additional})"
207
+ criteria
208
+ end
209
+
210
+ # Refresh the metadata object from SimpleDB. If the record doesn't
211
+ # exist it will raise an UnknownObject error
212
+ def refresh!
213
+ raise UnknownObject, self.name unless self.exists?
214
+ h = Rudy::Metadata.get self.name
215
+ return false if h.nil? || h.empty?
216
+ obj = self.from_hash(h)
217
+ obj.postprocess
218
+ obj
219
+ end
220
+
221
+ # Compares the names between two Rudy::Metadata objects.
222
+ def ==(other)
223
+ return false unless other === self.class
224
+ self.name == other.name
225
+ end
226
+
227
+ # Is there an object in SimpleDB where the key == self.name
228
+ def exists?
229
+ !Rudy::Metadata.get(self.name).nil?
56
230
  end
57
231
 
58
232
  end
59
233
  end
60
234
 
61
- Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'metadata', 'objectbase.rb')
62
235
  Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'metadata', '*.rb')
236
+