class Chef::Knife::RackspaceServerCreate

Attributes

initial_sleep_delay[RW]

Public Instance Methods

bootstrap_common_params(bootstrap, server) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 606
def bootstrap_common_params(bootstrap, server)
  bootstrap.config[:environment] = config[:environment]
  bootstrap.config[:run_list] = config[:run_list]
  if version_one?
    bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
  else
    bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.name
  end
  bootstrap.config[:prerelease] = locate_config_value(:prerelease)
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
  bootstrap.config[:bootstrap_template] = locate_config_value(:bootstrap_template)
  bootstrap.config[:first_boot_attributes] = locate_config_value(:first_boot_attributes)
  bootstrap.config[:bootstrap_proxy] = locate_config_value(:bootstrap_proxy)
  bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:secret)
  bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:secret_file)
  bootstrap.config[:secret] = locate_config_value(:secret)
  bootstrap.config[:secret_file] = locate_config_value(:secret_file)

  Chef::Config[:knife][:hints] ||= {}
  Chef::Config[:knife][:hints]["rackspace"] ||= {}
  bootstrap
end
bootstrap_for_node(server, bootstrap_ip_address) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 590
def bootstrap_for_node(server, bootstrap_ip_address)
  bootstrap = Chef::Knife::Bootstrap.new
  bootstrap.name_args = [bootstrap_ip_address]
  bootstrap.config[:ssh_user] = locate_config_value(:ssh_user) || "root"
  bootstrap.config[:ssh_password] = server.password
  bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
  bootstrap.config[:identity_file] = locate_config_value(:identity_file)
  bootstrap.config[:host_key_verify] = locate_config_value(:host_key_verify)
  bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file) if locate_config_value(:bootstrap_vault_file)
  bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json) if locate_config_value(:bootstrap_vault_json)
  bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item) if locate_config_value(:bootstrap_vault_item)
  # bootstrap will run as root...sudo (by default) also messes up Ohai on CentOS boxes
  bootstrap.config[:use_sudo] = true unless locate_config_value(:ssh_user) == "root"
  bootstrap_common_params(bootstrap, server)
end
bootstrap_for_windows_node(server, bootstrap_ip_address) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 629
def bootstrap_for_windows_node(server, bootstrap_ip_address)
  bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
  bootstrap.name_args = [bootstrap_ip_address]
  bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || "Administrator"
  bootstrap.config[:winrm_password] = locate_config_value(:winrm_password) || server.password
  bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
  bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
  bootstrap.config[:distro] = locate_config_value(:distro) || "windows-chef-client-msi"
  bootstrap_common_params(bootstrap, server)
end
encode_file(file) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 342
def encode_file(file)
  begin
    filename = File.expand_path(file)
    content = File.read(filename)
  rescue Errno::ENOENT => e
    ui.error "Unable to read source file - #{filename}"
    exit 1
  end
  Base64.encode64(content)
end
files() click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 353
def files
  return {} unless Chef::Config[:knife][:file]

  files = []
  Chef::Config[:knife][:file].each do |arg|
    dest, src = parse_file_argument(arg)
    Chef::Log.debug("Inject file #{src} into #{dest}")
    files << {
      path: dest,
      contents: encode_file(src),
    }
  end
  files
end
load_winrm_deps() click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 301
def load_winrm_deps
  require "winrm"
  require "chef/knife/winrm"
  require "chef/knife/bootstrap_windows_winrm"
  require "chef/knife/core/windows_bootstrap_context"
end
parse_file_argument(arg) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 333
def parse_file_argument(arg)
  dest, src = arg.split("=")
  unless dest && src
    ui.error "Unable to process file arguments #{arg}. The --file option requires both the destination on the remote machine as well as the local source be supplied using the form DESTINATION-PATH=SOURCE-PATH"
    exit 1
  end
  [dest, src]
end
run() click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 390
def run
  $stdout.sync = true

  server_create_options = {
    metadata: locate_config_value(:rackspace_metadata),
    disk_config: locate_config_value(:rackspace_disk_config),
    user_data: user_data,
    config_drive: locate_config_value(:rackspace_config_drive) || false,
    personality: files,
    key_name: locate_config_value(:rackspace_ssh_keypair),
    name: get_node_name(config[:chef_node_name] || config[:server_name]),
    networks: get_networks(locate_config_value(:rackspace_networks), locate_config_value(:rackconnect_v3_network_id)),
  }

  # Maybe deprecate this option at some point
  config[:bootstrap_network] = "private" if locate_config_value(:private_network)

  flavor_id = locate_config_value(:flavor)
  flavor = connection.flavors.get(flavor_id)
  if !flavor
    ui.error("Invalid Flavor ID: #{flavor_id}")
    exit 1
  else
    server_create_options[:flavor_id] = flavor.id
  end

  # This is somewhat a hack, but Rackspace's API returns '0' for flavors
  # that must be backed by a CBS volume.
  #
  # In the case we are trying to create one of these flavors, we should
  # swap out the image_id argument with the boot_image_id argument.
  if flavor.disk == 0
    server_create_options[:image_id] = ""
    server_create_options[:boot_volume_id] = locate_config_value(:boot_volume_id)
    server_create_options[:boot_image_id] = locate_config_value(:image)
    server_create_options[:boot_volume_size] = locate_config_value(:boot_volume_size)

    if server_create_options[:boot_image_id] && server_create_options[:boot_volume_id]
      ui.error("Please specify either --boot-volume-id (-B) or --image (-I)")
      exit 1
    end
  else
    server_create_options[:image_id] = locate_config_value(:image)

    unless server_create_options[:image_id]
      ui.error("Please specify an Image ID for the server with --image (-I)")
      exit 1
    end
  end

  if locate_config_value(:bootstrap_protocol) == "winrm"
    load_winrm_deps
  end

  server = connection.servers.new(server_create_options)

  if version_one?
    server.save
  else
    server.save(networks: server_create_options[:networks])
  end

  rackconnect_wait = locate_config_value(:rackconnect_wait)
  rackspace_servicelevel_wait = locate_config_value(:rackspace_servicelevel_wait)

  msg_pair("Instance ID", server.id)
  msg_pair("Host ID", server.host_id)
  msg_pair("Name", server.name)
  msg_pair("Flavor", server.flavor.name)
  msg_pair("Image", server.image.name) if server.image
  msg_pair("Boot Image ID", server.boot_image_id) if server.boot_image_id
  msg_pair("Metadata", server.metadata.all)
  msg_pair("ConfigDrive", server.config_drive)
  msg_pair("UserData", locate_config_value(:rackspace_user_data))
  msg_pair("RackConnect Wait", rackconnect_wait ? "yes" : "no")
  msg_pair("RackConnect V3", locate_config_value(:rackconnect_v3_network_id) ? "yes" : "no")
  msg_pair("ServiceLevel Wait", rackspace_servicelevel_wait ? "yes" : "no")
  msg_pair("SSH Key", locate_config_value(:rackspace_ssh_keypair))

  # wait for it to be ready to do stuff
  begin
    server.wait_for(Integer(locate_config_value(:server_create_timeout))) do
      print "."
      Chef::Log.debug("#{progress}%")

      if rackconnect_wait && rackspace_servicelevel_wait
        Chef::Log.debug("rackconnect_automation_status: #{metadata.all["rackconnect_automation_status"]}")
        Chef::Log.debug("rax_service_level_automation: #{metadata.all["rax_service_level_automation"]}")
        ready? && metadata.all["rackconnect_automation_status"] == "DEPLOYED" && metadata.all["rax_service_level_automation"] == "Complete"
      elsif rackconnect_wait
        Chef::Log.debug("rackconnect_automation_status: #{metadata.all["rackconnect_automation_status"]}")
        ready? && metadata.all["rackconnect_automation_status"] == "DEPLOYED"
      elsif rackspace_servicelevel_wait
        Chef::Log.debug("rax_service_level_automation: #{metadata.all["rax_service_level_automation"]}")
        ready? && metadata.all["rax_service_level_automation"] == "Complete"
      else
        ready?
      end
    end
  rescue Fog::Errors::TimeoutError
    ui.error("Timeout waiting for the server to be created")
    msg_pair("Progress", "#{server.progress}%")
    msg_pair("rackconnect_automation_status", server.metadata.all["rackconnect_automation_status"])
    msg_pair("rax_service_level_automation", server.metadata.all["rax_service_level_automation"])
    Chef::Application.fatal! "Server didn't finish on time"
  end

  msg_pair("Metadata", server.metadata)

  print "\n#{ui.color("Waiting server", :magenta)}"

  puts("\n")

  if locate_config_value(:rackconnect_v3_network_id)
    print "\n#{ui.color("Setting up RackconnectV3 network and IPs", :magenta)}"
    setup_rackconnect_network!(server)
    while server.ipv4_address == ""
      server.reload
      sleep 5
    end
  end

  if server_create_options[:networks] && locate_config_value(:rackspace_networks)
    msg_pair("Networks", locate_config_value(:rackspace_networks).sort.join(", "))
  end

  msg_pair("Public DNS Name", public_dns_name(server))
  msg_pair("Public IP Address", ip_address(server, "public"))
  msg_pair("Private IP Address", ip_address(server, "private"))
  msg_pair("Password", server.password)
  msg_pair("Metadata", server.metadata.all)

  bootstrap_ip_address = ip_address(server, locate_config_value(:bootstrap_network))

  Chef::Log.debug("Bootstrap IP Address #{bootstrap_ip_address}")
  if bootstrap_ip_address.nil?
    ui.error("No IP address available for bootstrapping.")
    exit 1
  end

  if locate_config_value(:bootstrap_protocol) == "winrm"
    print "\n#{ui.color("Waiting for winrm", :magenta)}"
    print(".") until tcp_test_winrm(bootstrap_ip_address, locate_config_value(:winrm_port))
    bootstrap_for_windows_node(server, bootstrap_ip_address).run
  else
    print "\n#{ui.color("Waiting for sshd", :magenta)}"
    tcp_test_ssh(server, bootstrap_ip_address)
    bootstrap_for_node(server, bootstrap_ip_address).run
  end

  puts "\n"
  msg_pair("Instance ID", server.id)
  msg_pair("Host ID", server.host_id)
  msg_pair("Name", server.name)
  msg_pair("Flavor", server.flavor.name)
  msg_pair("Image", server.image.name) if server.image
  msg_pair("Boot Image ID", server.boot_image_id) if server.boot_image_id
  msg_pair("Metadata", server.metadata)
  msg_pair("Public DNS Name", public_dns_name(server))
  msg_pair("Public IP Address", ip_address(server, "public"))
  msg_pair("Private IP Address", ip_address(server, "private"))
  msg_pair("Password", server.password)
  msg_pair("Environment", config[:environment] || "_default")
  msg_pair("Run List", config[:run_list].join(", "))
end
setup_rackconnect_network!(server) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 556
def setup_rackconnect_network!(server)
  auth_token = connection.authenticate
  tenant_id  = connection.endpoint_uri.path.split("/").last
  region     = connection.region
  uri        = URI("https://#{region}.rackconnect.api.rackspacecloud.com/v3/#{tenant_id}/public_ips")

  Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
    begin
      req                 = Net::HTTP::Post.new(uri.request_uri)
      req["X-Auth-Token"] = auth_token
      req["Content-Type"] = "application/json"
      req.body            = JSON.dump("cloud_server" => { "id" => server.id })
      http.use_ssl        = true
      http.request req
    rescue StandardError => e
      puts "HTTP Request failed (#{e.message})"
    end
  end
end
tcp_test_ssh(server, bootstrap_ip) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 308
def tcp_test_ssh(server, bootstrap_ip)
  return true if locate_config_value(:tcp_test_ssh).nil?

  limit = locate_config_value(:retry_ssh_limit).to_i
  count = 0

  begin
    Net::SSH.start(bootstrap_ip, "root", password: server.password ) do |ssh|
      Chef::Log.debug("sshd accepting connections on #{bootstrap_ip}")
      break
    end
  rescue
    count += 1

    if count <= limit
      print "."
      sleep locate_config_value(:retry_ssh_every).to_i
      tcp_test_ssh(server, bootstrap_ip)
    else
      ui.error "Unable to SSH into #{bootstrap_ip}"
      exit 1
    end
  end
end
tcp_test_winrm(hostname, port) click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 368
def tcp_test_winrm(hostname, port)
  tcp_socket = TCPSocket.new(hostname, port)
  true
rescue SocketError
  sleep 2
  false
rescue Errno::ETIMEDOUT
  false
rescue Errno::EPERM
  false
rescue Errno::ECONNREFUSED
  sleep 2
  false
rescue Errno::EHOSTUNREACH
  sleep 2
  false
rescue Errno::ENETUNREACH
  sleep 2
  false
  tcp_socket && tcp_socket.close
end
user_data() click to toggle source
# File lib/chef/knife/rackspace_server_create.rb, line 576
def user_data
  file = locate_config_value(:rackspace_user_data)
  return unless file

  begin
    filename = File.expand_path(file)
    content = File.read(filename)
  rescue Errno::ENOENT => e
    ui.error "Unable to read source file - #{filename}"
    exit 1
  end
  content
end