/**
  * Update the assignment plan into hbase:meta
  *
  * @param plan the assignments plan to be updated into hbase:meta
  * @throws IOException if cannot update assignment plan in hbase:meta
  */
 public void updateAssignmentPlanToMeta(FavoredNodesPlan plan) throws IOException {
   try {
     LOG.info("Start to update the hbase:meta with the new assignment plan");
     Map<HRegionInfo, List<ServerName>> assignmentMap = plan.getAssignmentMap();
     FavoredNodeAssignmentHelper.updateMetaWithFavoredNodesInfo(assignmentMap, conf);
     LOG.info("Updated the hbase:meta with the new assignment plan");
   } catch (Exception e) {
     LOG.error(
         "Failed to update hbase:meta with the new assignment" + "plan because " + e.getMessage());
   }
 }
  /**
   * Print the assignment plan to the system output stream
   *
   * @param plan
   */
  public static void printAssignmentPlan(FavoredNodesPlan plan) {
    if (plan == null) return;
    LOG.info("========== Start to print the assignment plan ================");
    // sort the map based on region info
    Map<HRegionInfo, List<ServerName>> assignmentMap =
        new TreeMap<HRegionInfo, List<ServerName>>(plan.getAssignmentMap());

    for (Map.Entry<HRegionInfo, List<ServerName>> entry : assignmentMap.entrySet()) {

      String serverList = FavoredNodeAssignmentHelper.getFavoredNodesAsString(entry.getValue());
      String regionName = entry.getKey().getRegionNameAsString();
      LOG.info("Region: " + regionName);
      LOG.info("Its favored nodes: " + serverList);
    }
    LOG.info("========== Finish to print the assignment plan ================");
  }
  /**
   * Update the assignment plan to all the region servers
   *
   * @param plan
   * @throws IOException
   */
  private void updateAssignmentPlanToRegionServers(FavoredNodesPlan plan) throws IOException {
    LOG.info("Start to update the region servers with the new assignment plan");
    // Get the region to region server map
    Map<ServerName, List<HRegionInfo>> currentAssignment =
        this.getRegionAssignmentSnapshot().getRegionServerToRegionMap();

    // track of the failed and succeeded updates
    int succeededNum = 0;
    Map<ServerName, Exception> failedUpdateMap = new HashMap<ServerName, Exception>();

    for (Map.Entry<ServerName, List<HRegionInfo>> entry : currentAssignment.entrySet()) {
      List<Pair<HRegionInfo, List<ServerName>>> regionUpdateInfos =
          new ArrayList<Pair<HRegionInfo, List<ServerName>>>();
      try {
        // Keep track of the favored updates for the current region server
        FavoredNodesPlan singleServerPlan = null;
        // Find out all the updates for the current region server
        for (HRegionInfo region : entry.getValue()) {
          List<ServerName> favoredServerList = plan.getFavoredNodes(region);
          if (favoredServerList != null
              && favoredServerList.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
            // Create the single server plan if necessary
            if (singleServerPlan == null) {
              singleServerPlan = new FavoredNodesPlan();
            }
            // Update the single server update
            singleServerPlan.updateAssignmentPlan(region, favoredServerList);
            regionUpdateInfos.add(
                new Pair<HRegionInfo, List<ServerName>>(region, favoredServerList));
          }
        }
        if (singleServerPlan != null) {
          // Update the current region server with its updated favored nodes
          BlockingInterface currentRegionServer =
              ((ClusterConnection) this.connection).getAdmin(entry.getKey());
          UpdateFavoredNodesRequest request =
              RequestConverter.buildUpdateFavoredNodesRequest(regionUpdateInfos);

          UpdateFavoredNodesResponse updateFavoredNodesResponse =
              currentRegionServer.updateFavoredNodes(null, request);
          LOG.info(
              "Region server "
                  + ProtobufUtil.getServerInfo(null, currentRegionServer).getServerName()
                  + " has updated "
                  + updateFavoredNodesResponse.getResponse()
                  + " / "
                  + singleServerPlan.getAssignmentMap().size()
                  + " regions with the assignment plan");
          succeededNum++;
        }
      } catch (Exception e) {
        failedUpdateMap.put(entry.getKey(), e);
      }
    }
    // log the succeeded updates
    LOG.info("Updated " + succeededNum + " region servers with " + "the new assignment plan");

    // log the failed updates
    int failedNum = failedUpdateMap.size();
    if (failedNum != 0) {
      LOG.error(
          "Failed to update the following + "
              + failedNum
              + " region servers with its corresponding favored nodes");
      for (Map.Entry<ServerName, Exception> entry : failedUpdateMap.entrySet()) {
        LOG.error(
            "Failed to update "
                + entry.getKey().getHostAndPort()
                + " because of "
                + entry.getValue().getMessage());
      }
    }
  }