Module: Ironfan::KnifeCommon

Included in:
Chef::Knife::ClusterDiff, Chef::Knife::ClusterLaunch, Chef::Knife::ClusterList, Chef::Knife::ClusterPry, Chef::Knife::ClusterShow, Chef::Knife::ClusterSsh, Chef::Knife::EnvironmentFromRealm, Script
Defined in:
lib/chef/knife/ironfan_knife_common.rb

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Instance Attribute Details

- (Object) broker

Returns the value of attribute broker



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

def broker
  @broker
end

- (Object) problems

list of problems encountered



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

def problems
  @problems
end

Class Method Details

+ (Object) included(base)



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

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

+ (Object) load_deps



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

- (Object) all_computers(slice_string, *args)



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

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

- (Object) bootstrapper(computer)



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

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

- (Object) configure_dry_run

Put Fog into mock mode if –dry_run



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

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

- (Object) confirm_execution(*args)

override in subclass to confirm risky actions



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

def confirm_execution(*args)
  # pass
end

- (Object) confirm_or_exit(question, correct_answer)



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

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

- (Object) die(*args)



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

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

- (Object) discover_computers(realm_name, cluster_name, facet_name, slice_indexes)

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



48
49
50
51
52
53
54
# 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 }
  realm.resolve!
  clusters = cluster_name ? Array(realm.clusters[cluster_name.to_sym]) : realm.clusters.to_a
  return broker.discover!(clusters, config[:cloud])
end

- (Object) display(target, display_style = nil, &block)

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



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

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

- (Object) exit_if_unhealthy!



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

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

- (Object) gemfile(realm_or_cluster_name)



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

- (Object) get_relevant_slice(*predicate)

Get a slice of nodes matching the given filter

Examples:

target = get_relevant_slice(* @name_args)


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

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

- (Ironfan::ServerSlice) get_slice(slice_string, *args)

A slice of a cluster:

Returns:

  • (Ironfan::ServerSlice)

    the requested slice



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

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

- (Object) has_problem(desc)

register that a problem was encountered



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

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

- (Boolean) healthy?

healthy if no problems

Returns:

  • (Boolean)


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

def healthy?() problems.blank? ; end

- (Object) load_ironfan



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

- (Object) pick_apart(slice_string, *args)



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

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

- (Object) predicate_str(realm_name, cluster_name, facet_name, slice_indexes)



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

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

- (Object) progressbar_for_threads(threads)

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



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

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

- (Boolean) relevant?(computer)

method to nodes should be filtered on

Returns:

  • (Boolean)


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

def relevant?(computer)
  computer.running?
end

- (Object) run



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

- (Object) run_bootstrap(computer)



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

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

- (Object) section(desc, *style)

Announce a new section of tasks



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

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

- (Object) sub_command

Utilities



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

def sub_command
  self.class.sub_command
end

- (Object) wait_for_ssh(computer)



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
227
# File 'lib/chef/knife/ironfan_knife_common.rb', line 194

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