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