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, #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



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

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



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

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



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

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

#before_compileObject



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

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



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

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)



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

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



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

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

#contract!(hsh = {}) ⇒ Object

Contract the cloud



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

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



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

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



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

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

#describe_instances(o = {}) ⇒ Object



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

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

#ensure_meta_fun_are_resourcesObject



313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/poolparty/cloud.rb', line 313

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



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

def ensure_not_cyclic
  if resources_graph.cyclic?
    cycles = []
    
    resources_graph.edges.each do |edge|
      if resources_graph.adjacent?(edge.source, edge.target) && resources_graph.adjacent?(edge.target, edge.source) && !cycles.include?(edge.source)
        cycles << "#{edge.source.class}(#{edge.source.name}) depends on #{edge.target.class}(#{edge.target.name})"
      end
    end
    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.
  
      #{cycles.join("\n          ")}
  
    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



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

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



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

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

#keypair(n = nil) ⇒ 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



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

def keypair(n=nil)
  @keypair ||= Keypair.new(n)
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


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

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



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

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



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

def monitors
  @monitors ||= {}
end

#nodes(o = {}) ⇒ Object

Cloud provider methods



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

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



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

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



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

def pool
  parent
end

#public_ipObject

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



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

def public_ip
  nodes.first.public_ip
end

#resolve_with(a) ⇒ Object

Resolve with the dependency resolver



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

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



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

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



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

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

#run_monitor(monitor_name, value) ⇒ Object

Run the monitor logic



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

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



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

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

#terminate_instance!(o = {}) ⇒ Object



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

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



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

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


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

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



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

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