class Chef::Knife::CloudstackServerCreate

Public Instance Methods

add_port_forward(public_start_port, public_end_port, server_id, ipaddressid, privateport) click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 223
def add_port_forward(public_start_port, public_end_port, server_id, ipaddressid, privateport)
  pfwdops = {}
  pfwdops['ipaddressid'] = ipaddressid
  pfwdops['privateport'] = privateport
  pfwdops['protocol'] = "TCP"
  pfwdops['virtualmachineid'] = server_id
  pfwdops['openfirewall'] = "true"
  pfwdops['publicport'] = public_start_port
  pfwdops['publicendport'] = public_end_port
  rule_create_job = connection.create_port_forwarding_rule(pfwdops)
  print "#{ui.color("Creating port forwarding rule.", :cyan)}"
  while (@connection.query_async_job_result({'jobid' => rule_create_job['createportforwardingruleresponse']['jobid']})['queryasyncjobresultresponse'].fetch('jobstatus') == 0)
    print("#{ui.color(".", :cyan)}")
    sleep 2
  end
  print("\n")
end
bootstrap_for_node(server, ssh_host) click to toggle source

def bootstrap_for_node(host, user, password)

# File lib/chef/knife/cloudstack_server_create.rb, line 163
def bootstrap_for_node(server, ssh_host)
  host = server["name"]
  user = config[:ssh_user]
  password = server["password"]
  Chef::Log.debug("Bootstrap host: #{host}")
  Chef::Log.debug("Bootstrap user: #{user}")
  Chef::Log.debug("Bootstrap pass: #{password}")
  bootstrap = Chef::Knife::Bootstrap.new
  bootstrap.name_args = [ssh_host]
  bootstrap.config[:run_list] = config[:run_list]
  bootstrap.config[:ssh_user] = user
  bootstrap.config[:ssh_password] = password
  bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
  bootstrap.config[:identity_file] = locate_config_value(:identity_file)
  bootstrap.config[:chef_node_name] = config[:server_display_name] if config[:server_display_name]
  bootstrap.config[:prerelease] = config[:prerelease]
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
  bootstrap.config[:distro] = locate_config_value(:distro)
  bootstrap.config[:use_sudo] = true
  bootstrap.config[:template_file] = locate_config_value(:template_file)
  bootstrap.config[:environment] = config[:environment]
  # may be needed for vpc_mode
  bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
  begin
    bootstrap
  rescue
    sleep @initial_sleep_delay
    retry
  end
end
check_port_available(public_port, ipaddressid) click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 194
def check_port_available(public_port, ipaddressid)
  Chef::Log.debug("Checking if port #{public_port} is available.")
  pubport = public_port.to_i
  port_forward_rules_query = connection.list_port_forwarding_rules({'ipaddressid' => ipaddressid })
  port_rules = port_forward_rules_query['listportforwardingrulesresponse']['portforwardingrule']
  is_available = true
  some_possible_rules = port_rules.select { |rule| rule['publicport'].to_i <= pubport }
  possible_rules = some_possible_rules.select { |rule| rule['publicendport'].to_i >= pubport }
  possible_rules.each do |rule|
    startport = rule['publicport'].to_i
    endport = rule['publicendport'].to_i
    Chef::Log.debug("Determining if #{pubport} is between #{startport} and #{endport}.")
    if (endport != startport)
      if pubport.between?(startport, endport)
        is_available = false
      else
        is_available = true
      end
    else
      if (pubport == startport)
        is_available = false
      else
        is_available = true
      end
    end
  end
  return is_available
end
create_server_def() click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 241
def create_server_def
  server_def = {}

  if locate_config_value(:server_display_name) != nil
    server_def["displayname"] = locate_config_value(:server_display_name)
  end

  if locate_config_value(:host_name) != nil
    server_def["name"] = locate_config_value(:host_name)
  end

  network_ids = ""
  if locate_config_value(:cloudstack_networkids) != []
    cs_networkids = locate_config_value(:cloudstack_networkids)
    cs_networkids.join(",")
    server_def["networkids"] = network_ids
  end

  security_groups = ""
  if locate_config_value(:cloudstack_groupids) != []
    cs_groupids = locate_config_value(:cloudstack_groupids)
    security_groups = cs_groupids.join(",")
    server_def["securitygroupids"] = security_groups
  elsif locate_config_value(:cloudstack_groupnames) != []
    cs_groupnames = locate_config_value(:cloudstack_groupnames)
    security_groups = cs_groupnames.join(",")
    server_def["securitygroupnames"] = security_groups
  end

  if locate_config_value(:keypair) != nil
    server_def["keypair"] = locate_config_value(:keypair)
  end

  if locate_config_value(:diskoffering) != nil
    server_def["diskofferingid"] = locate_config_value(:diskoffering)
  end

  if locate_config_value(:size) != nil
    server_def["size"] = locate_config_value(:size)
  end

  if locate_config_value(:hypervisor) != nil
    server_def["hypervisor"] = locate_config_value(:hypervisor)
  end

  if locate_config_value(:cloudstack_user_data) != nil
    require 'base64'
    begin
      server_def["userdata"] = Base64.strict_encode64(File.read(Chef::Config[:knife][:cloudstack_user_data]))
    rescue
      ui.warn("Cannot read #{Chef::Config[:knife][:cloudstack_user_data]}: #{$!.inspect}. Ignoring")
    end
  end
  if locate_config_value(:keyboard) != nil
    server_def["keyboard"] = locate_config_value(:keyboard)
  end

  server_def
end
find_template(template=nil) click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 322
def find_template(template=nil)
  # Are we bootstrapping using an already shipped template?
  if config[:template_file]
    bootstrap_files = config[:template_file]
  else
    bootstrap_files = []
    bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap', "#{config[:distro]}.erb")
    bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{config[:distro]}.erb") if Knife.chef_config_dir
    bootstrap_files << File.join(ENV['HOME'], '.chef', 'bootstrap', "#{config[:distro]}.erb") if ENV['HOME']
    bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{config[:distro]}.erb"))
    bootstrap_files.flatten!
  end

  template = Array(bootstrap_files).find do |bootstrap_template|
    Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
    File.exists?(bootstrap_template)
  end

  unless template
    ui.info("Can not find bootstrap definition for #{config[:distro]}")
    raise Errno::ENOENT
  end

  Chef::Log.debug("Found bootstrap template in #{File.dirname(template)}")

  template
end
knife_ssh() click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 301
def knife_ssh
  ssh = Chef::Knife::Ssh.new
  ssh.ui = ui
  ssh.name_args = [ @primary_ip, ssh_command ]
  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
  Chef::Log.debug("SSH User: #{ssh.config[:ssh_user]}")
  Chef::Log.debug("SSH Password: #{ssh.config[:ssh_password]}")
  Chef::Log.debug("SSH Port: #{ssh.config[:ssh_port]}")
  Chef::Log.debug("SSH Gateway: #{ssh.config[:ssh_gateway]}")
  Chef::Log.debug("SSH Identity File: #{ssh.config[:identity_file]}")
  ssh
end
knife_ssh_with_password_auth() click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 359
def knife_ssh_with_password_auth
  ssh = knife_ssh
  ssh.config[:identity_file] = nil
  Chef::Log.debug("Private Key failed or not specified. Trying password of #{ssh.get_password}")
  ssh.config[:ssh_password] = ssh.get_password
  ssh
end
read_template() click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 355
def read_template
  IO.read(@template_file).chomp
end
render_template(template=nil) click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 350
def render_template(template=nil)
  context = Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config)
  Erubis::Eruby.new(template).evaluate(context)
end
run() click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 377
def run
  $stdout.sync = true
  options = create_server_def
  Chef::Log.debug("Options: #{options} \n")

  @initial_sleep_delay = 10       
  @sshport = 22

  config[:host_key_verify] = false

  if locate_config_value(:ssh_port) != nil
    @sshport = locate_config_value(:ssh_port).to_i
  end

  # binding.pry
  serverdeploy = connection.deploy_virtual_machine(locate_config_value(:cloudstack_serviceid), 
    locate_config_value(:cloudstack_templateid), locate_config_value(:cloudstack_zoneid), options)
  
  jobid = serverdeploy['deployvirtualmachineresponse'].fetch('jobid')

  server_start = connection.query_async_job_result(jobid)

  Chef::Log.debug("Job ID: #{jobid} \n")

  print "#{ui.color("Waiting for server", :magenta)}"
  while server_start['queryasyncjobresultresponse'].fetch('jobstatus') == 0
    print "#{ui.color(".", :magenta)}"
    sleep @initial_sleep_delay
    server_start = connection.query_async_job_result(jobid)
    Chef::Log.debug("Server_Start: #{server_start} \n")
  end
  puts "\n\n"

  if server_start['queryasyncjobresultresponse'].fetch('jobstatus') == 2
    errortext = server_start['queryasyncjobresultresponse'].fetch('jobresult').fetch('errortext')
    puts "#{ui.color("ERROR! Job failed with #{errortext}", :red)}"
  end

  if server_start['queryasyncjobresultresponse'].fetch('jobstatus') == 1

    Chef::Log.debug("Job ID: #{jobid} \n")
    Chef::Log.debug("Options: #{options} \n")
    server_start = connection.query_async_job_result(jobid)
    Chef::Log.debug("Server_Start: #{server_start} \n")

    @server = server_start['queryasyncjobresultresponse']['jobresult']['virtualmachine']

    server_display_name = @server['displayname']
    server_id = @server['name']
    server_serviceoffering = @server['serviceofferingname']
    server_template = @server['templatename']
    if @server['password'] != nil
      config[:ssh_password] = @server['password']
    else
      config[:ssh_password] = locate_config_value(:ssh_password)
    end

    ssh_user = locate_config_value(:ssh_user)

    @primary_ip = nil

    if @server['nic'].size > 0
      @primary_ip = @server['nic'].first['ipaddress']
    end

    if locate_config_value(:random_ssh_port) != nil
      public_ips = connection.list_public_ip_addresses("associatednetworkid" => @server['nic'][0]['networkid'])
      primary_public_ip_id = public_ips['listpublicipaddressesresponse']['publicipaddress'][0]['id']
      @primary_ip = public_ips['listpublicipaddressesresponse']['publicipaddress'][0]['ipaddress']
      pubport = rand(49152..65535)
      while (check_port_available(pubport, primary_public_ip_id) == false)
        pubport = rand(49152..65535)
      end
      add_port_forward(pubport, pubport, server_id, primary_public_ip_id, @sshport)
      @sshport = pubport
    end

    Chef::Log.debug("Connecting over port #{@sshport}")
    config[:ssh_port] = @sshport
    config[:server_name] = @primary_ip
    @template_file = find_template(config[:bootstrap_template])
    
    puts "\n\n"
    puts "#{ui.color("Name", :cyan)}: #{server_display_name}"
    puts "#{ui.color("Primary IP", :cyan)}: #{@primary_ip}"
    puts "#{ui.color("Username", :cyan)}: #{ssh_user}"
    puts "#{ui.color("Password", :cyan)}: #{config[:ssh_password]}"

    print "#{ui.color("Waiting for SSH.", :magenta)}"
    if config[:ssh_gateway]
      Chef::Log.debug("Using SSH Gateway: #{config[:ssh_gateway]}")
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
    end
    begin
      knife_ssh.run
    rescue Net::SSH::AuthenticationFailed
      unless config[:ssh_password]
        ui.info("Failed to authenticate #{config[:ssh_user]} - trying password auth")
        knife_ssh_with_password_auth.run
      end
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Errno::ECONNREFUSED
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue SocketError
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Errno::ETIMEDOUT
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Errno::EPERM
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Errno::EHOSTUNREACH
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Errno::ENETUNREACH
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Net::SSH::Disconnect
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue Net::SSH::AuthenticationFailed
      sleep @initial_sleep_delay
      print "#{ui.color(".", :magenta)}"
      retry
    rescue
      puts caller
      puts $!.inspect
    end     

    Chef::Log.debug("#{@server}")

    puts "\n"
    puts "#{ui.color("Instance Name", :green)}: #{server_display_name}"
    puts "#{ui.color("Instance ID", :green)}: #{server_id}"
    puts "#{ui.color("Service Offering", :green)}: #{server_serviceoffering}"
    puts "#{ui.color("Template", :green)}: #{server_template}"
    puts "#{ui.color("Public IP Address", :green)}: #{@primary_ip}"
    puts "#{ui.color("Port", :green)}: #{@sshport}"
    puts "#{ui.color("User", :green)}: #{ssh_user}"
    puts "#{ui.color("Password", :green)}: #{config[:ssh_password]}"
    puts "#{ui.color("Environment", :green)}: #{config[:environment] || '_default'}"
    puts "#{ui.color("Run List", :green)}: #{config[:run_list].join(', ')}"
  end

end
ssh_command() click to toggle source
# File lib/chef/knife/cloudstack_server_create.rb, line 367
def ssh_command
  command = render_template(read_template)

  if config[:use_sudo]
    command = config[:use_sudo_password] ? "echo #{config[:ssh_password]} | sudo -S #{command}" : "sudo #{command}"
  end

  command
end