#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2021, 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
    LIB_LOCATION      = '/usr/lib/one'
    RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
    GEMS_LOCATION     = '/usr/share/one/gems'
    REMOTES_LOCATION  = '/var/lib/one/remotes'
else
    LIB_LOCATION      = ONE_LOCATION + '/lib'
    RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
    GEMS_LOCATION     = ONE_LOCATION + '/share/gems'
    REMOTES_LOCATION  = ONE_LOCATION + '/var/remotes'
end

# %%RUBYGEMS_SETUP_BEGIN%%
if File.directory?(GEMS_LOCATION)
    real_gems_path = File.realpath(GEMS_LOCATION)
    if !defined?(Gem) || Gem.path != [real_gems_path]
        $LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }

        # Suppress warnings from Rubygems
        # https://github.com/OpenNebula/one/issues/5379
        begin
            verb = $VERBOSE
            $VERBOSE = nil
            require 'rubygems'
            Gem.use_paths(real_gems_path)
        ensure
            $VERBOSE = verb
        end
    end
end
# %%RUBYGEMS_SETUP_END%%

$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << RUBY_LIB_LOCATION + '/cli'
$LOAD_PATH << LIB_LOCATION      + '/oneprovision/lib'
$LOAD_PATH << LIB_LOCATION      + '/oneflow/lib'

require 'command_parser'

require 'one_helper'
require 'one_helper/onegroup_helper'
require 'one_helper/oneprovision_helper'
require 'one_helper/oneprovision_template_helper'

require 'oneprovision'

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

    helper = OneProvisionTemplateHelper.new

    before_proc do
        helper.set_client(options)
    end

    PLAIN = {
        :name => 'plain',
        :large => '--plain',
        :description => 'Update plain information'
    }

    ########################################################################
    # Global options
    ########################################################################

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

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

    set :format, :templateid, OneProvisionTemplateHelper.to_id_desc do |arg|
        helper.to_id(arg)
    end

    set :format,
        :templateid_list,
        OneProvisionTemplateHelper.list_to_id_desc do |arg|
        helper.list_to_id(arg)
    end

    set :format, :groupid, OneGroupHelper.to_id_desc do |arg|
        h = OneGroupHelper.new
        h.set_client(options)
        h.to_id(arg)
    end

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

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

    create_desc = <<-EOT.unindent
        Allocate a new template. File must be written in YAML.
    EOT

    command :create, create_desc, :template do
        rc = helper.create(args[0])

        if OpenNebula.is_error?(rc)
            STDERR.puts rc.message
            exit(-1)
        else
            puts "ID: #{rc}"
            0
        end
    end

    ###

    delete_desc = <<-EOT.unindent
        Delete a template
    EOT

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

    ###

    list_desc = <<-EOT.unindent
        List all avaliable templates
    EOT

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

    ###

    show_desc = <<-EOT.unindent
        Show template details
    EOT

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

    ###

    update_desc = <<-EOT.unindent
        Update template information
    EOT

    command :update,
            update_desc,
            :templateid,
            [:file, nil],
            :options => PLAIN do
        helper.perform_action(args[0], options, 'updated') do |obj|
            helper.update(obj, args[1], (options.key? :plain))
        end
    end

    ###

    instantiate_desc = <<-EOT.unindent
        Instantiate the template
    EOT

    command :instantiate,
            instantiate_desc,
            :templateid,
            [:extra_file, nil],
            :options => OneProvisionHelper::CREATE_OPTIONS do
        helper.perform_action(args[0], options, 'instantiated') do |obj|
            h = OneProvisionHelper.new
            h.parse_options(options)

            if options[:cleanup_timeout].nil?
                timeout = 20
            else
                timeout = options[:cleanup_timeout]
            end

            # Get skip mode
            if options.key? :skip_provision
                skip = :all
            elsif options.key? :skip_config
                skip = :config
            else
                skip = :none
            end

            rc = obj.instantiate(options[:inputs],
                                 (options.key? :cleanup),
                                 timeout,
                                 skip,
                                 options[:provider],
                                 args[1])

            if OpenNebula.is_error?(rc)
                STDERR.puts rc.message
                exit(-1)
            else
                puts "ID: #{rc}"
                0
            end
        end
    end

    ###

    chgrp_desc = <<-EOT.unindent
        Changes the Provision Template group
    EOT

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

    ###

    chown_desc = <<-EOT.unindent
        Changes the Provision Template owner and group
    EOT

    command :chown,
            chown_desc,
            [:range, :templateid_list],
            :userid,
            [:groupid, nil] do
        args[2].nil? ? gid = -1 : gid = args[2].to_i

        helper.perform_actions(args[0], options, 'Owner/Group changed') do |p|
            p.chown(args[1].to_i, gid)
        end
    end

    ###

    chmod_desc = <<-EOT.unindent
        Changes the Provision Template permissions
    EOT

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