@Override
  public List<? extends VmwareDatacenter> listVmwareDatacenters(ListVmwareDcsCmd cmd)
      throws CloudRuntimeException, InvalidParameterValueException {
    Long zoneId = cmd.getZoneId();
    List<VmwareDatacenterVO> vmwareDcList = new ArrayList<VmwareDatacenterVO>();
    VmwareDatacenterZoneMapVO vmwareDcZoneMap;
    VmwareDatacenterVO vmwareDatacenter;
    long vmwareDcId;

    // Validate if zone id parameter passed to API is valid
    validateZone(zoneId);

    // Check if zone is associated with VMware DC
    vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId);
    if (vmwareDcZoneMap == null) {
      return null;
    }
    // Retrieve details of VMware DC associated with zone.
    vmwareDcId = vmwareDcZoneMap.getVmwareDcId();
    vmwareDatacenter = _vmwareDcDao.findById(vmwareDcId);
    vmwareDcList.add(vmwareDatacenter);

    // Currently a zone can have only 1 VMware DC associated with.
    // Returning list of VmwareDatacenterVO objects, in-line with future requirements, if any, like
    // participation of multiple VMware DCs in a zone.
    return vmwareDcList;
  }
  @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;
  }