content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you 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.
  package com.cloud.hypervisor.vmware.mo;
  
  import java.net.URI;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang3.StringUtils;
  
  import com.google.gson.Gson;
  import com.vmware.vim25.ArrayOfHostIpRouteEntry;
  import com.vmware.vim25.ArrayUpdateOperation;
  import com.vmware.vim25.ClusterComputeResourceSummary;
  import com.vmware.vim25.ClusterConfigInfoEx;
  import com.vmware.vim25.ClusterDasConfigInfo;
  import com.vmware.vim25.ClusterDasVmSettingsRestartPriority;
  import com.vmware.vim25.ClusterHostRecommendation;
  import com.vmware.vim25.ComputeResourceSummary;
  import com.vmware.vim25.CustomFieldStringValue;
  import com.vmware.vim25.DasVmPriority;
  import com.vmware.vim25.DatastoreInfo;
  import com.vmware.vim25.DynamicProperty;
  import com.vmware.vim25.GuestOsDescriptor;
  import com.vmware.vim25.HostHardwareSummary;
  import com.vmware.vim25.HostIpRouteEntry;
  import com.vmware.vim25.HostRuntimeInfo;
  import com.vmware.vim25.HostSystemConnectionState;
  import com.vmware.vim25.ManagedObjectReference;
  import com.vmware.vim25.NasDatastoreInfo;
  import com.vmware.vim25.ObjectContent;
  import com.vmware.vim25.ObjectSpec;
  import com.vmware.vim25.OptionValue;
  import com.vmware.vim25.PropertyFilterSpec;
  import com.vmware.vim25.PropertySpec;
  import com.vmware.vim25.TraversalSpec;
  import com.vmware.vim25.VirtualMachineConfigOption;
  import com.vmware.vim25.VirtualMachineConfigSpec;
  import com.vmware.vim25.ClusterDasVmConfigInfo;
  import com.vmware.vim25.ClusterDasVmConfigSpec;
  import com.vmware.vim25.ClusterDasVmSettings;
  import com.vmware.vim25.ClusterConfigSpecEx;
  
  import com.cloud.hypervisor.vmware.util.VmwareContext;
  import com.cloud.hypervisor.vmware.util.VmwareHelper;
  import com.cloud.utils.Pair;
  import com.cloud.utils.exception.CloudRuntimeException;
  
  //
  // interface. This has changed as ClusterMO no longer works as a special host anymore. Need to refactor accordingly
  //
  public class ClusterMO extends BaseMO implements VmwareHypervisorHost {
      protected ManagedObjectReference _environmentBrowser = null;
  
      public ClusterMO(VmwareContext context, ManagedObjectReference morCluster) {
          super(context, morCluster);
      }
  
      public ClusterMO(VmwareContext context, String morType, String morValue) {
          super(context, morType, morValue);
      }
  
      @Override
      public String getHyperHostName() throws Exception {
          return getName();
      }
  
      @Override
      public ClusterDasConfigInfo getDasConfig() throws Exception {
          ClusterConfigInfoEx configInfo = getClusterConfigInfo();
          if (configInfo != null) {
              // Note getDynamicProperty() with "configurationEx.dasConfig" does not work here because of that dasConfig is a property in subclass
              return configInfo.getDasConfig();
          }
  
          return null;
      }
  
      public ClusterConfigInfoEx getClusterConfigInfo() throws Exception {
          ClusterConfigInfoEx configInfo = (ClusterConfigInfoEx)_context.getVimClient().getDynamicProperty(_mor, "configurationEx");
          return configInfo;
      }
  
      @Override
      public boolean isHAEnabled() throws Exception {
          ClusterDasConfigInfo dasConfig = getDasConfig();
          if (dasConfig != null && dasConfig.isEnabled() != null && dasConfig.isEnabled().booleanValue()) {
              return true;
          }
  
          return false;
      }
  
      private String getRestartPriorityForVM(VirtualMachineMO vmMo) throws Exception {
          if (vmMo == null) {
              logger.debug("Failed to get restart priority for VM, invalid VM object reference");
              return null;
          }
  
          ManagedObjectReference vmMor = vmMo.getMor();
          if (vmMor == null || !vmMor.getType().equals("VirtualMachine")) {
              logger.debug("Failed to get restart priority for VM: " + vmMo.getName() + ", invalid VM object reference");
              return null;
          }
  
          ClusterConfigInfoEx configInfo = getClusterConfigInfo();
          if (configInfo == null) {
              logger.debug("Failed to get restart priority for VM: " + vmMo.getName() + ", no cluster config information");
              return null;
          }
  
          List<ClusterDasVmConfigInfo> dasVmConfig = configInfo.getDasVmConfig();
          for (int dasVmConfigIndex = 0; dasVmConfigIndex < dasVmConfig.size(); dasVmConfigIndex++) {
              ClusterDasVmConfigInfo dasVmConfigInfo = dasVmConfig.get(dasVmConfigIndex);
              if (dasVmConfigInfo != null && dasVmConfigInfo.getKey().getValue().equals(vmMor.getValue())) {
                  DasVmPriority dasVmPriority = dasVmConfigInfo.getRestartPriority();
                  if (dasVmPriority != null) {
                      return dasVmPriority.value();
                  } else {
                      //VM uses cluster restart priority when DasVmPriority for the VM is null.
                      return ClusterDasVmSettingsRestartPriority.CLUSTER_RESTART_PRIORITY.value();
                  }
              }
          }
  
          logger.debug("VM: " + vmMo.getName() + " uses default restart priority in the cluster: " + getName());
          return null;
      }
  
      @Override
      public void setRestartPriorityForVM(VirtualMachineMO vmMo, String priority) throws Exception {
          if (vmMo == null || StringUtils.isBlank(priority)) {
              return;
          }
  
          if (!isHAEnabled()) {
              logger.debug("Couldn't set restart priority for VM: " + vmMo.getName() + ", HA disabled in the cluster");
              return;
          }
  
          ManagedObjectReference vmMor = vmMo.getMor();
          if (vmMor == null || !vmMor.getType().equals("VirtualMachine")) {
              logger.debug("Failed to set restart priority for VM: " + vmMo.getName() + ", invalid VM object reference");
              return;
          }
  
          String currentVmRestartPriority = getRestartPriorityForVM(vmMo);
          if (StringUtils.isNotBlank(currentVmRestartPriority) && currentVmRestartPriority.equalsIgnoreCase(priority)) {
              return;
          }
  
          ClusterDasVmSettings clusterDasVmSettings = new ClusterDasVmSettings();
          clusterDasVmSettings.setRestartPriority(priority);
  
          ClusterDasVmConfigInfo clusterDasVmConfigInfo = new ClusterDasVmConfigInfo();
          clusterDasVmConfigInfo.setKey(vmMor);
          clusterDasVmConfigInfo.setDasSettings(clusterDasVmSettings);
  
          ClusterDasVmConfigSpec clusterDasVmConfigSpec = new ClusterDasVmConfigSpec();
          clusterDasVmConfigSpec.setOperation((StringUtils.isNotBlank(currentVmRestartPriority)) ? ArrayUpdateOperation.EDIT : ArrayUpdateOperation.ADD);
          clusterDasVmConfigSpec.setInfo(clusterDasVmConfigInfo);
  
          ClusterConfigSpecEx clusterConfigSpecEx = new ClusterConfigSpecEx();
          ClusterDasConfigInfo clusterDasConfigInfo = new ClusterDasConfigInfo();
          clusterConfigSpecEx.setDasConfig(clusterDasConfigInfo);
          clusterConfigSpecEx.getDasVmConfigSpec().add(clusterDasVmConfigSpec);
  
          ManagedObjectReference morTask = _context.getService().reconfigureComputeResourceTask(_mor, clusterConfigSpecEx, true);
  
          boolean result = _context.getVimClient().waitForTask(morTask);
  
          if (result) {
              _context.waitForTaskProgressDone(morTask);
  
              if (logger.isTraceEnabled())
                  logger.trace("vCenter API trace - setRestartPriority done(successfully)");
          } else {
              if (logger.isTraceEnabled())
                  logger.trace("vCenter API trace - setRestartPriority done(failed)");
              logger.error("Set restart priority failed for VM: " + vmMo.getName() + " due to " + TaskMO.getTaskFailureInfo(_context, morTask));
          }
      }
  
      @Override
      public ManagedObjectReference getHyperHostDatacenter() throws Exception {
          Pair<DatacenterMO, String> dcPair = DatacenterMO.getOwnerDatacenter(getContext(), getMor());
          assert (dcPair != null);
          return dcPair.first().getMor();
      }
  
      @Override
      public ManagedObjectReference getHyperHostOwnerResourcePool() throws Exception {
          return (ManagedObjectReference)_context.getVimClient().getDynamicProperty(getMor(), "resourcePool");
      }
  
      @Override
      public ManagedObjectReference getHyperHostCluster() throws Exception {
          return _mor;
      }
  
      @Override
      public synchronized List<VirtualMachineMO> listVmsOnHyperHostWithHypervisorName(String vmName) throws Exception {
          List<VirtualMachineMO> vms = new ArrayList<>();
          List<ManagedObjectReference> hosts = _context.getVimClient().getDynamicProperty(_mor, "host");
          if (CollectionUtils.isNotEmpty(hosts)) {
              for (ManagedObjectReference morHost : hosts) {
                  HostMO hostMo = new HostMO(_context, morHost);
                  vms.addAll(hostMo.listVmsOnHyperHostWithHypervisorName(vmName));
              }
          }
          return vms;
      }
  
      @Override
      public VirtualMachineMO findVmOnHyperHost(String name) throws Exception {
  
          int key = getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME);
          if (key == 0) {
              logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!");
          }
  
          String instanceNameCustomField = "value[" + key + "]";
          ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] {"name", instanceNameCustomField});
          return HypervisorHostHelper.findVmFromObjectContent(_context, ocs, name, instanceNameCustomField);
      }
  
      @Override
      public VirtualMachineMO findVmOnPeerHyperHost(String name) throws Exception {
          int key = getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME);
          if (key == 0) {
              logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!");
          }
  
          String instanceNameCustomField = "value[" + key + "]";
  
          ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] {"name", instanceNameCustomField});
          return HypervisorHostHelper.findVmFromObjectContent(_context, ocs, name, instanceNameCustomField);
      }
  
      @Override
      public ObjectContent[] getVmPropertiesOnHyperHost(String[] propertyPaths) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - retrieveProperties() for VM properties. target MOR: " + _mor.getValue() + ", properties: " + new Gson().toJson(propertyPaths));
  
          PropertySpec pSpec = new PropertySpec();
          pSpec.setType("VirtualMachine");
          pSpec.getPathSet().addAll(Arrays.asList(propertyPaths));
  
          TraversalSpec host2VmFolderTraversal = new TraversalSpec();
          host2VmFolderTraversal.setType("HostSystem");
          host2VmFolderTraversal.setPath("vm");
          host2VmFolderTraversal.setName("host2VmFolderTraversal");
  
          TraversalSpec cluster2HostFolderTraversal = new TraversalSpec();
          cluster2HostFolderTraversal.setType("ClusterComputeResource");
          cluster2HostFolderTraversal.setPath("host");
          cluster2HostFolderTraversal.setName("cluster2HostFolderTraversal");
          cluster2HostFolderTraversal.getSelectSet().add(host2VmFolderTraversal);
  
          ObjectSpec oSpec = new ObjectSpec();
          oSpec.setObj(getMor());
          oSpec.setSkip(Boolean.TRUE);
          oSpec.getSelectSet().add(cluster2HostFolderTraversal);
  
          PropertyFilterSpec pfSpec = new PropertyFilterSpec();
          pfSpec.getPropSet().add(pSpec);
          pfSpec.getObjectSet().add(oSpec);
          List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
          pfSpecArr.add(pfSpec);
  
          List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - retrieveProperties() done");
          return properties.toArray(new ObjectContent[properties.size()]);
      }
  
      @Override
      public ObjectContent[] getDatastorePropertiesOnHyperHost(String[] propertyPaths) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - retrieveProperties() on Datastore properties. target MOR: " + _mor.getValue() + ", properties: " + new Gson().toJson(propertyPaths));
  
          PropertySpec pSpec = new PropertySpec();
          pSpec.setType("Datastore");
          pSpec.getPathSet().addAll(Arrays.asList(propertyPaths));
  
          TraversalSpec cluster2DatastoreTraversal = new TraversalSpec();
          cluster2DatastoreTraversal.setType("ClusterComputeResource");
          cluster2DatastoreTraversal.setPath("datastore");
          cluster2DatastoreTraversal.setName("cluster2DatastoreTraversal");
  
          ObjectSpec oSpec = new ObjectSpec();
          oSpec.setObj(_mor);
          oSpec.setSkip(Boolean.TRUE);
          oSpec.getSelectSet().add(cluster2DatastoreTraversal);
  
          PropertyFilterSpec pfSpec = new PropertyFilterSpec();
          pfSpec.getPropSet().add(pSpec);
          pfSpec.getObjectSet().add(oSpec);
          List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
          pfSpecArr.add(pfSpec);
  
          List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - retrieveProperties() done");
          return properties.toArray(new ObjectContent[properties.size()]);
      }
  
      public ObjectContent[] getHostPropertiesOnCluster(String[] propertyPaths) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - retrieveProperties() on Host properties. target MOR: " + _mor.getValue() + ", properties: " + new Gson().toJson(propertyPaths));
  
          PropertySpec pSpec = new PropertySpec();
          pSpec.setType("HostSystem");
          pSpec.getPathSet().addAll(Arrays.asList(propertyPaths));
  
          TraversalSpec cluster2HostTraversal = new TraversalSpec();
          cluster2HostTraversal.setType("ClusterComputeResource");
          cluster2HostTraversal.setPath("host");
          cluster2HostTraversal.setName("cluster2HostTraversal");
  
          ObjectSpec oSpec = new ObjectSpec();
          oSpec.setObj(_mor);
          oSpec.setSkip(Boolean.TRUE);
          oSpec.getSelectSet().add(cluster2HostTraversal);
  
          PropertyFilterSpec pfSpec = new PropertyFilterSpec();
          pfSpec.getPropSet().add(pSpec);
          pfSpec.getObjectSet().add(oSpec);
  
          List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
          pfSpecArr.add(pfSpec);
  
          List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - retrieveProperties() done");
          return properties.toArray(new ObjectContent[properties.size()]);
      }
  
      @Override
      public boolean createVm(VirtualMachineConfigSpec vmSpec) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - createVM_Task(). target MOR: " + _mor.getValue() + ", VirtualMachineConfigSpec: " + new Gson().toJson(vmSpec));
  
          assert (vmSpec != null);
          DatacenterMO dcMo = new DatacenterMO(_context, getHyperHostDatacenter());
          ManagedObjectReference morPool = getHyperHostOwnerResourcePool();
  
          ManagedObjectReference morTask = _context.getService().createVMTask(dcMo.getVmFolder(), vmSpec, morPool, null);
          boolean result = _context.getVimClient().waitForTask(morTask);
  
          if (result) {
              _context.waitForTaskProgressDone(morTask);
  
              if (logger.isTraceEnabled())
                  logger.trace("vCenter API trace - createVM_Task() done(successfully)");
              return true;
          } else {
              logger.error("VMware createVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
          }
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - createVM_Task() done(failed)");
          return false;
      }
  
      @Override
      public void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, String configurationId) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - importVmFromOVF(). target MOR: " + _mor.getValue() + ", ovfFilePath: " + ovfFilePath + ", vmName: " + vmName +
                      ", datastore: " + dsMo.getMor().getValue() + ", diskOption: " + diskOption);
  
          ManagedObjectReference morRp = getHyperHostOwnerResourcePool();
          assert (morRp != null);
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - importVmFromOVF(). resource pool: " + morRp.getValue());
  
          HypervisorHostHelper.importVmFromOVF(this, ovfFilePath, vmName, dsMo, diskOption, morRp, null, configurationId);
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - importVmFromOVF() done");
      }
  
      @Override
      public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB,
                                   int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " +
                      cpuSpeedMHz + ", cpuReservedMHz: " + cpuReservedMHz + ", limitCpu: " + limitCpuUse + ", memoryMB: " + memoryMB + ", guestOS: " + guestOsIdentifier +
                      ", datastore: " + morDs.getValue() + ", snapshotDirToParent: " + snapshotDirToParent);
  
          boolean result =
                  HypervisorHostHelper.createBlankVm(this, vmName, vmInternalCSName, cpuCount, cpuSpeedMHz, cpuReservedMHz, limitCpuUse, memoryMB, memoryReserveMB,
                          guestOsIdentifier, morDs, snapshotDirToParent, controllerInfo, systemVm);
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - createBlankVm() done");
  
          return result;
      }
  
      @Override
      public ManagedObjectReference mountDatastore(boolean vmfsDatastore, String poolHostAddress, int poolHostPort, String poolPath, String poolUuid, boolean createBaseFolder) throws Exception {
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - mountDatastore(). target MOR: " + _mor.getValue() + ", vmfs: " + vmfsDatastore + ", poolHost: " + poolHostAddress +
                      ", poolHostPort: " + poolHostPort + ", poolPath: " + poolPath + ", poolUuid: " + poolUuid);
  
          ManagedObjectReference morDs = null;
          ManagedObjectReference morDsFirst = null;
          List<ManagedObjectReference> hosts = _context.getVimClient().getDynamicProperty(_mor, "host");
          if (hosts != null && hosts.size() > 0) {
              for (ManagedObjectReference morHost : hosts) {
                  HostMO hostMo = new HostMO(_context, morHost);
                  morDs = hostMo.mountDatastore(vmfsDatastore, poolHostAddress, poolHostPort, poolPath, poolUuid, true);
                  if (morDsFirst == null)
                      morDsFirst = morDs;
  
                  // assume datastore is in scope of datacenter
                  assert (morDsFirst.getValue().equals(morDs.getValue()));
              }
          }
  
          if (morDs == null) {
              String msg = "Failed to mount datastore in all hosts within the cluster";
              logger.error(msg);
  
              if (logger.isTraceEnabled())
                  logger.trace("vCenter API trace - mountDatastore() done(failed)");
              throw new Exception(msg);
          }
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - mountDatastore() done(successfully)");
  
          return morDs;
      }
  
      @Override
      public void unmountDatastore(String poolUuid) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - unmountDatastore(). target MOR: " + _mor.getValue() + ", poolUuid: " + poolUuid);
  
          List<ManagedObjectReference> hosts = _context.getVimClient().getDynamicProperty(_mor, "host");
          if (hosts != null && hosts.size() > 0) {
              for (ManagedObjectReference morHost : hosts) {
                  HostMO hostMo = new HostMO(_context, morHost);
                  hostMo.unmountDatastore(poolUuid);
              }
          }
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - unmountDatastore() done");
      }
  
      @Override
      public ManagedObjectReference findDatastore(String poolUuid) throws Exception {
          logger.trace(String.format("Searching datastore in target MOR [%s] with poolUuid [%s].", _mor.getValue(), poolUuid));
  
          CustomFieldsManagerMO cfmMo = new CustomFieldsManagerMO(_context, _context.getServiceContent().getCustomFieldsManager());
          int key = cfmMo.getCustomFieldKey("Datastore", CustomFieldConstants.CLOUD_UUID);
          assert (key != 0);
  
          ObjectContent[] ocs = getDatastorePropertiesOnHyperHost(new String[] {"name", String.format("value[%d]", key)});
          if (ocs != null) {
              for (ObjectContent oc : ocs) {
                  if (oc.getPropSet().get(0).getVal().equals(poolUuid)) {
                      logger.trace(String.format("Found datastore [%s] in target MOR [%s].", oc.getObj(), _mor.getValue()));
                      return oc.getObj();
                  }
                  if (oc.getPropSet().size() > 1) {
                      DynamicProperty prop = oc.getPropSet().get(1);
                      if (prop != null && prop.getVal() != null) {
                          if (prop.getVal() instanceof CustomFieldStringValue) {
                              String val = ((CustomFieldStringValue)prop.getVal()).getValue();
                              if (val.equalsIgnoreCase(poolUuid)) {
                                  logger.trace(String.format("Found datastore [%s] in target MOR [%s].", oc.getObj(), _mor.getValue()));
                                  return oc.getObj();
                              }
                          }
                      }
                  }
              }
          }
  
          logger.trace(String.format("Failed to find a datastore with UUID [%s].", poolUuid));
          return null;
      }
  
      @Override
      public ManagedObjectReference findDatastoreByName(String datastoreName) throws Exception {
          throw new UnsupportedOperationException();
      }
  
      @Override
      public ManagedObjectReference findDatastoreByExportPath(String exportPath) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - findDatastoreByExportPath(). target MOR: " + _mor.getValue() + ", exportPath: " + exportPath);
  
          ObjectContent[] ocs = getDatastorePropertiesOnHyperHost(new String[] {"info"});
          if (ocs != null && ocs.length > 0) {
              for (ObjectContent oc : ocs) {
                  DatastoreInfo dsInfo = (DatastoreInfo)oc.getPropSet().get(0).getVal();
                  if (dsInfo != null && dsInfo instanceof NasDatastoreInfo) {
                      NasDatastoreInfo info = (NasDatastoreInfo)dsInfo;
                      if (info != null) {
                          String vmwareUrl = info.getUrl();
                          if (vmwareUrl.charAt(vmwareUrl.length() - 1) == '/')
                              vmwareUrl = vmwareUrl.substring(0, vmwareUrl.length() - 1);
  
                          URI uri = new URI(vmwareUrl);
                          if (uri.getPath().equals("/" + exportPath)) {
  
                              if (logger.isTraceEnabled())
                                  logger.trace("vCenter API trace - findDatastoreByExportPath() done(successfully)");
                              return oc.getObj();
                          }
                      }
                  }
              }
          }
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - findDatastoreByExportPath() done(failed)");
          return null;
      }
  
      @Override
      public ManagedObjectReference findMigrationTarget(VirtualMachineMO vmMo) throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - findMigrationTarget(). target MOR: " + _mor.getValue() + ", vm: " + vmMo.getName());
  
          List<ClusterHostRecommendation> candidates = recommendHostsForVm(vmMo);
          if (candidates != null && candidates.size() > 0) {
              if (logger.isTraceEnabled())
                  logger.trace("vCenter API trace - findMigrationTarget() done(successfully)");
              return candidates.get(0).getHost();
          }
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - findMigrationTarget() done(failed)");
          return null;
      }
  
      @Override
      public boolean isHyperHostConnected() throws Exception {
          ObjectContent[] ocs = getHostPropertiesOnCluster(new String[] {"runtime"});
          if (ocs != null && ocs.length > 0) {
              for (ObjectContent oc : ocs) {
                  HostRuntimeInfo runtimeInfo = (HostRuntimeInfo)oc.getPropSet().get(0).getVal();
                  // as long as we have one host connected, we assume the cluster is up
                  if (runtimeInfo.getConnectionState() == HostSystemConnectionState.CONNECTED)
                      return true;
              }
          }
          return false;
      }
  
      @Override
      public String getHyperHostDefaultGateway() throws Exception {
          ObjectContent[] ocs = getHostPropertiesOnCluster(new String[] {"config.network.routeTableInfo.ipRoute"});
          if (ocs != null && ocs.length > 0) {
              for (ObjectContent oc : ocs) {
                  ArrayOfHostIpRouteEntry entries = (ArrayOfHostIpRouteEntry)oc.getPropSet().get(0).getVal();
                  if (entries != null) {
                      for (HostIpRouteEntry entry : entries.getHostIpRouteEntry()) {
                          if (entry.getNetwork().equalsIgnoreCase("0.0.0.0"))
                              return entry.getGateway();
                      }
                  }
              }
          }
  
          throw new Exception("Could not find host default gateway, host is not properly configured?");
      }
  
      @Override
      public VmwareHypervisorHostResourceSummary getHyperHostResourceSummary() throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - getHyperHostResourceSummary(). target MOR: " + _mor.getValue());
  
          VmwareHypervisorHostResourceSummary summary = new VmwareHypervisorHostResourceSummary();
  
          ComputeResourceSummary vmwareSummary = (ComputeResourceSummary)_context.getVimClient().getDynamicProperty(_mor, "summary");
  
          // TODO, need to use traversal to optimize retrieve of
          int cpuNumInCpuThreads = 1;
          List<ManagedObjectReference> hosts = _context.getVimClient().getDynamicProperty(_mor, "host");
          if (hosts != null && hosts.size() > 0) {
              for (ManagedObjectReference morHost : hosts) {
                  HostMO hostMo = new HostMO(_context, morHost);
                  HostHardwareSummary hardwareSummary = hostMo.getHostHardwareSummary();
  
                  if (hardwareSummary.getNumCpuCores() * hardwareSummary.getNumCpuThreads() > cpuNumInCpuThreads)
                      cpuNumInCpuThreads = hardwareSummary.getNumCpuCores() * hardwareSummary.getNumCpuThreads();
              }
          }
          summary.setCpuCount(cpuNumInCpuThreads);
          summary.setCpuSpeed(vmwareSummary.getTotalCpu());
          summary.setMemoryBytes(vmwareSummary.getTotalMemory());
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - getHyperHostResourceSummary() done");
          return summary;
      }
  
      @Override
      public VmwareHypervisorHostNetworkSummary getHyperHostNetworkSummary(String esxServiceConsolePort) throws Exception {
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - getHyperHostNetworkSummary(). target MOR: " + _mor.getValue() + ", mgmtPortgroup: " + esxServiceConsolePort);
  
          List<ManagedObjectReference> hosts = _context.getVimClient().getDynamicProperty(_mor, "host");
          if (hosts != null && hosts.size() > 0) {
              VmwareHypervisorHostNetworkSummary summary = new HostMO(_context, hosts.get(0)).getHyperHostNetworkSummary(esxServiceConsolePort);
  
              if (logger.isTraceEnabled())
                  logger.trace("vCenter API trace - getHyperHostResourceSummary() done(successfully)");
              return summary;
          }
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - getHyperHostResourceSummary() done(failed)");
          return null;
      }
  
      @Override
      public ComputeResourceSummary getHyperHostHardwareSummary() throws Exception {
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - getHyperHostHardwareSummary(). target MOR: " + _mor.getValue());
  
          ClusterComputeResourceSummary hardwareSummary = (ClusterComputeResourceSummary)_context.getVimClient().getDynamicProperty(_mor, "summary");
  
          if (logger.isTraceEnabled())
              logger.trace("vCenter API trace - getHyperHostHardwareSummary() done");
          return hardwareSummary;
      }
  
      public List<ClusterHostRecommendation> recommendHostsForVm(VirtualMachineMO vmMo) throws Exception {
          return _context.getService().recommendHostsForVm(_mor, vmMo.getMor(), getHyperHostOwnerResourcePool());
      }
  
      public List<Pair<ManagedObjectReference, String>> getClusterHosts() throws Exception {
          List<Pair<ManagedObjectReference, String>> hosts = new ArrayList<Pair<ManagedObjectReference, String>>();
  
          ObjectContent[] ocs = getHostPropertiesOnCluster(new String[] {"name"});
          if (ocs != null) {
              for (ObjectContent oc : ocs) {
                  ManagedObjectReference morHost = oc.getObj();
                  String name = (String)oc.getPropSet().get(0).getVal();
  
                  hosts.add(new Pair<ManagedObjectReference, String>(morHost, name));
              }
          }
          return hosts;
      }
  
      public HashMap<String, Integer> getVmVncPortsOnCluster() throws Exception {
          ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] {"name", "config.extraConfig[\"RemoteDisplay.vnc.port\"]"});
  
          HashMap<String, Integer> portInfo = new HashMap<String, Integer>();
          if (ocs != null && ocs.length > 0) {
              for (ObjectContent oc : ocs) {
                  List<DynamicProperty> objProps = oc.getPropSet();
                  if (objProps != null) {
                      String name = null;
                      String value = null;
                      for (DynamicProperty objProp : objProps) {
                          if (objProp.getName().equals("name")) {
                              name = (String)objProp.getVal();
                          } else {
                              OptionValue optValue = (OptionValue)objProp.getVal();
                              value = (String)optValue.getValue();
                          }
                      }
  
                      if (name != null && value != null) {
                          portInfo.put(name, Integer.parseInt(value));
                      }
                  }
              }
          }
  
          return portInfo;
      }
  
      @Override
      public LicenseAssignmentManagerMO getLicenseAssignmentManager() throws Exception {
          // LicenseAssignmentManager deals with only host/vcenter licenses only. Has nothing todo with cluster
          throw new CloudRuntimeException("Unable to get LicenseAssignmentManager at cluster level");
      }
      private ManagedObjectReference getEnvironmentBrowser() throws Exception {
          if (_environmentBrowser == null) {
              _environmentBrowser = _context.getVimClient().getMoRefProp(_mor, "environmentBrowser");
          }
          return _environmentBrowser;
      }
      @Override
      public String getRecommendedDiskController(String guestOsId) throws Exception {
          VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null);
          GuestOsDescriptor guestOsDescriptor = null;
          String diskController = null;
          List<GuestOsDescriptor> guestDescriptors = vmConfigOption.getGuestOSDescriptor();
          for (GuestOsDescriptor descriptor : guestDescriptors) {
              if (guestOsId != null && guestOsId.equalsIgnoreCase(descriptor.getId())) {
                  guestOsDescriptor = descriptor;
                  break;
              }
          }
          if (guestOsDescriptor != null) {
              diskController = VmwareHelper.getRecommendedDiskControllerFromDescriptor(guestOsDescriptor);
              logger.debug("Retrieved recommended disk controller for guest OS : " + guestOsId + " in cluster " + getHyperHostName() + " : " + diskController);
              return diskController;
          } else {
              String msg = "Unable to retrieve recommended disk controller for guest OS : " + guestOsId + " in cluster " + getHyperHostName();
              logger.error(msg);
              throw new CloudRuntimeException(msg);
          }
      }
  
      @Override
      public GuestOsDescriptor getGuestOsDescriptor(String guestOsId) throws Exception {
          VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null);
          List<GuestOsDescriptor> guestDescriptors = vmConfigOption.getGuestOSDescriptor();
          for (GuestOsDescriptor descriptor : guestDescriptors) {
              if (guestOsId != null && guestOsId.equalsIgnoreCase(descriptor.getId())) {
                  return descriptor;
              }
          }
          return null;
      }
  
      @Override
      public List<GuestOsDescriptor> getGuestOsDescriptors() throws Exception {
          VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null);
          return vmConfigOption.getGuestOSDescriptor();
      }
  }
  