Class: Chef::Knife::SoftlayerServerCreate

Inherits:
Chef::Knife
  • Object
show all
Includes:
SoftlayerBase
Defined in:
lib/chef/knife/softlayer_server_create.rb

Constant Summary

Constants included from SoftlayerBase

Chef::Knife::SoftlayerBase::USER_AGENT

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from SoftlayerBase

#compute, #connection, included, #locate_config_value, #msg_pair, #network

Instance Attribute Details

#cciObject (readonly)

Returns the value of attribute cci.



15
16
17
# File 'lib/chef/knife/softlayer_server_create.rb', line 15

def cci
  @cci
end

Instance Method Details

#apply_tags(instance) ⇒ Object



430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/chef/knife/softlayer_server_create.rb', line 430

def apply_tags(instance)
  Proc.new do
    chef = Chef::Search::Query.new
    chef.search('node', "name:#{locate_config_value(:chef_node_name) || instance.id}") do |n|
      config[:tags] = [] if config[:tags].nil? # we're going to tag every Chef node with the SL id no matter what
      config[:tags] << "slid=#{instance.id}"
      config[:tags].each do |tag|
        n.tag(tag)
      end
      n.save
    end
  end
end

#linux_bootstrap(instance) ⇒ Chef::Knife::Bootstrap

Parameters:

  • instance (Hash)

Returns:

  • (Chef::Knife::Bootstrap)


367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/chef/knife/softlayer_server_create.rb', line 367

def linux_bootstrap(instance)
  bootstrap = Chef::Knife::Bootstrap.new
  instance.ssh_ip_address = instance.private_ip_address if config[:private_network_only]
  bootstrap.name_args = [instance.ssh_ip_address]
  bootstrap.config[:ssh_user] = config[:ssh_user]
  bootstrap.config[:ssh_password] = config[:ssh_password] if config[:ssh_password]
  bootstrap.config[:identity_file] = config[:identity_file] if config[:identity_file]
  bootstrap.config[:ssh_port] = config[:ssh_port]
  bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
  bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || instance.id
  bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
  bootstrap.config[:host_key_verify] = config[:host_key_verify]
  shared_bootstrap(bootstrap)
end

#progress(proc) ⇒ Object



407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/chef/knife/softlayer_server_create.rb', line 407

def progress(proc)
  t = Thread.new { Thread.current[:output] = proc.call }
  i = 0
  while t.alive?
    sleep 0.5
    putc('.')
    i += 1
    putc("\n") if i == 76
    i = 0 if i == 76
  end
  putc("\n")
  t.join
  t[:output]
end

#runnil

Run the procedure to create a SoftLayer VM and bootstrap it.

Returns:

  • (nil)

Raises:



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/chef/knife/softlayer_server_create.rb', line 249

def run
  $stdout.sync = true
  config.merge!(slurp_from_file(config[:from_file])) if config[:from_file]

  # TODO: Windows support.
  raise SoftlayerServerCreateError, "#{ui.color("Windows VMs not currently supported.", :red)}" if config[:os_code] =~ /^WIN_/
  raise SoftlayerServerCreateError, "#{ui.color("identity file (-i) option is incompatible with password (-P) option required.", :red)}" if !!config[:identity_file] and !!config[:ssh_password]
  raise SoftlayerServerCreateError, "#{ui.color("--new-global-ip value must be 'v4' or 'v6'.", :red)}" if config[:new_global_ip] and !config[:new_global_ip].to_s.match(/^v[4,6]$/i)

  # TODO: create a pre-launch method for clean up tasks.
  # TODO: create a pre-launch method for clean up tasks.
  config[:vlan] = config[:vlan].to_i if config[:vlan]
  config[:private_vlan] = config[:private_vlan].to_i if config[:private_vlan]
  Fog.credentials[:private_key_path] = config[:identity_file] if config[:identity_file]
  # TODO: create a pre-launch method for clean up tasks.
  # TODO: create a pre-launch method for clean up tasks.

  opts = {
      :flavor => :flavor_id,
      :hostname => :name,
      :domain => nil,
      :cores => :cpu,
      :os_code => nil,
      :ram => nil,
      :block_storage => :disk,
      :local_storage => :ephemeral_storage,
      :datacenter => nil,
      :ssh_keys => :key_pairs,
      :vlan => nil,
      :private_vlan => nil,
      :image_id => nil,
      :private_network_only => nil,
      #:tags => nil
  }


  opts.keys.each do |opt|
    if opts[opt].nil?
      opts[opt] = config[opt]
    else
      opts[opts.delete(opt)] = config[opt]  # clever shit like this is why I like programming :-]
    end
  end

  # FIXME: make the above deal with nested opts and get rid of this one-off
  opts[:network_components] = [ {:speed => config[:nic]} ] if !!config[:nic]

  opts.delete_if { |k,v| v.nil? }

  puts ui.color("Launching SoftLayer VM, this may take a few minutes.", :green)
  instance = connection.servers.create(opts)
  if config[:private_network_only]
    instance.ssh_ip_address = Proc.new {|server| server.private_ip_address }
  end
  progress Proc.new { instance.wait_for { ready? and sshable? } }
  putc("\n")

  if config[:tags]
    puts ui.color("Applying tags to SoftLayer instance.", :green)
    progress Proc.new { instance.add_tags(config[:tags]) }
    putc("\n")
  end


  if config[:new_global_ip] || config[:assign_global_ip]
    if config[:new_global_ip] # <— the value of this will be v4 or v6
      begin
        puts ui.color('Provisioning new Global IP' + config[:new_global_ip].downcase + ', this may take a few minutes.', :green)
        create_global_ip =  Proc.new do
          existing_records = connection(:network).get_global_ip_records.body
          connection(:network).send('create_new_global_ip' + config[:new_global_ip].downcase) or raise SoftlayerServerCreateError, "Unable to create new Global IP Address.";
          sleep 20 # if we look for the new record too quickly it won't be there yet...
          new_record_global_id = (connection(:network).get_global_ip_records.body - existing_records).reduce['id']
          connection(:network).ips.select { |ip| ip.global_id == new_record_global_id }.reduce
        end
        global_ip = progress(create_global_ip) or raise SoftlayerServerCreateError, "Error encountered creating Global IP Address."
        puts ui.color('Global IP Address successfully created.', :green)
      rescue SoftlayerServerCreateError => e
        puts ui.color('We have encountered a problem ordering the requested global IP.  The transaction may not have completed.', :red)
        puts ui.color(e.message, :yellow)
      end
    end

    if config[:assign_global_ip]
      case config[:assign_global_ip].to_s
        #ipv4
        when /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/
          global_ip = connection(:network).ips.by_address(config[:assign_global_ip])
        #ipv6
        when /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
          global_ip = connection(:network).ips.by_address(config[:assign_global_ip])
        else
          raise SoftlayerServerCreateError, "--assign-global-ip value must be valid IPv4 or IPv6 address"
      end
      global_ip or raise SoftlayerServerCreateError, "Global IP address not found.  Please check the address or id supplied and try again."
      global_ip.reload
    end

    route_global_ip = Proc.new do
      puts ui.color('Routing Global IP Address to Instance.', :green)
      global_ip.route(connection(:network).ips.by_address(instance.public_ip_address)) or raise SoftlayerServerCreateError, "Global IP address failed to route."
      puts ui.color('Global IP Address has been assigned.', :green)
      puts ui.color('Global IP Address will not function without networking rules on the endpoint operating system.  See http://knowledgelayer.softlayer.com/learning/global-ip-addresses for details.', :yellow)
    end
    progress(route_global_ip)

  end

  puts ui.color('Bootstrapping Chef node, this may take a few minutes.', :green)
  linux_bootstrap(instance).run

  puts ui.color("Applying tags to Chef node.", :green)
  progress apply_tags(instance)

end

#shared_bootstrap(bootstrap) ⇒ Chef::Knife::Bootstrap

Parameters:

  • bootstrap (Chef::Knife::Bootstrap)

Returns:

  • (Chef::Knife::Bootstrap)


384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/chef/knife/softlayer_server_create.rb', line 384

def shared_bootstrap(bootstrap)
  bootstrap.config[:run_list] = config[:run_list]
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
  bootstrap.config[:distro] = locate_config_value(:distro)
  bootstrap.config[:template_file] = locate_config_value(:template_file)
  bootstrap.config[:environment] = locate_config_value(:environment)
  bootstrap.config[:prerelease] = config[:prerelease]
  bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
  bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
  bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
  bootstrap.config[:secret] = locate_config_value(:secret)
  bootstrap.config[:secret_file] = locate_config_value(:secret_file)
  bootstrap.config[:tags] = locate_config_value(:tags)
  bootstrap.config[:fqdn] = locate_config_value(:fqdn)
  Chef::Config[:knife][:hints] ||= {}
  Chef::Config[:knife][:hints]['softlayer'] ||= {}
  bootstrap
end

#slurp_from_file(path) ⇒ Object



422
423
424
425
426
427
428
# File 'lib/chef/knife/softlayer_server_create.rb', line 422

def slurp_from_file(path)
  args = JSON.parse(IO.read(path))
  args.keys.each { |key| args[key.gsub('-', '_').to_sym] = args.delete(key) }
  # TODO: Something less ugly than the empty rescue block below.  The :proc Procs/Lambdas aren't idempotent...
  args.keys.each { |key| begin; args[key] = options[key][:proc] ? options[key][:proc].call(args[key]) : args[key]; rescue; end }
  args
end

#windows_bootstrap(server, fqdn) ⇒ Object



403
404
405
# File 'lib/chef/knife/softlayer_server_create.rb', line 403

def windows_bootstrap(server, fqdn)
  # TODO: Windows support....
end