private boolean isStaleConfiguration(Configuration config) {
    String delimiter = "-";
    String configId = config.getId();

    // Bypasses item of "global" and folders of "version", just check db configurations.
    if (configId == null || configId.equals(Constants.GLOBAL_ID) || !configId.contains(delimiter)) {
      return false;
    }

    if (_serviceInfo.getId().endsWith(Constants.STANDALONE_ID)) {
      if (!configId.equals(_serviceInfo.getId())) {
        return true;
      }
    } else {
      CoordinatorClientInetAddressMap nodeMap = _coordinator.getInetAddessLookupMap();
      int nodeCount = nodeMap.getControllerNodeIPLookupMap().size();

      String nodeIndex = configId.split(delimiter)[1];
      if (Constants.STANDALONE_ID.equalsIgnoreCase(nodeIndex)
          || Integer.parseInt(nodeIndex) > nodeCount) {
        return true;
      }
    }
    return false;
  }
 public void setConfigValue(String key, String value) {
   String configKind = _coordinator.getDbConfigPath(_serviceInfo.getName());
   Configuration config =
       _coordinator.queryConfiguration(_coordinator.getSiteId(), configKind, _serviceInfo.getId());
   if (config != null) {
     config.setConfig(key, value);
     _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), config);
   }
 }
 public String getConfigValue(String key) {
   String configKind = _coordinator.getDbConfigPath(_serviceInfo.getName());
   Configuration config =
       _coordinator.queryConfiguration(_coordinator.getSiteId(), configKind, _serviceInfo.getId());
   if (config != null) {
     return config.getConfig(key);
   }
   return null;
 }
  /**
   * set up the ssl connectors with strong ciphers
   *
   * @throws Exception
   */
  protected void initConnectors() throws Exception {
    if (!_disableHTTP) {
      if (_unsecuredConnector == null) {
        _unsecuredConnector = new SelectChannelConnector();
      }
      if (_unsecurePort != null) {
        _unsecuredConnector.setPort(Integer.parseInt(_unsecurePort));
      } else {
        _unsecuredConnector.setPort(_serviceInfo.getEndpoint().getPort());
      }
      if (_httpBindAddress != null) {
        _unsecuredConnector.setHost(_httpBindAddress);
      }
      if (lowResourcesConnections != null) {
        _unsecuredConnector.setLowResourcesConnections(lowResourcesConnections);
      }
      if (lowResourcesMaxIdleTime != null) {
        _unsecuredConnector.setLowResourcesMaxIdleTime(lowResourcesMaxIdleTime);
      }
      if (threadPool != null) {
        _unsecuredConnector.setThreadPool(threadPool);
      }
      _server.addConnector(_unsecuredConnector);
    }
    if (!_disableSSL) {
      SslContextFactory sslFac = new SslContextFactory();
      sslFac.setIncludeCipherSuites(_ciphers);

      KeyStore ks = KeyStoreUtil.getViPRKeystore(_coordinatorClient);
      _log.debug(
          "The certificates in Jetty is {}. ",
          ks.getCertificateChain(KeystoreEngine.ViPR_KEY_AND_CERTIFICATE_ALIAS));

      sslFac.setCertAlias(KeystoreEngine.ViPR_KEY_AND_CERTIFICATE_ALIAS);
      sslFac.setKeyStore(ks);
      _securedConnector = new SslSelectChannelConnector(sslFac);
      if (_securePort != null) {
        _securedConnector.setPort(Integer.parseInt(_securePort));
      } else {
        _securedConnector.setPort(_serviceInfo.getEndpoint().getPort());
      }
      if (_bindAddress != null) {
        _securedConnector.setHost(_bindAddress);
      }
      if (lowResourcesConnections != null) {
        _securedConnector.setLowResourcesConnections(lowResourcesConnections);
      }
      if (lowResourcesMaxIdleTime != null) {
        _securedConnector.setLowResourcesMaxIdleTime(lowResourcesMaxIdleTime);
      }
      if (threadPool != null) {
        _securedConnector.setThreadPool(threadPool);
      }
      _server.addConnector(_securedConnector);
    }
    _server.setSendServerVersion(false);
  }
 private void removeStaleVersionedDbConfiguration() {
   String configKind =
       _coordinator.getVersionedDbConfigPath(_serviceInfo.getName(), _serviceInfo.getVersion());
   List<Configuration> configs =
       _coordinator.queryAllConfiguration(_coordinator.getSiteId(), configKind);
   for (Configuration config : configs) {
     if (isStaleConfiguration(config)) {
       _coordinator.removeServiceConfiguration(_coordinator.getSiteId(), config);
       _log.info("Remove stale version db config, id: {}", config.getId());
     }
   }
 }
 /**
  * Checks and sets INIT_DONE state this means we are done with the actual cf changes on the
  * cassandra side for the target version
  */
 private void setDbConfigInitDone() {
   String configKind =
       _coordinator.getVersionedDbConfigPath(_serviceInfo.getName(), _serviceInfo.getVersion());
   Configuration config =
       _coordinator.queryConfiguration(_coordinator.getSiteId(), configKind, _serviceInfo.getId());
   if (config != null) {
     if (config.getConfig(DbConfigConstants.INIT_DONE) == null) {
       config.setConfig(DbConfigConstants.INIT_DONE, Boolean.TRUE.toString());
       _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), config);
     }
   } else {
     // we are expecting this to exist, because its initialized from checkVersionedConfiguration
     throw new IllegalStateException("unexpected error, db versioned configuration is null");
   }
 }
  // check and initialize global configuration
  private Configuration checkGlobalConfiguration() {
    String configKind = _coordinator.getDbConfigPath(_serviceInfo.getName());
    Configuration config =
        _coordinator.queryConfiguration(_coordinator.getSiteId(), configKind, Constants.GLOBAL_ID);
    if (config == null) {
      // check if it is upgraded from previous version to yoda - configuration may be stored in
      // znode /config. Since SeedProvider still need access that, so we remove the config
      // from global in migration callback after migration is done.
      config = _coordinator.queryConfiguration(configKind, Constants.GLOBAL_ID);
      if (config != null) {
        _log.info("Upgrade from pre-yoda release, move global config to new location");
        _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), config);
        return config;
      }

      ConfigurationImpl cfg = new ConfigurationImpl();
      cfg.setId(Constants.GLOBAL_ID);
      cfg.setKind(configKind);
      cfg.setConfig(Constants.SCHEMA_VERSION, this._serviceInfo.getVersion());

      // persist configuration
      _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), cfg);
      config = cfg;
    }
    return config;
  }
  /**
   * Retrieve the vdc config info of the current site, return such info with precheck. 1. For adding
   * a new vdc, the current vdc should be in ISOLATED status and is a fresh installation. 2. For
   * updating an existing vdc, the current vdc should be in CONNECTED status.
   *
   * @param checkParam
   * @return VirtualDataCenterResponse
   */
  @POST
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Path("/precheck")
  public VdcPreCheckResponse precheckVdcConfig(VdcPreCheckParam checkParam) {
    log.info("Start vdc config precheck for {} ...", checkParam.getConfigChangeType());

    if (service.getId().endsWith("standalone")) {
      throw GeoException.fatals.remoteVDCWrongStandaloneInstall();
    }

    log.info("Loading local vdc config ...");
    VirtualDataCenter vdc = VdcUtil.getLocalVdc();
    Boolean isFresher = checkParam.getFresher();
    if (isFresher != null && isFresher) {
      // check if VDC is a fresh installation, in ISOLATED status
      if (VirtualDataCenter.ConnectionStatus.ISOLATED != vdc.getConnectionStatus()) {
        throw GeoException.fatals.remoteFreshVDCWrongStatus(vdc.getId());
      }
    } else {
      // check if VDC is in CONNECTED status
      // check if VDC is in CONNECTED status- remove, add; update will skip-CTRL3549
      if (checkParam.getConfigChangeType().equals(VdcConfig.ConfigChangeType.CONNECT_VDC.toString())
          || (checkParam
              .getConfigChangeType()
              .equals(VdcConfig.ConfigChangeType.REMOVE_VDC.toString()))) {
        if (vdc.getConnectionStatus() != VirtualDataCenter.ConnectionStatus.CONNECTED) {
          throw GeoException.fatals.remoteVDCWrongOperationStatus(
              vdc.getId(), checkParam.getConfigChangeType());
        }
      }
    }

    boolean hasData = false;
    if (isFresher) {
      hasData = hasDataInDb();
    }

    hasData |= hasDataService();

    log.info("Checking software version ...");
    SoftwareVersion remoteSoftVer = null;

    try {
      remoteSoftVer = new SoftwareVersion(checkParam.getSoftwareVersion());
      log.info("Software version of remote vdc: {}", remoteSoftVer);
    } catch (Exception e) {
      log.info(
          "Cannot get software version from checkParam, the version of remote vdc is lower than v2.3 with exception {}",
          e.getMessage());
    }

    SoftwareVersion localSoftVer;
    try {
      localSoftVer = coordinator.getTargetInfo(RepositoryInfo.class).getCurrentVersion();
    } catch (Exception ex) {
      throw GeoException.fatals.remoteVDCFailedToGetVersion(vdc.getId());
    }

    return toVirtualDataCenterResponse(vdc, hasData, remoteSoftVer, localSoftVer);
  }
  // check and initialize versioned configuration
  private Configuration checkVersionedConfiguration() {
    String serviceVersion = _serviceInfo.getVersion();
    String dbSchemaVersion = _dbClient.getSchemaVersion();
    if (!serviceVersion.equals(dbSchemaVersion)) {
      _log.warn(
          "The db service version {} doesn't equals Db schema version {}, "
              + "set db service version to Db schema version",
          serviceVersion,
          dbSchemaVersion);
      _serviceInfo.setVersion(dbSchemaVersion);
    }

    String kind =
        _coordinator.getVersionedDbConfigPath(_serviceInfo.getName(), _serviceInfo.getVersion());
    Configuration config =
        _coordinator.queryConfiguration(_coordinator.getSiteId(), kind, _serviceInfo.getId());
    if (config == null) {
      // check if it is upgraded from previous version to yoda - configuration may be stored in
      // znode /config
      config = _coordinator.queryConfiguration(kind, _serviceInfo.getId());
      if (config != null) {
        _log.info("Upgrade from pre-2.5 release, move versioned dbconfig to new location");
        _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), config);
        return config;
      }

      ConfigurationImpl cfg = new ConfigurationImpl();
      cfg.setId(_serviceInfo.getId());
      cfg.setKind(kind);
      // persist configuration
      _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), cfg);
      config = cfg;
    }
    return config;
  }
  private void removeStaleServiceConfiguration() {
    boolean isGeoDBSvc = isGeoDbsvc();
    boolean resetAutoBootFlag = false;

    String configKind = _coordinator.getDbConfigPath(_serviceInfo.getName());
    List<Configuration> configs =
        _coordinator.queryAllConfiguration(_coordinator.getSiteId(), configKind);

    for (Configuration config : configs) {
      if (isStaleConfiguration(config)) {
        boolean autoboot = Boolean.parseBoolean(config.getConfig(DbConfigConstants.AUTOBOOT));
        String configId = config.getId();

        if (isGeoDBSvc && !autoboot && (configId.equals("geodb-4") || configId.equals("geodb-5"))) {
          // for geodbsvc, if restore with the backup of 5 nodes to 3 nodes and the backup is made
          // on the cluster that the 'autoboot=false' is set on vipr4 or vipr5
          // we should set the autoboot=false on the current node or no node with autoboot=false

          // TODO:This is a temporary/safest solution in Yoda, we'll provide a better soltuion post
          // Yoda
          resetAutoBootFlag = true;
        }

        if (isStaleConfiguration(config)) {
          _coordinator.removeServiceConfiguration(_coordinator.getSiteId(), config);
          _log.info("Remove stale db config, id: {}", config.getId());
        }
      }
    }

    if (resetAutoBootFlag) {
      _log.info("set autoboot flag to false on {}", _serviceInfo.getId());
      Configuration config =
          _coordinator.queryConfiguration(
              _coordinator.getSiteId(), configKind, _serviceInfo.getId());
      config.setConfig(DbConfigConstants.AUTOBOOT, Boolean.FALSE.toString());
      _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), config);
    }
  }
  /**
   * Checks and registers db configuration information, this is one time when cluster is coming up
   * for the first time
   */
  private Configuration checkConfiguration() {
    String configKind = _coordinator.getDbConfigPath(_serviceInfo.getName());
    Configuration config =
        _coordinator.queryConfiguration(_coordinator.getSiteId(), configKind, _serviceInfo.getId());
    if (config == null) {
      // check if it is upgraded from previous version to yoda - configuration may be stored in
      // zk global area /config. Since SeedProvider still need access that, so we remove the config
      // from global in migration callback after migration is done.
      config = _coordinator.queryConfiguration(configKind, _serviceInfo.getId());
      if (config != null) {
        _log.info("Upgrade from pre-yoda release, move dbconfig to new location");
        _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), config);
        return config;
      }

      // this is a new node
      // 1. register its configuration with coordinator
      // 2. assume autobootstrap configuration
      // this means that when a node is added, it take 1/2 of biggest token rage and
      // copies its data over
      ConfigurationImpl cfg = new ConfigurationImpl();
      cfg.setId(_serviceInfo.getId());
      cfg.setKind(configKind);
      cfg.setConfig(DbConfigConstants.NODE_ID, _coordinator.getInetAddessLookupMap().getNodeId());
      cfg.setConfig(DbConfigConstants.AUTOBOOT, Boolean.TRUE.toString());

      // check other existing db nodes
      List<Configuration> configs =
          _coordinator.queryAllConfiguration(_coordinator.getSiteId(), configKind);
      if (configs.isEmpty()) {
        // we are the first node - turn off autobootstrap
        cfg.setConfig(DbConfigConstants.AUTOBOOT, Boolean.FALSE.toString());
      }
      // persist configuration
      _coordinator.persistServiceConfiguration(_coordinator.getSiteId(), cfg);
      config = cfg;
    }
    return config;
  }
  /** Check offline event info to see if dbsvc/geodbsvc on this node could get started */
  private void checkDBOfflineInfo() {
    Configuration config =
        _coordinator.queryConfiguration(
            _coordinator.getSiteId(), Constants.DB_DOWNTIME_TRACKER_CONFIG, _serviceInfo.getName());
    DbOfflineEventInfo dbOfflineEventInfo = new DbOfflineEventInfo(config);

    String localNodeId = _coordinator.getInetAddessLookupMap().getNodeId();
    Long lastActiveTimestamp = dbOfflineEventInfo.geLastActiveTimestamp(localNodeId);
    long zkTimeStamp =
        (lastActiveTimestamp == null) ? TimeUtils.getCurrentTime() : lastActiveTimestamp;

    File localDbDir = new File(dbDir);
    Date lastModified = getLastModified(localDbDir);
    boolean isDirEmpty = lastModified == null || localDbDir.list().length == 0;
    long localTimeStamp = (isDirEmpty) ? TimeUtils.getCurrentTime() : lastModified.getTime();

    _log.info("Service timestamp in ZK is {}, local file is: {}", zkTimeStamp, localTimeStamp);
    long diffTime = (zkTimeStamp > localTimeStamp) ? (zkTimeStamp - localTimeStamp) : 0;
    if (diffTime >= MAX_SERVICE_OUTAGE_TIME) {
      String errMsg =
          String.format(
              "We detect database files on local disk are more than %s days older "
                  + "than last time it was seen in the cluster. It may bring stale data into the database, "
                  + "so the service cannot continue to boot. It may be the result of a VM snapshot rollback. "
                  + "Please contact with EMC support engineer for solution.",
              diffTime / TimeUtils.DAYS);
      alertLog.error(errMsg);
      throw new IllegalStateException(errMsg);
    }

    Long offlineTime = dbOfflineEventInfo.getOfflineTimeInMS(localNodeId);
    if (!isDirEmpty && offlineTime != null && offlineTime >= MAX_SERVICE_OUTAGE_TIME) {
      String errMsg =
          String.format(
              "This node is offline for more than %s days. It may bring stale data into "
                  + "database, so the service cannot continue to boot. Please poweroff this node and follow our "
                  + "node recovery procedure to recover this node",
              offlineTime / TimeUtils.DAYS);
      alertLog.error(errMsg);
      throw new IllegalStateException(errMsg);
    }
  }
  /**
   * check to see if the individual nodes of one vdc are visible from another
   *
   * @param checkParam
   * @return
   */
  @POST
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Path("/nodecheck")
  public VdcNodeCheckResponse checkNodeConnections(
      VdcNodeCheckParam checkParam, @HeaderParam("X-Forwarded-For") String clientIp) {
    List<VdcConfig> vdcList = checkParam.getVirtualDataCenters();

    log.info("checking nodes for vdcs {} ...", getVdcIds(vdcList));

    if (service.getId().endsWith("standalone")) {
      throw GeoException.fatals.remoteVDCWrongStandaloneInstall();
    }

    ArgValidator.checkFieldNotEmpty(vdcList, "vdc");
    VirtualDataCenter localVdc = VdcUtil.getLocalVdc();
    if (localVdc == null) {
      throw GeoException.fatals.failedToFindLocalVDC();
    }

    return toVdcNodeCheckResponse(localVdc, helper.areNodesReachable(vdcList, false));
  }
  /** Kick off background jobs */
  private void startBackgroundTasks() {
    if (!_schemaUtil.isStandby()) {
      if (!disableScheduledDbRepair) {
        startBackgroundNodeRepairTask();
      }

      if (_gcExecutor != null) {
        _gcExecutor.setDbServiceId(_serviceInfo.getId());
        _gcExecutor.start();
      }

      if (_taskScrubber != null) {
        _taskScrubber.start();
      }

      if (_eventScrubber != null) {
        _eventScrubber.start();
      }
    }
    startBackgroundDetectorTask();
    startBackgroundCompactTask();
  }
  /**
   * Do more precheck For disconnecting a vdc, check if there is a VDC that is under disconnecting
   * If yes, return the VDC under disconnecting, otherwise set the VDC (given by parameter) status
   * to DISCONNECTING
   *
   * @param checkParam
   * @return VdcPreCheckResponse
   */
  @POST
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Path("/precheck2")
  public VdcPreCheckResponse2 precheckVdcConfig(VdcPreCheckParam2 checkParam) {
    log.info("Start vdc config precheck2 for {} ...", checkParam.getConfigChangeType());

    if (service.getId().endsWith("standalone")) {
      throw GeoException.fatals.remoteVDCWrongStandaloneInstall();
    }

    VdcConfig.ConfigChangeType type = checkParam.getConfigChangeType();

    VirtualDataCenter vdc = null;

    VdcPreCheckResponse2 resp2 = new VdcPreCheckResponse2();
    resp2.setCompatible(true);

    // BZ
    // TODO Need to use a different method to update info on lock on a remote system.
    // Need to use a different field (not connection status of VDC object) as a locking mechanism)
    boolean precheckFailed = checkParam.isPrecheckFailed();
    switch (type) {
      case DISCONNECT_VDC:
        log.info("Precheck2 for disconnect ops");
        vdc = helper.getDisconnectingVdc();
        if (checkParam.getIsAllNotReachable()) {
          URI targetVdcId = checkParam.getVdcIds().get(0);
          log.info("Precheck2 to check the disconnect vdc {} is reachable", targetVdcId);
          VirtualDataCenter targetVdc = dbClient.queryObject(VirtualDataCenter.class, targetVdcId);

          resp2.setIsAllNodesNotReachable(
              !helper.areNodesReachable(
                  getLocalVdc().getShortId(),
                  targetVdc.getHostIPv4AddressesMap(),
                  targetVdc.getHostIPv6AddressesMap(),
                  checkParam.getIsAllNotReachable()));
          break;
        }
        if (precheckFailed) {
          log.info("Precheck2 to update reconnect precheck fail status");
          String vdcState = checkParam.getDefaultVdcState();
          if (StringUtils.isNotEmpty(vdcState)) {
            vdc.setConnectionStatus(VirtualDataCenter.ConnectionStatus.valueOf(vdcState));
            dbClient.updateAndReindexObject(vdc);
          }
          break;
        }

        if (vdc == null) {
          // no DISCONNECTING_VDC
          log.info("Precheck2: there is no disconnecting vdc");
          URI srcVdcId = checkParam.getVdcIds().get(1);
          VirtualDataCenter srcVdc = dbClient.queryObject(VirtualDataCenter.class, srcVdcId);
          if (srcVdc.getConnectionStatus() == VirtualDataCenter.ConnectionStatus.DISCONNECTED) {
            resp2.setCompatible(false);
            break;
          }

          // BZ
          // TODO need to use a different field to set locks on concurrent VDC operation
          URI id = checkParam.getVdcIds().get(0);
          vdc = dbClient.queryObject(VirtualDataCenter.class, id);
          vdc.setConnectionStatus(VirtualDataCenter.ConnectionStatus.DISCONNECTING);
          dbClient.updateAndReindexObject(vdc);
        } else {
          resp2 = toVirtualDataCenterResponse2(vdc, true, null);
        }

        break;
      case RECONNECT_VDC:
        log.info("Precheck2 for reconnect ops checkParam={}", checkParam);
        List<String> blackList = checkParam.getBlackList();
        List<String> whiteList = checkParam.getWhiteList();
        log.info("Precheck2 to check if two vdc disconnect each other");
        resp2.setCompatible(true);
        if (isDisconnectedEachOther(blackList, whiteList)) {
          log.info("Precheck2: two vdc have disconnected each other");
          resp2.setCompatible(false);
          break;
        }
        if (precheckFailed) {
          log.info("Precheck2 to update reconnect precheck fail status");
          URI targetVdcId = checkParam.getVdcIds().get(0);
          log.info("Precheck2 to check the disconnect vdc {} is reachable", targetVdcId);
          VirtualDataCenter targetVdc = dbClient.queryObject(VirtualDataCenter.class, targetVdcId);
          String vdcState = checkParam.getDefaultVdcState();
          if (StringUtils.isNotEmpty(vdcState)) {
            targetVdc.setConnectionStatus(VirtualDataCenter.ConnectionStatus.valueOf(vdcState));
            dbClient.updateAndReindexObject(targetVdc);
          }
          break;
        }

        break;
    }

    log.info("Precheck2 done, resp is {}", resp2.toString());
    return resp2;
  }