rudy 0.8.5 → 0.9.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.
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
+