#!/usr/bin/env ruby
"""
Copyright 2016-2017 VMware, Inc.  All rights reserved.

Require a minimum Ruby version of 2.3.0.

This file includes sample codes for vSAN iSCSI service Ruby binding
accessing from vCenter. An example of vSAN iSCSI service enable/disable,
target and LUN creation/deletion are included in this sample code.

NOTE: using vSAN iSCSI target service API requires a minimal
vim.version.version11 Stub.

"""

require 'rbvmomi'
require 'rbvmomi/trollop'
require 'rbvmomi/pbm'

require_relative 'vsanmgmt.api'
require_relative 'vsanapiutils'

opts = Trollop.options do
  banner <<-EOS
Check health summary and print cluster status.

Usage:
    vsanapisamples.rb [options] CLUSTER

VIM connection options:
    EOS

    rbvmomi_connection_opts

    text <<-EOS

Other options:
  EOS
end

Trollop.die("must specify host") unless opts[:host]

def get_cluster_instance(cluster_name, service_instance)
  cluster = nil
  datacenters = service_instance.content.rootFolder.childEntity
  datacenters.each do |datacenter|
    cluster = datacenter.hostFolder.childEntity.find {|x| x.name==cluster_name}
    return cluster if cluster
  end
end

def get_vsan_storage_policy(conn)
  pm = conn.pbm.serviceContent.profileManager
  profile_ids = pm.PbmQueryProfile(:resourceType=>{:resourceType=>"STORAGE"})
  profiles = pm.PbmRetrieveContent(:profileIds=>profile_ids)
  profiles.each do |profile|
    # vSAN default storage profile possesses a unique profile ID of
    # 'aa6d5a82-1c88-45da-85d3-3d74b91a5bad' across different releases.
    # Other profiles may also be looked up when needed to apply to vSAN
    # iSCSI services.
    if profile.profileId.uniqueId == 'aa6d5a82-1c88-45da-85d3-3d74b91a5bad'
      return VIM::VirtualMachineDefinedProfileSpec(
               :profileId => profile.profileId.uniqueId)
    end
  end
  return nil
end

def wait_for_task(vsan_task, conn, ops)
  vc_task = vsan_task.onConnection(conn)
  begin
    vc_task.wait_for_completion
    puts "#{ops} task completed with state: #{vc_task.info.state}"
  rescue Exception => ex
    pp ex
    raise
  end
end

VIM = RbVmomi::VIM
opts[:ssl] = true
opts[:insecure] = true
conn = VIM.connect(opts)

about_info = conn.serviceContent.about
api_version = about_info.apiVersion.split('.')[0]
if Gem::Version.new(api_version) >= Gem::Version.new('6.5')
  abort "The vCenter/ESXi with version #{aboutInfo.apiVersion} \
         (lower than 6.5) is not supported."
end

# Find the cluster instance.
cluster_name = ARGV[0] or Trollop.die("no cluster name given")
cluster = get_cluster_instance(cluster_name, conn.serviceInstance)
abort "Cluster #{cluster_name} is not found for #{opts[:host]}" unless cluster

# Construct the corresponding vSAN systems for iSCSI operations.
vsan = conn.vsan
vits = vsan.vsanIscsiTargetSystem
vccs = vsan.vsanClusterConfigSystem

vsan_policy = get_vsan_storage_policy(conn)
abort "Cannot find the vSAN Default Storage Policy..." unless vsan_policy

# Enable iSCSI service through vSAN Cluster Reconfiguration API, and the config
# port defaults to 3260 and can be customized.
vsan_config_Spec = VIM::VsanIscsiTargetServiceDefaultConfigSpec(
                      :networkInterface => "vmk0",
                      :port => 2300)
vit_enable_spec = VIM::VsanIscsiTargetServiceSpec(
                      :homeObjectStoragePolicy => vsan_policy,
                      :defaultConfig => vsan_config_Spec,
                      :enabled => true)
cluster_reconfig_spec = VIM::VimVsanReconfigSpec(:iscsiSpec => vit_enable_spec)
# Note: vSAN Cluster Reconfiguration API only supported on VC, not on ESX hosts
vit_enable_task = vccs.VsanClusterReconfig(
                      :cluster => cluster,
                      :vsanReconfigSpec => cluster_reconfig_spec)
wait_for_task(vit_enable_task, conn, "Enable vSAN iSCSI service")

# Create vSAN iSCSI targets and an associated LUN with the size of 1GB.
target_alias = "sampleTarget"
target_spec = VIM::VsanIscsiTargetSpec(
                  :alias => target_alias,
                  :iqn => 'iqn.2015-08.com.vmware:vit.target1')
create_target_task = vits.VsanVitAddIscsiTarget(
                         :cluster => cluster,
                         :targetSpec => target_spec)
wait_for_task(create_target_task, conn, "Create vSAN iSCSI target")

lun_size = 1 * 1024 * 1024 * 1024 # 1GB
lun_spec = VIM::VsanIscsiLUNSpec(
               :lunId => 0,
               :lunSize => lun_size,
               :storagePolicy => vsan_policy)
create_lun_task = vits.VsanVitAddIscsiLUN(
                      :cluster => cluster,
                      :targetAlias => target_alias,
                      :lunSpec => lun_spec)
wait_for_task(create_lun_task, conn, "Create vSAN iSCSI LUN")

# Remove vSAN iSCSI LUN from target.
remove_lun_task = vits.VsanVitRemoveIscsiLUN(
                        :cluster => cluster,
                        :targetAlias => target_alias,
                        :lunId => 0)
wait_for_task(remove_lun_task, conn, "Remove vSAN iSCSI LUN")

# Remove vSAN iSCSI target.
remove_target_task = vits.VsanVitRemoveIscsiTarget(
                        :cluster => cluster,
                        :targetAlias => target_alias)
wait_for_task(remove_target_task, conn, "Remove vSAN iSCSI target")

# Disable iSCSI service through vSAN Cluster Reconfiguration API.
vit_disable_spec = VIM::VsanIscsiTargetServiceSpec(:enabled => false)
cluster_reconfig_spec = VIM::VimVsanReconfigSpec(:iscsiSpec => vit_disable_spec)
vit_disable_task = vccs.VsanClusterReconfig(
                       :cluster => cluster,
                       :vsanReconfigSpec => cluster_reconfig_spec)
wait_for_task(vit_disable_task, conn, "Disable vSAN iSCSI service")
