#!/usr/bin/env ruby

# Do not buffer output
STDOUT.sync = TRUE

##############################################################################
# Environment Configuration
##############################################################################
ONE_LOCATION=ENV["ONE_LOCATION"]
USER=ENV["user"]
RUNVMFILE="/var/lib/one/running.bck"
TIMEOUT=20

# oneadmin user flag value
USERFLAG=-2

if !ONE_LOCATION
    RUBY_LIB_LOCATION="/usr/lib/one/ruby"
else
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
end

$: << RUBY_LIB_LOCATION

##############################################################################
# Required libraries
##############################################################################
require 'opennebula'
require 'optparse'

include OpenNebula

MAXWAIT=60
INTERVAL=1

# List of supported actions
ACTIONS = [
  'status',  # Get the status of all VMs in OpenNebula VM pool
  'suspend', # Suspend all VMs in RUNNING state
  'resume',  # Resume all VMs in SUSPENDED or UNKNOWN state
]


# Map each action with a target state
EXPECTED_STATUS_MAP = {
  'status'  => nil,
  'boot'    => 'runn',
  'suspend' => 'susp',
  'resume'  => 'runn'
}

def dump_running_vms_file()
    if File.exist?(RUNVMFILE)
        running_vms = File.readlines(RUNVMFILE).uniq
    else
        running_vms = []
    end

    return running_vms
end

def CreoleGet(variable)
    begin
        value = `CreoleGet #{variable}`
        return value
    rescue
        return nil
    end
end

def _do_wait(vms, action, maxwait)
    if maxwait == 0 and action == 'resume'
        # User explicitely don't want to wait
        vms.clear
        return 0
    end

    print "Wait #{maxwait}s for VMs to #{action}"
    for try in 0..maxwait
        vms.delete_if do |vm|
            vm.info
            vm.status == EXPECTED_STATUS_MAP[action]
        end
        break if vms.empty?
        print "."
        sleep(1)
    end
    if vms.empty?
        puts " OK"
        return 0
    else
        puts " FAIL"
        return -1
    end
end


#
# NAME: _do_suspend
# PARAM: OpenNebula::VirtualMachine object
# AIM: Suspend a virtual machine
#
def _do_suspend(vm)
    fd = File.open(RUNVMFILE,'a')
    if vm.status == "runn"
        puts("Suspending #{vm.id} - #{vm.name}... ")
        fd.write("#{vm.id}\n")
        rc = vm.suspend
        if OpenNebula.is_error?(rc)
            puts rc.message
        else
            puts "scheduled"
        end
    end
    fd.close
end

#
# NAME: _do_resume
# PARAM: OpenNebula::VirtualMachine object
# AIM: Resum a suspended virtual machines
#
def _do_resume(vm)
    print("Resume #{vm.id} - #{vm.name}... ")
    rc = vm.resume
    if OpenNebula.is_error?(rc)
        puts rc.message
    else
        puts "scheduled"
    end
end


options = {:creds => nil, :action => nil, :endpoint => nil,
           :timeout => nil}

parser = OptionParser.new do|opts|
    opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
    opts.on('-c', '--creds file', 'Crediential file') do |value|
        options[:creds] = value;
    end

    opts.on('-a', '--action action', 'Action to run') do |value|
        options[:action] = value;
    end

    opts.on('-e', '--end-point url', 'End point URL') do |value|
        options[:endpoint] = value;
    end

    opts.on('-t', '--timeout timeout', 'Timeout for opennebula connection') do |value|
        options[:timeout] = value.to_i;
    end

    opts.on('-w', '--wait timeout', 'Wait for action ends') do |value|
        options[:wait] = value.to_i
    end

    opts.on('-h', '--help', 'Displays Help') do
        puts opts
        exit
    end

end

parser.parse!

# OpenNebula credentials
if not options[:creds]
    options[:creds] = "/var/lib/one/.one/one_auth"
end

if not options[:action]
    options[:action] = "status"
end

if not options[:endpoint]
    ip = CreoleGet('adresse_ip_eth0').chomp
    options[:endpoint] = "http://#{ip}:2633/RPC2"
end

if not options[:timeout]
    options[:timeout] = TIMEOUT
end

if not options[:wait]
    options[:wait] = MAXWAIT
end


if not ACTIONS.include?(options[:action])
    puts("Action : #{options[:action]}) is not supported")
    exit(-1)
end


begin
    File.readlines(options[:creds]).each do |line|
        CREDENTIALS = line
    end
rescue
    puts("#{options[:creds]}: Problem loading credentials, check if file exists.")
    exit(-1)
end


exit_code = 0
begin
    client = Client.new(CREDENTIALS, options[:endpoint])

    vm_pool = VirtualMachinePool.new(client, USERFLAG)

    # Try to load vm pool infos from OpenNebula until timeout expires
    rc = vm_pool.info
    cnt = 0
    while OpenNebula.is_error?(rc)
        if cnt == options[:timeout]
            puts rc.message
            exit(-1)
        end
        rc = vm_pool.info
        sleep(1)
        cnt += 1
    end

    if options[:action] == "resume"
        running_vms = dump_running_vms_file()
        running_vms.each do |vmid|
            vm = VirtualMachine.new_with_id(vmid, client)
            vm.info
            _do_resume(vm)
        end

    else
        vm_pool.each do |vm|
            case options[:action]
            when "status"
                puts "#{vm.name}\t#{vm.status}"

            when "suspend"
                _do_suspend(vm)
            end
        end

        # Update list of suspended VMs
        running_vms = dump_running_vms_file()
    end

    if options[:action] != 'status'
        vms = []
        running_vms.each do |vmid|
            vm = VirtualMachine.new_with_id(vmid, client)
            vms.push(vm)
        end
        exit_code = _do_wait(vms, options[:action], options[:wait])
    end

    if options[:action] == "resume"
        if vms.empty?
            File.truncate(RUNVMFILE, 0) if File.exists?(RUNVMFILE)
        else
            fd = File.open(RUNVMFILE,'w')
            vms.each do |vm|
                fd.write("#{vm.id}\n")
            end
        end
    end

rescue Exception => e
    puts e.message
    puts e.backtrace
    exit(-1)
end

exit(exit_code)

# Local Variables:
# ruby-indent-level: 4
# End: