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

Require a minimum Ruby version of 1.8.7.

This module defines basic helper functions used in the sampe codes
"""

require 'nokogiri'
require 'open-uri'

require 'rbvmomi'
# Use require_relative if vsanmgmt.api.rb file is copied to current work
# directory, otherwise, use 'require' if the file is in ruby path.
require_relative 'vsanmgmt.api'
#require 'vsanmgmt.api'

class RbVmomi::VIM::Vsan < RbVmomi::VIM
  VSAN_API_VC_ENDPOINT = '/vsanHealth'
  VSAN_API_ESXI_ENDPOINT = '/vsan'

  VSAN_VMODL_NAMESPACE = "urn:vsan"
  VIM_VMODL_NAMESPACE = "urn:vim25"

  VSAN_VMODL_VERSION_XML_URI = "/sdk/vsanServiceVersions.xml"
  VIM_VMODL_VERSION_XML_URI = "/sdk/vimServiceVersions.xml"

  def initialize(conn)
    @isVc = conn.serviceContent.about.apiType == 'VirtualCenter'
    @vsanStub = getVsanStub(conn)
  end

  # Construct a stub for vSAN API access using vCenter or ESXi sessions from
  # existing stubs. Corresponding vCenter or ESXi service endpoint is required.
  # vCenter service endpoint is used by default.
  def getVsanStub(conn)
    endpoint = @isVc ? VSAN_API_VC_ENDPOINT : VSAN_API_ESXI_ENDPOINT
    ns, version = getLatestVmodlInfo(conn)
    vsanStub = RbVmomi::VIM.new(
      :host => conn.host,
      :ns => ns,
      :rev => version,
      :ssl => conn.http.use_ssl?,
      :insecure => conn.http.verify_mode == OpenSSL::SSL::VERIFY_NONE,
      :port => (conn.http.use_ssl? ? 443 : 80),
      :path => endpoint,
    )
    vsanStub.cookie = conn.cookie
    return vsanStub
  end

  # Get the VMODL version by checking the existence of vSAN namespace.
  def getLatestVmodlInfo(conn)
    vsanVersion = getServiceVersionXml(conn,
      VSAN_VMODL_NAMESPACE, VSAN_VMODL_VERSION_XML_URI)
    if vsanVersion
      return VSAN_VMODL_NAMESPACE, vsanVersion
    else
      vimVersion = getServiceVersionXml(conn,
        VIM_VMODL_NAMESPACE, VIM_VMODL_VERSION_XML_URI)
      abort "Cannot fetch the #{VIM_VMODL_NAMESPACE} service version on #{conn.host}!" unless vimVersion
      return VIM_VMODL_NAMESPACE, vimVersion
    end
  end

  def getServiceVersionXml(conn, ns, serviceXmlUri)
    serviceName = ns.split(":")[1]
    versionXml = open(
      'https://' + conn.host + serviceXmlUri,
      { ssl_verify_mode: conn.http.verify_mode }
    )
    versionXmlDoc = Nokogiri::XML(versionXml)
    return versionXmlDoc.xpath('//name')[0].text == ns ?
      versionXmlDoc.xpath('//version')[0].text : nil
  rescue
    return nil
  end

  def getVsanMos
    if @isVc
      @vsanMos ||= {
        :vsanDiskManagementSystem => RbVmomi::VIM::VimClusterVsanVcDiskManagementSystem(
          @vsanStub,
          'vsan-disk-management-system'
        ),
        :vsanStrechedClusterSystem => RbVmomi::VIM::VimClusterVsanVcStretchedClusterSystem(
          @vsanStub,
          'vsan-stretched-cluster-system'
        ),
        :vsanClusterConfigSystem => RbVmomi::VIM::VsanVcClusterConfigSystem(
          @vsanStub,
          'vsan-cluster-config-system'
        ),
        :vsanPerformanceManager => RbVmomi::VIM::VsanPerformanceManager(
          @vsanStub,
          'vsan-performance-manager'
        ),
        :vsanClusterHealthSystem => RbVmomi::VIM::VsanVcClusterHealthSystem(
          @vsanStub,
          'vsan-cluster-health-system'
        ),
        :vsanUpgradeSystemEx => RbVmomi::VIM::VsanUpgradeSystemEx(
          @vsanStub,
          'vsan-upgrade-systemex'
        ),
        :vsanSpaceReportSystem => RbVmomi::VIM::VsanSpaceReportSystem(
          @vsanStub,
          'vsan-cluster-space-report-system'
        ),
        :vsanObjectSystem => RbVmomi::VIM::VsanObjectSystem(
          @vsanStub,
          'vsan-cluster-object-system'
        ),
        :vsanIscsiTargetSystem => RbVmomi::VIM::VsanIscsiTargetSystem(
          @vsanStub,
          'vsan-cluster-iscsi-target-system'
        ),
        :VsanVcsaDeployerSystem => RbVmomi::VIM::VsanVcsaDeployerSystem(
           @vsanStub,
           'vsan-vcsa-deployer-system'
        ),
        :VsanVdsSystem => RbVmomi::VIM::VsanVdsSystem(
           @vsanStub,
           'vsan-vds-system'
        ),
        :VsanCapabilitySystem => RbVmomi::VIM::VsanCapabilitySystem(
           @vsanStub,
           'vsan-vc-capability-system'
        ),
        :VsanMassCollector => RbVmomi::VIM::VsanMassCollector(
           @vsanStub,
           'vsan-mass-collector'
        ),
        :VsanPhoneHomeSystem => RbVmomi::VIM::VsanPhoneHomeSystem(
           @vsanStub,
           'vsan-phonehome-system'
        ),
        :VsanVumSystem => RbVmomi::VIM::VsanVumSystem(
           @vsanStub,
           'vsan-vum-system'
        )
      }
    else
      @vsanMos ||= {
        :vsanPerformanceManager => RbVmomi::VIM::VsanPerformanceManager(
          @vsanStub,
          'vsan-performance-manager'
        ),
        :vsanClusterHealthSystem => RbVmomi::VIM::VsanVcClusterHealthSystem(
          @vsanStub,
          'vsan-cluster-health-system'
        ),
        :vsanHealthSystem => RbVmomi::VIM::HostVsanHealthSystem(
          @vsanStub,
          'ha-vsan-health-system'
        ),
        :vsanObjectSystem => RbVmomi::VIM::VsanObjectSystem(
          @vsanStub,
          'vsan-object-system'
        ),
        :VsanVcsaDeployerSystem => RbVmomi::VIM::VsanVcsaDeployerSystem(
           @vsanStub,
           'vsan-vcsa-deployer-system'
        ),
        :VsanCapabilitySystem => RbVmomi::VIM::VsanCapabilitySystem(
           @vsanStub,
           'vsan-capability-system'
        ),
        :vsanSystemEx => RbVmomi::VIM::VsanSystemEx(@vsanStub, 'vsanSystemEx'),
        :VsanUpdateManager => RbVmomi::VIM::VsanUpdateManager(
          @vsanStub,
          'vsan-update-manager'
        ),
        :vsanIscsiTargetSystem => RbVmomi::VIM::VsanIscsiTargetSystem(
          @vsanStub,
          'vsan-cluster-iscsi-target-system'
        )
      }
    end
  end

  def method_missing(method_id, *args, &block)
    if getVsanMos.key?(method_id)
       getVsanMos[method_id]
    else
       super
    end
  end

  def respond_to?(method_id, include_private = false)
    if getVsanMos.key?(method_id)
       true
    else
       super
    end
  end
end

RbVmomi::VIM
class RbVmomi::VIM
  def vsan
    @vsan ||= RbVmomi::VIM::Vsan.new(self)
  end

  def pbm
     @pbm ||= RbVmomi::PBM.connect self, :insecure => true
  end
end

RbVmomi::VIM::Task
class RbVmomi::VIM::Task
  # Convert a vSAN Task to a Task MO binding to vCenter service
  def onConnection(conn)
    return RbVmomi::VIM::Task(conn, self._ref)
  end
end
