#!/usr/bin/env ruby
require 'rubygems'
require 'fileutils'
require 'rest-client'
require 'xmlsimple'
require 'abiquo-chef-agent'
require 'logger'
include Abiquo::Chef

at_exit do
  if $!
    log "BACKTRACE", :error
    log $!.to_s, :error
    log $@.to_s, :error
  end
end

Log = Logger.new Abiquo::Chef::Config.log_file

def log(msg, level = :info)
  if level == :debug and not $DEBUG
    return
  end
  if level == :info
    Log.info msg.to_s
  elsif level == :warning
    Log.warn msg.to_s
  elsif level == :error
    Log.error msg.to_s
  else
    Log.debug msg.to_s
  end
end

CHEF_CONFIG_DIR = Abiquo::Chef::Config.chef_config_dir 
BOOTSTRAP_XML =  Abiquo::Chef::Config.bootstrap_backup_file
CLIENT_CERT = Abiquo::Chef::Config.client_cert_file

if File.exist? CLIENT_CERT
  log "#{CLIENT_CERT} file found. If you want to re-run the Abiquo Chef Agent,\n" +
      "delete the #{Abiquo::Chef::Config.chef_config_dir} directory first.\n" +
      "Aborting.", :warning
  exit 1
end

if not File.directory? CHEF_CONFIG_DIR
  log "Creating #{CHEF_CONFIG_DIR} directory."
  FileUtils.mkdir_p '/etc/chef'
end

#
# Try to fix system clock with ntpdate
#
log "Trying to synchronize system clock"
`/usr/sbin/ntpdate #{Abiquo::Chef::Config.ntp_server} > /dev/null 2>&1`
if $? != 0
  log "Could not update the system clock using ntpdate and #{Abiquo::Chef::Config.ntp_server} ntp server", :warning
end

#
# Parse info from DCHP client leases file
#
log "Parsing leases file"
leases = Util.parse_leases_file
if not leases
  log "Leases file not found. Aborting.", :error
  exit 1
else
  log "Leases found #{leases.inspect}"

  #
  # Request node info from Abiquo API
  #
  log "Requesting Chef config from API #{leases[:abiquo_api_url]} OneTime #{leases[:abiquo_api_token]}"
  begin
    xml = RestClient.get leases[:abiquo_api_url], :authorization => "OneTime #{leases[:abiquo_api_token]}"
  rescue Exception => e
    log "Error requesting node info from API", :error
    log e.message, :error
    log e.backtrace.join("\n    "), :debug
    exit 1
  end
  #
  # Write the bootstrap XML
  # Daemon will not run if this XML is found
  #
  File.open(BOOTSTRAP_XML, 'w') do |f|
    f.puts xml
  end

  #
  # Parse the XML returned by API
  #
  log "Parsing Bootstrap XML from API"
  begin
    bootstrap_config = BootstrapConfigParser.new(xml)
  rescue Exception => e
    log "Error parsing XML bootstrap file", :error
    log e.message, :error
    log e.backtrace.join("\n    "), :debug
    exit 1
  end

  #
  # Write Chef validation pem
  #
  File.open(Abiquo::Chef::Config.validation_cert, 'w') do |f|
    f.puts bootstrap_config.validation_cert
  end

  #
  # Get required node info and write chef-client config file
  #
  validation_client_name = bootstrap_config.validation_client_name
  chef_server_url = bootstrap_config.chef_server_url
  recipes = bootstrap_config.run_list
  if recipes.empty?
    log "No recipes selected", :warning
    recipes = []
  else
    log "Recipes found #{recipes.inspect}"
  end
  File.open('/etc/chef/client.rb', 'w') do |f|
    f.puts "log_level        :info"
    f.puts "log_location     STDOUT"
    f.puts "chef_server_url  '#{chef_server_url}'"
    f.puts "validation_client_name '#{validation_client_name}'"
  end

  #
  # Write first-boot.json attributes
  #
  if not recipes.empty?
    recipes_json = '"run_list": ['
    recipes_json << (recipes.map{ |r| "\"#{r}\""}).join(',')
    recipes_json << ']'
    File.open('/etc/chef/first-boot.json', 'w') do |f|
      f.puts "{"
      f.puts recipes_json
      f.puts "}"
    end
  else
    File.open('/etc/chef/first-boot.json', 'w') do |f|
      f.puts "{}"
    end
  end

  #
  # Everything in place, no run the client
  #
  # first, we run it once to register and apply recipes
  node_name = bootstrap_config.node_name
  cmd = "chef-client -N #{node_name} --once -j /etc/chef/first-boot.json -L /var/log/chef-client.log"
  log "Running chef-client first time"
  log cmd
  output = `#{cmd}`
  #
  # Remove validation certs and bootstrap XML
  #
  if not ENV['ABIQUO_DEBUG']
    if File.exist?(Abiquo::Chef::Config.validation_cert)
      FileUtils.rm(Abiquo::Chef::Config.validation_cert)
    end
    if File.exist?(Abiquo::Chef::Config.bootstrap_backup_file)
      FileUtils.rm(Abiquo::Chef::Config.bootstrap_backup_file)
    end
  end
  if $? != 0
    log "chef-client run failed", :error
    log output, :debug
  else
    log "Setting hostname"
    output = `hostname #{node_name} 2>&1`
    log output, :debug
    File.open('/etc/hostname', 'w') do |f|
      f.puts node_name
    end
    log "chef-client run OK"
    log "Running chef-client in daemon mode"
    # Now we run it in daemon mode
    cmd = "chef-client -N #{node_name} -i 1800 -d -L /var/log/chef-client.log"
    output = `#{cmd}`
    if $? != 0
      log "Running chef-client in daemon mode failed.", :error
      log output, :debug
    else
      log "Success."
    end
  end
end
