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



446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/chef/knife/softlayer_server_create.rb', line 446

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)


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

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



423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/chef/knife/softlayer_server_create.rb', line 423

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:



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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/chef/knife/softlayer_server_create.rb', line 265

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,
      :user_data => 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] || config[:use_private_network]
    instance.ssh_ip_address = Proc.new {|server| server.private_ip_address }
  end
	progress Proc.new { instance.wait_for(timeout=config[:wait_for_timeout].to_i) { 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)


400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/chef/knife/softlayer_server_create.rb', line 400

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



438
439
440
441
442
443
444
# File 'lib/chef/knife/softlayer_server_create.rb', line 438

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



419
420
421
# File 'lib/chef/knife/softlayer_server_create.rb', line 419

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