#!/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 or invalid. Current leases search path ['/var/lib/dhcp3', '/var/lib/dhcp', '/var/lib/dhclient'].", :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]} Accept #{Abiquo::Chef::Config.bootstrap_mediatype}"
  begin
    xml = RestClient.get leases[:abiquo_api_url], :authorization => "OneTime #{leases[:abiquo_api_token]}", :accept => Abiquo::Chef::Config.bootstrap_mediatype
  rescue Exception => e
    log "Error requesting node info from API", :error
    log e.message, :error
    log e.backtrace.join("\n    ")
    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    ")
    exit 1
  end

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

  #
  # 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
  node_config = bootstrap_config.node_config
  if node_config['run_list']
    log "Recipes found #{node_config.inspect}"
  else
    log "No recipes selected", :warning
  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
  #
  File.open('/etc/chef/first-boot.json', 'w') do |f|
    node_config ||= {}
    f.puts node_config.to_json
  end
  log "Written '#{node_config.to_json}' to /etc/chef/first-boot.json file"


  #
  # Set the hostname
  #
  node_name = bootstrap_config.node_name

  log "Setting hostname"
  output = `hostname #{node_name} 2>&1`
  log output, :info

  net_config_file = '/etc/sysconfig/network'
  if File.exists? net_config_file
    log "Configuring /etc/sysconfig/network with the new hostname"
    data = File.read net_config_file
    File.open net_config_file, 'w' do |f|
     data.each_line {|l| l.include?('HOSTNAME') ? f.puts("HOSTNAME=#{node_name}\n") : f.puts(l)}
    end
  end
  File.open('/etc/hostname', 'w') do |f|
      f.puts node_name
  end
  File.open('/etc/hosts', 'a') do |f|
      f.puts "127.0.0.1         #{node_name}"
  end
  log "done"

  #
  # Everything in place, no run the client
  #
  # first, we run it once to register and apply recipes
  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, :info
  else
    log "Running chef-client in daemon mode"
    # Run the chef-client if not already running
    if not (`ps aux|grep "chef-client "| grep -v grep|wc -l`.to_i > 0)
        # 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
        else
          log "Success."
        end
    else
        log "chef-client already running."
        log `ps aux|grep "chef-client "| grep -v grep`
    end
  end
end
