String getSafeModeTip() {
    StringBuilder msg = new StringBuilder();
    boolean isBlockThresholdMet = false;

    synchronized (this) {
      isBlockThresholdMet = (blockSafe >= blockThreshold);
      if (!isBlockThresholdMet) {
        msg.append(String.format(
            "The reported blocks %d needs additional %d"
                + " blocks to reach the threshold %.4f of total blocks %d.%n",
            blockSafe, (blockThreshold - blockSafe), threshold, blockTotal));
      } else {
        msg.append(String.format(
            "The reported blocks %d has reached the threshold %.4f of total"
                + " blocks %d. ", blockSafe, threshold, blockTotal));
      }
    }

    if (datanodeThreshold > 0) {
      if (isBlockThresholdMet) {
        int numLive = blockManager.getDatanodeManager().getNumLiveDataNodes();
        if (numLive < datanodeThreshold) {
          msg.append(String.format(
              "The number of live datanodes %d needs an additional %d live "
                  + "datanodes to reach the minimum number %d.%n",
              numLive, (datanodeThreshold - numLive), datanodeThreshold));
        } else {
          msg.append(String.format(
              "The number of live datanodes %d has reached the minimum number"
                  + " %d. ", numLive, datanodeThreshold));
        }
      } else {
        msg.append("The number of live datanodes is not calculated ")
            .append("since reported blocks hasn't reached the threshold. ");
      }
    } else {
      msg.append("The minimum number of live datanodes is not required. ");
    }

    if (getBytesInFuture() > 0) {
      msg.append("Name node detected blocks with generation stamps in future. ")
          .append("This means that Name node metadata is inconsistent. This ")
          .append("can happen if Name node metadata files have been manually ")
          .append("replaced. Exiting safe mode will cause loss of ")
          .append(getBytesInFuture())
          .append(" byte(s). Please restart name node with right metadata ")
          .append("or use \"hdfs dfsadmin -safemode forceExit\" if you ")
          .append("are certain that the NameNode was started with the correct ")
          .append("FsImage and edit logs. If you encountered this during ")
          .append("a rollback, it is safe to exit with -safemode forceExit.");
      return msg.toString();
    }

    final String turnOffTip = "Safe mode will be turned off automatically ";
    switch(status) {
    case PENDING_THRESHOLD:
      msg.append(turnOffTip).append("once the thresholds have been reached.");
      break;
    case EXTENSION:
      msg.append("In safe mode extension. ").append(turnOffTip).append("in ")
          .append(timeToLeaveExtension() / 1000).append(" seconds.");
      break;
    case OFF:
      msg.append(turnOffTip).append("soon.");
      break;
    default:
      assert false : "Non-recognized block manager safe mode status: " + status;
    }
    return msg.toString();
  }
@Test(timeout = 30000)
  public void testGetSafeModeTip() throws Exception {
    bmSafeMode.activate(BLOCK_TOTAL);
    String tip = bmSafeMode.getSafeModeTip();
    assertTrue(tip.contains(
        String.format(
            "The reported blocks %d needs additional %d blocks to reach the " +
                "threshold %.4f of total blocks %d.%n",
            0, BLOCK_THRESHOLD, THRESHOLD, BLOCK_TOTAL)));
    assertTrue(tip.contains(
        "The number of live datanodes is not calculated " +
            "since reported blocks hasn't reached the threshold."));
    assertTrue(tip.contains("Safe mode will be turned off automatically once " +
        "the thresholds have been reached."));

    // safe blocks are enough
    setBlockSafe(BLOCK_THRESHOLD);
    bmSafeMode.checkSafeMode();
    tip = bmSafeMode.getSafeModeTip();
    assertTrue(tip.contains(
        String.format("The reported blocks %d has reached the threshold"
                + " %.4f of total blocks %d. ",
            getblockSafe(), THRESHOLD, BLOCK_TOTAL)));
    assertTrue(tip.contains(
        String.format("The number of live datanodes %d has reached the " +
            "minimum number %d. ", dn.getNumLiveDataNodes(), DATANODE_NUM)));
    assertTrue(tip.contains("In safe mode extension. Safe mode will be turned" +
        " off automatically in"));

    waitForExtensionPeriod();
    tip = bmSafeMode.getSafeModeTip();
    assertTrue(tip.contains(
        String.format("The reported blocks %d has reached the threshold"
                + " %.4f of total blocks %d. ",
            getblockSafe(), THRESHOLD, BLOCK_TOTAL)));
    assertTrue(tip.contains(
        String.format("The number of live datanodes %d has reached the " +
            "minimum number %d. ", dn.getNumLiveDataNodes(), DATANODE_NUM)));
    assertTrue(tip.contains("Safe mode will be turned off automatically soon"));
  }