#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2019, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
#--------------------------------------------------------------------------- #

ONE_LOCATION = ENV['ONE_LOCATION']

if !ONE_LOCATION
    RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
    GEMS_LOCATION     = '/usr/share/one/gems'
    REMOTES_LOCATION  = '/var/lib/one/remotes/'
else
    RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
    GEMS_LOCATION     = ONE_LOCATION + '/share/gems'
    REMOTES_LOCATION  = ONE_LOCATION + '/var/remotes/'
end

if File.directory?(GEMS_LOCATION)
    Gem.use_paths(GEMS_LOCATION)
end

$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << RUBY_LIB_LOCATION + '/cli'

require 'command_parser'
require 'one_helper/onehost_helper'
require 'one_helper/onecluster_helper'

CommandParser::CmdParser.new(ARGV) do
    usage '`onehost` <command> [<args>] [<options>]'
    version OpenNebulaHelper::ONE_VERSION

    helper = OneHostHelper.new

    before_proc do
        helper.set_client(options)
    end

    ########################################################################
    # Global Options
    ########################################################################
    cmd_options = CommandParser::OPTIONS - [CommandParser::VERBOSE]
    set :option, cmd_options + OpenNebulaHelper::CLIENT_OPTIONS

    IM = {
        :name => 'im',
        :short => '-i im_mad',
        :large => '--im im_mad',
        :description => 'Set the information driver for the host',
        :format => String
    }

    VMM = {
        :name => 'vm',
        :short => '-v vmm_mad',
        :large => '--vm vmm_mad',
        :description => 'Set the virtualization driver for the host',
        :format => String
    }

    FORCE = {
        :name => 'force',
        :large => '--force',
        :description => 'Force probe upgrade in onehost sync'
    }

    RSYNC = {
        :name => 'rsync',
        :large => '--rsync',
        :description => 'Use rsync to synchronize remotes. In case some '\
                        'probes are no longer in the fronted ithey will be '\
                        'deleted in the hosts. rsync command must be '\
                        'installed in the frontend and nodes.'
    }

    TYPE = {
        :name => 'type',
        :short => '-t remote_provider',
        :large => '--type remote_provider',
        :description => 'Use type to create a host using Cloud Bursting',
        :format => String
    }

    CREAT_OPTIONS = [IM, VMM, OneClusterHelper::CLUSTER, TYPE]
    SYNC_OPTIONS  = [OneClusterHelper::CLUSTER, FORCE, RSYNC]

    ########################################################################
    # Formatters for arguments
    ########################################################################

    set :format, :hostid, OneHostHelper.to_id_desc do |arg|
        helper.to_id(arg)
    end

    set :format, :hostid_list, OneHostHelper.list_to_id_desc do |arg|
        helper.list_to_id(arg)
    end

    ########################################################################
    # Commands
    ########################################################################

    create_desc = <<-EOT.unindent
        Creates a new Host
    EOT

    command :create, create_desc, :hostname,
            [:file, nil], :options => CREAT_OPTIONS do
        if options[:im].nil? || options[:vm].nil?
            STDERR.puts 'Drivers are mandatory to create a host:'
            STDERR.puts '\t -i information driver'
            STDERR.puts '\t -v hypervisor driver'
            exit(-1)
        end

        cid = options[:cluster] || ClusterPool::NONE_CLUSTER_ID
        helper.create_resource(options) do |host|
            if !options[:type].nil?
                str = helper.set_hybrid(options[:type], args[1])
            end
            rc = host.allocate(args[0], options[:im], options[:vm], cid)
            if !OpenNebula.is_error?(rc) && !options[:type].nil?
                host.update(str, true)
            end

            rc
        end
    end

    delete_desc = <<-EOT.unindent
        Deletes the given Host
    EOT

    command :delete, delete_desc, [:range, :hostid_list] do
        helper.perform_actions(args[0], options, 'deleted') do |host|
            host.delete
        end
    end

    enable_desc = <<-EOT.unindent
        Enables the given host, fully operational
    EOT

    command :enable, enable_desc, [:range, :hostid_list] do
        helper.perform_actions(args[0], options, 'enabled') do |host|
            host.enable
        end
    end

    disable_desc = <<-EOT.unindent
        Disables the given host:
          - monitor: enabled
          - scheduler deployment: disabled
          - manual deployment: enabled
    EOT

    command :disable, disable_desc, [:range, :hostid_list] do
        helper.perform_actions(args[0], options, 'disabled') do |host|
            host.disable
        end
    end

    offline_desc = <<-EOT.unindent
        Sets the host offline:
          - monitor: disabled
          - scheduler deployment: disabled
          - manual deployment: disabled
    EOT

    command :offline, offline_desc, [:range, :hostid_list] do
        helper.perform_actions(args[0], options, 'offline') do |host|
            host.offline
        end
    end

    update_desc = <<-EOT.unindent
        Update the template contents. If a path is not provided the editor will
        be launched to modify the current content.
    EOT

    command :update, update_desc, :hostid, [:file, nil],
            :options => OpenNebulaHelper::APPEND do
        helper.perform_action(args[0], options, 'updated') do |obj|
            if options[:append]
                str = OpenNebulaHelper.append_template(args[0], obj, args[1])
            else
                str = OpenNebulaHelper.update_template(args[0], obj, args[1])
            end

            helper.set_client(options)
            obj = helper.retrieve_resource(obj.id)

            obj.update(str, options[:append])
        end
    end

    sync_desc = <<-EOT.unindent
        Synchronizes probes in /var/lib/one/remotes ($ONE_LOCATION/var/remotes
        in self-contained installations) with Hosts.
        Examples:
            onehost sync
            onehost sync -c myCluster
            onehost sync host01,host02,host03
    EOT

    command :sync, sync_desc, [:range, :hostid_list, nil],
            :options => SYNC_OPTIONS do
        helper.sync(args[0], options)
    end

    list_desc = <<-EOT.unindent
        Lists Hosts in the pool
    EOT

    command :list, list_desc,
            :options => CLIHelper::OPTIONS + OpenNebulaHelper::OPTIONS +
                        [OpenNebulaHelper::DESCRIBE] do
        helper.list_pool(options)
    end

    show_desc = <<-EOT.unindent
        Shows information for the given Host
    EOT

    command :show, show_desc, :hostid,
            :options => [OpenNebulaHelper::XML, OpenNebulaHelper::DECRYPT] do
        helper.show_resource(args[0], options)
    end

    top_desc = <<-EOT.unindent
        Lists Hosts continuously
    EOT

    command :top, top_desc,
            :options => CLIHelper::OPTIONS + OpenNebulaHelper::OPTIONS do
        helper.list_pool(options, true)
    end

    flush_desc = <<-EOT.unindent
        Disables the host and reschedules all the running VMs in it.
    EOT

    command :flush, flush_desc, [:range, :hostid_list] do
        begin
            if !ONE_LOCATION
                action = YAML.load_file('/etc/one/cli/onehost.yaml')
            else
                action = YAML.load_file("#{ONE_LOCATION}/etc/cli/onehost.yaml")
            end
            action = action[:default_actions][0][:flush]
        rescue StandardError => e
            STDERR.puts e
        end
        helper.perform_actions(args[0], options, 'flush') do |host|
            host.flush action
        end
    end

    rename_desc = <<-EOT.unindent
        Renames the Host
    EOT

    command :rename, rename_desc, :hostid, :name do
        helper.perform_action(args[0], options, 'renamed') do |o|
            o.rename(args[1])
        end
    end

    importvm_desc = <<-EOT.unindent
        Import VM to OpenNebula
    EOT

    command :importvm, importvm_desc, :hostid, :name do
        helper.perform_action(args[0], options, 'imported') do |o|
            rc = o.info
            next rc if OpenNebula.is_error?(rc)

            o.import_wild(args[1])
        end
    end

    forceupdate_desc = <<-EOT.unindent
        Forces host monitoring update
        Examples:
            onehost forceupdate host01
            onehost forceupdate host01,host02,host03
            onehost forceupdate -c myCluster
    EOT

    command :forceupdate, forceupdate_desc, [:range, :hostid_list, nil],
            :options => [OneClusterHelper::CLUSTER] do
        helper.forceupdate(args[0], options)
    end
end
