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.balancer;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023
024import java.io.FileNotFoundException;
025import java.io.IOException;
026import java.security.SecureRandom;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.HashMap;
030import java.util.HashSet;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035import java.util.TreeMap;
036import java.util.TreeSet;
037import org.apache.commons.lang3.StringUtils;
038import org.apache.hadoop.conf.Configuration;
039import org.apache.hadoop.hbase.HBaseClassTestRule;
040import org.apache.hadoop.hbase.HBaseConfiguration;
041import org.apache.hadoop.hbase.HTableDescriptor;
042import org.apache.hadoop.hbase.ServerName;
043import org.apache.hadoop.hbase.TableDescriptors;
044import org.apache.hadoop.hbase.TableName;
045import org.apache.hadoop.hbase.client.RegionInfo;
046import org.apache.hadoop.hbase.client.RegionInfoBuilder;
047import org.apache.hadoop.hbase.master.HMaster;
048import org.apache.hadoop.hbase.master.LoadBalancer;
049import org.apache.hadoop.hbase.master.MasterServices;
050import org.apache.hadoop.hbase.master.RegionPlan;
051import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
052import org.apache.hadoop.hbase.master.assignment.RegionStates;
053import org.apache.hadoop.hbase.net.Address;
054import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer;
055import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
056import org.apache.hadoop.hbase.rsgroup.RSGroupInfoManager;
057import org.apache.hadoop.hbase.testclassification.SmallTests;
058import org.apache.hadoop.hbase.util.Bytes;
059import org.junit.BeforeClass;
060import org.junit.ClassRule;
061import org.junit.Test;
062import org.junit.experimental.categories.Category;
063import org.mockito.Mockito;
064import org.mockito.invocation.InvocationOnMock;
065import org.mockito.stubbing.Answer;
066import org.slf4j.Logger;
067import org.slf4j.LoggerFactory;
068
069import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap;
070import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
071
072//TODO use stochastic based load balancer instead
073@Category(SmallTests.class)
074public class TestRSGroupBasedLoadBalancer {
075
076  @ClassRule
077  public static final HBaseClassTestRule CLASS_RULE =
078      HBaseClassTestRule.forClass(TestRSGroupBasedLoadBalancer.class);
079
080  private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupBasedLoadBalancer.class);
081  private static RSGroupBasedLoadBalancer loadBalancer;
082  private static SecureRandom rand;
083
084  static String[]  groups = new String[] { RSGroupInfo.DEFAULT_GROUP, "dg2", "dg3", "dg4" };
085  static TableName table0 = TableName.valueOf("dt0");
086  static TableName[] tables =
087      new TableName[] { TableName.valueOf("dt1"),
088          TableName.valueOf("dt2"),
089          TableName.valueOf("dt3"),
090          TableName.valueOf("dt4")};
091  static List<ServerName> servers;
092  static Map<String, RSGroupInfo> groupMap;
093  static Map<TableName, String> tableMap;
094  static List<HTableDescriptor> tableDescs;
095  int[] regionAssignment = new int[] { 2, 5, 7, 10, 4, 3, 1 };
096  static int regionId = 0;
097
098  @BeforeClass
099  public static void beforeAllTests() throws Exception {
100    rand = new SecureRandom();
101    servers = generateServers(7);
102    groupMap = constructGroupInfo(servers, groups);
103    tableMap = new HashMap<>();
104    tableDescs = constructTableDesc();
105    Configuration conf = HBaseConfiguration.create();
106    conf.set("hbase.regions.slop", "0");
107    conf.set("hbase.rsgroup.grouploadbalancer.class", SimpleLoadBalancer.class.getCanonicalName());
108    loadBalancer = new RSGroupBasedLoadBalancer();
109    loadBalancer.setRsGroupInfoManager(getMockedGroupInfoManager());
110    loadBalancer.setMasterServices(getMockedMaster());
111    loadBalancer.setConf(conf);
112    loadBalancer.initialize();
113  }
114
115  /**
116   * Test the load balancing algorithm.
117   *
118   * Invariant is that all servers of the group should be hosting either floor(average) or
119   * ceiling(average)
120   */
121  @Test
122  public void testBalanceCluster() throws Exception {
123    Map<ServerName, List<RegionInfo>> servers = mockClusterServers();
124    ArrayListMultimap<String, ServerAndLoad> list = convertToGroupBasedMap(servers);
125    LOG.info("Mock Cluster :  " + printStats(list));
126    List<RegionPlan> plans = loadBalancer.balanceCluster(servers);
127    ArrayListMultimap<String, ServerAndLoad> balancedCluster = reconcile(
128        list, plans);
129    LOG.info("Mock Balance : " + printStats(balancedCluster));
130    assertClusterAsBalanced(balancedCluster);
131  }
132
133  /**
134   * Invariant is that all servers of a group have load between floor(avg) and
135   * ceiling(avg) number of regions.
136   */
137  private void assertClusterAsBalanced(
138      ArrayListMultimap<String, ServerAndLoad> groupLoadMap) {
139    for (String gName : groupLoadMap.keySet()) {
140      List<ServerAndLoad> groupLoad = groupLoadMap.get(gName);
141      int numServers = groupLoad.size();
142      int numRegions = 0;
143      int maxRegions = 0;
144      int minRegions = Integer.MAX_VALUE;
145      for (ServerAndLoad server : groupLoad) {
146        int nr = server.getLoad();
147        if (nr > maxRegions) {
148          maxRegions = nr;
149        }
150        if (nr < minRegions) {
151          minRegions = nr;
152        }
153        numRegions += nr;
154      }
155      if (maxRegions - minRegions < 2) {
156        // less than 2 between max and min, can't balance
157        return;
158      }
159      int min = numRegions / numServers;
160      int max = numRegions % numServers == 0 ? min : min + 1;
161
162      for (ServerAndLoad server : groupLoad) {
163        assertTrue(server.getLoad() <= max);
164        assertTrue(server.getLoad() >= min);
165      }
166    }
167  }
168
169  /**
170   * All regions have an assignment.
171   */
172  private void assertImmediateAssignment(List<RegionInfo> regions,
173                                         List<ServerName> servers,
174                                         Map<RegionInfo, ServerName> assignments)
175      throws IOException {
176    for (RegionInfo region : regions) {
177      assertTrue(assignments.containsKey(region));
178      ServerName server = assignments.get(region);
179      TableName tableName = region.getTable();
180
181      String groupName = getMockedGroupInfoManager().getRSGroupOfTable(tableName);
182      assertTrue(StringUtils.isNotEmpty(groupName));
183      RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(groupName);
184      assertTrue("Region is not correctly assigned to group servers.",
185          gInfo.containsServer(server.getAddress()));
186    }
187  }
188
189  /**
190   * Tests the bulk assignment used during cluster startup.
191   *
192   * Round-robin. Should yield a balanced cluster so same invariant as the
193   * load balancer holds, all servers holding either floor(avg) or
194   * ceiling(avg).
195   */
196  @Test
197  public void testBulkAssignment() throws Exception {
198    List<RegionInfo> regions = randomRegions(25);
199    Map<ServerName, List<RegionInfo>> assignments = loadBalancer
200        .roundRobinAssignment(regions, servers);
201    //test empty region/servers scenario
202    //this should not throw an NPE
203    loadBalancer.roundRobinAssignment(regions,
204        Collections.EMPTY_LIST);
205    //test regular scenario
206    assertTrue(assignments.keySet().size() == servers.size());
207    for (ServerName sn : assignments.keySet()) {
208      List<RegionInfo> regionAssigned = assignments.get(sn);
209      for (RegionInfo region : regionAssigned) {
210        TableName tableName = region.getTable();
211        String groupName =
212            getMockedGroupInfoManager().getRSGroupOfTable(tableName);
213        assertTrue(StringUtils.isNotEmpty(groupName));
214        RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(
215            groupName);
216        assertTrue(
217            "Region is not correctly assigned to group servers.",
218            gInfo.containsServer(sn.getAddress()));
219      }
220    }
221    ArrayListMultimap<String, ServerAndLoad> loadMap = convertToGroupBasedMap(assignments);
222    assertClusterAsBalanced(loadMap);
223  }
224
225  @Test
226  public void testGetMisplacedRegions() throws Exception {
227    // Test case where region is not considered misplaced if RSGroupInfo cannot be determined
228    Map<RegionInfo, ServerName> inputForTest = new HashMap<>();
229    RegionInfo ri = RegionInfoBuilder.newBuilder(table0)
230        .setStartKey(new byte[16])
231        .setEndKey(new byte[16])
232        .setSplit(false)
233        .setRegionId(regionId++)
234        .build();
235    inputForTest.put(ri, servers.iterator().next());
236    Set<RegionInfo> misplacedRegions = loadBalancer.getMisplacedRegions(inputForTest);
237    assertFalse(misplacedRegions.contains(ri));
238  }
239  /**
240   * Test the cluster startup bulk assignment which attempts to retain assignment info.
241   */
242  @Test
243  public void testRetainAssignment() throws Exception {
244    // Test simple case where all same servers are there
245    Map<ServerName, List<RegionInfo>> currentAssignments = mockClusterServers();
246    Map<RegionInfo, ServerName> inputForTest = new HashMap<>();
247    for (ServerName sn : currentAssignments.keySet()) {
248      for (RegionInfo region : currentAssignments.get(sn)) {
249        inputForTest.put(region, sn);
250      }
251    }
252    //verify region->null server assignment is handled
253    inputForTest.put(randomRegions(1).get(0), null);
254    Map<ServerName, List<RegionInfo>> newAssignment = loadBalancer
255        .retainAssignment(inputForTest, servers);
256    assertRetainedAssignment(inputForTest, servers, newAssignment);
257  }
258
259  /**
260   * Test BOGUS_SERVER_NAME among groups do not overwrite each other.
261   */
262  @Test
263  public void testRoundRobinAssignment() throws Exception {
264    List<ServerName> onlineServers = new ArrayList<ServerName>(servers.size());
265    onlineServers.addAll(servers);
266    List<RegionInfo> regions = randomRegions(25);
267    int bogusRegion = 0;
268    for(RegionInfo region : regions){
269      String group = tableMap.get(region.getTable());
270      if("dg3".equals(group) || "dg4".equals(group)){
271        bogusRegion++;
272      }
273    }
274    Set<Address> offlineServers = new HashSet<Address>();
275    offlineServers.addAll(groupMap.get("dg3").getServers());
276    offlineServers.addAll(groupMap.get("dg4").getServers());
277    for(Iterator<ServerName> it =  onlineServers.iterator(); it.hasNext();){
278      ServerName server = it.next();
279      Address address = server.getAddress();
280      if(offlineServers.contains(address)){
281        it.remove();
282      }
283    }
284    Map<ServerName, List<RegionInfo>> assignments = loadBalancer
285        .roundRobinAssignment(regions, onlineServers);
286    assertEquals(bogusRegion, assignments.get(LoadBalancer.BOGUS_SERVER_NAME).size());
287  }
288
289  /**
290   * Asserts a valid retained assignment plan.
291   * <p>
292   * Must meet the following conditions:
293   * <ul>
294   * <li>Every input region has an assignment, and to an online server
295   * <li>If a region had an existing assignment to a server with the same
296   * address a a currently online server, it will be assigned to it
297   * </ul>
298   */
299  private void assertRetainedAssignment(
300      Map<RegionInfo, ServerName> existing, List<ServerName> servers,
301      Map<ServerName, List<RegionInfo>> assignment)
302      throws FileNotFoundException, IOException {
303    // Verify condition 1, every region assigned, and to online server
304    Set<ServerName> onlineServerSet = new TreeSet<>(servers);
305    Set<RegionInfo> assignedRegions = new TreeSet<>(RegionInfo.COMPARATOR);
306    for (Map.Entry<ServerName, List<RegionInfo>> a : assignment.entrySet()) {
307      assertTrue(
308          "Region assigned to server that was not listed as online",
309          onlineServerSet.contains(a.getKey()));
310      for (RegionInfo r : a.getValue()) {
311        assignedRegions.add(r);
312      }
313    }
314    assertEquals(existing.size(), assignedRegions.size());
315
316    // Verify condition 2, every region must be assigned to correct server.
317    Set<String> onlineHostNames = new TreeSet<>();
318    for (ServerName s : servers) {
319      onlineHostNames.add(s.getHostname());
320    }
321
322    for (Map.Entry<ServerName, List<RegionInfo>> a : assignment.entrySet()) {
323      ServerName currentServer = a.getKey();
324      for (RegionInfo r : a.getValue()) {
325        ServerName oldAssignedServer = existing.get(r);
326        TableName tableName = r.getTable();
327        String groupName =
328            getMockedGroupInfoManager().getRSGroupOfTable(tableName);
329        assertTrue(StringUtils.isNotEmpty(groupName));
330        RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(
331            groupName);
332        assertTrue(
333            "Region is not correctly assigned to group servers.",
334            gInfo.containsServer(currentServer.getAddress()));
335        if (oldAssignedServer != null
336            && onlineHostNames.contains(oldAssignedServer
337            .getHostname())) {
338          // this region was previously assigned somewhere, and that
339          // host is still around, then the host must have been is a
340          // different group.
341          if (!oldAssignedServer.getAddress().equals(currentServer.getAddress())) {
342            assertFalse(gInfo.containsServer(oldAssignedServer.getAddress()));
343          }
344        }
345      }
346    }
347  }
348
349  private String printStats(
350      ArrayListMultimap<String, ServerAndLoad> groupBasedLoad) {
351    StringBuffer sb = new StringBuffer();
352    sb.append("\n");
353    for (String groupName : groupBasedLoad.keySet()) {
354      sb.append("Stats for group: " + groupName);
355      sb.append("\n");
356      sb.append(groupMap.get(groupName).getServers());
357      sb.append("\n");
358      List<ServerAndLoad> groupLoad = groupBasedLoad.get(groupName);
359      int numServers = groupLoad.size();
360      int totalRegions = 0;
361      sb.append("Per Server Load: \n");
362      for (ServerAndLoad sLoad : groupLoad) {
363        sb.append("Server :" + sLoad.getServerName() + " Load : "
364            + sLoad.getLoad() + "\n");
365        totalRegions += sLoad.getLoad();
366      }
367      sb.append(" Group Statistics : \n");
368      float average = (float) totalRegions / numServers;
369      int max = (int) Math.ceil(average);
370      int min = (int) Math.floor(average);
371      sb.append("[srvr=" + numServers + " rgns=" + totalRegions + " avg="
372          + average + " max=" + max + " min=" + min + "]");
373      sb.append("\n");
374      sb.append("===============================");
375      sb.append("\n");
376    }
377    return sb.toString();
378  }
379
380  private ArrayListMultimap<String, ServerAndLoad> convertToGroupBasedMap(
381      final Map<ServerName, List<RegionInfo>> serversMap) throws IOException {
382    ArrayListMultimap<String, ServerAndLoad> loadMap = ArrayListMultimap
383        .create();
384    for (RSGroupInfo gInfo : getMockedGroupInfoManager().listRSGroups()) {
385      Set<Address> groupServers = gInfo.getServers();
386      for (Address hostPort : groupServers) {
387        ServerName actual = null;
388        for(ServerName entry: servers) {
389          if(entry.getAddress().equals(hostPort)) {
390            actual = entry;
391            break;
392          }
393        }
394        List<RegionInfo> regions = serversMap.get(actual);
395        assertTrue("No load for " + actual, regions != null);
396        loadMap.put(gInfo.getName(),
397            new ServerAndLoad(actual, regions.size()));
398      }
399    }
400    return loadMap;
401  }
402
403  private ArrayListMultimap<String, ServerAndLoad> reconcile(
404      ArrayListMultimap<String, ServerAndLoad> previousLoad,
405      List<RegionPlan> plans) {
406    ArrayListMultimap<String, ServerAndLoad> result = ArrayListMultimap
407        .create();
408    result.putAll(previousLoad);
409    if (plans != null) {
410      for (RegionPlan plan : plans) {
411        ServerName source = plan.getSource();
412        updateLoad(result, source, -1);
413        ServerName destination = plan.getDestination();
414        updateLoad(result, destination, +1);
415      }
416    }
417    return result;
418  }
419
420  private void updateLoad(
421      ArrayListMultimap<String, ServerAndLoad> previousLoad,
422      final ServerName sn, final int diff) {
423    for (String groupName : previousLoad.keySet()) {
424      ServerAndLoad newSAL = null;
425      ServerAndLoad oldSAL = null;
426      for (ServerAndLoad sal : previousLoad.get(groupName)) {
427        if (ServerName.isSameAddress(sn, sal.getServerName())) {
428          oldSAL = sal;
429          newSAL = new ServerAndLoad(sn, sal.getLoad() + diff);
430          break;
431        }
432      }
433      if (newSAL != null) {
434        previousLoad.remove(groupName, oldSAL);
435        previousLoad.put(groupName, newSAL);
436        break;
437      }
438    }
439  }
440
441  private Map<ServerName, List<RegionInfo>> mockClusterServers() throws IOException {
442    assertTrue(servers.size() == regionAssignment.length);
443    Map<ServerName, List<RegionInfo>> assignment = new TreeMap<>();
444    for (int i = 0; i < servers.size(); i++) {
445      int numRegions = regionAssignment[i];
446      List<RegionInfo> regions = assignedRegions(numRegions, servers.get(i));
447      assignment.put(servers.get(i), regions);
448    }
449    return assignment;
450  }
451
452  /**
453   * Generate a list of regions evenly distributed between the tables.
454   *
455   * @param numRegions The number of regions to be generated.
456   * @return List of RegionInfo.
457   */
458  private List<RegionInfo> randomRegions(int numRegions) {
459    List<RegionInfo> regions = new ArrayList<>(numRegions);
460    byte[] start = new byte[16];
461    byte[] end = new byte[16];
462    rand.nextBytes(start);
463    rand.nextBytes(end);
464    int regionIdx = rand.nextInt(tables.length);
465    for (int i = 0; i < numRegions; i++) {
466      Bytes.putInt(start, 0, numRegions << 1);
467      Bytes.putInt(end, 0, (numRegions << 1) + 1);
468      int tableIndex = (i + regionIdx) % tables.length;
469      regions.add(RegionInfoBuilder.newBuilder(tables[tableIndex])
470          .setStartKey(start)
471          .setEndKey(end)
472          .setSplit(false)
473          .setRegionId(regionId++)
474          .build());
475    }
476    return regions;
477  }
478
479  /**
480   * Generate assigned regions to a given server using group information.
481   *
482   * @param numRegions the num regions to generate
483   * @param sn the servername
484   * @return the list of regions
485   * @throws java.io.IOException Signals that an I/O exception has occurred.
486   */
487  private List<RegionInfo> assignedRegions(int numRegions, ServerName sn) throws IOException {
488    List<RegionInfo> regions = new ArrayList<>(numRegions);
489    byte[] start = new byte[16];
490    byte[] end = new byte[16];
491    Bytes.putInt(start, 0, numRegions << 1);
492    Bytes.putInt(end, 0, (numRegions << 1) + 1);
493    for (int i = 0; i < numRegions; i++) {
494      TableName tableName = getTableName(sn);
495      regions.add(RegionInfoBuilder.newBuilder(tableName)
496          .setStartKey(start)
497          .setEndKey(end)
498          .setSplit(false)
499          .setRegionId(regionId++)
500          .build());
501    }
502    return regions;
503  }
504
505  private static List<ServerName> generateServers(int numServers) {
506    List<ServerName> servers = new ArrayList<>(numServers);
507    for (int i = 0; i < numServers; i++) {
508      String host = "server" + rand.nextInt(100000);
509      int port = rand.nextInt(60000);
510      servers.add(ServerName.valueOf(host, port, -1));
511    }
512    return servers;
513  }
514
515  /**
516   * Construct group info, with each group having at least one server.
517   *
518   * @param servers the servers
519   * @param groups the groups
520   * @return the map
521   */
522  private static Map<String, RSGroupInfo> constructGroupInfo(
523      List<ServerName> servers, String[] groups) {
524    assertTrue(servers != null);
525    assertTrue(servers.size() >= groups.length);
526    int index = 0;
527    Map<String, RSGroupInfo> groupMap = new HashMap<>();
528    for (String grpName : groups) {
529      RSGroupInfo RSGroupInfo = new RSGroupInfo(grpName);
530      RSGroupInfo.addServer(servers.get(index).getAddress());
531      groupMap.put(grpName, RSGroupInfo);
532      index++;
533    }
534    while (index < servers.size()) {
535      int grpIndex = rand.nextInt(groups.length);
536      groupMap.get(groups[grpIndex]).addServer(
537          servers.get(index).getAddress());
538      index++;
539    }
540    return groupMap;
541  }
542
543  /**
544   * Construct table descriptors evenly distributed between the groups.
545   *
546   * @return the list
547   */
548  private static List<HTableDescriptor> constructTableDesc() {
549    List<HTableDescriptor> tds = Lists.newArrayList();
550    int index = rand.nextInt(groups.length);
551    for (int i = 0; i < tables.length; i++) {
552      HTableDescriptor htd = new HTableDescriptor(tables[i]);
553      int grpIndex = (i + index) % groups.length ;
554      String groupName = groups[grpIndex];
555      tableMap.put(tables[i], groupName);
556      tds.add(htd);
557    }
558    tableMap.put(table0, "");
559    tds.add(new HTableDescriptor(table0));
560    return tds;
561  }
562
563  private static MasterServices getMockedMaster() throws IOException {
564    TableDescriptors tds = Mockito.mock(TableDescriptors.class);
565    Mockito.when(tds.get(tables[0])).thenReturn(tableDescs.get(0));
566    Mockito.when(tds.get(tables[1])).thenReturn(tableDescs.get(1));
567    Mockito.when(tds.get(tables[2])).thenReturn(tableDescs.get(2));
568    Mockito.when(tds.get(tables[3])).thenReturn(tableDescs.get(3));
569    MasterServices services = Mockito.mock(HMaster.class);
570    Mockito.when(services.getTableDescriptors()).thenReturn(tds);
571    AssignmentManager am = Mockito.mock(AssignmentManager.class);
572    Mockito.when(services.getAssignmentManager()).thenReturn(am);
573    RegionStates rss = Mockito.mock(RegionStates.class);
574    Mockito.when(am.getRegionStates()).thenReturn(rss);
575    return services;
576  }
577
578  private static RSGroupInfoManager getMockedGroupInfoManager() throws IOException {
579    RSGroupInfoManager gm = Mockito.mock(RSGroupInfoManager.class);
580    Mockito.when(gm.getRSGroup(groups[0])).thenReturn(
581        groupMap.get(groups[0]));
582    Mockito.when(gm.getRSGroup(groups[1])).thenReturn(
583        groupMap.get(groups[1]));
584    Mockito.when(gm.getRSGroup(groups[2])).thenReturn(
585        groupMap.get(groups[2]));
586    Mockito.when(gm.getRSGroup(groups[3])).thenReturn(
587        groupMap.get(groups[3]));
588    Mockito.when(gm.listRSGroups()).thenReturn(
589        Lists.newLinkedList(groupMap.values()));
590    Mockito.when(gm.isOnline()).thenReturn(true);
591    Mockito.when(gm.getRSGroupOfTable(Mockito.any()))
592        .thenAnswer(new Answer<String>() {
593          @Override
594          public String answer(InvocationOnMock invocation) throws Throwable {
595            return tableMap.get(invocation.getArgument(0));
596          }
597        });
598    return gm;
599  }
600
601  private TableName getTableName(ServerName sn) throws IOException {
602    TableName tableName = null;
603    RSGroupInfoManager gm = getMockedGroupInfoManager();
604    RSGroupInfo groupOfServer = null;
605    for(RSGroupInfo gInfo : gm.listRSGroups()){
606      if(gInfo.containsServer(sn.getAddress())){
607        groupOfServer = gInfo;
608        break;
609      }
610    }
611
612    for(HTableDescriptor desc : tableDescs){
613      if(gm.getRSGroupOfTable(desc.getTableName()).endsWith(groupOfServer.getName())){
614        tableName = desc.getTableName();
615      }
616    }
617    return tableName;
618  }
619}