@Before
  public void setup() throws Exception {
    MockitoAnnotations.initMocks(this);
    storageCmd = PowerMockito.mock(CopyCommand.class);
    doReturn(context).when(_resource).getServiceContext(null);
    when(cmd.getVirtualMachine()).thenReturn(vmSpec);

    when(storageCmd.getSrcTO()).thenReturn(srcDataTO);
    when(srcDataTO.getDataStore()).thenReturn(srcDataNfsTO);
    when(srcDataNfsTO.getNfsVersion()).thenReturn(NFS_VERSION);
    when(videoCard.getVideoRamSizeInKB()).thenReturn(VIDEO_CARD_MEMORY_SIZE);
    when(volume.getPath()).thenReturn(VOLUME_PATH);

    when(storageCmd.getDestTO()).thenReturn(destDataTO);
    when(destDataTO.getHypervisorType()).thenReturn(HypervisorType.VMware);
    when(destDataTO.getDataStore()).thenReturn(destDataStoreTO);
    when(destDataStoreTO.isFullCloneFlag()).thenReturn(FULL_CLONE_FLAG);
    when(volume.getPath()).thenReturn(VOLUME_PATH);
    when(vmSpec.getDetails()).thenReturn(specsArray);

    when(vmMo.getContext()).thenReturn(context);
    when(vmMo.getRunningHost()).thenReturn(host);
    when(host.getMor()).thenReturn(hostRef);
    when(context.getVimClient()).thenReturn(client);
    when(client.getMoRefProp(hostRef, "parent")).thenReturn(computeRef);
    when(client.getMoRefProp(computeRef, "environmentBrowser")).thenReturn(envRef);
    when(context.getService()).thenReturn(vimService);
    when(vimService.queryTargetCapabilities(envRef, hostRef)).thenReturn(hostCapability);
    when(hostCapability.isNestedHVSupported()).thenReturn(true);
  }
  @Deprecated
  private ManagedObjectReference addHostToVCenterCluster(
      VmwareContext serviceContext,
      ManagedObjectReference morCluster,
      String host,
      String userName,
      String password)
      throws Exception {

    VmwareClient vclient = serviceContext.getVimClient();
    ManagedObjectReference morHost = vclient.getDecendentMoRef(morCluster, "HostSystem", host);
    if (morHost == null) {
      HostConnectSpec hostSpec = new HostConnectSpec();
      hostSpec.setUserName(userName);
      hostSpec.setPassword(password);
      hostSpec.setHostName(host);
      hostSpec.setForce(true); // forcely take over the host

      ManagedObjectReference morTask =
          serviceContext.getService().addHostTask(morCluster, hostSpec, true, null, null);
      boolean taskResult = vclient.waitForTask(morTask);
      if (!taskResult) {
        s_logger.error(
            "Unable to add host "
                + host
                + " to vSphere cluster due to "
                + TaskMO.getTaskFailureInfo(serviceContext, morTask));
        throw new CloudRuntimeException(
            "Unable to add host " + host + " to vSphere cluster due to " + taskResult);
      }
      serviceContext.waitForTaskProgressDone(morTask);

      // init morHost after it has been created
      morHost = vclient.getDecendentMoRef(morCluster, "HostSystem", host);
      if (morHost == null) {
        throw new CloudRuntimeException(
            "Successfully added host into vSphere but unable to find it later on?!. Please make sure you are either using IP address or full qualified domain name for host");
      }
    }

    // For ESX host, we need to enable host firewall to allow VNC access
    HostMO hostMo = new HostMO(serviceContext, morHost);
    HostFirewallSystemMO firewallMo = hostMo.getHostFirewallSystemMO();
    if (firewallMo != null) {
      firewallMo.enableRuleset("vncServer");
      firewallMo.refreshFirewall();
    }
    return morHost;
  }
  private boolean validateDiscoveredHosts(
      VmwareContext context,
      ManagedObjectReference morCluster,
      List<ManagedObjectReference> morHosts)
      throws Exception {
    if (morCluster == null) {
      for (ManagedObjectReference morHost : morHosts) {
        ManagedObjectReference morParent =
            (ManagedObjectReference) context.getVimClient().getDynamicProperty(morHost, "parent");
        if (morParent.getType().equalsIgnoreCase("ClusterComputeResource")) return false;
      }
    } else {
      for (ManagedObjectReference morHost : morHosts) {
        ManagedObjectReference morParent =
            (ManagedObjectReference) context.getVimClient().getDynamicProperty(morHost, "parent");
        if (!morParent.getType().equalsIgnoreCase("ClusterComputeResource")) return false;

        if (!morParent.getValue().equals(morCluster.getValue())) return false;
      }
    }

    return true;
  }
  @Override
  public Map<? extends ServerResource, Map<String, String>> find(
      long dcId,
      Long podId,
      Long clusterId,
      URI url,
      String username,
      String password,
      List<String> hostTags)
      throws DiscoveryException {

    if (s_logger.isInfoEnabled())
      s_logger.info(
          "Discover host. dc: "
              + dcId
              + ", pod: "
              + podId
              + ", cluster: "
              + clusterId
              + ", uri host: "
              + url.getHost());

    if (podId == null) {
      if (s_logger.isInfoEnabled())
        s_logger.info(
            "No pod is assigned, assuming that it is not for vmware and skip it to next discoverer");
      return null;
    }

    ClusterVO cluster = _clusterDao.findById(clusterId);
    if (cluster == null || cluster.getHypervisorType() != HypervisorType.VMware) {
      if (s_logger.isInfoEnabled())
        s_logger.info("invalid cluster id or cluster is not for VMware hypervisors");
      return null;
    }

    List<HostVO> hosts = _resourceMgr.listAllHostsInCluster(clusterId);
    if (hosts != null && hosts.size() > 0) {
      int maxHostsPerCluster =
          _hvCapabilitiesDao.getMaxHostsPerCluster(
              hosts.get(0).getHypervisorType(), hosts.get(0).getHypervisorVersion());
      if (hosts.size() > maxHostsPerCluster) {
        String msg =
            "VMware cluster "
                + cluster.getName()
                + " is too big to add new host now. (current configured cluster size: "
                + maxHostsPerCluster
                + ")";
        s_logger.error(msg);
        throw new DiscoveredWithErrorException(msg);
      }
    }

    String privateTrafficLabel = null;
    String publicTrafficLabel = null;
    String guestTrafficLabel = null;
    Map<String, String> vsmCredentials = null;

    VirtualSwitchType defaultVirtualSwitchType = VirtualSwitchType.StandardVirtualSwitch;

    String paramGuestVswitchType = null;
    String paramGuestVswitchName = null;
    String paramPublicVswitchType = null;
    String paramPublicVswitchName = null;

    VmwareTrafficLabel guestTrafficLabelObj = new VmwareTrafficLabel(TrafficType.Guest);
    VmwareTrafficLabel publicTrafficLabelObj = new VmwareTrafficLabel(TrafficType.Public);
    Map<String, String> clusterDetails = _clusterDetailsDao.findDetails(clusterId);
    DataCenterVO zone = _dcDao.findById(dcId);
    NetworkType zoneType = zone.getNetworkType();
    _readGlobalConfigParameters();

    // Set default physical network end points for public and guest traffic
    // Private traffic will be only on standard vSwitch for now.
    if (useDVS) {
      // Parse url parameters for type of vswitch and name of vswitch specified at cluster level
      paramGuestVswitchType = _urlParams.get(ApiConstants.VSWITCH_TYPE_GUEST_TRAFFIC);
      paramGuestVswitchName = _urlParams.get(ApiConstants.VSWITCH_NAME_GUEST_TRAFFIC);
      paramPublicVswitchType = _urlParams.get(ApiConstants.VSWITCH_TYPE_PUBLIC_TRAFFIC);
      paramPublicVswitchName = _urlParams.get(ApiConstants.VSWITCH_NAME_PUBLIC_TRAFFIC);
      defaultVirtualSwitchType = getDefaultVirtualSwitchType();
    }

    // Zone level vSwitch Type depends on zone level traffic labels
    //
    // User can override Zone wide vswitch type (for public and guest) by providing following
    // optional parameters in addClusterCmd
    // param "guestvswitchtype" with valid values vmwaredvs, vmwaresvs, nexusdvs
    // param "publicvswitchtype" with valid values vmwaredvs, vmwaresvs, nexusdvs
    //
    // Format of label is <VSWITCH>,<VLANID>,<VSWITCHTYPE>
    // If a field <VLANID> OR <VSWITCHTYPE> is not present leave it empty.
    // Ex: 1) vswitch0
    // 2) dvswitch0,200,vmwaredvs
    // 3) nexusepp0,300,nexusdvs
    // 4) vswitch1,400,vmwaresvs
    // 5) vswitch0
    // default vswitchtype is 'vmwaresvs'.
    // <VSWITCHTYPE> 'vmwaresvs' is for vmware standard vswitch
    // <VSWITCHTYPE> 'vmwaredvs' is for vmware distributed virtual switch
    // <VSWITCHTYPE> 'nexusdvs' is for cisco nexus distributed virtual switch
    // Get zone wide traffic labels for Guest traffic and Public traffic
    guestTrafficLabel = _netmgr.getDefaultGuestTrafficLabel(dcId, HypervisorType.VMware);

    // Process traffic label information provided at zone level and cluster level
    guestTrafficLabelObj =
        getTrafficInfo(
            TrafficType.Guest,
            guestTrafficLabel,
            defaultVirtualSwitchType,
            paramGuestVswitchType,
            paramGuestVswitchName,
            clusterId);

    if (zoneType == NetworkType.Advanced) {
      // Get zone wide traffic label for Public traffic
      publicTrafficLabel = _netmgr.getDefaultPublicTrafficLabel(dcId, HypervisorType.VMware);

      // Process traffic label information provided at zone level and cluster level
      publicTrafficLabelObj =
          getTrafficInfo(
              TrafficType.Public,
              publicTrafficLabel,
              defaultVirtualSwitchType,
              paramPublicVswitchType,
              paramPublicVswitchName,
              clusterId);

      // Configuration Check: A physical network cannot be shared by different types of virtual
      // switches.
      //
      // Check if different vswitch types are chosen for same physical network
      // 1. Get physical network for guest traffic - multiple networks
      // 2. Get physical network for public traffic - single network
      // See if 2 is in 1
      //  if no - pass
      //  if yes - compare publicTrafficLabelObj.getVirtualSwitchType() ==
      // guestTrafficLabelObj.getVirtualSwitchType()
      //      true  - pass
      //      false - throw exception - fail cluster add operation

      List<? extends PhysicalNetwork> pNetworkListGuestTraffic =
          _netmgr.getPhysicalNtwksSupportingTrafficType(dcId, TrafficType.Guest);
      List<? extends PhysicalNetwork> pNetworkListPublicTraffic =
          _netmgr.getPhysicalNtwksSupportingTrafficType(dcId, TrafficType.Public);
      // Public network would be on single physical network hence getting first object of the list
      // would suffice.
      PhysicalNetwork pNetworkPublic = pNetworkListPublicTraffic.get(0);
      if (pNetworkListGuestTraffic.contains(pNetworkPublic)) {
        if (publicTrafficLabelObj.getVirtualSwitchType()
            != guestTrafficLabelObj.getVirtualSwitchType()) {
          String msg =
              "Both public traffic and guest traffic is over same physical network "
                  + pNetworkPublic
                  + ". And virtual switch type chosen for each traffic is different"
                  + ". A physical network cannot be shared by different types of virtual switches.";
          s_logger.error(msg);
          throw new InvalidParameterValueException(msg);
        }
      }
    } else {
      // Distributed virtual switch is not supported in Basic zone for now.
      // Private / Management network traffic is not yet supported over distributed virtual switch.
      if (guestTrafficLabelObj.getVirtualSwitchType() != VirtualSwitchType.StandardVirtualSwitch) {
        String msg =
            "Detected that Guest traffic is over Distributed virtual switch in Basic zone. Only Standard vSwitch is supported in Basic zone.";
        s_logger.error(msg);
        throw new DiscoveredWithErrorException(msg);
      }
    }

    privateTrafficLabel = _netmgr.getDefaultManagementTrafficLabel(dcId, HypervisorType.VMware);
    if (privateTrafficLabel != null) {
      s_logger.info("Detected private network label : " + privateTrafficLabel);
    }

    if (nexusDVS) {
      if (zoneType != NetworkType.Basic) {
        publicTrafficLabel = _netmgr.getDefaultPublicTrafficLabel(dcId, HypervisorType.VMware);
        if (publicTrafficLabel != null) {
          s_logger.info("Detected public network label : " + publicTrafficLabel);
        }
      }
      // Get physical network label
      guestTrafficLabel = _netmgr.getDefaultGuestTrafficLabel(dcId, HypervisorType.VMware);
      if (guestTrafficLabel != null) {
        s_logger.info("Detected guest network label : " + guestTrafficLabel);
      }
      vsmCredentials = _vmwareMgr.getNexusVSMCredentialsByClusterId(clusterId);
    }

    VmwareContext context = null;
    try {
      context = VmwareContextFactory.create(url.getHost(), username, password);
      if (privateTrafficLabel != null)
        context.registerStockObject("privateTrafficLabel", privateTrafficLabel);

      if (nexusDVS) {
        if (vsmCredentials != null) {
          s_logger.info("Stocking credentials of Nexus VSM");
          context.registerStockObject("vsmcredentials", vsmCredentials);
        }
      }
      List<ManagedObjectReference> morHosts =
          _vmwareMgr.addHostToPodCluster(
              context, dcId, podId, clusterId, URLDecoder.decode(url.getPath()));
      if (morHosts == null) s_logger.info("Found 0 hosts.");
      if (privateTrafficLabel != null) context.uregisterStockObject("privateTrafficLabel");

      if (morHosts == null) {
        s_logger.error(
            "Unable to find host or cluster based on url: " + URLDecoder.decode(url.getPath()));
        return null;
      }

      ManagedObjectReference morCluster = null;
      clusterDetails = _clusterDetailsDao.findDetails(clusterId);
      if (clusterDetails.get("url") != null) {
        URI uriFromCluster = new URI(UriUtils.encodeURIComponent(clusterDetails.get("url")));
        morCluster = context.getHostMorByPath(URLDecoder.decode(uriFromCluster.getPath()));

        if (morCluster == null
            || !morCluster.getType().equalsIgnoreCase("ClusterComputeResource")) {
          s_logger.warn(
              "Cluster url does not point to a valid vSphere cluster, url: "
                  + clusterDetails.get("url"));
          return null;
        } else {
          ClusterMO clusterMo = new ClusterMO(context, morCluster);
          ClusterDasConfigInfo dasConfig = clusterMo.getDasConfig();
          if (dasConfig != null
              && dasConfig.isEnabled() != null
              && dasConfig.isEnabled().booleanValue()) {
            clusterDetails.put("NativeHA", "true");
            _clusterDetailsDao.persist(clusterId, clusterDetails);
          }
        }
      }

      if (!validateDiscoveredHosts(context, morCluster, morHosts)) {
        if (morCluster == null)
          s_logger.warn(
              "The discovered host is not standalone host, can not be added to a standalone cluster");
        else s_logger.warn("The discovered host does not belong to the cluster");
        return null;
      }

      Map<VmwareResource, Map<String, String>> resources =
          new HashMap<VmwareResource, Map<String, String>>();
      for (ManagedObjectReference morHost : morHosts) {
        Map<String, String> details = new HashMap<String, String>();
        Map<String, Object> params = new HashMap<String, Object>();

        HostMO hostMo = new HostMO(context, morHost);
        details.put("url", hostMo.getHostName());
        details.put("username", username);
        details.put("password", password);
        String guid = morHost.getType() + ":" + morHost.getValue() + "@" + url.getHost();
        details.put("guid", guid);

        params.put("url", hostMo.getHostName());
        params.put("username", username);
        params.put("password", password);
        params.put("zone", Long.toString(dcId));
        params.put("pod", Long.toString(podId));
        params.put("cluster", Long.toString(clusterId));
        params.put("guid", guid);
        if (privateTrafficLabel != null) {
          params.put("private.network.vswitch.name", privateTrafficLabel);
        }
        params.put("guestTrafficInfo", guestTrafficLabelObj);
        params.put("publicTrafficInfo", publicTrafficLabelObj);

        VmwareResource resource = new VmwareResource();
        try {
          resource.configure("VMware", params);
        } catch (ConfigurationException e) {
          _alertMgr.sendAlert(
              AlertManager.ALERT_TYPE_HOST,
              dcId,
              podId,
              "Unable to add " + url.getHost(),
              "Error is " + e.getMessage());
          s_logger.warn("Unable to instantiate " + url.getHost(), e);
        }
        resource.start();

        resources.put(resource, details);
      }

      // place a place holder guid derived from cluster ID
      cluster.setGuid(UUID.nameUUIDFromBytes(String.valueOf(clusterId).getBytes()).toString());
      _clusterDao.update(clusterId, cluster);

      return resources;
    } catch (DiscoveredWithErrorException e) {
      throw e;
    } catch (Exception e) {
      s_logger.warn(
          "Unable to connect to Vmware vSphere server. service address: " + url.getHost());
      return null;
    } finally {
      if (context != null) context.close();
    }
  }
  @Override
  public List<ManagedObjectReference> addHostToPodCluster(
      VmwareContext serviceContext, long dcId, Long podId, Long clusterId, String hostInventoryPath)
      throws Exception {
    ManagedObjectReference mor = null;
    if (serviceContext != null) {
      mor = serviceContext.getHostMorByPath(hostInventoryPath);
    }
    String privateTrafficLabel = null;
    privateTrafficLabel = serviceContext.getStockObject("privateTrafficLabel");
    if (privateTrafficLabel == null) {
      privateTrafficLabel = _privateNetworkVSwitchName;
    }

    if (mor != null) {
      List<ManagedObjectReference> returnedHostList = new ArrayList<ManagedObjectReference>();

      if (mor.getType().equals("ComputeResource")) {
        List<ManagedObjectReference> hosts =
            (List<ManagedObjectReference>)
                serviceContext.getVimClient().getDynamicProperty(mor, "host");
        assert (hosts != null && hosts.size() > 0);

        // For ESX host, we need to enable host firewall to allow VNC access
        HostMO hostMo = new HostMO(serviceContext, hosts.get(0));

        prepareHost(hostMo, privateTrafficLabel);
        returnedHostList.add(hosts.get(0));
        return returnedHostList;
      } else if (mor.getType().equals("ClusterComputeResource")) {
        List<ManagedObjectReference> hosts =
            (List<ManagedObjectReference>)
                serviceContext.getVimClient().getDynamicProperty(mor, "host");
        assert (hosts != null);

        if (hosts.size() > 0) {
          AboutInfo about =
              (AboutInfo)
                  (serviceContext
                      .getVimClient()
                      .getDynamicProperty(hosts.get(0), "config.product"));
          String version = about.getApiVersion();
          int maxHostsPerCluster =
              _hvCapabilitiesDao.getMaxHostsPerCluster(HypervisorType.VMware, version);
          if (hosts.size() > maxHostsPerCluster) {
            String msg =
                "Failed to add VMware cluster as size is too big, current size: "
                    + hosts.size()
                    + ", max. size: "
                    + maxHostsPerCluster;
            s_logger.error(msg);
            throw new DiscoveredWithErrorException(msg);
          }
        }

        for (ManagedObjectReference morHost : hosts) {
          // For ESX host, we need to enable host firewall to allow VNC access
          HostMO hostMo = new HostMO(serviceContext, morHost);
          prepareHost(hostMo, privateTrafficLabel);
          returnedHostList.add(morHost);
        }
        return returnedHostList;
      } else if (mor.getType().equals("HostSystem")) {
        // For ESX host, we need to enable host firewall to allow VNC access
        HostMO hostMo = new HostMO(serviceContext, mor);
        prepareHost(hostMo, privateTrafficLabel);
        returnedHostList.add(mor);
        return returnedHostList;
      } else {
        s_logger.error(
            "Unsupport host type "
                + mor.getType()
                + ":"
                + mor.getValue()
                + " from inventory path: "
                + hostInventoryPath);
        return null;
      }
    }

    s_logger.error("Unable to find host from inventory path: " + hostInventoryPath);
    return null;
  }
  @Override
  public boolean removeVmwareDatacenter(RemoveVmwareDcCmd cmd) throws ResourceInUseException {
    Long zoneId = cmd.getZoneId();
    // Validate zone
    validateZone(zoneId);
    // Zone validation to check if the zone already has resources.
    // Association of VMware DC to zone is not allowed if zone already has resources added.
    validateZoneWithResources(zoneId, "remove VMware datacenter to zone");

    // Get DC associated with this zone
    VmwareDatacenterZoneMapVO vmwareDcZoneMap;
    VmwareDatacenterVO vmwareDatacenter;
    String vmwareDcName;
    long vmwareDcId;
    String vCenterHost;
    String userName;
    String password;
    DatacenterMO dcMo = null;
    Transaction txn;

    vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId);
    // Check if zone is associated with VMware DC
    if (vmwareDcZoneMap == null) {
      throw new CloudRuntimeException(
          "Zone " + zoneId + " is not associated with any VMware datacenter.");
    }

    vmwareDcId = vmwareDcZoneMap.getVmwareDcId();
    vmwareDatacenter = _vmwareDcDao.findById(vmwareDcId);
    vmwareDcName = vmwareDatacenter.getVmwareDatacenterName();
    vCenterHost = vmwareDatacenter.getVcenterHost();
    userName = vmwareDatacenter.getUser();
    password = vmwareDatacenter.getPassword();
    txn = Transaction.currentTxn();
    try {
      txn.start();
      // Remove the VMware datacenter entry in table vmware_data_center
      _vmwareDcDao.remove(vmwareDcId);
      // Remove the map entry in table vmware_data_center_zone_map
      _vmwareDcZoneMapDao.remove(vmwareDcZoneMap.getId());
      txn.commit();
    } catch (Exception e) {
      s_logger.info(
          "Caught exception when trying to delete VMware datacenter record." + e.getMessage());
      throw new CloudRuntimeException("Failed to delete VMware datacenter.");
    }

    // Construct context
    VmwareContext context = null;
    try {
      context = VmwareContextFactory.create(vCenterHost, userName, password);

      // Check if DC exists on vCenter
      try {
        dcMo = new DatacenterMO(context, vmwareDcName);
      } catch (Throwable t) {
        String msg = "Unable to find DC " + vmwareDcName + " in vCenter " + vCenterHost;
        s_logger.error(msg);
        throw new DiscoveryException(msg);
      }

      assert (dcMo != null);

      // Reset custom field property cloud.zone over this DC
      dcMo.setCustomFieldValue(CustomFieldConstants.CLOUD_ZONE, "false");
      s_logger.info("Sucessfully reset custom field property cloud.zone over DC " + vmwareDcName);
    } catch (Exception e) {
      String msg =
          "Unable to reset custom field property cloud.zone over DC "
              + vmwareDcName
              + " due to : "
              + VmwareHelper.getExceptionMessage(e);
      s_logger.error(msg);
      throw new CloudRuntimeException(msg);
    } finally {
      if (context != null) {
        context.close();
      }
      context = null;
    }
    return true;
  }
  @Override
  @DB
  public VmwareDatacenterVO addVmwareDatacenter(AddVmwareDcCmd cmd) throws ResourceInUseException {
    VmwareDatacenterVO vmwareDc = null;
    Long zoneId = cmd.getZoneId();
    String userName = cmd.getUsername();
    String password = cmd.getPassword();
    String vCenterHost = cmd.getVcenter();
    String vmwareDcName = cmd.getName();

    // Validate username, password, VMware DC name and vCenter
    if (userName == null) {
      throw new InvalidParameterValueException("Missing or invalid parameter username.");
    }

    if (password == null) {
      throw new InvalidParameterValueException("Missing or invalid parameter username.");
    }

    if (vmwareDcName == null) {
      throw new InvalidParameterValueException(
          "Missing or invalid parameter name. Please provide valid VMware datacenter name.");
    }

    if (vCenterHost == null) {
      throw new InvalidParameterValueException(
          "Missing or invalid parameter name. "
              + "Please provide valid VMware vCenter server's IP address or fully qualified domain name.");
    }

    if (zoneId == null) {
      throw new InvalidParameterValueException(
          "Missing or invalid parameter name. " + "Please provide valid zone id.");
    }

    // Zone validation
    validateZone(zoneId);

    VmwareDatacenterZoneMapVO vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId);
    // Check if zone is associated with VMware DC
    if (vmwareDcZoneMap != null) {
      // Check if the associated VMware DC matches the one specified in API params
      // This check would yield success as the association exists between same entities (zone and
      // VMware DC)
      // This scenario would result in if the API addVmwareDc is called more than once with same
      // parameters.
      Long associatedVmwareDcId = vmwareDcZoneMap.getVmwareDcId();
      VmwareDatacenterVO associatedVmwareDc = _vmwareDcDao.findById(associatedVmwareDcId);
      if (associatedVmwareDc.getVcenterHost().equalsIgnoreCase(vCenterHost)
          && associatedVmwareDc.getVmwareDatacenterName().equalsIgnoreCase(vmwareDcName)) {
        s_logger.info(
            "Ignoring API call addVmwareDc, because VMware DC "
                + vCenterHost
                + "/"
                + vmwareDcName
                + " is already associated with specified zone with id "
                + zoneId);
        return associatedVmwareDc;
      } else {
        throw new CloudRuntimeException(
            "Zone "
                + zoneId
                + " is already associated with a VMware datacenter. "
                + "Only 1 VMware DC can be associated with a zone.");
      }
    }
    // Zone validation to check if the zone already has resources.
    // Association of VMware DC to zone is not allowed if zone already has resources added.
    validateZoneWithResources(zoneId, "add VMware datacenter to zone");

    // Check if DC is already part of zone
    // In that case vmware_data_center table should have the DC
    vmwareDc = _vmwareDcDao.getVmwareDatacenterByGuid(vmwareDcName + "@" + vCenterHost);
    if (vmwareDc != null) {
      throw new ResourceInUseException(
          "This DC is already part of other CloudStack zone(s). Cannot add this DC to more zones.");
    }

    VmwareContext context = null;
    DatacenterMO dcMo = null;
    String dcCustomFieldValue;
    boolean addDcCustomFieldDef = false;
    boolean dcInUse = false;
    String guid;
    ManagedObjectReference dcMor;
    try {
      context = VmwareContextFactory.create(vCenterHost, userName, password);

      // Check if DC exists on vCenter
      dcMo = new DatacenterMO(context, vmwareDcName);
      dcMor = dcMo.getMor();
      if (dcMor == null) {
        String msg =
            "Unable to find VMware DC " + vmwareDcName + " in vCenter " + vCenterHost + ". ";
        s_logger.error(msg);
        throw new InvalidParameterValueException(msg);
      }

      // Check if DC is already associated with another cloudstack deployment
      // Get custom field property cloud.zone over this DC
      guid = vmwareDcName + "@" + vCenterHost;

      dcCustomFieldValue = dcMo.getCustomFieldValue(CustomFieldConstants.CLOUD_ZONE);
      if (dcCustomFieldValue == null) {
        addDcCustomFieldDef = true;
      }
      dcInUse = Boolean.parseBoolean(dcCustomFieldValue);
      if (dcInUse) {
        throw new ResourceInUseException(
            "This DC is being managed by other CloudStack deployment. Cannot add this DC to zone.");
      }

      // Add DC to database into vmware_data_center table
      vmwareDc = new VmwareDatacenterVO(guid, vmwareDcName, vCenterHost, userName, password);
      Transaction txn = Transaction.currentTxn();
      try {
        txn.start();
        vmwareDc = _vmwareDcDao.persist(vmwareDc);
        txn.commit();
      } catch (Exception e) {
        txn.rollback();
        s_logger.error(
            "Failed to persist VMware datacenter details to database. Exception: "
                + e.getMessage());
        throw new CloudRuntimeException(e.getMessage());
      }

      // Map zone with vmware datacenter
      vmwareDcZoneMap = new VmwareDatacenterZoneMapVO(zoneId, vmwareDc.getId());

      txn = Transaction.currentTxn();
      try {
        txn.start();
        vmwareDcZoneMap = _vmwareDcZoneMapDao.persist(vmwareDcZoneMap);
        txn.commit();
      } catch (Exception e) {
        txn.rollback();
        s_logger.error(
            "Failed to associate VMware datacenter with zone "
                + zoneId
                + ". Exception: "
                + e.getMessage());
        // Removing VMware datacenter from vmware_data_center table because association with zone
        // failed.
        _vmwareDcDao.remove(vmwareDcZoneMap.getId());
        throw new CloudRuntimeException(e.getMessage());
      }

      // Set custom field for this DC
      if (addDcCustomFieldDef) {
        dcMo.ensureCustomFieldDef(CustomFieldConstants.CLOUD_ZONE);
      }
      dcMo.setCustomFieldValue(CustomFieldConstants.CLOUD_ZONE, "true");

    } catch (Throwable e) {
      String msg = "Failed to add VMware DC to zone ";
      if (e instanceof RemoteException) {
        msg = "Encountered remote exception at vCenter. " + VmwareHelper.getExceptionMessage(e);
      } else {
        msg += "due to : " + e.getMessage();
      }
      throw new CloudRuntimeException(msg);
    } finally {
      if (context != null) {
        context.close();
      }
      context = null;
    }
    return vmwareDc;
  }
  @Override
  public List<ManagedObjectReference> addHostToPodCluster(
      VmwareContext serviceContext, long dcId, Long podId, Long clusterId, String hostInventoryPath)
      throws Exception {
    ManagedObjectReference mor = serviceContext.getHostMorByPath(hostInventoryPath);
    if (mor != null) {
      List<ManagedObjectReference> returnedHostList = new ArrayList<ManagedObjectReference>();

      if (mor.getType().equals("ComputeResource")) {
        ManagedObjectReference[] hosts =
            (ManagedObjectReference[])
                serviceContext.getServiceUtil().getDynamicProperty(mor, "host");
        assert (hosts != null);

        // For ESX host, we need to enable host firewall to allow VNC access
        HostMO hostMo = new HostMO(serviceContext, hosts[0]);
        HostFirewallSystemMO firewallMo = hostMo.getHostFirewallSystemMO();
        if (firewallMo != null) {
          firewallMo.enableRuleset("vncServer");
          firewallMo.refreshFirewall();
        }

        // prepare at least one network on the vswitch to enable OVF importing
        String managementPortGroupName =
            hostMo.getPortGroupNameByNicType(HostVirtualNicType.management);
        assert (managementPortGroupName != null);
        HostPortGroupSpec spec = hostMo.getPortGroupSpec(managementPortGroupName);
        Integer vlanId = null;
        if (spec.getVlanId() != 0) {
          vlanId = spec.getVlanId();
        }

        HypervisorHostHelper.preparePrivateNetwork(
            _privateNetworkVSwitchName, hostMo, vlanId, 180000);
        returnedHostList.add(hosts[0]);
        return returnedHostList;
      } else if (mor.getType().equals("ClusterComputeResource")) {
        ManagedObjectReference[] hosts =
            (ManagedObjectReference[])
                serviceContext.getServiceUtil().getDynamicProperty(mor, "host");
        assert (hosts != null);

        if (hosts.length > _maxHostsPerCluster) {
          String msg =
              "vCenter cluster size is too big (current configured cluster size: "
                  + _maxHostsPerCluster
                  + ")";
          s_logger.error(msg);
          throw new DiscoveredWithErrorException(msg);
        }

        for (ManagedObjectReference morHost : hosts) {
          // For ESX host, we need to enable host firewall to allow VNC access
          HostMO hostMo = new HostMO(serviceContext, morHost);
          HostFirewallSystemMO firewallMo = hostMo.getHostFirewallSystemMO();
          if (firewallMo != null) {
            firewallMo.enableRuleset("vncServer");
            firewallMo.refreshFirewall();
          }

          String managementPortGroupName =
              hostMo.getPortGroupNameByNicType(HostVirtualNicType.management);
          assert (managementPortGroupName != null);
          HostPortGroupSpec spec = hostMo.getPortGroupSpec(managementPortGroupName);
          Integer vlanId = null;
          if (spec.getVlanId() != 0) {
            vlanId = spec.getVlanId();
          }

          // prepare at least one network on the vswitch to enable OVF importing
          HypervisorHostHelper.preparePrivateNetwork(
              _privateNetworkVSwitchName, hostMo, vlanId, 180000);
          returnedHostList.add(morHost);
        }
        return returnedHostList;
      } else if (mor.getType().equals("HostSystem")) {
        // For ESX host, we need to enable host firewall to allow VNC access
        HostMO hostMo = new HostMO(serviceContext, mor);
        HostFirewallSystemMO firewallMo = hostMo.getHostFirewallSystemMO();
        if (firewallMo != null) {
          firewallMo.enableRuleset("vncServer");
          firewallMo.refreshFirewall();
        }

        String managementPortGroupName =
            hostMo.getPortGroupNameByNicType(HostVirtualNicType.management);
        assert (managementPortGroupName != null);
        HostPortGroupSpec spec = hostMo.getPortGroupSpec(managementPortGroupName);
        Integer vlanId = null;
        if (spec.getVlanId() != 0) {
          vlanId = spec.getVlanId();
        }

        // prepare at least one network on the vswitch to enable OVF importing
        HypervisorHostHelper.preparePrivateNetwork(
            _privateNetworkVSwitchName, hostMo, vlanId, 180000);
        returnedHostList.add(mor);
        return returnedHostList;
      } else {
        s_logger.error(
            "Unsupport host type "
                + mor.getType()
                + ":"
                + mor.get_value()
                + " from inventory path: "
                + hostInventoryPath);
        return null;
      }
    }

    s_logger.error("Unable to find host from inventory path: " + hostInventoryPath);
    return null;
  }