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;
019
020import java.io.IOException;
021import java.security.PrivilegedAction;
022import java.util.ArrayList;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.fs.FileSystem;
028import org.apache.hadoop.hbase.client.RegionReplicaUtil;
029import org.apache.hadoop.hbase.master.HMaster;
030import org.apache.hadoop.hbase.regionserver.HRegion;
031import org.apache.hadoop.hbase.regionserver.HRegion.FlushResult;
032import org.apache.hadoop.hbase.regionserver.HRegionServer;
033import org.apache.hadoop.hbase.regionserver.Region;
034import org.apache.hadoop.hbase.security.User;
035import org.apache.hadoop.hbase.test.MetricsAssertHelper;
036import org.apache.hadoop.hbase.util.JVMClusterUtil;
037import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread;
038import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
039import org.apache.hadoop.hbase.util.Threads;
040import org.apache.yetus.audience.InterfaceAudience;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
045import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
046import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService;
047import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.RegionServerStartupResponse;
048
049/**
050 * This class creates a single process HBase cluster. each server. The master uses the 'default'
051 * FileSystem. The RegionServers, if we are running on DistributedFilesystem, create a FileSystem
052 * instance each and will close down their instance on the way out.
053 */
054@InterfaceAudience.Public
055public class MiniHBaseCluster extends HBaseCluster {
056  private static final Logger LOG = LoggerFactory.getLogger(MiniHBaseCluster.class.getName());
057  public LocalHBaseCluster hbaseCluster;
058  private static int index;
059
060  /**
061   * Start a MiniHBaseCluster.
062   * @param conf             Configuration to be used for cluster
063   * @param numRegionServers initial number of region servers to start.
064   */
065  public MiniHBaseCluster(Configuration conf, int numRegionServers)
066    throws IOException, InterruptedException {
067    this(conf, 1, numRegionServers);
068  }
069
070  /**
071   * Start a MiniHBaseCluster.
072   * @param conf             Configuration to be used for cluster
073   * @param numMasters       initial number of masters to start.
074   * @param numRegionServers initial number of region servers to start.
075   */
076  public MiniHBaseCluster(Configuration conf, int numMasters, int numRegionServers)
077    throws IOException, InterruptedException {
078    this(conf, numMasters, numRegionServers, null, null);
079  }
080
081  /**
082   * Start a MiniHBaseCluster.
083   * @param conf             Configuration to be used for cluster
084   * @param numMasters       initial number of masters to start.
085   * @param numRegionServers initial number of region servers to start.
086   */
087  public MiniHBaseCluster(Configuration conf, int numMasters, int numRegionServers,
088    Class<? extends HMaster> masterClass,
089    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
090    throws IOException, InterruptedException {
091    this(conf, numMasters, 0, numRegionServers, null, masterClass, regionserverClass);
092  }
093
094  /**
095   * @param rsPorts Ports that RegionServer should use; pass ports if you want to test cluster
096   *                restart where for sure the regionservers come up on same address+port (but just
097   *                with different startcode); by default mini hbase clusters choose new arbitrary
098   *                ports on each cluster start.
099   */
100  public MiniHBaseCluster(Configuration conf, int numMasters, int numAlwaysStandByMasters,
101    int numRegionServers, List<Integer> rsPorts, Class<? extends HMaster> masterClass,
102    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
103    throws IOException, InterruptedException {
104    super(conf);
105
106    // Hadoop 2
107    CompatibilityFactory.getInstance(MetricsAssertHelper.class).init();
108
109    init(numMasters, numAlwaysStandByMasters, numRegionServers, rsPorts, masterClass,
110      regionserverClass);
111    this.initialClusterStatus = getClusterMetrics();
112  }
113
114  public Configuration getConfiguration() {
115    return this.conf;
116  }
117
118  /**
119   * Subclass so can get at protected methods (none at moment). Also, creates a FileSystem instance
120   * per instantiation. Adds a shutdown own FileSystem on the way out. Shuts down own Filesystem
121   * only, not All filesystems as the FileSystem system exit hook does.
122   */
123  public static class MiniHBaseClusterRegionServer extends HRegionServer {
124    private Thread shutdownThread = null;
125    private User user = null;
126    /**
127     * List of RegionServers killed so far. ServerName also comprises startCode of a server, so any
128     * restarted instances of the same server will have different ServerName and will not coincide
129     * with past dead ones. So there's no need to cleanup this list.
130     */
131    static Set<ServerName> killedServers = new HashSet<>();
132
133    public MiniHBaseClusterRegionServer(Configuration conf)
134      throws IOException, InterruptedException {
135      super(conf);
136      this.user = User.getCurrent();
137    }
138
139    /*
140     * @param currentfs We return this if we did not make a new one.
141     * @param uniqueName Same name used to help identify the created fs.
142     * @return A new fs instance if we are up on DistributeFileSystem.
143     */
144
145    @Override
146    protected void handleReportForDutyResponse(final RegionServerStartupResponse c)
147      throws IOException {
148      super.handleReportForDutyResponse(c);
149      // Run this thread to shutdown our filesystem on way out.
150      this.shutdownThread = new SingleFileSystemShutdownThread(getFileSystem());
151    }
152
153    @Override
154    public void run() {
155      try {
156        this.user.runAs(new PrivilegedAction<Object>() {
157          @Override
158          public Object run() {
159            runRegionServer();
160            return null;
161          }
162        });
163      } catch (Throwable t) {
164        LOG.error("Exception in run", t);
165      } finally {
166        // Run this on the way out.
167        if (this.shutdownThread != null) {
168          this.shutdownThread.start();
169          Threads.shutdown(this.shutdownThread, 30000);
170        }
171      }
172    }
173
174    private void runRegionServer() {
175      super.run();
176    }
177
178    @Override
179    protected void kill() {
180      killedServers.add(getServerName());
181      super.kill();
182    }
183
184    @Override
185    public void abort(final String reason, final Throwable cause) {
186      this.user.runAs(new PrivilegedAction<Object>() {
187        @Override
188        public Object run() {
189          abortRegionServer(reason, cause);
190          return null;
191        }
192      });
193    }
194
195    private void abortRegionServer(String reason, Throwable cause) {
196      super.abort(reason, cause);
197    }
198  }
199
200  /**
201   * Alternate shutdown hook. Just shuts down the passed fs, not all as default filesystem hook
202   * does.
203   */
204  static class SingleFileSystemShutdownThread extends Thread {
205    private final FileSystem fs;
206
207    SingleFileSystemShutdownThread(final FileSystem fs) {
208      super("Shutdown of " + fs);
209      this.fs = fs;
210    }
211
212    @Override
213    public void run() {
214      try {
215        LOG.info("Hook closing fs=" + this.fs);
216        this.fs.close();
217      } catch (IOException e) {
218        LOG.warn("Running hook", e);
219      }
220    }
221  }
222
223  private void init(final int nMasterNodes, final int numAlwaysStandByMasters,
224    final int nRegionNodes, List<Integer> rsPorts, Class<? extends HMaster> masterClass,
225    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
226    throws IOException, InterruptedException {
227    try {
228      if (masterClass == null) {
229        masterClass = HMaster.class;
230      }
231      if (regionserverClass == null) {
232        regionserverClass = MiniHBaseCluster.MiniHBaseClusterRegionServer.class;
233      }
234
235      // start up a LocalHBaseCluster
236      hbaseCluster = new LocalHBaseCluster(conf, nMasterNodes, numAlwaysStandByMasters, 0,
237        masterClass, regionserverClass);
238
239      // manually add the regionservers as other users
240      for (int i = 0; i < nRegionNodes; i++) {
241        Configuration rsConf = HBaseConfiguration.create(conf);
242        if (rsPorts != null) {
243          rsConf.setInt(HConstants.REGIONSERVER_PORT, rsPorts.get(i));
244        }
245        User user = HBaseTestingUtility.getDifferentUser(rsConf, ".hfs." + index++);
246        hbaseCluster.addRegionServer(rsConf, i, user);
247      }
248
249      hbaseCluster.startup();
250    } catch (IOException e) {
251      shutdown();
252      throw e;
253    } catch (Throwable t) {
254      LOG.error("Error starting cluster", t);
255      shutdown();
256      throw new IOException("Shutting down", t);
257    }
258  }
259
260  @Override
261  public void startRegionServer(String hostname, int port) throws IOException {
262    final Configuration newConf = HBaseConfiguration.create(conf);
263    newConf.setInt(HConstants.REGIONSERVER_PORT, port);
264    startRegionServer(newConf);
265  }
266
267  @Override
268  public void killRegionServer(ServerName serverName) throws IOException {
269    HRegionServer server = getRegionServer(getRegionServerIndex(serverName));
270    if (server instanceof MiniHBaseClusterRegionServer) {
271      LOG.info("Killing " + server.toString());
272      ((MiniHBaseClusterRegionServer) server).kill();
273    } else {
274      abortRegionServer(getRegionServerIndex(serverName));
275    }
276  }
277
278  @Override
279  public boolean isKilledRS(ServerName serverName) {
280    return MiniHBaseClusterRegionServer.killedServers.contains(serverName);
281  }
282
283  @Override
284  public void stopRegionServer(ServerName serverName) throws IOException {
285    stopRegionServer(getRegionServerIndex(serverName));
286  }
287
288  @Override
289  public void suspendRegionServer(ServerName serverName) throws IOException {
290    suspendRegionServer(getRegionServerIndex(serverName));
291  }
292
293  @Override
294  public void resumeRegionServer(ServerName serverName) throws IOException {
295    resumeRegionServer(getRegionServerIndex(serverName));
296  }
297
298  @Override
299  public void waitForRegionServerToStop(ServerName serverName, long timeout) throws IOException {
300    // ignore timeout for now
301    waitOnRegionServer(getRegionServerIndex(serverName));
302  }
303
304  @Override
305  public void startZkNode(String hostname, int port) throws IOException {
306    LOG.warn("Starting zookeeper nodes on mini cluster is not supported");
307  }
308
309  @Override
310  public void killZkNode(ServerName serverName) throws IOException {
311    LOG.warn("Aborting zookeeper nodes on mini cluster is not supported");
312  }
313
314  @Override
315  public void stopZkNode(ServerName serverName) throws IOException {
316    LOG.warn("Stopping zookeeper nodes on mini cluster is not supported");
317  }
318
319  @Override
320  public void waitForZkNodeToStart(ServerName serverName, long timeout) throws IOException {
321    LOG.warn("Waiting for zookeeper nodes to start on mini cluster is not supported");
322  }
323
324  @Override
325  public void waitForZkNodeToStop(ServerName serverName, long timeout) throws IOException {
326    LOG.warn("Waiting for zookeeper nodes to stop on mini cluster is not supported");
327  }
328
329  @Override
330  public void startDataNode(ServerName serverName) throws IOException {
331    LOG.warn("Starting datanodes on mini cluster is not supported");
332  }
333
334  @Override
335  public void killDataNode(ServerName serverName) throws IOException {
336    LOG.warn("Aborting datanodes on mini cluster is not supported");
337  }
338
339  @Override
340  public void stopDataNode(ServerName serverName) throws IOException {
341    LOG.warn("Stopping datanodes on mini cluster is not supported");
342  }
343
344  @Override
345  public void waitForDataNodeToStart(ServerName serverName, long timeout) throws IOException {
346    LOG.warn("Waiting for datanodes to start on mini cluster is not supported");
347  }
348
349  @Override
350  public void waitForDataNodeToStop(ServerName serverName, long timeout) throws IOException {
351    LOG.warn("Waiting for datanodes to stop on mini cluster is not supported");
352  }
353
354  @Override
355  public void startNameNode(ServerName serverName) throws IOException {
356    LOG.warn("Starting namenodes on mini cluster is not supported");
357  }
358
359  @Override
360  public void killNameNode(ServerName serverName) throws IOException {
361    LOG.warn("Aborting namenodes on mini cluster is not supported");
362  }
363
364  @Override
365  public void stopNameNode(ServerName serverName) throws IOException {
366    LOG.warn("Stopping namenodes on mini cluster is not supported");
367  }
368
369  @Override
370  public void waitForNameNodeToStart(ServerName serverName, long timeout) throws IOException {
371    LOG.warn("Waiting for namenodes to start on mini cluster is not supported");
372  }
373
374  @Override
375  public void waitForNameNodeToStop(ServerName serverName, long timeout) throws IOException {
376    LOG.warn("Waiting for namenodes to stop on mini cluster is not supported");
377  }
378
379  @Override
380  public void startJournalNode(ServerName serverName) {
381    LOG.warn("Starting journalnodes on mini cluster is not supported");
382  }
383
384  @Override
385  public void killJournalNode(ServerName serverName) {
386    LOG.warn("Aborting journalnodes on mini cluster is not supported");
387  }
388
389  @Override
390  public void stopJournalNode(ServerName serverName) {
391    LOG.warn("Stopping journalnodes on mini cluster is not supported");
392  }
393
394  @Override
395  public void waitForJournalNodeToStart(ServerName serverName, long timeout) {
396    LOG.warn("Waiting for journalnodes to start on mini cluster is not supported");
397  }
398
399  @Override
400  public void waitForJournalNodeToStop(ServerName serverName, long timeout) {
401    LOG.warn("Waiting for journalnodes to stop on mini cluster is not supported");
402  }
403
404  @Override
405  public void startMaster(String hostname, int port) throws IOException {
406    this.startMaster();
407  }
408
409  @Override
410  public void killMaster(ServerName serverName) throws IOException {
411    abortMaster(getMasterIndex(serverName));
412  }
413
414  @Override
415  public void stopMaster(ServerName serverName) throws IOException {
416    stopMaster(getMasterIndex(serverName));
417  }
418
419  @Override
420  public void waitForMasterToStop(ServerName serverName, long timeout) throws IOException {
421    // ignore timeout for now
422    waitOnMaster(getMasterIndex(serverName));
423  }
424
425  /**
426   * Starts a region server thread running
427   * @return New RegionServerThread
428   */
429  public JVMClusterUtil.RegionServerThread startRegionServer() throws IOException {
430    final Configuration newConf = HBaseConfiguration.create(conf);
431    return startRegionServer(newConf);
432  }
433
434  private JVMClusterUtil.RegionServerThread startRegionServer(Configuration configuration)
435    throws IOException {
436    User rsUser = HBaseTestingUtility.getDifferentUser(configuration, ".hfs." + index++);
437    JVMClusterUtil.RegionServerThread t = null;
438    try {
439      t =
440        hbaseCluster.addRegionServer(configuration, hbaseCluster.getRegionServers().size(), rsUser);
441      t.start();
442      t.waitForServerOnline();
443    } catch (InterruptedException ie) {
444      throw new IOException("Interrupted adding regionserver to cluster", ie);
445    }
446    return t;
447  }
448
449  /**
450   * Starts a region server thread and waits until its processed by master. Throws an exception when
451   * it can't start a region server or when the region server is not processed by master within the
452   * timeout.
453   * @return New RegionServerThread
454   */
455  public JVMClusterUtil.RegionServerThread startRegionServerAndWait(long timeout)
456    throws IOException {
457
458    JVMClusterUtil.RegionServerThread t = startRegionServer();
459    ServerName rsServerName = t.getRegionServer().getServerName();
460
461    long start = System.currentTimeMillis();
462    ClusterStatus clusterStatus = getClusterStatus();
463    while ((System.currentTimeMillis() - start) < timeout) {
464      if (clusterStatus != null && clusterStatus.getServers().contains(rsServerName)) {
465        return t;
466      }
467      Threads.sleep(100);
468    }
469    if (t.getRegionServer().isOnline()) {
470      throw new IOException("RS: " + rsServerName + " online, but not processed by master");
471    } else {
472      throw new IOException("RS: " + rsServerName + " is offline");
473    }
474  }
475
476  /**
477   * Cause a region server to exit doing basic clean up only on its way out.
478   * @param serverNumber Used as index into a list.
479   */
480  public String abortRegionServer(int serverNumber) {
481    HRegionServer server = getRegionServer(serverNumber);
482    LOG.info("Aborting " + server.toString());
483    server.abort("Aborting for tests", new Exception("Trace info"));
484    return server.toString();
485  }
486
487  /**
488   * Shut down the specified region server cleanly
489   * @param serverNumber Used as index into a list.
490   * @return the region server that was stopped
491   */
492  public JVMClusterUtil.RegionServerThread stopRegionServer(int serverNumber) {
493    return stopRegionServer(serverNumber, true);
494  }
495
496  /**
497   * Shut down the specified region server cleanly
498   * @param serverNumber Used as index into a list.
499   * @param shutdownFS   True is we are to shutdown the filesystem as part of this regionserver's
500   *                     shutdown. Usually we do but you do not want to do this if you are running
501   *                     multiple regionservers in a test and you shut down one before end of the
502   *                     test.
503   * @return the region server that was stopped
504   */
505  public JVMClusterUtil.RegionServerThread stopRegionServer(int serverNumber,
506    final boolean shutdownFS) {
507    JVMClusterUtil.RegionServerThread server = hbaseCluster.getRegionServers().get(serverNumber);
508    LOG.info("Stopping " + server.toString());
509    server.getRegionServer().stop("Stopping rs " + serverNumber);
510    return server;
511  }
512
513  /**
514   * Suspend the specified region server
515   * @param serverNumber Used as index into a list.
516   */
517  public JVMClusterUtil.RegionServerThread suspendRegionServer(int serverNumber) {
518    JVMClusterUtil.RegionServerThread server = hbaseCluster.getRegionServers().get(serverNumber);
519    LOG.info("Suspending {}", server.toString());
520    server.suspend();
521    return server;
522  }
523
524  /**
525   * Resume the specified region server
526   * @param serverNumber Used as index into a list.
527   */
528  public JVMClusterUtil.RegionServerThread resumeRegionServer(int serverNumber) {
529    JVMClusterUtil.RegionServerThread server = hbaseCluster.getRegionServers().get(serverNumber);
530    LOG.info("Resuming {}", server.toString());
531    server.resume();
532    return server;
533  }
534
535  /**
536   * Wait for the specified region server to stop. Removes this thread from list of running threads.
537   * @return Name of region server that just went down.
538   */
539  public String waitOnRegionServer(final int serverNumber) {
540    return this.hbaseCluster.waitOnRegionServer(serverNumber);
541  }
542
543  /**
544   * Starts a master thread running
545   * @return New RegionServerThread
546   */
547  public JVMClusterUtil.MasterThread startMaster() throws IOException {
548    Configuration c = HBaseConfiguration.create(conf);
549    User user = HBaseTestingUtility.getDifferentUser(c, ".hfs." + index++);
550
551    JVMClusterUtil.MasterThread t = null;
552    try {
553      t = hbaseCluster.addMaster(c, hbaseCluster.getMasters().size(), user);
554      t.start();
555    } catch (InterruptedException ie) {
556      throw new IOException("Interrupted adding master to cluster", ie);
557    }
558    conf.set(HConstants.MASTER_ADDRS_KEY,
559      hbaseCluster.getConfiguration().get(HConstants.MASTER_ADDRS_KEY));
560    return t;
561  }
562
563  /**
564   * Returns the current active master, if available.
565   * @return the active HMaster, null if none is active.
566   */
567  @Override
568  public MasterService.BlockingInterface getMasterAdminService() {
569    return this.hbaseCluster.getActiveMaster().getMasterRpcServices();
570  }
571
572  /**
573   * Returns the current active master, if available.
574   * @return the active HMaster, null if none is active.
575   */
576  public HMaster getMaster() {
577    return this.hbaseCluster.getActiveMaster();
578  }
579
580  /**
581   * Returns the current active master thread, if available.
582   * @return the active MasterThread, null if none is active.
583   */
584  public MasterThread getMasterThread() {
585    for (MasterThread mt : hbaseCluster.getLiveMasters()) {
586      if (mt.getMaster().isActiveMaster()) {
587        return mt;
588      }
589    }
590    return null;
591  }
592
593  /**
594   * Returns the master at the specified index, if available.
595   * @return the active HMaster, null if none is active.
596   */
597  public HMaster getMaster(final int serverNumber) {
598    return this.hbaseCluster.getMaster(serverNumber);
599  }
600
601  /**
602   * Cause a master to exit without shutting down entire cluster.
603   * @param serverNumber Used as index into a list.
604   */
605  public String abortMaster(int serverNumber) {
606    HMaster server = getMaster(serverNumber);
607    LOG.info("Aborting " + server.toString());
608    server.abort("Aborting for tests", new Exception("Trace info"));
609    return server.toString();
610  }
611
612  /**
613   * Shut down the specified master cleanly
614   * @param serverNumber Used as index into a list.
615   * @return the region server that was stopped
616   */
617  public JVMClusterUtil.MasterThread stopMaster(int serverNumber) {
618    return stopMaster(serverNumber, true);
619  }
620
621  /**
622   * Shut down the specified master cleanly
623   * @param serverNumber Used as index into a list.
624   * @param shutdownFS   True is we are to shutdown the filesystem as part of this master's
625   *                     shutdown. Usually we do but you do not want to do this if you are running
626   *                     multiple master in a test and you shut down one before end of the test.
627   * @return the master that was stopped
628   */
629  public JVMClusterUtil.MasterThread stopMaster(int serverNumber, final boolean shutdownFS) {
630    JVMClusterUtil.MasterThread server = hbaseCluster.getMasters().get(serverNumber);
631    LOG.info("Stopping " + server.toString());
632    server.getMaster().stop("Stopping master " + serverNumber);
633    return server;
634  }
635
636  /**
637   * Wait for the specified master to stop. Removes this thread from list of running threads.
638   * @return Name of master that just went down.
639   */
640  public String waitOnMaster(final int serverNumber) {
641    return this.hbaseCluster.waitOnMaster(serverNumber);
642  }
643
644  /**
645   * Blocks until there is an active master and that master has completed initialization.
646   * @return true if an active master becomes available. false if there are no masters left.
647   */
648  @Override
649  public boolean waitForActiveAndReadyMaster(long timeout) throws IOException {
650    List<JVMClusterUtil.MasterThread> mts;
651    long start = System.currentTimeMillis();
652    while (
653      !(mts = getMasterThreads()).isEmpty() && (System.currentTimeMillis() - start) < timeout
654    ) {
655      for (JVMClusterUtil.MasterThread mt : mts) {
656        if (mt.getMaster().isActiveMaster() && mt.getMaster().isInitialized()) {
657          return true;
658        }
659      }
660
661      Threads.sleep(100);
662    }
663    return false;
664  }
665
666  /** Returns List of master threads. */
667  public List<JVMClusterUtil.MasterThread> getMasterThreads() {
668    return this.hbaseCluster.getMasters();
669  }
670
671  /** Returns List of live master threads (skips the aborted and the killed) */
672  public List<JVMClusterUtil.MasterThread> getLiveMasterThreads() {
673    return this.hbaseCluster.getLiveMasters();
674  }
675
676  /**
677   * Wait for Mini HBase Cluster to shut down.
678   */
679  public void join() {
680    this.hbaseCluster.join();
681  }
682
683  /**
684   * Shut down the mini HBase cluster
685   */
686  @Override
687  public void shutdown() throws IOException {
688    if (this.hbaseCluster != null) {
689      this.hbaseCluster.shutdown();
690    }
691  }
692
693  @Override
694  public void close() throws IOException {
695  }
696
697  /**
698   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0 Use
699   *             {@link #getClusterMetrics()} instead.
700   */
701  @Deprecated
702  public ClusterStatus getClusterStatus() throws IOException {
703    HMaster master = getMaster();
704    return master == null ? null : new ClusterStatus(master.getClusterMetrics());
705  }
706
707  @Override
708  public ClusterMetrics getClusterMetrics() throws IOException {
709    HMaster master = getMaster();
710    return master == null ? null : master.getClusterMetrics();
711  }
712
713  private void executeFlush(HRegion region) throws IOException {
714    if (!RegionReplicaUtil.isDefaultReplica(region.getRegionInfo())) {
715      return;
716    }
717    // retry 5 times if we can not flush
718    for (int i = 0; i < 5; i++) {
719      FlushResult result = region.flush(true);
720      if (result.getResult() != FlushResult.Result.CANNOT_FLUSH) {
721        return;
722      }
723      Threads.sleep(1000);
724    }
725  }
726
727  /**
728   * Call flushCache on all regions on all participating regionservers.
729   */
730  public void flushcache() throws IOException {
731    for (JVMClusterUtil.RegionServerThread t : this.hbaseCluster.getRegionServers()) {
732      for (HRegion r : t.getRegionServer().getOnlineRegionsLocalContext()) {
733        executeFlush(r);
734      }
735    }
736  }
737
738  /**
739   * Call flushCache on all regions of the specified table.
740   */
741  public void flushcache(TableName tableName) throws IOException {
742    for (JVMClusterUtil.RegionServerThread t : this.hbaseCluster.getRegionServers()) {
743      for (HRegion r : t.getRegionServer().getOnlineRegionsLocalContext()) {
744        if (r.getTableDescriptor().getTableName().equals(tableName)) {
745          executeFlush(r);
746        }
747      }
748    }
749  }
750
751  /**
752   * Call flushCache on all regions on all participating regionservers.
753   */
754  public void compact(boolean major) throws IOException {
755    for (JVMClusterUtil.RegionServerThread t : this.hbaseCluster.getRegionServers()) {
756      for (HRegion r : t.getRegionServer().getOnlineRegionsLocalContext()) {
757        if (RegionReplicaUtil.isDefaultReplica(r.getRegionInfo())) {
758          r.compact(major);
759        }
760      }
761    }
762  }
763
764  /**
765   * Call flushCache on all regions of the specified table.
766   */
767  public void compact(TableName tableName, boolean major) throws IOException {
768    for (JVMClusterUtil.RegionServerThread t : this.hbaseCluster.getRegionServers()) {
769      for (HRegion r : t.getRegionServer().getOnlineRegionsLocalContext()) {
770        if (r.getTableDescriptor().getTableName().equals(tableName)) {
771          if (RegionReplicaUtil.isDefaultReplica(r.getRegionInfo())) {
772            r.compact(major);
773          }
774        }
775      }
776    }
777  }
778
779  /** Returns Number of live region servers in the cluster currently. */
780  public int getNumLiveRegionServers() {
781    return this.hbaseCluster.getLiveRegionServers().size();
782  }
783
784  /**
785   * @return List of region server threads. Does not return the master even though it is also a
786   *         region server.
787   */
788  public List<JVMClusterUtil.RegionServerThread> getRegionServerThreads() {
789    return this.hbaseCluster.getRegionServers();
790  }
791
792  /** Returns List of live region server threads (skips the aborted and the killed) */
793  public List<JVMClusterUtil.RegionServerThread> getLiveRegionServerThreads() {
794    return this.hbaseCluster.getLiveRegionServers();
795  }
796
797  /**
798   * Grab a numbered region server of your choice.
799   * @return region server
800   */
801  public HRegionServer getRegionServer(int serverNumber) {
802    return hbaseCluster.getRegionServer(serverNumber);
803  }
804
805  public HRegionServer getRegionServer(ServerName serverName) {
806    return hbaseCluster.getRegionServers().stream().map(t -> t.getRegionServer())
807      .filter(r -> r.getServerName().equals(serverName)).findFirst().orElse(null);
808  }
809
810  public List<HRegion> getRegions(byte[] tableName) {
811    return getRegions(TableName.valueOf(tableName));
812  }
813
814  public List<HRegion> getRegions(TableName tableName) {
815    List<HRegion> ret = new ArrayList<>();
816    for (JVMClusterUtil.RegionServerThread rst : getRegionServerThreads()) {
817      HRegionServer hrs = rst.getRegionServer();
818      for (Region region : hrs.getOnlineRegionsLocalContext()) {
819        if (region.getTableDescriptor().getTableName().equals(tableName)) {
820          ret.add((HRegion) region);
821        }
822      }
823    }
824    return ret;
825  }
826
827  /**
828   * @return Index into List of {@link MiniHBaseCluster#getRegionServerThreads()} of HRS carrying
829   *         regionName. Returns -1 if none found.
830   */
831  public int getServerWithMeta() {
832    return getServerWith(HRegionInfo.FIRST_META_REGIONINFO.getRegionName());
833  }
834
835  /**
836   * Get the location of the specified region
837   * @param regionName Name of the region in bytes
838   * @return Index into List of {@link MiniHBaseCluster#getRegionServerThreads()} of HRS carrying
839   *         hbase:meta. Returns -1 if none found.
840   */
841  public int getServerWith(byte[] regionName) {
842    int index = -1;
843    int count = 0;
844    for (JVMClusterUtil.RegionServerThread rst : getRegionServerThreads()) {
845      HRegionServer hrs = rst.getRegionServer();
846      if (!hrs.isStopped()) {
847        Region region = hrs.getOnlineRegion(regionName);
848        if (region != null) {
849          index = count;
850          break;
851        }
852      }
853      count++;
854    }
855    return index;
856  }
857
858  @Override
859  public ServerName getServerHoldingRegion(final TableName tn, byte[] regionName)
860    throws IOException {
861    // Assume there is only one master thread which is the active master.
862    // If there are multiple master threads, the backup master threads
863    // should hold some regions. Please refer to #countServedRegions
864    // to see how we find out all regions.
865    HMaster master = getMaster();
866    Region region = master.getOnlineRegion(regionName);
867    if (region != null) {
868      return master.getServerName();
869    }
870    int index = getServerWith(regionName);
871    if (index < 0) {
872      return null;
873    }
874    return getRegionServer(index).getServerName();
875  }
876
877  /**
878   * Counts the total numbers of regions being served by the currently online region servers by
879   * asking each how many regions they have. Does not look at hbase:meta at all. Count includes
880   * catalog tables.
881   * @return number of regions being served by all region servers
882   */
883  public long countServedRegions() {
884    long count = 0;
885    for (JVMClusterUtil.RegionServerThread rst : getLiveRegionServerThreads()) {
886      count += rst.getRegionServer().getNumberOfOnlineRegions();
887    }
888    for (JVMClusterUtil.MasterThread mt : getLiveMasterThreads()) {
889      count += mt.getMaster().getNumberOfOnlineRegions();
890    }
891    return count;
892  }
893
894  /**
895   * Do a simulated kill all masters and regionservers. Useful when it is impossible to bring the
896   * mini-cluster back for clean shutdown.
897   */
898  public void killAll() {
899    // Do backups first.
900    MasterThread activeMaster = null;
901    for (MasterThread masterThread : getMasterThreads()) {
902      if (!masterThread.getMaster().isActiveMaster()) {
903        masterThread.getMaster().abort("killAll");
904      } else {
905        activeMaster = masterThread;
906      }
907    }
908    // Do active after.
909    if (activeMaster != null) {
910      activeMaster.getMaster().abort("killAll");
911    }
912    for (RegionServerThread rst : getRegionServerThreads()) {
913      rst.getRegionServer().abort("killAll");
914    }
915  }
916
917  @Override
918  public void waitUntilShutDown() {
919    this.hbaseCluster.join();
920  }
921
922  public List<HRegion> findRegionsForTable(TableName tableName) {
923    ArrayList<HRegion> ret = new ArrayList<>();
924    for (JVMClusterUtil.RegionServerThread rst : getRegionServerThreads()) {
925      HRegionServer hrs = rst.getRegionServer();
926      for (Region region : hrs.getRegions(tableName)) {
927        if (region.getTableDescriptor().getTableName().equals(tableName)) {
928          ret.add((HRegion) region);
929        }
930      }
931    }
932    return ret;
933  }
934
935  protected int getRegionServerIndex(ServerName serverName) {
936    // we have a small number of region servers, this should be fine for now.
937    List<RegionServerThread> servers = getRegionServerThreads();
938    for (int i = 0; i < servers.size(); i++) {
939      if (servers.get(i).getRegionServer().getServerName().equals(serverName)) {
940        return i;
941      }
942    }
943    return -1;
944  }
945
946  protected int getMasterIndex(ServerName serverName) {
947    List<MasterThread> masters = getMasterThreads();
948    for (int i = 0; i < masters.size(); i++) {
949      if (masters.get(i).getMaster().getServerName().equals(serverName)) {
950        return i;
951      }
952    }
953    return -1;
954  }
955
956  @Override
957  public AdminService.BlockingInterface getAdminProtocol(ServerName serverName) throws IOException {
958    return getRegionServer(getRegionServerIndex(serverName)).getRSRpcServices();
959  }
960
961  @Override
962  public ClientService.BlockingInterface getClientProtocol(ServerName serverName)
963    throws IOException {
964    return getRegionServer(getRegionServerIndex(serverName)).getRSRpcServices();
965  }
966}