Module: Ironfan::KnifeCommon

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#brokerObject

Returns the value of attribute broker.



3
4
5
# File 'lib/chef/knife/ironfan_knife_common.rb', line 3

def broker
  @broker
end

#problemsObject

list of problems encountered



244
245
246
# File 'lib/chef/knife/ironfan_knife_common.rb', line 244

def problems
  @problems
end

Class Method Details

.included(base) ⇒ Object



292
293
294
295
296
# File 'lib/chef/knife/ironfan_knife_common.rb', line 292

def self.included(base)
  base.class_eval do
    extend ClassMethods
  end
end

.load_depsObject



5
6
7
8
9
10
11
# File 'lib/chef/knife/ironfan_knife_common.rb', line 5

def self.load_deps
  require 'formatador'
  require 'chef/node'
  require 'chef/api_client'
  require 'fog'
  require 'rbvmomi'
end

Instance Method Details

#all_computers(slice_string, *args) ⇒ Object



70
71
72
73
74
75
# File 'lib/chef/knife/ironfan_knife_common.rb', line 70

def all_computers(slice_string, *args)
  realm_name, cluster_name, facet_name, slice_indexes = pick_apart(slice_string, *args)
  computers = discover_computers(realm_name, cluster_name, facet_name, slice_indexes)
  ui.info("Loaded information for #{computers.size} computer(s) in cluster #{cluster_name}")
  computers
end

#bootstrapper(computer) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/chef/knife/ironfan_knife_common.rb', line 157

def bootstrapper(computer)
  server   = computer.server
  hostname = computer.dns_name
  #
  bootstrap = Chef::Knife::Bootstrap.new
  bootstrap.config.merge!(config)
  #
  bootstrap.name_args               = [ hostname ]
  bootstrap.config[:computer]       = computer
  bootstrap.config[:server]         = server
  bootstrap.config[:run_list]       = server.run_list
  bootstrap.config[:ssh_user]       = config[:ssh_user]       || computer.ssh_user
  bootstrap.config[:attribute]      = config[:attribute]
  bootstrap.config[:identity_file]  = config[:identity_file]  || computer.ssh_identity_file
  bootstrap.config[:distro]         = config[:distro]         || computer.bootstrap_distro
  bootstrap.config[:use_sudo]       = true unless config[:use_sudo] == false
  bootstrap.config[:chef_node_name] = server.full_name
  bootstrap.config[:client_key]     = ( computer.client.private_key rescue nil )
  #
  bootstrap
end

#configure_dry_runObject

Put Fog into mock mode if –dry_run



129
130
131
132
133
134
# File 'lib/chef/knife/ironfan_knife_common.rb', line 129

def configure_dry_run
  if config[:dry_run]
    Fog.mock!
    Fog::Mock.delay = 0
  end
end

#confirm_execution(*args) ⇒ Object

override in subclass to confirm risky actions



100
101
102
# File 'lib/chef/knife/ironfan_knife_common.rb', line 100

def confirm_execution(*args)
  # pass
end

#confirm_or_exit(question, correct_answer) ⇒ Object



235
236
237
238
239
240
241
# File 'lib/chef/knife/ironfan_knife_common.rb', line 235

def confirm_or_exit question, correct_answer
  response = ui.ask_question(question)
  unless response.chomp == correct_answer
    die "I didn't think so.", "Aborting!", 1
  end
  ui.info("")
end

#die(*args) ⇒ Object



272
273
274
# File 'lib/chef/knife/ironfan_knife_common.rb', line 272

def die *args
  Ironfan.die(*args)
end

#discover_computers(realm_name, cluster_name, facet_name, slice_indexes) ⇒ Object

Common code for calling broker.discover with an appropriately trimmed list of clusters to discover.



48
49
50
51
52
53
# File 'lib/chef/knife/ironfan_knife_common.rb', line 48

def discover_computers(realm_name, cluster_name, facet_name, slice_indexes)
  realm = Ironfan.load_realm(realm_name)
  realm.clusters.each{ |cluster| Ironfan.load_cluster cluster.name }
  clusters = cluster_name ? Array(realm.clusters[cluster_name.to_sym]) : realm.clusters.to_a
  return broker.discover!(clusters, config[:cloud])
end

#display(target, display_style = nil, &block) ⇒ Object

passes target to Broker::Conductor#display, will show headings in server slice tables based on the –verbose flag



121
122
123
124
# File 'lib/chef/knife/ironfan_knife_common.rb', line 121

def display(target, display_style=nil, &block)
  display_style ||= (config[:verbosity] == 0 ? :default : :expanded)
  broker.display(target, display_style, &block)
end

#exit_if_unhealthy!Object



252
253
254
255
256
257
258
259
260
261
262
# File 'lib/chef/knife/ironfan_knife_common.rb', line 252

def exit_if_unhealthy!
  return if healthy?
  problems.each do |problem|
    if problem.respond_to?(:call)
      problem.call
    else
      ui.warn(problem)
    end
  end
  exit(2) if not healthy?
end

#gemfile(realm_or_cluster_name) ⇒ Object



39
40
41
# File 'lib/chef/knife/ironfan_knife_common.rb', line 39

def gemfile(realm_or_cluster_name)
  "Gemfile.#{realm_or_cluster_name}"
end

#get_relevant_slice(*predicate) ⇒ Object

Get a slice of nodes matching the given filter

Examples:

target = get_relevant_slice(* @name_args)


110
111
112
113
114
115
116
117
# File 'lib/chef/knife/ironfan_knife_common.rb', line 110

def get_relevant_slice( *predicate )
  full_target = get_slice( *predicate )
  display(full_target) do |mach|
    rel = relevant?(mach)
    { :relevant? => (rel ? "[green]#{rel}[reset]" : '-' ) }
  end
  full_target.select{|mach| relevant?(mach) }
end

#get_slice(slice_string, *args) ⇒ Ironfan::ServerSlice

A slice of a cluster:

Returns:

  • (Ironfan::ServerSlice)

    the requested slice



60
61
62
63
64
65
66
67
68
# File 'lib/chef/knife/ironfan_knife_common.rb', line 60

def get_slice(slice_string, *args)
  realm_name, cluster_name, facet_name, slice_indexes = pick_apart(slice_string, *args)
  desc = predicate_str(realm_name, cluster_name, facet_name, slice_indexes)
  ui.info("Inventorying servers in #{desc}")
  computers = discover_computers(realm_name, cluster_name, facet_name, slice_indexes)
  
  Chef::Log.info("Inventoried #{computers.size} computers")      
  return computers.slice(cluster_name, facet_name, slice_indexes)
end

#has_problem(desc) ⇒ Object

register that a problem was encountered



246
247
248
# File 'lib/chef/knife/ironfan_knife_common.rb', line 246

def has_problem(desc)
  (@problems||=[]) << desc
end

#healthy?Boolean

healthy if no problems

Returns:

  • (Boolean)


250
# File 'lib/chef/knife/ironfan_knife_common.rb', line 250

def healthy?() problems.blank? ; end

#load_ironfanObject



13
14
15
16
17
18
19
20
21
22
# File 'lib/chef/knife/ironfan_knife_common.rb', line 13

def load_ironfan
  $LOAD_PATH << File.join(Chef::Config[:ironfan_path], '/lib') if Chef::Config[:ironfan_path]
  require 'ironfan'
  $stdout.sync = true
  Ironfan.ui           = self.ui
  self.config[:cloud]  = Chef::Config[:cloud] if Chef::Config.has_key?(:cloud)
  Ironfan.knife_config = self.config
  Ironfan.chef_config  = Chef::Config
  self.broker          = Ironfan.broker
end

#pick_apart(slice_string, *args) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/chef/knife/ironfan_knife_common.rb', line 77

def pick_apart(slice_string, *args)
  if not args.empty?
    slice_string = [slice_string, args].flatten.join("-")
    ui.info("")
    ui.warn("Please specify server slices joined by dashes and not separate args:\n\n  knife cluster #{sub_command} #{slice_string}\n\n")
  end
  slice_string.split(/[\s\-]/, 4)
end

#predicate_str(realm_name, cluster_name, facet_name, slice_indexes) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/chef/knife/ironfan_knife_common.rb', line 86

def predicate_str(realm_name, cluster_name, facet_name, slice_indexes)
  [ "#{ui.color(realm_name, :bold)} realm",
    (cluster_name  ? "#{ui.color(cluster_name, :bold)} cluster"  : "#{ui.color("all", :bold)} clusters"),
    (facet_name    ? "#{ui.color(facet_name, :bold)} facet"      : "#{ui.color("all", :bold)} facets"),
    (slice_indexes ? "servers #{ui.color(slice_indexes, :bold)}" : "#{ui.color("all", :bold)} servers")
  ].join(', ')
end

#progressbar_for_threads(threads) ⇒ Object

Show a pretty progress bar while we wait for a set of threads to finish.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/chef/knife/ironfan_knife_common.rb', line 137

def progressbar_for_threads(threads)
  section "Waiting for servers:"
  total      = threads.length
  remaining  = threads.select(&:alive?)
  start_time = Time.now
  until remaining.empty?
    remaining = remaining.select(&:alive?)
    if config[:verbose]
      ui.info "waiting: #{total - remaining.length} / #{total}, #{(Time.now - start_time).to_i}s"
      sleep 5
    else
      Formatador.redisplay_progressbar(total - remaining.length, total, {:started_at => start_time })
      sleep 1
    end
  end
  # Collapse the threads
  threads.each(&:join)
  ui.info ''
end

#relevant?(computer) ⇒ Boolean

method to nodes should be filtered on

Returns:

  • (Boolean)


95
96
97
# File 'lib/chef/knife/ironfan_knife_common.rb', line 95

def relevant?(computer)
  computer.running?
end

#runObject



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/chef/knife/ironfan_knife_common.rb', line 24

def run()
  gemfile_v = gemfile(@name_args.first.to_s.split(/[_-]/).first)

  if ENV['BUNDLE_GEMFILE'] == gemfile_v
    _run
  elsif not File.exist?(gemfile_v)
    ui.info("no realm-specific Gemfile found. using default Gemfile.")
    _run
  else
    cmd = "bundle exec knife #{ARGV.join(' ')}"
    ui.info("re-running `#{cmd}` with BUNDLE_GEMFILE=#{gemfile_v}")
    return Bundler.clean_exec({'BUNDLE_GEMFILE' => gemfile_v}, cmd)
  end
end

#run_bootstrap(computer) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/chef/knife/ironfan_knife_common.rb', line 179

def run_bootstrap(computer)
  bs = bootstrapper(computer)
  if config[:skip].to_s == 'true'
    ui.info "Skipping: bootstrap #{computer.name} with #{JSON.pretty_generate(bs.config)}"
    return
  end
  #
  Ironfan.step(computer.name, "Running bootstrap")
  Chef::Log.info("Bootstrapping:\n  Computer #{computer}\n  Bootstrap config #{bs.config}")
  Ironfan.safely([computer, bs.config].inspect) do
    bs.run
  end
end

#section(desc, *style) ⇒ Object

Announce a new section of tasks



267
268
269
270
# File 'lib/chef/knife/ironfan_knife_common.rb', line 267

def section(desc, *style)
  style = [:blue] if style.empty?
  ui.info(ui.color(desc, *style))
end

#sub_commandObject

Utilities



231
232
233
# File 'lib/chef/knife/ironfan_knife_common.rb', line 231

def sub_command
  self.class.sub_command
end

#wait_for_ssh(computer) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/chef/knife/ironfan_knife_common.rb', line 193

def wait_for_ssh(computer)
  ssh = Chef::Knife::Ssh.new
  ssh.ui = ui
  ssh.name_args = [ computer.name, "ls" ]
  ssh.config[:ssh_user] = Chef::Config[:knife][:ssh_user] || config[:ssh_user]
  ssh.config[:ssh_password] = config[:ssh_password]
  ssh.config[:ssh_port] = Chef::Config[:knife][:ssh_port] || config[:ssh_port]
  ssh.config[:ssh_gateway] = Chef::Config[:knife][:ssh_gateway] || config[:ssh_gateway]
  ssh.config[:forward_agent] = Chef::Config[:knife][:forward_agent] || config[:forward_agent]
  ssh.config[:identity_file] = Chef::Config[:knife][:identity_file] || config[:identity_file]
  ssh.config[:manual] = true
  ssh.config[:host_key_verify] = Chef::Config[:knife][:host_key_verify] || config[:host_key_verify]
  ssh.config[:on_error] = :raise
  session = ssh.session
  return true
rescue Errno::ETIMEDOUT
  Chef::Log.debug("ssh to #{computer.name} timed out")
  return false
rescue Errno::ECONNREFUSED
  Chef::Log.debug("ssh connection to #{computer.name} refused")
  yield
  return false
rescue Errno::EHOSTUNREACH
  Chef::Log.debug("ssh host #{computer.name} unreachable")
  yield
  return false
rescue 
  Chef::Log.debug("something else went wrong while wating for ssh host #{computer.name}")
  raise
  return false
else
  session && session.close
  session = nil
end