Class: PoolParty::Cloud

Inherits:
DslBase show all
Defined in:
lib/poolparty/cloud.rb

Instance Attribute Summary

Attributes inherited from Base

#base_name, #init_opts

Instance Method Summary collapse

Methods inherited from DslBase

#instances

Methods inherited from Base

#add_ordered_resources_to_result, #after_loaded, #all_resources, #before_load, #clouds_dot_rb_dir, clouds_dot_rb_dir, #clouds_dot_rb_file, clouds_dot_rb_file, #compile_opts, #create_graph, #dependencies, #get_resource, #has_searchable_paths, #method_missing, #ordered_resources, #output_resources_graph, #resources, #resources_graph, #resources_with_dependencies, #resources_without_dependencies, #run_in_context, #run_with_callbacks, #to_s, #valid?, #validations

Methods included from Delayed

included

Methods included from Callbacks

included

Methods included from SearchablePaths

included

Constructor Details

#initialize(n, o = {}, &block) ⇒ Cloud

Freeze the cloud_name so we can’t modify it at all, set the plugin_directory call and run instance_eval on the block and then call the after_create callback



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/poolparty/cloud.rb', line 37

def initialize(n, o={}, &block)
  @cloud_name = n
  @cloud_name.freeze
  
  # @init_block = block
  @init_opts = compile_opts(o)
  
  @init_block = Proc.new do
    super(n,o,&block)
  end
  
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class PoolParty::Base

Instance Method Details

#add_monitoring_stack_if_neededObject

Add the monitoring stack



184
185
186
187
188
189
190
191
192
193
194
# File 'lib/poolparty/cloud.rb', line 184

def add_monitoring_stack_if_needed
  if monitors.size > 0
    
    run_in_context do
      %w(collectd hermes).each do |m|
        self.send m.to_sym
      end
    end
    
  end
end

#after_all_loadedObject



275
276
277
278
279
# File 'lib/poolparty/cloud.rb', line 275

def after_all_loaded
  run_after_loaded do |b|
    run_in_context(&b)
  end
end

#before_compileObject



29
30
31
32
33
# File 'lib/poolparty/cloud.rb', line 29

def before_compile
  add_monitoring_stack_if_needed
  
  validate_all_resources unless ENV["POOLPARTY_NO_VALIDATION"]
end

#cloud_provider(opts = {}, &block) ⇒ Object

The actual cloud_provider instance



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/poolparty/cloud.rb', line 79

def cloud_provider(opts={}, &block)
  return @cloud_provider if @cloud_provider
  klass_name = "CloudProviders::#{cloud_provider_name}".classify
  if provider_klass = CloudProviders.all.detect {|k| k.to_s == klass_name }
    opts.merge!(:cloud => self, :keypair_name => self.keypair.basename)
    @cloud_provider = provider_klass.new(dsl_options.merge(opts), &block)
  else
    raise PoolParty::PoolPartyError.create("UnknownCloudProviderError", "Unknown cloud_provider: #{cloud_provider_name}")
  end
  @cloud_provider
end

#compile(caller = nil) ⇒ Object

Take the cloud’s resources and compile them down using the defined (or the default dependency_resolver, chef)



198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/poolparty/cloud.rb', line 198

def compile(caller=nil)
  callback :before_compile
  cloud_provider.before_compile(self)
  FileUtils.mkdir_p tmp_path unless File.directory?(tmp_path)
  ddputs <<-EOE
Compiling cloud #{self.name} to #{tmp_path/"etc"/"#{dependency_resolver_name}"} 
  number of resources: #{ordered_resources.size}
  EOE
  out = dependency_resolver.compile_to(ordered_resources, tmp_path/"etc"/"#{dependency_resolver_name}", caller)
  cloud_provider.after_compile(self)
  callback :after_compile
  out
end

#configure!(opts = {}, threaded = true) ⇒ Object

convenience method to loop thru all the nodes and configure them



131
132
133
# File 'lib/poolparty/cloud.rb', line 131

def configure!(opts={}, threaded=true)
  nodes.collect{|n| n.configure! }
end

#contract!(hsh = {}) ⇒ Object

Contract the cloud



122
123
124
125
126
127
128
# File 'lib/poolparty/cloud.rb', line 122

def contract!(hsh={})
  inst=nodes(hsh).last
  inst.callback :before_terminate
  inst.terminate!
  inst.callback :after_terminate
  inst
end

#dependency_resolver(sym = nil) ⇒ Object

Set the dependency resolver



175
176
177
178
179
180
181
# File 'lib/poolparty/cloud.rb', line 175

def dependency_resolver(sym=nil)
  @dependency_resolver ||= case sym
  when :chef, nil
    dsl_options[:dependency_resolver_name] = :chef
    DependencyResolvers::Chef
  end
end

#describe_instance(o = {}) ⇒ Object



71
# File 'lib/poolparty/cloud.rb', line 71

def describe_instance(o={}); cloud_provider.describe_instance(o);end

#describe_instances(o = {}) ⇒ Object



70
# File 'lib/poolparty/cloud.rb', line 70

def describe_instances(o={}); cloud_provider.describe_instances(o);end

#ensure_meta_fun_are_resourcesObject



318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/poolparty/cloud.rb', line 318

def ensure_meta_fun_are_resources
  resources.each do |res|
    
    if res.meta_notifies
      res.meta_notifies.each do |ty, arr|
        arr.each do |nm, action|
          raise PoolPartyError.create("ResourceNotFound", "A resource required for #{ty}(#{nm}) was not found: #{ty}(#{nm}). Please make sure you've specified this in your configuration.") unless get_resource(ty, nm)
        end
      end
    end
    
  end
end

#ensure_not_cyclicObject



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/poolparty/cloud.rb', line 288

def ensure_not_cyclic
  if resources_graph.cyclic?
    cycles = []
    
    cycles = resources_graph.find_cycle
    cycle_string = cycles.map do |k,v|
      "#{k} -> #{v}"
    end
    
    filepath = "/tmp"
    format = "png"
    dotpath = "#{filepath}/dot.#{format}"
    resources_graph.write_to_graphic_file(format, filepath)
    
    `open #{dotpath}`
    msg =<<-EOE
  
    Your resource graph is cyclic. Two resources depend on each other, Cannot decide which resource
    to go first. Dying instead. Correct this and then try again.
  
      #{dotpath}
  
    Hint: You can see the resource graph by generating it with:
      cloud compile -g name
    
    EOE
    raise PoolPartyError.create("CyclicResourceGraphError", msg)
  end
end

#expand(opts = {}, &block) ⇒ Object

1.) Launches a new instance, 2.) Waits for the instance to get an ip address 3.) Waits for port ssh_port to be open 4.) Calls call_after_launch_instance callbacks 5.) Executes passed &block, if any 6.) Returns the new instance object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/poolparty/cloud.rb', line 97

def expand(opts={}, &block)
  timeout = opts.delete(:timeout) || 300
  callback :before_launch_instance
  instance = cloud_provider.run_instance(opts)
  instance.cloud = self
  @instance = instance
  #wait for an ip and then wait for ssh port, then configure instance
  if instance.wait_for_public_ip(timeout) && instance.wait_for_port(ssh_port, :timeout=>timeout)
    callback :after_launch_instance
    instance.callback :before_bootstrap
    instance.bootstrap!
    instance.callback :after_bootstrap
    instance.callback :before_configure
    instance.configure!
    instance.callback :after_configure
    block.call(instance) if block
    instance
  else
     raise StandardError.new("Instance port #{ssh_port} not available")
  end
  instance.refresh!
  instance
end

#form_cloudsObject

Form the cloud Run the init block with the init_opts on the cloud This is run after the cloud.rb file has been consumed



270
271
272
273
# File 'lib/poolparty/cloud.rb', line 270

def form_clouds
  run_with_callbacks(@init_opts, &@init_block)
  loaded!
end

#keypair(n = nil, extra_paths = []) ⇒ Object

returns an instance of Keypair You can pass either a filename which will be searched for in ~/.ec2/ and ~/.ssh/ Or you can pass a full filepath



53
54
55
# File 'lib/poolparty/cloud.rb', line 53

def keypair(n=nil, extra_paths=[])
  @keypair ||= Keypair.new(n, extra_paths)
end

#monitor(monitor_symbol, &block) ⇒ Object

MONITORS ### Create a new monitor on the cloud

Usage

monitor :cpu do |v|
  vote_for(:expand) if v > 0.8
end


235
236
237
# File 'lib/poolparty/cloud.rb', line 235

def monitor(monitor_symbol, &block)
  monitors[monitor_symbol.to_sym] ||= PoolParty::Monitor.new(monitor_symbol, &block)
end

#monitor_format(mon_name, meth = nil, &block) ⇒ Object



254
255
256
257
258
259
260
# File 'lib/poolparty/cloud.rb', line 254

def monitor_format(mon_name, meth=nil, &block)
  if monitors.has_key?(mon_name.to_sym)
    monitors[mon_name.to_sym].format(meth, &block)
  else
    raise PoolPartyError.create("MonitorsFormattingError", "You created a monitor format for an unknown monitor. Please check and try again!")
  end
end

#monitorsObject

Store the monitors in an array



250
251
252
# File 'lib/poolparty/cloud.rb', line 250

def monitors
  @monitors ||= {}
end

#nodes(o = {}) ⇒ Object

Cloud provider methods



67
# File 'lib/poolparty/cloud.rb', line 67

def nodes(o={}); delayed_action {cloud_provider.nodes(o).collect{|n| n.cloud = self; n}}; end

#os(sym = nil) ⇒ Object Also known as: platform

Get the os of the first node if it was not explicity defined, we’ll assume they are all homogenous



214
215
216
217
218
219
220
# File 'lib/poolparty/cloud.rb', line 214

def os(sym=nil)
  if sym
    dsl_options[:os] = sym
  else
    nodes.size > 0 ? nodes.first.os : dsl_options[:os]
  end
end

#poolObject

The pool this cloud belongs to



159
160
161
# File 'lib/poolparty/cloud.rb', line 159

def pool
  parent
end

#public_ipObject

The public_ip of the cloud is equivalent to the public_ip of the cloud’s oldest node



225
226
227
# File 'lib/poolparty/cloud.rb', line 225

def public_ip
  nodes.first.public_ip
end

#resolve_with(a) ⇒ Object

Resolve with the dependency resolver



166
167
168
169
170
171
172
# File 'lib/poolparty/cloud.rb', line 166

def resolve_with(a)
  if DependencyResolvers.const_defined?(a.classify)        
    dependency_resolver DependencyResolvers.module_eval("#{a.classify}")
  else
    raise PoolParty::PoolPartyError.create("DependencyResolverError", "Undefined dependency resolver: #{a}. Please specify one of the following: #{DependencyResolvers.all.join(", ")}")
  end
end

#run(commands, opts = {}) ⇒ Object

Run command/s on all nodes in the cloud. Returns a hash of instance_id=>result pairs



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/poolparty/cloud.rb', line 137

def run(commands, opts={})
  results = {}
  threads = nodes.collect do |n|
     Thread.new{ results[n.name] = n.run(commands, opts)  }
  end
  threads.each{ |aThread|  aThread.join }
  results
  # Serial implementation #
  # nodes.inject({})do |results, n|
  #   results[n.instance_id] = n.run(commands, opts)
  #   results
  # end
end

#run_instance(o = {}) ⇒ Object



68
# File 'lib/poolparty/cloud.rb', line 68

def run_instance(o={}); cloud_provider.run_instance(o);end

#run_monitor(monitor_name, value) ⇒ Object

Run the monitor logic



240
241
242
243
244
245
246
247
# File 'lib/poolparty/cloud.rb', line 240

def run_monitor(monitor_name, value)
  mon = monitors[monitor_name.to_sym]
  if mon
    mon.run(value)
  else
    "unhandled monitor"
  end
end

#terminate!Object

Terminate all instances in the cloud



74
75
76
# File 'lib/poolparty/cloud.rb', line 74

def terminate!
  nodes.collect{|n| n.terminate! }
end

#terminate_instance!(o = {}) ⇒ Object



69
# File 'lib/poolparty/cloud.rb', line 69

def terminate_instance!(o={}); cloud_provider.terminate_instance!(o);end

#tmp_pathObject

Temporary path Starts at the global default tmp path and appends the pool name and the cloud name



154
155
156
# File 'lib/poolparty/cloud.rb', line 154

def tmp_path
  Default.tmp_path / pool.name / name
end

#using(provider_symbol, o = {}, &block) ⇒ Object

Declare the CloudProvider for a cloud

Create an instance of the cloud provider this cloud is using


59
60
61
62
63
64
# File 'lib/poolparty/cloud.rb', line 59

def using(provider_symbol, o={}, &block)
  return @cloud_provider if @cloud_provider
  self.cloud_provider_name = provider_symbol
  cloud_provider(o, &block)
  cloud_provider.keypair(keypair.full_filepath)
end

#validate_all_resourcesObject



281
282
283
284
285
286
# File 'lib/poolparty/cloud.rb', line 281

def validate_all_resources
  ddputs("Validating all the resources")
  [:ensure_not_cyclic, :ensure_meta_fun_are_resources].each do |meth|
    self.send meth
  end
end