#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2017, 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"
else
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
end

$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+"/cli"

require 'command_parser'
require 'one_helper/onevrouter_helper'
require 'one_helper/onetemplate_helper'
require 'one_helper/onevm_helper'

cmd=CommandParser::CmdParser.new(ARGV) do
    usage "`onevrouter` <command> [<args>] [<options>]"
    version OpenNebulaHelper::ONE_VERSION

    helper = OneVirtualRouterHelper.new

    before_proc do
        helper.set_client(options)
    end

    ########################################################################
    # Global Options
    ########################################################################
    set :option, CommandParser::OPTIONS+OpenNebulaHelper::CLIENT_OPTIONS

    list_options = CLIHelper::OPTIONS
    list_options << OpenNebulaHelper::XML
    list_options << OpenNebulaHelper::NUMERIC
    list_options << OpenNebulaHelper::DESCRIBE

    ########################################################################
    # Formatters for arguments
    ########################################################################
    set :format, :groupid, OpenNebulaHelper.rname_to_id_desc("GROUP") do |arg|
        OpenNebulaHelper.rname_to_id(arg, "GROUP")
    end

    set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg|
        OpenNebulaHelper.rname_to_id(arg, "USER")
    end

    set :format, :vrouterid, OneVirtualRouterHelper.to_id_desc do |arg|
        helper.to_id(arg)
    end

    set :format, :vrouterid_list, OneVirtualRouterHelper.list_to_id_desc do |arg|
        helper.list_to_id(arg)
    end

    set :format, :filterflag, OneVirtualRouterHelper.filterflag_to_i_desc do |arg|
        helper.filterflag_to_i(arg)
    end

    set :format, :templateid, OpenNebulaHelper.rname_to_id_desc("VMTEMPLATE") do |arg|
        OpenNebulaHelper.rname_to_id(arg, "VMTEMPLATE")
    end

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

    create_desc = <<-EOT.unindent
        Creates a new Virtual Router from the given description
    EOT

    command :create, create_desc, :file do
        helper.create_resource(options) do |obj|
            begin
                template = File.read(args[0])

                obj.allocate(template)
            rescue => e
                STDERR.puts e.messsage
                exit -1
            end
        end
    end

    instantiate_desc = <<-EOT.unindent
        Creates a new VM instance from the given Template. This VM can be
        managed with the 'onevm' command.

        The NIC elements defined in the Virtual Router will be used. The
        source Template can be modified adding or replacing attributes with
        the optional file argument, or with the options.
    EOT

    instantiate_options = [
        OneTemplateHelper::VM_NAME,
        OneTemplateHelper::MULTIPLE,
        OneVMHelper::HOLD
    ]

    command :instantiate, instantiate_desc, :vrouterid, :templateid, [:file, nil],
            :options=>instantiate_options+OpenNebulaHelper::TEMPLATE_OPTIONS do
        exit_code=0

        if args[2] && OpenNebulaHelper.create_template_options_used?(options)
            STDERR.puts "You cannot use both template file and template"<<
                " creation options."
            next -1
        end

        number = options[:multiple] || 1
        user_inputs = nil

        helper.perform_action(args[0], options, "instantiated") do |vr|
            name = options[:name] || ""

            t = OpenNebula::Template.new_with_id(args[1], helper.client)

            on_hold = options[:hold] != nil

            extra_template = ""
            rc = t.info

            if OpenNebula.is_error?(rc)
                STDERR.puts rc.message
                exit(-1)
            end

            if args[2]
                extra_template = File.read(args[2])
            else
                res = OpenNebulaHelper.create_template(options, t)

                if res.first != 0
                    STDERR.puts res.last
                    next -1
                end

                extra_template = res.last
            end

            user_inputs = OneTemplateHelper.get_user_inputs(t.to_hash) unless user_inputs

            extra_template << "\n" << user_inputs

            vr.instantiate(number, args[1], name, on_hold, extra_template)
        end
    end

    delete_desc = <<-EOT.unindent
        Deletes the given Virtual Router
    EOT

    command :delete, delete_desc, [:range, :vrouterid_list] do
        helper.perform_actions(args[0],options,"deleted") do |obj|
            obj.delete
        end
    end

    chgrp_desc = <<-EOT.unindent
        Changes the Virtual Router group
    EOT

    command :chgrp, chgrp_desc,[:range, :vrouterid_list], :groupid do
        helper.perform_actions(args[0],options,"Group changed") do |obj|
            obj.chown(-1, args[1].to_i)
        end
    end

    chown_desc = <<-EOT.unindent
        Changes the Virtual Router owner and group
    EOT

    command :chown, chown_desc, [:range, :vrouterid_list], :userid,
            [:groupid,nil] do
        gid = args[2].nil? ? -1 : args[2].to_i
        helper.perform_actions(args[0],options,"Owner/Group changed") do |obj|
            obj.chown(args[1].to_i, gid)
        end
    end

    chmod_desc = <<-EOT.unindent
        Changes the Virtual Router permissions
    EOT

    command :chmod, chmod_desc, [:range, :vrouterid_list], :octet do
        helper.perform_actions(args[0],options, "Permissions changed") do |obj|
            obj.chmod_octet(args[1])
        end
    end

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

    command :update, update_desc, :vrouterid, [:file, nil],
    :options=>OpenNebulaHelper::APPEND do
        helper.perform_action(args[0],options,"modified") 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

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

    rename_desc = <<-EOT.unindent
        Renames the Virtual Router
    EOT

    command :rename, rename_desc, :vrouterid, :name do
        helper.perform_action(args[0],options,"renamed") do |obj|
            obj.rename(args[1])
        end
    end

    nic_attach_desc = <<-EOT.unindent
        Attaches a NIC to a VirtualRouter, and each one of its VMs. When using
        --file add only one NIC instance.

        Note if you are using a HA configuration for this router, --float can be
        added. When an IP is requested in HA clusters use --float.
    EOT

    command :"nic-attach", nic_attach_desc, :vrouterid,
            :options => [OneVMHelper::FILE, OneVMHelper::NETWORK, OneVMHelper::IP,
                         OneVirtualRouterHelper::FLOAT] do

        if options[:file].nil? and options[:network].nil?
            STDERR.puts "Provide a template file or a network:"
            STDERR.puts "\t--file    <file>"
            STDERR.puts "\t--network <network>"
            exit -1
        end

        if options[:file]
            template = File.read(options[:file])
        else
            network_id = options[:network]
            ip         = options[:ip]
            float      = options[:float]

            template = "NIC = [ NETWORK_ID = #{network_id}"

            if ip
                template += ", IP = #{ip}"
            end

            if float
                template += ", FLOATING_IP =\"yes\""
            end

            template += " ]"
        end

        helper.perform_action(args[0],options,"Attach NIC") do |vr|
            vr.nic_attach(template)
        end
    end

    nic_detach_desc = <<-EOT.unindent
        Detaches a NIC from a VirtualRouter, and each one of its VMs
    EOT

    command :"nic-detach", nic_detach_desc, :vrouterid, :nicid do
        nicid = args[1].to_i

        helper.perform_action(args[0],options,"Detach NIC") do |vr|
            vr.nic_detach(nicid)
        end
    end

    list_desc = <<-EOT.unindent
        Lists the Virtual Routers in the pool
    EOT

    command :list, list_desc, [:filterflag, nil], :options=>list_options do
        helper.list_pool(options, false, args[0])
    end

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

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

    top_desc = <<-EOT.unindent
        Lists Virtual Routers continuously
    EOT

    command :top, top_desc, [:filterflag, nil], :options=>list_options do
        helper.list_pool(options, true, args[0])
    end
end
