#!/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 !defined?(ONE_LOCATION)

if !ONE_LOCATION
    RUBY_LIB_LOCATION="/usr/lib/one/ruby" if !defined?(RUBY_LIB_LOCATION)
else
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" if !defined?(RUBY_LIB_LOCATION)
end

$: << RUBY_LIB_LOCATION
$: << File.dirname(__FILE__)

require 'vcenter_driver'

drv_action_enc = ARGV[0]
drv_action = OpenNebula::XMLElement.new
drv_action.initialize_xml(Base64.decode64(drv_action_enc), 'VM')

#Get more VM's info from OpenNebula
vm_id           = drv_action["ID"]
one_vm          = VCenterDriver::VIHelper.one_item(OpenNebula::VirtualMachine, vm_id)
vc_cluster_name = one_vm["HISTORY_RECORDS/HISTORY[last()]/HOSTNAME"]

# Get host information
host = VCenterDriver::VIHelper.find_by_name(OpenNebula::HostPool, vc_cluster_name)
host_id = host['ID']

# Create VM object
vm_ref = drv_action["DEPLOY_ID"]
vi_client = VCenterDriver::VIClient.new_from_host(host_id)
vm = VCenterDriver::VirtualMachine.new_from_ref(vm_ref, vi_client)
vm.one_item = one_vm

# Check if clean operation is due to a hotplug detach nic
hotplug_nic = drv_action.retrieve_xmlelements("TEMPLATE/NIC[ATTACH=\"YES\"]").first rescue nil

if hotplug_nic  # A nic has been hotplug detached
    pg_name     = hotplug_nic["BRIDGE"]
    switch_name = hotplug_nic["VCENTER_SWITCH_NAME"]
    vnet_ref    = hotplug_nic["VCENTER_NET_REF"]

    if hotplug_nic["VCENTER_PORTGROUP_TYPE"] == "Port Group"
        esx_host = VCenterDriver::ESXHost.new_from_ref(vm["runtime.host._ref"], vi_client)

        begin
            esx_host.lock # Exclusive lock for ESX host operation

            if esx_host.pg_exists(pg_name)
                swname = esx_host.remove_pg(pg_name)
                exit if !swname || switch_name != swname

                # We must update XML so the VCENTER_NET_REF is unset
                VCenterDriver::Network.remove_net_ref(hotplug_nic["NETWORK_ID"])

                exit if !esx_host.vss_exists(switch_name)
                swname = esx_host.remove_vss(switch_name)
            end

        rescue Exception => e
            raise e
        ensure
            esx_host.unlock # Remove lock
            vi_client.close_connection if vi_client
        end
    end

    if hotplug_nic["VCENTER_PORTGROUP_TYPE"] == "Distributed Port Group"
        begin
            dc = vm.cluster.get_dc # Get datacenter

            dc.lock

            # Explore network folder in search of dpg and dvs
            net_folder = dc.network_folder
            net_folder.fetch!

            # Get distributed port group if it exists
            dpg = dc.dpg_exists(pg_name, net_folder)
            dc.remove_dpg(dpg) if dpg

            # We must update XML so the VCENTER_NET_REF is unset
            VCenterDriver::Network.remove_net_ref(hotplug_nic["NETWORK_ID"])

            # Get distributed virtual switch and try to remove it
            dvs = dc.dvs_exists(switch_name, net_folder)
            dc.remove_dvs(dvs) if dvs

        rescue Exception => e
            #TODO rollback
            raise e
        ensure
            dc.unlock if dc
        end
    end

else # VM is being terminated

    # If vm shutdown has been called
    if one_vm["/VM/LCM_STATE"] == "12"

        esx_host = VCenterDriver::ESXHost.new_from_ref(vm["runtime.host._ref"], vi_client)
        vm.detach_all_nics # Detach all NICs to prevent Resource in use when deleting

        begin
            esx_host.lock # Exclusive lock for ESX host operation
            nics = one_vm.retrieve_xmlelements("TEMPLATE/NIC[VN_MAD=\"vcenter\"]")
            nics.each do |nic|
                pg_name     = nic["BRIDGE"]
                switch_name = nic["VCENTER_SWITCH_NAME"]
                vnet_ref    = nic["VCENTER_NET_REF"]

                if nic["VCENTER_PORTGROUP_TYPE"] == "Port Group"
                    begin
                        next if !esx_host.pg_exists(pg_name)
                        swname = esx_host.remove_pg(pg_name)
                        next if !swname || switch_name != swname

                        # We must update XML so the VCENTER_NET_REF is unset
                        VCenterDriver::Network.remove_net_ref(nic["NETWORK_ID"])

                        next if !esx_host.vss_exists(switch_name)
                        swname = esx_host.remove_vss(switch_name)
                    rescue Exception => e
                        raise e
                    end
                end

                if nic["VCENTER_PORTGROUP_TYPE"] == "Distributed Port Group"
                    begin
                        dc = vm.cluster.get_dc # Get datacenter
                        dc.lock

                        # Explore network folder in search of dpg and dvs
                        net_folder = dc.network_folder
                        net_folder.fetch!

                        # Try to remove distributed port group if it exists
                        dpg = dc.dpg_exists(pg_name, net_folder)
                        next if !dpg
                        dc.remove_dpg(dpg)

                        # We must update XML so the VCENTER_NET_REF is unset
                        VCenterDriver::Network.remove_net_ref(nic["NETWORK_ID"])

                        # Get distributed virtual switch and try to remove it
                        dvs = dc.dvs_exists(switch_name, net_folder)
                        next if !dvs
                        dc.remove_dvs(dvs) if dvs

                    rescue Exception => e
                        raise e
                    ensure
                        dc.unlock if dc
                    end
                end
            end
        rescue Exception => e
            raise e
        ensure
            esx_host.unlock if esx_host
            vi_client.close_connection if vi_client
        end
    end
end