/*
 * ******************************************************
 * Copyright 2016-2021 VMware, Inc.  All Rights Reserved.
 * ******************************************************
 */

package com.vmware.vsan.connection;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.vmware.connection.BasicConnection;
import com.vmware.connection.Connection;
import com.vmware.connection.ConnectionException;
import com.vmware.connection.ConnectionMalformedUrlException;
import com.vmware.vim25.AboutInfo;
import com.vmware.vim25.InvalidLocaleFaultMsg;
import com.vmware.vim25.InvalidPropertyFaultMsg;
import com.vmware.vsan.sdk.ManagedObjectReference;
import com.vmware.vsan.sdk.VsanhealthPortType;
import com.vmware.vsan.sdk.VsanhealthService;

/**
 * This is the VSAN health connection extends the basic vCenter server connection
 * class to provide VSAN health service connection service
 *
 * VSAN health utilizes the per-authenticated HTTP session cookie from the vCenter
 * server for its authentication and sets the  VsanhealthPortType to use the same
 * session to make further health API calls
 *
 *
 * @see BasicConnection
 */
public class VsanHealthConnection extends BasicConnection {
   private static final String VSAN_VC_HEALTH_SERVICEINSTANCETYPE = "VsanVcClusterHealthSystem";
   private static final String VSAN_VC_HEALTH_SERVICEINSTANCEVALUE = "vsan-cluster-health-system";
   private static final String VSAN_HOST_HEALTH_SERVICEINSTANCETYPE = "HostVsanHealthSystem";
   private static final String VSAN_HOST_HEALTH_SERVICEINSTANCEVALUE = "ha-vsan-health-system";
   private static final String VSAN_VC_DISK_MGMT_SERVICEINSTANCETYPE = "VimClusterVsanVcDiskManagementSystem";
   private static final String VSAN_VC_DISK_MGMT_SERVICEINSTANCEVALUE = "vsan-disk-management-system";
   private static final String VSAN_VC_STRETCHED_CLUSTER_SERVICEINSTANCETYPE = "VimClusterVsanVcStretchedClusterSystem";
   private static final String VSAN_VC_STRETCHED_CLUSTER_SERVICEINSTANCEVALUE = "vsan-stretched-cluster-system";
   private static final String VSAN_VC_CLUSTER_CONFIG_SERVICEINSTANCETYPE = "VsanVcClusterConfigSystem";
   private static final String VSAN_VC_CLUSTER_CONFIG_SERVICEINSTANCEVALUE = "vsan-cluster-config-system";
   private static final String VSAN_VC_ISCSI_TARGET_SERVICEINSTANCETYPE = "VsanIscsiTargetSystem";
   private static final String VSAN_VC_ISCSI_TARGET_SERVICEINSTANCEVALUE = "vsan-cluster-iscsi-target-system";
   private static final String VSAN_PERF_MANAGER_SERVICEINSTANCETYPE = "VsanPerformanceManager";
   private static final String VSAN_PERF_MANAGER_SERVICEINSTANCEVALUE = "vsan-performance-manager";
   private static final String VSAN_UPGRADE_SYS_EX_SERVICEINSTANCETYPE = "VsanUpgradeSystemEx";
   private static final String VSAN_UPGRADE_SYS_EX_SERVICEINSTANCEVALUE = "vsan-upgrade-systemex";
   private static final String VSAN_SPACE_REPORT_SERVICEINSTANCETYPE = "VsanSpaceReportSystem";
   private static final String VSAN_SPACE_REPORT_SERVICEINSTANCEVALUE = "vsan-cluster-space-report-system";
   private static final String VSAN_OBJECT_SYSTEM_SERVICEINSTANCETYPE = "VsanObjectSystem";
   private static final String VSAN_CLUSTER_OBJECT_SYSTEM_SERVICEINSTANCEVALUE = "vsan-cluster-object-system";
   private static final String VSAN_OBJECT_SYSTEM_SERVICEINSTANCEVALUE = "vsan-object-system";
   private static final String VSAN_VCSA_DEPLOYER_SYSTEM_SERVICEINSTANCETYPE = "VsanVcsaDeployerSystem";
   private static final String VSAN_VCSA_DEPLOYER_SYSTEM_SERVICEINSTANCEVALUE = "vsan-vcsa-deployer-system";
   private static final String VSAN_SYSTEM_EX_SERVICEINSTANCETYPE = "vsanSystemEx";
   private static final String VSAN_SYSTEM_EX_SERVICEINSTANCEVALUE = "vsanSystemEx";
   private static final String VSAN_UPDATE_MANAGER_SERVICEINSTANCETYPE = "VsanUpdateManager";
   private static final String VSAN_UPDATE_MANAGER_SERVICEINSTANCEVALUE = "vsan-update-manager";
   private static final String VSAN_VDS_SYSTEM_SERVICEINSTANCETYPE = "VsanVdsSystem";
   private static final String VSAN_VDS_SYSTEM_SERVICEINSTANCEVALUE = "vsan-vds-system";
   private static final String VSAN_CAPABILITY_SYSTEM_SERVICEINSTANCETYPE = "VsanCapabilitySystem";
   private static final String VSAN_VC_CAPABILITY_SYSTEM_SERVICEINSTANCEVALUE = "vsan-vc-capability-system";
   private static final String VSAN_CAPABILITY_SYSTEM_SERVICEINSTANCEVALUE = "vsan-capability-system";
   private static final String VSAN_MASS_COLLECTOR_SERVICEINSTANCETYPE = "VsanMassCollector";
   private static final String VSAN_MASS_COLLECTOR_SERVICEINSTANCEVALUE = "vsan-mass-collector";
   private static final String VSAN_PHONEHOME_SYSTEM_SERVICEINSTANCETYPE = "VsanPhoneHomeSystem";
   private static final String VSAN_PHONEHOME_SYSTEM_SERVICEINSTANCEVALUE = "vsan-phonehome-system";
   private static final String VSAN_VUM_SYSTEM_SERVICEINSTANCETYPE = "VsanVumSystem";
   private static final String VSAN_VUM_SYSTEM_SERVICEINSTANCEVALUE = "vsan-vum-system";
   private static final String VSAN_VC_FILE_SERVICE_SYSTEM_SERVICEINSTANCETYPE = "VsanFileServiceSystem";
   private static final String VSAN_VC_FILE_SERVICE_SYSTEM_SERVICEINSTANCEVALUE = "vsan-cluster-file-service-system";
   private static final String VSAN_VC_REMOTE_DATASTORE_SYSTEM_SERVICEINSTANCETYPE = "VsanRemoteDatastoreSystem";
   private static final String VSAN_VC_REMOTE_DATASTORE_SYSTEM_SERVICEINSTANCEVALUE = "vsan-remote-datastore-system";
   private static final String VSAN_IO_INSIGHT_MANAGER_SERVICEINSTANCETYPE = "VsanIoInsightManager";
   private static final String VSAN_VC_IO_INSIGHT_MANAGER_SERVICEINSTANCEVALUE = "vsan-cluster-ioinsight-manager";
   private static final String VSAN_IO_INSIGHT_MANAGER_SERVICEINSTANCEVALUE = "vsan-host-ioinsight-manager";
   private static final String VSAN_VC_DIAGNOSTIC_SYSTEM_SERVICEINSTANCEVALUE = "vsan-cluster-diagnostics-system";
   private static final String VSAN_VC_CLUSTER_POWER_SYSTEM_SERVICEINSTANCEVALUE = "vsan-cluster-power-system";
   private static final String VSAN_VC_DIAGNOSTIC_SYSTEM_SERVICEINSTANCETYPE = "VsanDiagnosticsSystem";
   private static final String VSAN_VC_CLUSTER_POWER_SYSTEM_SERVICEINSTANCETYPE = "VsanClusterPowerSystem";
   private static Map<String, String> vsanMoTypeValueMap = new HashMap<String, String>();
   static {
      vsanMoTypeValueMap.put(VSAN_VC_HEALTH_SERVICEINSTANCEVALUE, VSAN_VC_HEALTH_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_HOST_HEALTH_SERVICEINSTANCEVALUE, VSAN_HOST_HEALTH_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_DISK_MGMT_SERVICEINSTANCEVALUE, VSAN_VC_DISK_MGMT_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_STRETCHED_CLUSTER_SERVICEINSTANCEVALUE, VSAN_VC_STRETCHED_CLUSTER_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_CLUSTER_CONFIG_SERVICEINSTANCEVALUE, VSAN_VC_CLUSTER_CONFIG_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_ISCSI_TARGET_SERVICEINSTANCEVALUE, VSAN_VC_ISCSI_TARGET_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_PERF_MANAGER_SERVICEINSTANCEVALUE, VSAN_PERF_MANAGER_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_UPGRADE_SYS_EX_SERVICEINSTANCEVALUE, VSAN_UPGRADE_SYS_EX_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_SPACE_REPORT_SERVICEINSTANCEVALUE, VSAN_SPACE_REPORT_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_CLUSTER_OBJECT_SYSTEM_SERVICEINSTANCEVALUE, VSAN_OBJECT_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_OBJECT_SYSTEM_SERVICEINSTANCEVALUE, VSAN_OBJECT_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VCSA_DEPLOYER_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VCSA_DEPLOYER_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_CAPABILITY_SYSTEM_SERVICEINSTANCEVALUE, VSAN_CAPABILITY_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_CAPABILITY_SYSTEM_SERVICEINSTANCEVALUE, VSAN_CAPABILITY_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_SYSTEM_EX_SERVICEINSTANCEVALUE, VSAN_SYSTEM_EX_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VDS_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VDS_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_UPDATE_MANAGER_SERVICEINSTANCEVALUE, VSAN_UPDATE_MANAGER_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_MASS_COLLECTOR_SERVICEINSTANCEVALUE, VSAN_MASS_COLLECTOR_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_PHONEHOME_SYSTEM_SERVICEINSTANCEVALUE, VSAN_PHONEHOME_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VUM_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VUM_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_FILE_SERVICE_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VC_FILE_SERVICE_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_REMOTE_DATASTORE_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VC_REMOTE_DATASTORE_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_IO_INSIGHT_MANAGER_SERVICEINSTANCEVALUE, VSAN_IO_INSIGHT_MANAGER_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_IO_INSIGHT_MANAGER_SERVICEINSTANCEVALUE, VSAN_IO_INSIGHT_MANAGER_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_DIAGNOSTIC_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VC_DIAGNOSTIC_SYSTEM_SERVICEINSTANCETYPE);
      vsanMoTypeValueMap.put(VSAN_VC_CLUSTER_POWER_SYSTEM_SERVICEINSTANCEVALUE, VSAN_VC_CLUSTER_POWER_SYSTEM_SERVICEINSTANCETYPE);
   }
   private ManagedObjectReference vsanVcHealthSvcInstRef;
   private ManagedObjectReference vsanHostHealthSvcInstRef;
   private ManagedObjectReference vsanVcDiskMgrSvcInstRef;
   private ManagedObjectReference vsanStretchedClusterSvcInstRef;
   private ManagedObjectReference vsanVcClusterConfigSvcInstRef;
   private ManagedObjectReference vsanVcIscsiTargetSvcInstRef;
   private ManagedObjectReference vsanPerfMgrSvcInstRef;
   private ManagedObjectReference vsanSpaceReportSvcInstRef;
   private ManagedObjectReference vsanClusterObjectSystemInstRef;
   private ManagedObjectReference vsanObjSystemSvcInstRef;
   private ManagedObjectReference vsanUpgradeSystemExSvcInstRef;
   private ManagedObjectReference vsanVcsaDeployerSystemInstRef;
   private ManagedObjectReference vsanSystemExInstRef;
   private ManagedObjectReference vsanVdsSystemInstRef;
   private ManagedObjectReference vsanUpdateManagerInstRef;
   private ManagedObjectReference vsanVcCapabilitySystemInstRef;
   private ManagedObjectReference vsanCapabilitySystemInstRef;
   private ManagedObjectReference vsanMassCollectorInstRef;
   private ManagedObjectReference vsanPhonehomeSystemInstRef;
   private ManagedObjectReference vsanVumSystemInstRef;
   private ManagedObjectReference vsanFileServiceSystemInstRef;

   private URL vsanhealthurl;

   private VsanhealthService vsanHealthService = new VsanhealthService();
   private VsanhealthPortType vsanHealthPort = vsanHealthService.getVsanhealthPort();

   @SuppressWarnings({ "unchecked" })
   private void _connectToVsanHealth() throws InvalidLocaleFaultMsg,
         com.vmware.vsan.sdk.RuntimeFaultFaultMsg,
         InvalidPropertyFaultMsg, IOException, SAXException,
         ParserConfigurationException {
      System.out.println("Connecting to vSAN Health Service...");
      System.out.println("===========================================");

      Map vimServiceCtxHeaders = super.getHeaders();
      Map VsanHealthHeaders = new HashMap();
      VsanHealthHeaders.put(
         "Cookie",
         vimServiceCtxHeaders.get("Set-Cookie"));

      // Check if the connected host is VC or ESXi host
      // and set the vsan health URL endpoint as "/vsanHealth
      // for VC and set the "/vsan" for ESXi host
      AboutInfo aboutInfo = getServiceContent().getAbout();
      if (aboutInfo.getApiType().equals("HostAgent")) {
         setHostVsanHealthURL();
      } else {
         setVcVsanHealthURL();
      }

      vsanHealthService = new VsanhealthService();

      String vmodlNamespace = this.getVsanVmodlNamespace();
      String vmodlVersion = null;
      if (vmodlNamespace.startsWith("urn:vsan")) {
         vmodlVersion = vmodlNamespace.split("/")[1];
         vsanHealthPort = vsanHealthService.getVsanhealthPort();
      } else {
         vsanHealthPort = vsanHealthService.getVsanhealthVimPort();
      }

      Map<String, Object> vsanCtxt =
            ((BindingProvider) vsanHealthPort).getRequestContext();
      vsanCtxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
      if (vmodlVersion != null && vmodlVersion.equals("6.6")) {
         vsanCtxt.put(BindingProvider.SOAPACTION_USE_PROPERTY, true);
         vsanCtxt.put(BindingProvider.SOAPACTION_URI_PROPERTY, "urn:vsan/6.6");
      }
      vsanCtxt
            .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, vsanhealthurl.toString());
      vsanCtxt.put(MessageContext.HTTP_REQUEST_HEADERS, VsanHealthHeaders);
      System.out.println("vSAN Health Connection established...");
      System.out.println("============================================\n");
   }

   @Override
   public Connection connect() {
      if (!isConnected()) {
         super.connect();
         try {
            _connectToVsanHealth();
         } catch (Exception e) {
            Throwable cause = (e.getCause() != null)?e.getCause():e;
            throw new VsanHealthConnectionException(
               "failed to connect: " + e.getMessage() + " : " + cause.getMessage(),
                cause);
         }
      }
      return this;
   }

   @Override
   public Connection disconnect() {
      try {
         return super.disconnect();
      } finally {
         vsanHealthPort = null;
         vsanHealthService = null;
      }
   }

   /**
    * Returns the {@link VsanhealthPortType} instance for the vSAN health service
    *
    * @return
    */
   public VsanhealthPortType getVsanHealthPort() {
      return vsanHealthPort;
   }

   /**
    * Returns the {@link VsanhealthService} instance for the vSAN health service
    *
    * @return
    */
   public VsanhealthService getVsanHealthService() {
      return vsanHealthService;
   }

   /**
    * Query vSAN VMODL namespace and version.
    *
    * It checks the content of the https://<host>/sdk/vsanServiceVersions.xml
    * to determine the VMODL version of vCenter/host.
    *
    * @return "urn:vsan" if the VC or host version is vSAN 6.6
    *         "urn:vim25" if the VC or host version before vSAN 6.6
    * @throws IOException if failed to connect to host due to network issues.
    * @throws SAXException if failed to parse the vsanServiceVersions.xml.
    * @throws ParserConfigurationException
    */
   public String getVsanVmodlNamespace() throws IOException, SAXException,
          ParserConfigurationException {
      String USER_AGENT = "Mozilla/5.0";
      String vsanNsXmlFile = "https://" + vsanhealthurl.getHost() +
                             "/sdk/vsanServiceVersions.xml";
      HttpURLConnection connection = null;

      try {
         URL url = new URL(vsanNsXmlFile);
         connection = (HttpURLConnection) url.openConnection();
         connection.setRequestMethod("GET");
         connection.setRequestProperty("User-Agent", USER_AGENT);
         connection.setRequestProperty("Content-Language", "en-US");
         int responseCode = connection.getResponseCode();
         if (responseCode == 200) {
            DocumentBuilderFactory factory =
               DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(connection.getInputStream());
            doc.getDocumentElement().normalize();
            String ApiVersion =
               doc.getElementsByTagName("version").item(0).getTextContent();
            return "urn:vsan/" + ApiVersion;
         } else {
            return "urn:vim25";
         }
      } finally {
         if (connection != null) {
            connection.disconnect();
         }
      }
   }

   private ManagedObjectReference createVsanManagedObjectReference(String value) {
      String type = vsanMoTypeValueMap.get(value);
      if (value == null) {
         return null;
      }
      ManagedObjectReference ref = new ManagedObjectReference();
      ref.setType(type);
      ref.setValue(value);
      return ref;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN VC health service
    *
    * @return
    */
   public ManagedObjectReference getVsanVcHealthServiceInstanceReference() {
      if (vsanVcHealthSvcInstRef == null) {
         vsanVcHealthSvcInstRef =
            createVsanManagedObjectReference(VSAN_VC_HEALTH_SERVICEINSTANCEVALUE);
      }
      return vsanVcHealthSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN host health service
    *
    * @return
    */
   public ManagedObjectReference getVsanHostHealthServiceInstanceReference() {
      if (vsanHostHealthSvcInstRef == null) {
         vsanHostHealthSvcInstRef =
            createVsanManagedObjectReference(VSAN_HOST_HEALTH_SERVICEINSTANCEVALUE);
      }
      return vsanHostHealthSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN VC disk management service
    *
    * @return
    */
   public ManagedObjectReference getVsanVcDiskMgrServiceInstanceReference() {
      if (vsanVcDiskMgrSvcInstRef == null) {
         vsanVcDiskMgrSvcInstRef =
            createVsanManagedObjectReference(VSAN_VC_DISK_MGMT_SERVICEINSTANCEVALUE);
      }
      return vsanVcDiskMgrSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN stretched cluster service
    *
    * @return
    */
   public ManagedObjectReference getVsanStretchedClusterServiceInstanceReference() {
      if (vsanStretchedClusterSvcInstRef == null) {
         vsanStretchedClusterSvcInstRef =
            createVsanManagedObjectReference(VSAN_VC_STRETCHED_CLUSTER_SERVICEINSTANCEVALUE);
      }
      return vsanStretchedClusterSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN VC cluster configuration service
    *
    * @return
    */
   public ManagedObjectReference getVsanVcClusterConfigServiceInstanceReference() {
      if (vsanVcClusterConfigSvcInstRef == null) {
         vsanVcClusterConfigSvcInstRef =
            createVsanManagedObjectReference(VSAN_VC_CLUSTER_CONFIG_SERVICEINSTANCEVALUE);
      }
      return vsanVcClusterConfigSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN VC iSCSI Target service
    *
    * @return
    */
   public ManagedObjectReference getVsanVcIscsiTargetServiceInstanceReference() {
      if (vsanVcIscsiTargetSvcInstRef == null) {
         vsanVcIscsiTargetSvcInstRef =
            createVsanManagedObjectReference(VSAN_VC_ISCSI_TARGET_SERVICEINSTANCEVALUE);
      }
      return vsanVcIscsiTargetSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN space report service
    *
    * @return
    */
   public ManagedObjectReference getVsanSpaceReportServiceInstanceReference() {
      if (vsanSpaceReportSvcInstRef == null) {
         vsanSpaceReportSvcInstRef =
            createVsanManagedObjectReference(VSAN_SPACE_REPORT_SERVICEINSTANCEVALUE);
      }
      return vsanSpaceReportSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN cluster object system service.
    *
    * @return
    */
   public ManagedObjectReference getVsanClusterObjectSystemServiceInstanceReference() {
      if (vsanClusterObjectSystemInstRef == null) {
         vsanClusterObjectSystemInstRef =
            createVsanManagedObjectReference(
                  VSAN_CLUSTER_OBJECT_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanClusterObjectSystemInstRef;
   }


   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN object system service
    *
    * @return
    */
   public ManagedObjectReference getVsanObjectSystemServiceInstanceReference() {
      if (vsanObjSystemSvcInstRef == null) {
         vsanObjSystemSvcInstRef =
            createVsanManagedObjectReference(VSAN_OBJECT_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanObjSystemSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN upgrade system service
    *
    * @return
    */
   public ManagedObjectReference getVsanUpgradeSystemExServiceInstanceReference() {
      if (vsanUpgradeSystemExSvcInstRef == null) {
         vsanUpgradeSystemExSvcInstRef =
            createVsanManagedObjectReference(VSAN_UPGRADE_SYS_EX_SERVICEINSTANCEVALUE);
      }
      return vsanUpgradeSystemExSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance of the
    * vSAN performance service
    *
    * @return
    */
   public ManagedObjectReference getVsanPerfMgrServiceInstanceReference() {
      if (vsanPerfMgrSvcInstRef == null) {
         vsanPerfMgrSvcInstRef =
            createVsanManagedObjectReference(VSAN_PERF_MANAGER_SERVICEINSTANCEVALUE);
      }
      return vsanPerfMgrSvcInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN VCSA deployer system.
    *
    * @return
    */
   public ManagedObjectReference getVsanVcsaDeployerSystemServiceInstanceReference() {
      if (vsanVcsaDeployerSystemInstRef == null) {
         vsanVcsaDeployerSystemInstRef =
            createVsanManagedObjectReference(VSAN_VCSA_DEPLOYER_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanVcsaDeployerSystemInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN system Ex.
    *
    * @return
    */
   public ManagedObjectReference getVsanSystemExInstanceReference() {
      if (vsanSystemExInstRef == null) {
         vsanSystemExInstRef =
            createVsanManagedObjectReference(VSAN_SYSTEM_EX_SERVICEINSTANCEVALUE);
      }
      return vsanSystemExInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN VDS system.
    *
    * @return
    */
   public ManagedObjectReference getVsanVdsSystem() {
      if (vsanVdsSystemInstRef == null) {
         vsanVdsSystemInstRef =
            createVsanManagedObjectReference(VSAN_VDS_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanVdsSystemInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN update manager.
    *
    * @return
    */
   public ManagedObjectReference getVsanUpdateManager() {
      if (vsanUpdateManagerInstRef == null) {
         vsanUpdateManagerInstRef =
            createVsanManagedObjectReference(VSAN_UPDATE_MANAGER_SERVICEINSTANCEVALUE);
      }
      return vsanUpdateManagerInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN VC capability system.
    *
    * @return
    */
   public ManagedObjectReference getVsanVcCapabilitySystem() {
      if (vsanVcCapabilitySystemInstRef == null) {
         vsanVcCapabilitySystemInstRef =
            createVsanManagedObjectReference(
               VSAN_VC_CAPABILITY_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanVcCapabilitySystemInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN capability system.
    *
    * @return
    */
   public ManagedObjectReference getVsanCapabilitySystem() {
      if (vsanCapabilitySystemInstRef == null) {
         vsanCapabilitySystemInstRef =
            createVsanManagedObjectReference(
               VSAN_CAPABILITY_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanCapabilitySystemInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN mass collector.
    *
    * @return
    */
   public ManagedObjectReference getVsanMassCollector() {
      if (vsanMassCollectorInstRef == null) {
         vsanMassCollectorInstRef =
            createVsanManagedObjectReference(VSAN_MASS_COLLECTOR_SERVICEINSTANCEVALUE);
      }
      return vsanMassCollectorInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN PhoneHome system .
    *
    * @return
    */
   public ManagedObjectReference getVsanPhoneHomeSystem () {
      if (vsanPhonehomeSystemInstRef == null) {
         vsanPhonehomeSystemInstRef =
            createVsanManagedObjectReference(VSAN_PHONEHOME_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanPhonehomeSystemInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN VUM system .
    *
    * @return
    */
   public ManagedObjectReference getVsanVumSystem () {
      if (vsanVumSystemInstRef == null) {
         vsanVumSystemInstRef =
            createVsanManagedObjectReference(VSAN_VUM_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanVumSystemInstRef;
   }

   /**
    * Returns the {@link ManagedObjectReference} instance for the ServiceInstance
    * of the vSAN File Service system .
    *
    * @return
    */
   public ManagedObjectReference getVsanFileServiceSystem () {
      if (vsanFileServiceSystemInstRef == null) {
         vsanFileServiceSystemInstRef =
            createVsanManagedObjectReference(VSAN_VC_FILE_SERVICE_SYSTEM_SERVICEINSTANCEVALUE);
      }
      return vsanFileServiceSystemInstRef;
   }

   private URL setVcVsanHealthURL() {
      try {
         this.vsanhealthurl =
               new URL(getUrl().toString().replace("/sdk", "/vsanHealth"));
      } catch (MalformedURLException e) {
         throw new ConnectionMalformedUrlException(
               "malformed URL argument: '" + vsanhealthurl + "'", e);
      }
      return vsanhealthurl;
   }

   private URL setHostVsanHealthURL() {
      try {
         this.vsanhealthurl =
               new URL(getUrl().toString().replace("/sdk", "/vsan"));
      } catch (MalformedURLException e) {
         throw new ConnectionMalformedUrlException(
               "malformed URL argument: '" + vsanhealthurl + "'", e);
      }
      return vsanhealthurl;
   }

   private class VsanHealthConnectionException extends ConnectionException {
      private static final long serialVersionUID = 1L;
      public VsanHealthConnectionException(String s, Throwable t) {
         super(s, t);
      }
   }
}
