#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2015, OpenNebula Project (OpenNebula.org), C12G Labs        #
#                                                                            #
# 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"
    REMOTES_LOCATION="/var/lib/one/remotes/"
else
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
    REMOTES_LOCATION=ONE_LOCATION+"/var/remotes/"
end

$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+"/cli"
$: << REMOTES_LOCATION+"vmm/vcenter/"

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

cmd=CommandParser::CmdParser.new(ARGV) do

    usage "`onevcenter` <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


    VCENTER = {
        :name   => "vcenter",
        :large  => "--vcenter vCenter" ,
        :description => "The vCenter hostname",
        :format => String
    }

    USER = {
        :name   => "vuser",
        :large  => "--vuser username" ,
        :description => "The username to interact with vCenter",
        :format => String
    }

    PASS = {
        :name   => "vpass",
        :large  => "--vpass password",
        :description => "The password for the user",
        :format => String
    }

    ############################################################################
    # Import clusters
    ############################################################################
    hosts_desc = <<-EOT.unindent
        Import vCenter clusters as OpenNebula hosts
    EOT

    command :hosts, hosts_desc, :options=>[ VCENTER, USER, PASS ] do
        if  options[:vuser].nil? ||
            options[:vpass].nil? ||
            options[:vcenter].nil?
            STDERR.puts "vCenter connection parameters are mandatory to import"\
                        " host:\n"\
                        "\t --vcenter vCenter hostname\n"\
                        "\t --vuser username to login in vcenter\n"\
                        "\t --vpass password for the user"
            exit -1
        end

        begin
            STDOUT.print "\nConnecting to vCenter: #{options[:vcenter]}..."

            vc = VCenterDriver::VIClient.new_connection(
                    :user     => options[:vuser],
                    :password => options[:vpass],
                    :host     => options[:vcenter])

            STDOUT.print "done!\n\n"

            STDOUT.print "Exploring vCenter resources..."

            rs = vc.hierarchy

            STDOUT.print "done!\n\n"

            rs.each {|dc, cluster|
                STDOUT.print "Do you want to process datacenter #{dc} [y/n]? "

                next if STDIN.gets.strip.downcase != 'y'

                if cluster.empty?
                    STDOUT.puts "    No clusters found in #{dc}..."
                    next
                end

                cluster.each{ |c|
                    STDOUT.print "  * Import cluster #{c} [y/n]? "

                    next if STDIN.gets.strip.downcase != 'y'

                    r, m = VCenterDriver::VCenterHost.to_one(c, vc)

                    if r == 0
                        STDOUT.puts "    OpenNebula host #{c} with id #{m}"\
                            " successfully created."
                    else
                        STDOUT.puts "    Error: #{m}"
                    end

                    STDOUT.puts
                }
            }
        rescue Exception => e
            STDOUT.puts "error: #{e.message}"
            exit -1
        end

        exit 0
    end

    templates_desc = <<-EOT.unindent
        Import vCenter VM Templates into OpenNebula
    EOT

    command :templates, templates_desc, :options=>[ VCENTER, USER, PASS ] do
        if  options[:vuser].nil? ||
            options[:vpass].nil? ||
            options[:vcenter].nil?
            STDERR.puts "vCenter connection parameters are mandatory to import"\
                        " VM templates:\n"\
                        "\t --vcenter vCenter hostname\n"\
                        "\t --vuser username to login in vcenter\n"\
                        "\t --vpass password for the user"
            exit -1
        end

        begin
            STDOUT.print "\nConnecting to vCenter: #{options[:vcenter]}..."

            vc = VCenterDriver::VIClient.new_connection(
                    :user     => options[:vuser],
                    :password => options[:vpass],
                    :host     => options[:vcenter])

            STDOUT.print "done!\n\n"

            STDOUT.print "Looking for VM Templates..."

            rs = vc.vm_templates

            STDOUT.print "done!\n"

            rs.each {|dc, tmps|
                STDOUT.print "\nDo you want to process datacenter #{dc} [y/n]? "

                next if STDIN.gets.strip.downcase != 'y'

                if tmps.empty?
                    STDOUT.print "    No new VM Templates found in #{dc}...\n\n"
                    next
                end

                tmps.each{ |t|
                    STDOUT.print "\n  * VM Template found:\n"\
                                 "      - Name   : #{t[:name]}\n"\
                                 "      - UUID   : #{t[:uuid]}\n"\
                                 "      - Cluster: #{t[:host]}\n"\
                                 "    Import this VM template [y/n]? "

                    next if STDIN.gets.strip.downcase != 'y'

                    one_t = ::OpenNebula::Template.new(
                                ::OpenNebula::Template.build_xml, vc.one)

                    rc = one_t.allocate(t[:one])

                    if ::OpenNebula.is_error?(rc)
                        STDOUT.puts "    Error creating template: #{rc.message}\n"
                    else
                        STDOUT.puts "    OpenNebula template #{one_t.id} created!\n"
                    end
                }
            }
        rescue Exception => e
            STDOUT.puts "error: #{e.message}"
            exit -1
        end

        exit 0
    end

    vms_desc = <<-EOT.unindent
        Import vCenter running Virtual Machines into OpenNebula
    EOT

    command :vms, vms_desc, :options=>[ VCENTER, USER, PASS ] do
        if  options[:vuser].nil? ||
            options[:vpass].nil? ||
            options[:vcenter].nil?
            STDERR.puts "vCenter connection parameters are mandatory to import"\
                        " VM templates:\n"\
                        "\t --vcenter vCenter hostname\n"\
                        "\t --vuser username to login in vcenter\n"\
                        "\t --vpass password for the user"
            exit -1
        end

        begin
            STDOUT.print "\nConnecting to vCenter: #{options[:vcenter]}..."

            vc = VCenterDriver::VIClient.new_connection(
                    :user     => options[:vuser],
                    :password => options[:vpass],
                    :host     => options[:vcenter])

            STDOUT.print "done!\n\n"

            STDOUT.print "Looking for running Virtual Machines..."

            rs = vc.running_vms

            STDOUT.print "done!\n"

            rs.each {|dc, tmps|
                STDOUT.print "\nDo you want to process datacenter #{dc} [y/n]? "

                next if STDIN.gets.strip.downcase != 'y'

                if tmps.empty?
                    STDOUT.print "    No new running Virtual Machines found in"\
                                 " #{dc}...\n\n"
                    next
                end

                tmps.each{ |v|
                    STDOUT.print "\n  * Running Virtual Machine found:\n"\
                                 "      - Name   : #{v[:name]}\n"\
                                 "      - UUID   : #{v[:uuid]}\n"\
                                 "      - Cluster: #{v[:host]}\n"\
                                 "    Import this Virtual Machine [y/n]? "

                    next if STDIN.gets.strip.downcase != 'y'

                    one_v = ::OpenNebula::VirtualMachine.new(
                                ::OpenNebula::VirtualMachine.build_xml, vc.one)

                    rc = one_v.allocate(v[:one])

                    if ::OpenNebula.is_error?(rc)
                        STDOUT.puts "    Error creating Virtual Machine: "\
                                    "#{rc.message}\n"
                    end

                    rc = one_v.deploy v[:host_id]

                    if ::OpenNebula.is_error?(rc)
                        STDOUT.puts "    Error creating Virtual Machine: "\
                                    "#{rc.message}\n"
                    else
                        STDOUT.puts "    OpenNebula VM #{one_v.id} "\
                                    "created!\n"
                    end
                }
            }
        rescue Exception => e
            STDOUT.puts "error: #{e.message}"
            exit -1
        end

        exit 0
    end

    network_desc = <<-EOT.unindent
        Import vCenter networks into OpenNebula
    EOT

    command :networks, network_desc, :options=>[ VCENTER, USER, PASS ] do
        if  options[:vuser].nil? ||
            options[:vpass].nil? ||
            options[:vcenter].nil?
            STDERR.puts "vCenter connection parameters are mandatory to import"\
                        " vCenter networks:\n"\
                        "\t --vcenter vCenter hostname\n"\
                        "\t --vuser username to login in vcenter\n"\
                        "\t --vpass password for the user"
            exit -1
        end

        begin
            STDOUT.print "\nConnecting to vCenter: #{options[:vcenter]}..."

            vc = VCenterDriver::VIClient.new_connection(
                    :user     => options[:vuser],
                    :password => options[:vpass],
                    :host     => options[:vcenter])

            STDOUT.print "done!\n\n"

            STDOUT.print "Looking for vCenter networks..."

            rs = vc.vcenter_networks

            STDOUT.print "done!\n"

            rs.each {|dc, tmps|
                STDOUT.print "\nDo you want to process datacenter #{dc} [y/n]? "

                next if STDIN.gets.strip.downcase != 'y'

                if tmps.empty?
                    STDOUT.print "    No new Networks found in #{dc}...\n\n"
                    next
                end

                tmps.each{ |n|
                    print_str =  "\n  * Network found:\n"\
                                 "      - Name    : #{n[:name]}\n"\
                                 "      - Type    : #{n[:type]}\n"
                    print_str += "      - VLAN ID : #{n[:vlan]}\n" if n[:vlan]
                    print_str += "    Import this Network [y/n]? "

                    STDOUT.print print_str

                    next if STDIN.gets.strip.downcase != 'y'

                    # Size

                    STDOUT.print "    How many VMs are you planning"\
                                 " to fit into this network [255]? "

                    size = STDIN.gets.strip

                    size = "255" if size.to_i.to_s != size

                    # Type

                    STDOUT.print "    What type of Virtual Network"\
                                 " do you want to create (IPv[4],IPv[6]"\
                                 ",[E]thernet) ?"

                    type = STDIN.gets.strip

                    ar_str =  "\nAR=[TYPE=\""

                    case type.downcase
                        when "4"
                            ar_str += "IP4\""
                            STDOUT.print "    Please input the first IP "\
                                         "in the range: "
                            ip = STDIN.gets.strip
                            ar_str += ",IP=" + ip

                            STDOUT.print "    Please input the first MAC "\
                                         "in the range [Enter for default]: "
                            mac = STDIN.gets.strip
                            ar_str += ",MAC=" + mac if !mac.empty?
                        when "6"
                            ar_str += "IP6\""
                            STDOUT.print "    Please input the first MAC "\
                                         "in the range [Enter for default]: "
                            mac = STDIN.gets.strip
                            ar_str += ",MAC=" + mac if !mac.empty?

                            STDOUT.print "    Please input the GLOBAL PREFIX "\
                                         "[Enter for default]: "
                            gp = STDIN.gets.strip
                            ar_str += ",GLOBAL_PREFIX=" + gp if !gp.empty?

                            STDOUT.print "    Please input the ULA PREFIX "\
                                         "[Enter for default]: "
                            up = STDIN.gets.strip
                            ar_str += ",ULA_PREFIX=" + up if !up.empty?
                        when "e"
                            ar_str += "ETHER\""
                            STDOUT.print "    Please input the first MAC "\
                                 "in the range [Enter for default]: "
                            mac = STDIN.gets.strip
                            ar_str += ",MAC=" + mac if !mac.empty?
                        else
                         STDOUT.puts "    Type [#{type}] not supported,"\
                                    " defaulting to Ethernet."
                         ar_str += "ETHER\""
                         STDOUT.print "    Please input the first MAC "\
                              "in the range [Enter for default]: "
                         mac = STDIN.gets.strip
                         ar_str += ",MAC=" + mac if !mac.empty?
                    end

                    ar_str += ",SIZE = \"#{size}\"]"

                    one_vn = ::OpenNebula::VirtualNetwork.new(
                                ::OpenNebula::Template.build_xml, vc.one)

                    vnet_template =  n[:one] + ar_str
                    
                    rc = one_vn.allocate(vnet_template)

                    if ::OpenNebula.is_error?(rc)
                        STDOUT.puts "    Error creating virtual network: " +
                                    " #{rc.message}\n"
                    else
                        STDOUT.puts "    OpenNebula virtual network " + 
                                    "#{one_vn.id} created with size #{size}!\n"
                    end
                }
            }
        rescue Exception => e
            STDOUT.puts "error: #{e.message}"
            exit -1
        end

        exit 0
    end
end
