Class: Bfire::Group

Inherits:
Object
  • Object
show all
Includes:
PubSub::Publisher, Enumerable
Defined in:
lib/bfire/group.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PubSub::Publisher

#error?, #hooks, included, #on, #trigger, #triggered_events

Constructor Details

#initialize(engine, name, options = {}) ⇒ Group

Returns a new instance of Group.

Raises:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/bfire/group.rb', line 16

def initialize(engine, name, options = {})
  @engine = engine
  @name = name
  @tag = options.delete(:tag)
  raise Error, "Tag name can't contain two or more consecutive dashes" if @tag && @tag =~ /-{2,}/
  @options = options
  @listeners = {}
  @dependencies = []

  @templates = []
  @default_template = Template.new(self, :default)
  @current_template = @default_template

  raise Error, "Group name can only contain [a-zA-Z0-9] characters" if name !~ /[a-z0-9]+/i

  on(:error) {|group| Thread.current.group.list.each{|t|
      t[:ko] = true
      t.kill
    }
  }
  on(:ready) {|group|
    group.engine.logger.info "#{group.banner}All VMs are now READY: #{computes.map{|vm|
      [vm['name'], (vm['nic'] || []).map{|n| n['ip']}.inspect].join("=")
    }.join("; ")}"
  }
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Delegates every unknown method to the current Template, except #conf.



90
91
92
93
94
95
96
# File 'lib/bfire/group.rb', line 90

def method_missing(method, *args, &block)
  if method == :conf
    engine.send(method, *args, &block)
  else
    @current_template.send(method, *args, &block)
  end
end

Instance Attribute Details

#dependenciesObject (readonly)

Returns the value of attribute dependencies.



11
12
13
# File 'lib/bfire/group.rb', line 11

def dependencies
  @dependencies
end

#engineObject (readonly)

Returns the value of attribute engine.



9
10
11
# File 'lib/bfire/group.rb', line 9

def engine
  @engine
end

#nameObject (readonly)

Returns the value of attribute name.



10
11
12
# File 'lib/bfire/group.rb', line 10

def name
  @name
end

#tagObject (readonly)

A free-form text tag to add to every compute name of this group.



14
15
16
# File 'lib/bfire/group.rb', line 14

def tag
  @tag
end

#templatesObject (readonly)

Returns the value of attribute templates.



12
13
14
# File 'lib/bfire/group.rb', line 12

def templates
  @templates
end

Instance Method Details

#at(location, &block) ⇒ Object



111
112
113
114
115
116
# File 'lib/bfire/group.rb', line 111

def at(location, &block)
  t = template(location)
  @current_template = t
  instance_eval(&block) unless block.nil?
  @current_template = @default_template
end

Helpers =



136
137
138
# File 'lib/bfire/group.rb', line 136

def banner
  "[#{name}] "
end

#check!Object



208
209
210
211
212
213
# File 'lib/bfire/group.rb', line 208

def check!
  check_templates!
  if provider && !provider.valid?
    raise Error, "#{banner}#{provider.errors.map(&:inspect).join(", ")}"
  end
end

#computesObject



146
147
148
# File 'lib/bfire/group.rb', line 146

def computes
  templates.map{|t| t.instances}.flatten
end

#depends_on(group_name, &block) ⇒ Object



118
119
120
# File 'lib/bfire/group.rb', line 118

def depends_on(group_name, &block)
  @dependencies.push [group_name, block]
end

#each(*args, &block) ⇒ Object

Iterates over the collection of compute resources. Required for the Enumerable module.



142
143
144
# File 'lib/bfire/group.rb', line 142

def each(*args, &block)
  computes.each(*args, &block)
end

#inspectObject



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/bfire/group.rb', line 163

def inspect
  s = "#<#{self.class.name}:0x#{object_id.to_s(16)}"
  s << " #{banner}" if banner
  s << "VMs: "
  s << computes.map{|vm|
    [vm['name'].inspect, (vm['nic'] || []).map{|n|
      n['ip']
    }.inspect].join("=")
  }.join("; ")
  s << ">"
end

#launch_initial_resourcesObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/bfire/group.rb', line 43

def launch_initial_resources
  merge_templates!
  engine.logger.debug "#{banner}Merged templates=#{templates.inspect}"
  check!
  if rule.launch_initial_resources
    trigger :launched
    true
  else
    trigger :error
    false
  end
rescue Exception => e
  engine.logger.error "#{banner}#{e.class.name}: #{e.message}"
  engine.logger.debug e.backtrace.join("; ")
  trigger :error
end

#merge_templates!Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/bfire/group.rb', line 215

def merge_templates!
  default = @default_template
  if engine.conf[:authorized_keys]
    default.context :authorized_keys => File.read(
      File.expand_path(engine.conf[:authorized_keys])
    )
  end
  if @templates.empty?
    @templates.push template(:any)
  end
  templates.each{|t|
    t.merge_defaults!(default).resolve!
  }
end

#monitorObject



60
61
62
63
64
65
66
67
# File 'lib/bfire/group.rb', line 60

def monitor
  rule.manage(computes)
  rule.monitor
rescue Exception => e
  engine.logger.error "#{banner}#{e.class.name}: #{e.message}"
  engine.logger.debug e.backtrace.join("; ")
  trigger :error
end

#provider(selected_provider = nil, options = {}) ⇒ Object

Define the provider to use to provision the compute resources (Puppet, Chef…). If selected_provider is nil, returns the current provider.



125
126
127
128
129
# File 'lib/bfire/group.rb', line 125

def provider(selected_provider = nil, options = {})
  return @provider if selected_provider.nil?
  options[:modules] = engine.path_to(options[:modules]) if options[:modules]
  @provider = Provider::Puppet.new(options)
end

#provision!(vms) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/bfire/group.rb', line 69

def provision!(vms)
  return true if provider.nil?
  engine.logger.info "#{banner}Provisioning..."
  vms.all?{|vm|
    provisioned = false
    ip = vm['nic'][0]['ip']
    engine.ssh(ip, 'root') {|s|
      provisioned = unless provider.install(s)
        engine.logger.error "Failed to install provider on #{vm.inspect} (IP=#{ip})."
        false
      else
        result = provider.run(s) do |stream|
          engine.logger.info "#{banner}[#{ip}] #{stream}"
        end
      end
    }
    provisioned
  }
end

#reloadObject



175
176
177
# File 'lib/bfire/group.rb', line 175

def reload
  each(&:reload)
end

#ruleObject

Group-only methods =



102
103
104
# File 'lib/bfire/group.rb', line 102

def rule
  @rule ||= Rule.new(self, :initial => 1, :range => 1..1)
end

#scale(range, options = {}) ⇒ Object

Defines the scaling rule for this group



107
108
109
# File 'lib/bfire/group.rb', line 107

def scale(range, options = {})
  @rule = Rule.new(self, options.merge(:range => range))
end

#ssh_accessible?(vms) ⇒ Boolean

Returns:

  • (Boolean)


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/bfire/group.rb', line 179

def ssh_accessible?(vms)
  vms.all?{|compute|
    begin
      ip = compute['nic'][0]['ip']
      Timeout.timeout(30) do
        engine.ssh(ip, 'root', :log => false) {|s|
          s.exec!("hostname")
        }
      end
      true
    rescue Exception => e
      engine.logger.debug "#{banner}Can't SSH yet to #{compute.signature} at IP=#{ip.inspect}. Reason: #{e.class.name}, #{e.message}. Will retry later."
      false
    end
  }
end

#take(how_many = :all) ⇒ Object

Return the first how_many compute resources of the group.



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/bfire/group.rb', line 151

def take(how_many = :all)
  case how_many
  when :all
    computes
  when :first
    computes[0]
  else
    raise ArgumentError, "You must pass :all, :first, or a Fixnum" unless how_many.kind_of?(Fixnum)
    computes.take(how_many)
  end
end

#template(location) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
# File 'lib/bfire/group.rb', line 196

def template(location)
  t = @templates.find{|t| t.name == location}
  if t.nil?
    t = Template.new(
      self,
      location
    )
    @templates.push(t)
  end
  t
end