001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotNull;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.IOException;
027import java.net.InetSocketAddress;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034import java.util.Random;
035import java.util.concurrent.ThreadLocalRandom;
036import java.util.concurrent.atomic.AtomicInteger;
037import org.apache.hadoop.conf.Configuration;
038import org.apache.hadoop.hbase.CatalogFamilyFormat;
039import org.apache.hadoop.hbase.ClientMetaTableAccessor;
040import org.apache.hadoop.hbase.HBaseClassTestRule;
041import org.apache.hadoop.hbase.HBaseTestingUtil;
042import org.apache.hadoop.hbase.HConstants;
043import org.apache.hadoop.hbase.HRegionLocation;
044import org.apache.hadoop.hbase.MetaTableAccessor;
045import org.apache.hadoop.hbase.NamespaceDescriptor;
046import org.apache.hadoop.hbase.ServerName;
047import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
048import org.apache.hadoop.hbase.TableName;
049import org.apache.hadoop.hbase.client.Admin;
050import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
051import org.apache.hadoop.hbase.client.Connection;
052import org.apache.hadoop.hbase.client.RegionInfo;
053import org.apache.hadoop.hbase.client.RegionLocator;
054import org.apache.hadoop.hbase.client.Result;
055import org.apache.hadoop.hbase.client.TableDescriptor;
056import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
057import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper;
058import org.apache.hadoop.hbase.favored.FavoredNodeLoadBalancer;
059import org.apache.hadoop.hbase.favored.FavoredNodesPlan;
060import org.apache.hadoop.hbase.favored.FavoredNodesPlan.Position;
061import org.apache.hadoop.hbase.regionserver.HRegion;
062import org.apache.hadoop.hbase.regionserver.HRegionServer;
063import org.apache.hadoop.hbase.regionserver.Region;
064import org.apache.hadoop.hbase.testclassification.MasterTests;
065import org.apache.hadoop.hbase.testclassification.MediumTests;
066import org.apache.hadoop.hbase.util.Bytes;
067import org.apache.hadoop.hbase.util.Pair;
068import org.apache.zookeeper.KeeperException;
069import org.junit.AfterClass;
070import org.junit.BeforeClass;
071import org.junit.ClassRule;
072import org.junit.Ignore;
073import org.junit.Test;
074import org.junit.experimental.categories.Category;
075import org.slf4j.Logger;
076import org.slf4j.LoggerFactory;
077
078@Category({ MasterTests.class, MediumTests.class })
079public class TestRegionPlacement {
080
081  @ClassRule
082  public static final HBaseClassTestRule CLASS_RULE =
083    HBaseClassTestRule.forClass(TestRegionPlacement.class);
084
085  private static final Logger LOG = LoggerFactory.getLogger(TestRegionPlacement.class);
086  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
087  private final static int SLAVES = 10;
088  private static Connection CONNECTION;
089  private static Admin admin;
090  private static RegionPlacementMaintainer rp;
091  private static Position[] positions = Position.values();
092  private int lastRegionOnPrimaryRSCount = 0;
093  private int REGION_NUM = 10;
094  private Map<RegionInfo, ServerName[]> favoredNodesAssignmentPlan = new HashMap<>();
095
096  @BeforeClass
097  public static void setupBeforeClass() throws Exception {
098    Configuration conf = TEST_UTIL.getConfiguration();
099    // Enable the favored nodes based load balancer
100    conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, FavoredNodeLoadBalancer.class,
101      LoadBalancer.class);
102    conf.setBoolean("hbase.tests.use.shortcircuit.reads", false);
103    TEST_UTIL.startMiniCluster(SLAVES);
104    CONNECTION = TEST_UTIL.getConnection();
105    admin = CONNECTION.getAdmin();
106    rp = new RegionPlacementMaintainer(conf);
107  }
108
109  @AfterClass
110  public static void tearDownAfterClass() throws Exception {
111    TEST_UTIL.shutdownMiniCluster();
112  }
113
114  @Ignore("Test for unfinished feature")
115  @Test
116  public void testRegionPlacement() throws Exception {
117    String tableStr = "testRegionAssignment";
118    TableName table = TableName.valueOf(tableStr);
119    // Create a table with REGION_NUM regions.
120    createTable(table, REGION_NUM);
121
122    TEST_UTIL.waitTableAvailable(table);
123
124    // Verify all the user regions are assigned to the primary region server
125    // based on the plan
126    verifyRegionOnPrimaryRS(REGION_NUM);
127
128    FavoredNodesPlan currentPlan = rp.getRegionAssignmentSnapshot().getExistingAssignmentPlan();
129    // Verify all the region server are update with the latest favored nodes
130    verifyRegionServerUpdated(currentPlan);
131    // Test Case 2: To verify whether the region placement tools can
132    // correctly update the new assignment plan to hbase:meta and Region Server.
133    // The new assignment plan is generated by shuffle the existing assignment
134    // plan by switching PRIMARY, SECONDARY and TERTIARY nodes.
135    // Shuffle the plan by switching the secondary region server with
136    // the tertiary.
137
138    // Shuffle the secondary with tertiary favored nodes
139    FavoredNodesPlan shuffledPlan = this.shuffleAssignmentPlan(currentPlan,
140      FavoredNodesPlan.Position.SECONDARY, FavoredNodesPlan.Position.TERTIARY);
141    // Let the region placement update the hbase:meta and Region Servers
142    rp.updateAssignmentPlan(shuffledPlan);
143
144    // Verify the region assignment. There are supposed to no region reassignment
145    // All the regions are still on the primary region server
146    verifyRegionAssignment(shuffledPlan, 0, REGION_NUM);
147
148    // Shuffle the plan by switching the primary with secondary and
149    // verify the region reassignment is consistent with the plan.
150    shuffledPlan = this.shuffleAssignmentPlan(currentPlan, FavoredNodesPlan.Position.PRIMARY,
151      FavoredNodesPlan.Position.SECONDARY);
152
153    // Let the region placement update the hbase:meta and Region Servers
154    rp.updateAssignmentPlan(shuffledPlan);
155
156    verifyRegionAssignment(shuffledPlan, REGION_NUM, REGION_NUM);
157
158    // also verify that the AssignmentVerificationReport has the correct information
159    RegionPlacementMaintainer rp = new RegionPlacementMaintainer(TEST_UTIL.getConfiguration());
160    // we are interested in only one table (and hence one report)
161    rp.setTargetTableName(new String[] { tableStr });
162    List<AssignmentVerificationReport> reports = rp.verifyRegionPlacement(false);
163    AssignmentVerificationReport report = reports.get(0);
164    assertTrue(report.getRegionsWithoutValidFavoredNodes().isEmpty());
165    assertTrue(report.getNonFavoredAssignedRegions().isEmpty());
166    assertTrue(report.getTotalFavoredAssignments() >= REGION_NUM);
167    assertTrue(report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.PRIMARY) != 0);
168    assertTrue(
169      report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY) == 0);
170    assertTrue(
171      report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY) == 0);
172    assertTrue(report.getUnassignedRegions().isEmpty());
173
174    // Check when a RS stops, the regions get assigned to their secondary/tertiary
175    killRandomServerAndVerifyAssignment();
176
177    // also verify that the AssignmentVerificationReport has the correct information
178    reports = rp.verifyRegionPlacement(false);
179    report = reports.get(0);
180    assertTrue(report.getRegionsWithoutValidFavoredNodes().isEmpty());
181    assertTrue(report.getNonFavoredAssignedRegions().isEmpty());
182    assertTrue(report.getTotalFavoredAssignments() >= REGION_NUM);
183    assertTrue(report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.PRIMARY) > 0);
184    assertTrue(
185      "secondary "
186        + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY)
187        + " tertiary "
188        + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY),
189      (report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY) > 0
190        || report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY) > 0));
191    assertTrue((report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.PRIMARY)
192      + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY)
193      + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY))
194        == REGION_NUM);
195    RegionPlacementMaintainer.printAssignmentPlan(currentPlan);
196  }
197
198  private void killRandomServerAndVerifyAssignment()
199    throws IOException, InterruptedException, KeeperException {
200    ServerName serverToKill = null;
201    int killIndex = 0;
202    Random rand = ThreadLocalRandom.current();
203    ServerName metaServer = TEST_UTIL.getHBaseCluster().getServerHoldingMeta();
204    LOG.debug("Server holding meta " + metaServer);
205    boolean isNamespaceServer = false;
206    do {
207      // kill a random non-meta server carrying at least one region
208      killIndex = rand.nextInt(SLAVES);
209      serverToKill = TEST_UTIL.getHBaseCluster().getRegionServer(killIndex).getServerName();
210      Collection<HRegion> regs =
211        TEST_UTIL.getHBaseCluster().getRegionServer(killIndex).getOnlineRegionsLocalContext();
212      isNamespaceServer = false;
213      for (HRegion r : regs) {
214        if (
215          r.getRegionInfo().getTable().getNamespaceAsString()
216            .equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR)
217        ) {
218          isNamespaceServer = true;
219          break;
220        }
221      }
222    } while (
223      ServerName.isSameAddress(metaServer, serverToKill) || isNamespaceServer
224        || TEST_UTIL.getHBaseCluster().getRegionServer(killIndex).getNumberOfOnlineRegions() == 0
225    );
226    LOG.debug("Stopping RS " + serverToKill);
227    Map<RegionInfo, Pair<ServerName, ServerName>> regionsToVerify = new HashMap<>();
228    // mark the regions to track
229    for (Map.Entry<RegionInfo, ServerName[]> entry : favoredNodesAssignmentPlan.entrySet()) {
230      ServerName s = entry.getValue()[0];
231      if (ServerName.isSameAddress(s, serverToKill)) {
232        regionsToVerify.put(entry.getKey(), new Pair<>(entry.getValue()[1], entry.getValue()[2]));
233        LOG.debug("Adding " + entry.getKey() + " with sedcondary/tertiary " + entry.getValue()[1]
234          + " " + entry.getValue()[2]);
235      }
236    }
237    int orig = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getNumRegionsOpened();
238    TEST_UTIL.getHBaseCluster().stopRegionServer(serverToKill);
239    TEST_UTIL.getHBaseCluster().waitForRegionServerToStop(serverToKill, 60000);
240    int curr = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getNumRegionsOpened();
241    while (curr - orig < regionsToVerify.size()) {
242      LOG.debug("Waiting for " + regionsToVerify.size() + " to come online " + " Current #regions "
243        + curr + " Original #regions " + orig);
244      Thread.sleep(200);
245      curr = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getNumRegionsOpened();
246    }
247    // now verify
248    for (Map.Entry<RegionInfo, Pair<ServerName, ServerName>> entry : regionsToVerify.entrySet()) {
249      ServerName newDestination = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
250        .getRegionStates().getRegionServerOfRegion(entry.getKey());
251      Pair<ServerName, ServerName> secondaryTertiaryServers = entry.getValue();
252      LOG.debug("New destination for region " + entry.getKey().getEncodedName() + " "
253        + newDestination + ". Secondary/Tertiary are " + secondaryTertiaryServers.getFirst() + "/"
254        + secondaryTertiaryServers.getSecond());
255      if (
256        !(ServerName.isSameAddress(newDestination, secondaryTertiaryServers.getFirst())
257          || ServerName.isSameAddress(newDestination, secondaryTertiaryServers.getSecond()))
258      ) {
259        fail("Region " + entry.getKey() + " not present on any of the expected servers");
260      }
261    }
262    // start(reinstate) region server since we killed one before
263    TEST_UTIL.getHBaseCluster().startRegionServer();
264  }
265
266  /**
267   * Used to test the correctness of this class.
268   */
269  @Ignore("Test for unfinished feature")
270  @Test
271  public void testRandomizedMatrix() {
272    int rows = 100;
273    int cols = 100;
274    float[][] matrix = new float[rows][cols];
275    Random rand = ThreadLocalRandom.current();
276    for (int i = 0; i < rows; i++) {
277      for (int j = 0; j < cols; j++) {
278        matrix[i][j] = rand.nextFloat();
279      }
280    }
281
282    // Test that inverting a transformed matrix gives the original matrix.
283    RegionPlacementMaintainer.RandomizedMatrix rm =
284      new RegionPlacementMaintainer.RandomizedMatrix(rows, cols);
285    float[][] transformed = rm.transform(matrix);
286    float[][] invertedTransformed = rm.invert(transformed);
287    for (int i = 0; i < rows; i++) {
288      for (int j = 0; j < cols; j++) {
289        if (matrix[i][j] != invertedTransformed[i][j]) {
290          throw new RuntimeException();
291        }
292      }
293    }
294
295    // Test that the indices on a transformed matrix can be inverted to give
296    // the same values on the original matrix.
297    int[] transformedIndices = new int[rows];
298    for (int i = 0; i < rows; i++) {
299      transformedIndices[i] = rand.nextInt(cols);
300    }
301    int[] invertedTransformedIndices = rm.invertIndices(transformedIndices);
302    float[] transformedValues = new float[rows];
303    float[] invertedTransformedValues = new float[rows];
304    for (int i = 0; i < rows; i++) {
305      transformedValues[i] = transformed[i][transformedIndices[i]];
306      invertedTransformedValues[i] = matrix[i][invertedTransformedIndices[i]];
307    }
308    Arrays.sort(transformedValues);
309    Arrays.sort(invertedTransformedValues);
310    if (!Arrays.equals(transformedValues, invertedTransformedValues)) {
311      throw new RuntimeException();
312    }
313  }
314
315  /**
316   * Shuffle the assignment plan by switching two favored node positions.
317   * @param plan The assignment plan
318   * @param p1   The first switch position
319   * @param p2   The second switch position
320   * @return the shuffled assignment plan
321   */
322  private FavoredNodesPlan shuffleAssignmentPlan(FavoredNodesPlan plan,
323    FavoredNodesPlan.Position p1, FavoredNodesPlan.Position p2) throws IOException {
324    FavoredNodesPlan shuffledPlan = new FavoredNodesPlan();
325
326    Map<String, RegionInfo> regionToHRegion =
327      rp.getRegionAssignmentSnapshot().getRegionNameToRegionInfoMap();
328    for (Map.Entry<String, List<ServerName>> entry : plan.getAssignmentMap().entrySet()) {
329
330      // copy the server list from the original plan
331      List<ServerName> shuffledServerList = new ArrayList<>();
332      shuffledServerList.addAll(entry.getValue());
333
334      // start to shuffle
335      shuffledServerList.set(p1.ordinal(), entry.getValue().get(p2.ordinal()));
336      shuffledServerList.set(p2.ordinal(), entry.getValue().get(p1.ordinal()));
337
338      // update the plan
339      shuffledPlan.updateFavoredNodesMap(regionToHRegion.get(entry.getKey()), shuffledServerList);
340    }
341    return shuffledPlan;
342  }
343
344  /**
345   * To verify the region assignment status. It will check the assignment plan consistency between
346   * hbase:meta and region servers. Also it will verify weather the number of region movement and
347   * the number regions on the primary region server are expected
348   */
349  private void verifyRegionAssignment(FavoredNodesPlan plan, int regionMovementNum,
350    int numRegionsOnPrimaryRS) throws InterruptedException, IOException {
351    // Verify the assignment plan in hbase:meta is consistent with the expected plan.
352    verifyMETAUpdated(plan);
353
354    // Verify the number of region movement is expected
355    verifyRegionMovementNum(regionMovementNum);
356
357    // Verify the number of regions is assigned to the primary region server
358    // based on the plan is expected
359    verifyRegionOnPrimaryRS(numRegionsOnPrimaryRS);
360
361    // Verify all the online region server are updated with the assignment plan
362    verifyRegionServerUpdated(plan);
363  }
364
365  /**
366   * Verify the meta has updated to the latest assignment plan
367   * @param expectedPlan the region assignment plan
368   * @throws IOException if an IO problem is encountered
369   */
370  private void verifyMETAUpdated(FavoredNodesPlan expectedPlan) throws IOException {
371    FavoredNodesPlan planFromMETA = rp.getRegionAssignmentSnapshot().getExistingAssignmentPlan();
372    assertTrue("The assignment plan is NOT consistent with the expected plan ",
373      planFromMETA.equals(expectedPlan));
374  }
375
376  /**
377   * Verify the number of region movement is expected
378   */
379  private void verifyRegionMovementNum(int expected) throws InterruptedException, IOException {
380    SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
381    HMaster m = cluster.getMaster();
382    int lastRegionOpenedCount = m.getAssignmentManager().getNumRegionsOpened();
383    // get the assignments start to execute
384    m.balance();
385
386    int retry = 10;
387    long sleep = 3000;
388    int attempt = 0;
389    int currentRegionOpened, regionMovement;
390    do {
391      currentRegionOpened = m.getAssignmentManager().getNumRegionsOpened();
392      regionMovement = currentRegionOpened - lastRegionOpenedCount;
393      LOG.debug("There are " + regionMovement + "/" + expected + " regions moved after " + attempt
394        + " attempts");
395      Thread.sleep((++attempt) * sleep);
396    } while (regionMovement != expected && attempt <= retry);
397
398    // update the lastRegionOpenedCount
399    lastRegionOpenedCount = currentRegionOpened;
400
401    assertEquals("There are only " + regionMovement + " instead of " + expected
402      + " region movement for " + attempt + " attempts", expected, regionMovement);
403  }
404
405  /**
406   * Verify the number of user regions is assigned to the primary region server based on the plan is
407   * expected
408   * @param expectedNum the expected number of assigned regions
409   */
410  private void verifyRegionOnPrimaryRS(int expectedNum) throws IOException {
411    lastRegionOnPrimaryRSCount = getNumRegionisOnPrimaryRS();
412    assertEquals(
413      "Only " + expectedNum + " of user regions running " + "on the primary region server",
414      expectedNum, lastRegionOnPrimaryRSCount);
415  }
416
417  /**
418   * Verify all the online region servers has been updated to the latest assignment plan
419   */
420  private void verifyRegionServerUpdated(FavoredNodesPlan plan) throws IOException {
421    // Verify all region servers contain the correct favored nodes information
422    SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
423    for (int i = 0; i < SLAVES; i++) {
424      HRegionServer rs = cluster.getRegionServer(i);
425      for (Region region : rs.getRegions(TableName.valueOf("testRegionAssignment"))) {
426        InetSocketAddress[] favoredSocketAddress =
427          rs.getFavoredNodesForRegion(region.getRegionInfo().getEncodedName());
428        String regionName = region.getRegionInfo().getRegionNameAsString();
429        List<ServerName> favoredServerList = plan.getAssignmentMap().get(regionName);
430
431        // All regions are supposed to have favored nodes,
432        // except for hbase:meta and ROOT
433        if (favoredServerList == null) {
434          TableDescriptor desc = region.getTableDescriptor();
435          // Verify they are ROOT and hbase:meta regions since no favored nodes
436          assertNull(favoredSocketAddress);
437          assertTrue("User region " + region.getTableDescriptor().getTableName()
438            + " should have favored nodes", desc.isMetaRegion());
439        } else {
440          // For user region, the favored nodes in the region server should be
441          // identical to favored nodes in the assignmentPlan
442          assertTrue(favoredSocketAddress.length == favoredServerList.size());
443          assertTrue(favoredServerList.size() > 0);
444          for (int j = 0; j < favoredServerList.size(); j++) {
445            InetSocketAddress addrFromRS = favoredSocketAddress[j];
446            InetSocketAddress addrFromPlan = InetSocketAddress.createUnresolved(
447              favoredServerList.get(j).getHostname(), favoredServerList.get(j).getPort());
448
449            assertNotNull(addrFromRS);
450            assertNotNull(addrFromPlan);
451            assertTrue(
452              "Region server " + rs.getServerName().getAddress() + " has the " + positions[j]
453                + " for region " + region.getRegionInfo().getRegionNameAsString() + " is "
454                + addrFromRS + " which is inconsistent with the plan " + addrFromPlan,
455              addrFromRS.equals(addrFromPlan));
456          }
457        }
458      }
459    }
460  }
461
462  /**
463   * Check whether regions are assigned to servers consistent with the explicit hints that are
464   * persisted in the hbase:meta table. Also keep track of the number of the regions are assigned to
465   * the primary region server.
466   * @return the number of regions are assigned to the primary region server
467   */
468  private int getNumRegionisOnPrimaryRS() throws IOException {
469    final AtomicInteger regionOnPrimaryNum = new AtomicInteger(0);
470    final AtomicInteger totalRegionNum = new AtomicInteger(0);
471    LOG.info("The start of region placement verification");
472    ClientMetaTableAccessor.Visitor visitor = new ClientMetaTableAccessor.Visitor() {
473      @Override
474      public boolean visit(Result result) throws IOException {
475        try {
476          @SuppressWarnings("deprecation")
477          RegionInfo info = CatalogFamilyFormat.getRegionInfo(result);
478          if (
479            info.getTable().getNamespaceAsString()
480              .equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR)
481          ) {
482            return true;
483          }
484          byte[] server = result.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
485          byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY,
486            FavoredNodeAssignmentHelper.FAVOREDNODES_QUALIFIER);
487          // Add the favored nodes into assignment plan
488          ServerName[] favoredServerList =
489            FavoredNodeAssignmentHelper.getFavoredNodesList(favoredNodes);
490          favoredNodesAssignmentPlan.put(info, favoredServerList);
491
492          Position[] positions = Position.values();
493          if (info != null) {
494            totalRegionNum.incrementAndGet();
495            if (server != null) {
496              ServerName serverName = ServerName.valueOf(Bytes.toString(server), -1);
497              if (favoredNodes != null) {
498                String placement = "[NOT FAVORED NODE]";
499                for (int i = 0; i < favoredServerList.length; i++) {
500                  if (favoredServerList[i].equals(serverName)) {
501                    placement = positions[i].toString();
502                    if (i == Position.PRIMARY.ordinal()) {
503                      regionOnPrimaryNum.incrementAndGet();
504                    }
505                    break;
506                  }
507                }
508                LOG.info(info.getRegionNameAsString() + " on " + serverName + " " + placement);
509              } else {
510                LOG.info(info.getRegionNameAsString() + " running on " + serverName
511                  + " but there is no favored region server");
512              }
513            } else {
514              LOG.info(info.getRegionNameAsString() + " not assigned to any server");
515            }
516          }
517          return true;
518        } catch (RuntimeException e) {
519          LOG.error("Result=" + result);
520          throw e;
521        }
522      }
523    };
524    MetaTableAccessor.fullScanRegions(CONNECTION, visitor);
525    LOG.info("There are " + regionOnPrimaryNum.intValue() + " out of " + totalRegionNum.intValue()
526      + " regions running on the primary" + " region servers");
527    return regionOnPrimaryNum.intValue();
528  }
529
530  /**
531   * Create a table with specified table name and region number.
532   * @param tableName the name of the table to be created
533   * @param regionNum number of regions to create
534   */
535  private static void createTable(TableName tableName, int regionNum) throws IOException {
536    int expectedRegions = regionNum;
537    byte[][] splitKeys = new byte[expectedRegions - 1][];
538    for (int i = 1; i < expectedRegions; i++) {
539      byte splitKey = (byte) i;
540      splitKeys[i - 1] = new byte[] { splitKey, splitKey, splitKey };
541    }
542
543    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
544      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
545    admin.createTable(tableDescriptor, splitKeys);
546
547    try (RegionLocator r = CONNECTION.getRegionLocator(tableName)) {
548      List<HRegionLocation> regions = r.getAllRegionLocations();
549      assertEquals(
550        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
551        expectedRegions, regions.size());
552    }
553  }
554}