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 static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import edu.umd.cs.findbugs.annotations.Nullable;
025import java.io.File;
026import java.io.IOException;
027import java.io.OutputStream;
028import java.io.UncheckedIOException;
029import java.lang.reflect.Field;
030import java.lang.reflect.Modifier;
031import java.net.BindException;
032import java.net.DatagramSocket;
033import java.net.InetAddress;
034import java.net.ServerSocket;
035import java.net.Socket;
036import java.net.UnknownHostException;
037import java.nio.charset.StandardCharsets;
038import java.security.MessageDigest;
039import java.util.ArrayList;
040import java.util.Arrays;
041import java.util.Collection;
042import java.util.Collections;
043import java.util.HashSet;
044import java.util.Iterator;
045import java.util.List;
046import java.util.Map;
047import java.util.NavigableSet;
048import java.util.Properties;
049import java.util.Random;
050import java.util.Set;
051import java.util.TreeSet;
052import java.util.concurrent.ExecutionException;
053import java.util.concurrent.ThreadLocalRandom;
054import java.util.concurrent.TimeUnit;
055import java.util.concurrent.atomic.AtomicReference;
056import java.util.function.BooleanSupplier;
057import java.util.stream.Collectors;
058import org.apache.commons.io.FileUtils;
059import org.apache.commons.lang3.RandomStringUtils;
060import org.apache.hadoop.conf.Configuration;
061import org.apache.hadoop.fs.FileSystem;
062import org.apache.hadoop.fs.Path;
063import org.apache.hadoop.hbase.Waiter.ExplainingPredicate;
064import org.apache.hadoop.hbase.Waiter.Predicate;
065import org.apache.hadoop.hbase.client.Admin;
066import org.apache.hadoop.hbase.client.AsyncAdmin;
067import org.apache.hadoop.hbase.client.BufferedMutator;
068import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
069import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
070import org.apache.hadoop.hbase.client.Connection;
071import org.apache.hadoop.hbase.client.ConnectionFactory;
072import org.apache.hadoop.hbase.client.Consistency;
073import org.apache.hadoop.hbase.client.Delete;
074import org.apache.hadoop.hbase.client.Durability;
075import org.apache.hadoop.hbase.client.Get;
076import org.apache.hadoop.hbase.client.HBaseAdmin;
077import org.apache.hadoop.hbase.client.Hbck;
078import org.apache.hadoop.hbase.client.ImmutableHRegionInfo;
079import org.apache.hadoop.hbase.client.ImmutableHTableDescriptor;
080import org.apache.hadoop.hbase.client.MasterRegistry;
081import org.apache.hadoop.hbase.client.Put;
082import org.apache.hadoop.hbase.client.RegionInfo;
083import org.apache.hadoop.hbase.client.RegionInfoBuilder;
084import org.apache.hadoop.hbase.client.RegionLocator;
085import org.apache.hadoop.hbase.client.Result;
086import org.apache.hadoop.hbase.client.ResultScanner;
087import org.apache.hadoop.hbase.client.Scan;
088import org.apache.hadoop.hbase.client.Table;
089import org.apache.hadoop.hbase.client.TableDescriptor;
090import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
091import org.apache.hadoop.hbase.client.TableState;
092import org.apache.hadoop.hbase.fs.HFileSystem;
093import org.apache.hadoop.hbase.io.compress.Compression;
094import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
095import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
096import org.apache.hadoop.hbase.io.hfile.BlockCache;
097import org.apache.hadoop.hbase.io.hfile.ChecksumUtil;
098import org.apache.hadoop.hbase.io.hfile.HFile;
099import org.apache.hadoop.hbase.ipc.RpcServerInterface;
100import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
101import org.apache.hadoop.hbase.logging.Log4jUtils;
102import org.apache.hadoop.hbase.mapreduce.MapreduceTestingShim;
103import org.apache.hadoop.hbase.master.HMaster;
104import org.apache.hadoop.hbase.master.RegionState;
105import org.apache.hadoop.hbase.master.ServerManager;
106import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
107import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil;
108import org.apache.hadoop.hbase.master.assignment.RegionStateStore;
109import org.apache.hadoop.hbase.master.assignment.RegionStates;
110import org.apache.hadoop.hbase.mob.MobFileCache;
111import org.apache.hadoop.hbase.regionserver.BloomType;
112import org.apache.hadoop.hbase.regionserver.ChunkCreator;
113import org.apache.hadoop.hbase.regionserver.HRegion;
114import org.apache.hadoop.hbase.regionserver.HRegionServer;
115import org.apache.hadoop.hbase.regionserver.HStore;
116import org.apache.hadoop.hbase.regionserver.InternalScanner;
117import org.apache.hadoop.hbase.regionserver.MemStoreLAB;
118import org.apache.hadoop.hbase.regionserver.Region;
119import org.apache.hadoop.hbase.regionserver.RegionScanner;
120import org.apache.hadoop.hbase.regionserver.RegionServerServices;
121import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
122import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
123import org.apache.hadoop.hbase.security.User;
124import org.apache.hadoop.hbase.security.visibility.VisibilityLabelsCache;
125import org.apache.hadoop.hbase.util.Bytes;
126import org.apache.hadoop.hbase.util.CommonFSUtils;
127import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
128import org.apache.hadoop.hbase.util.FSTableDescriptors;
129import org.apache.hadoop.hbase.util.FSUtils;
130import org.apache.hadoop.hbase.util.JVM;
131import org.apache.hadoop.hbase.util.JVMClusterUtil;
132import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread;
133import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
134import org.apache.hadoop.hbase.util.Pair;
135import org.apache.hadoop.hbase.util.ReflectionUtils;
136import org.apache.hadoop.hbase.util.RegionSplitter;
137import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm;
138import org.apache.hadoop.hbase.util.RetryCounter;
139import org.apache.hadoop.hbase.util.Threads;
140import org.apache.hadoop.hbase.wal.WAL;
141import org.apache.hadoop.hbase.wal.WALFactory;
142import org.apache.hadoop.hbase.zookeeper.EmptyWatcher;
143import org.apache.hadoop.hbase.zookeeper.ZKConfig;
144import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
145import org.apache.hadoop.hdfs.DFSClient;
146import org.apache.hadoop.hdfs.DistributedFileSystem;
147import org.apache.hadoop.hdfs.MiniDFSCluster;
148import org.apache.hadoop.hdfs.server.datanode.DataNode;
149import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi;
150import org.apache.hadoop.hdfs.server.namenode.EditLogFileOutputStream;
151import org.apache.hadoop.mapred.JobConf;
152import org.apache.hadoop.mapred.MiniMRCluster;
153import org.apache.hadoop.mapred.TaskLog;
154import org.apache.hadoop.minikdc.MiniKdc;
155import org.apache.yetus.audience.InterfaceAudience;
156import org.apache.zookeeper.WatchedEvent;
157import org.apache.zookeeper.ZooKeeper;
158import org.apache.zookeeper.ZooKeeper.States;
159
160import org.apache.hbase.thirdparty.com.google.common.io.Closeables;
161
162import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
163
164/**
165 * Facility for testing HBase. Replacement for old HBaseTestCase and HBaseClusterTestCase
166 * functionality. Create an instance and keep it around testing HBase. This class is meant to be
167 * your one-stop shop for anything you might need testing. Manages one cluster at a time only.
168 * Managed cluster can be an in-process {@link MiniHBaseCluster}, or a deployed cluster of type
169 * {@code DistributedHBaseCluster}. Not all methods work with the real cluster. Depends on log4j
170 * being on classpath and hbase-site.xml for logging and test-run configuration. It does not set
171 * logging levels. In the configuration properties, default values for master-info-port and
172 * region-server-port are overridden such that a random port will be assigned (thus avoiding port
173 * contention if another local HBase instance is already running).
174 * <p>
175 * To preserve test data directories, pass the system property "hbase.testing.preserve.testdir"
176 * setting it to true.
177 */
178@InterfaceAudience.Public
179@SuppressWarnings("deprecation")
180public class HBaseTestingUtility extends HBaseZKTestingUtility {
181
182  /**
183   * System property key to get test directory value. Name is as it is because mini dfs has
184   * hard-codings to put test data here. It should NOT be used directly in HBase, as it's a property
185   * used in mini dfs.
186   * @deprecated since 2.0.0 and will be removed in 3.0.0. Can be used only with mini dfs.
187   * @see <a href="https://issues.apache.org/jira/browse/HBASE-19410">HBASE-19410</a>
188   */
189  @Deprecated
190  private static final String TEST_DIRECTORY_KEY = "test.build.data";
191
192  public static final String REGIONS_PER_SERVER_KEY = "hbase.test.regions-per-server";
193  /**
194   * The default number of regions per regionserver when creating a pre-split table.
195   */
196  public static final int DEFAULT_REGIONS_PER_SERVER = 3;
197
198  public static final String PRESPLIT_TEST_TABLE_KEY = "hbase.test.pre-split-table";
199  public static final boolean PRESPLIT_TEST_TABLE = true;
200
201  private MiniDFSCluster dfsCluster = null;
202  private FsDatasetAsyncDiskServiceFixer dfsClusterFixer = null;
203
204  private volatile HBaseCluster hbaseCluster = null;
205  private MiniMRCluster mrCluster = null;
206
207  /** If there is a mini cluster running for this testing utility instance. */
208  private volatile boolean miniClusterRunning;
209
210  private String hadoopLogDir;
211
212  /**
213   * Directory on test filesystem where we put the data for this instance of HBaseTestingUtility
214   */
215  private Path dataTestDirOnTestFS = null;
216
217  private final AtomicReference<Connection> connection = new AtomicReference<>();
218
219  /** Filesystem URI used for map-reduce mini-cluster setup */
220  private static String FS_URI;
221
222  /** This is for unit tests parameterized with a single boolean. */
223  public static final List<Object[]> MEMSTORETS_TAGS_PARAMETRIZED = memStoreTSAndTagsCombination();
224
225  /**
226   * Checks to see if a specific port is available.
227   * @param port the port number to check for availability
228   * @return <tt>true</tt> if the port is available, or <tt>false</tt> if not
229   */
230  public static boolean available(int port) {
231    ServerSocket ss = null;
232    DatagramSocket ds = null;
233    try {
234      ss = new ServerSocket(port);
235      ss.setReuseAddress(true);
236      ds = new DatagramSocket(port);
237      ds.setReuseAddress(true);
238      return true;
239    } catch (IOException e) {
240      // Do nothing
241    } finally {
242      if (ds != null) {
243        ds.close();
244      }
245
246      if (ss != null) {
247        try {
248          ss.close();
249        } catch (IOException e) {
250          /* should not be thrown */
251        }
252      }
253    }
254
255    return false;
256  }
257
258  /**
259   * Create all combinations of Bloom filters and compression algorithms for testing.
260   */
261  private static List<Object[]> bloomAndCompressionCombinations() {
262    List<Object[]> configurations = new ArrayList<>();
263    for (Compression.Algorithm comprAlgo : HBaseCommonTestingUtility.COMPRESSION_ALGORITHMS) {
264      for (BloomType bloomType : BloomType.values()) {
265        configurations.add(new Object[] { comprAlgo, bloomType });
266      }
267    }
268    return Collections.unmodifiableList(configurations);
269  }
270
271  /**
272   * Create combination of memstoreTS and tags
273   */
274  private static List<Object[]> memStoreTSAndTagsCombination() {
275    List<Object[]> configurations = new ArrayList<>();
276    configurations.add(new Object[] { false, false });
277    configurations.add(new Object[] { false, true });
278    configurations.add(new Object[] { true, false });
279    configurations.add(new Object[] { true, true });
280    return Collections.unmodifiableList(configurations);
281  }
282
283  public static List<Object[]> memStoreTSTagsAndOffheapCombination() {
284    List<Object[]> configurations = new ArrayList<>();
285    configurations.add(new Object[] { false, false, true });
286    configurations.add(new Object[] { false, false, false });
287    configurations.add(new Object[] { false, true, true });
288    configurations.add(new Object[] { false, true, false });
289    configurations.add(new Object[] { true, false, true });
290    configurations.add(new Object[] { true, false, false });
291    configurations.add(new Object[] { true, true, true });
292    configurations.add(new Object[] { true, true, false });
293    return Collections.unmodifiableList(configurations);
294  }
295
296  public static final Collection<Object[]> BLOOM_AND_COMPRESSION_COMBINATIONS =
297    bloomAndCompressionCombinations();
298
299  /**
300   * <p>
301   * Create an HBaseTestingUtility using a default configuration.
302   * <p>
303   * Initially, all tmp files are written to a local test data directory. Once
304   * {@link #startMiniDFSCluster} is called, either directly or via {@link #startMiniCluster()}, tmp
305   * data will be written to the DFS directory instead.
306   * <p>
307   * Previously, there was a distinction between the type of utility returned by
308   * {@link #createLocalHTU()} and this constructor; this is no longer the case. All
309   * HBaseTestingUtility objects will behave as local until a DFS cluster is started, at which point
310   * they will switch to using mini DFS for storage.
311   */
312  public HBaseTestingUtility() {
313    this(HBaseConfiguration.create());
314  }
315
316  /**
317   * <p>
318   * Create an HBaseTestingUtility using a given configuration.
319   * <p>
320   * Initially, all tmp files are written to a local test data directory. Once
321   * {@link #startMiniDFSCluster} is called, either directly or via {@link #startMiniCluster()}, tmp
322   * data will be written to the DFS directory instead.
323   * <p>
324   * Previously, there was a distinction between the type of utility returned by
325   * {@link #createLocalHTU()} and this constructor; this is no longer the case. All
326   * HBaseTestingUtility objects will behave as local until a DFS cluster is started, at which point
327   * they will switch to using mini DFS for storage.
328   * @param conf The configuration to use for further operations
329   */
330  public HBaseTestingUtility(@Nullable Configuration conf) {
331    super(conf);
332
333    // a hbase checksum verification failure will cause unit tests to fail
334    ChecksumUtil.generateExceptionForChecksumFailureForTest(true);
335
336    // Save this for when setting default file:// breaks things
337    if (this.conf.get("fs.defaultFS") != null) {
338      this.conf.set("original.defaultFS", this.conf.get("fs.defaultFS"));
339    }
340    if (this.conf.get(HConstants.HBASE_DIR) != null) {
341      this.conf.set("original.hbase.dir", this.conf.get(HConstants.HBASE_DIR));
342    }
343    // Every cluster is a local cluster until we start DFS
344    // Note that conf could be null, but this.conf will not be
345    String dataTestDir = getDataTestDir().toString();
346    this.conf.set("fs.defaultFS", "file:///");
347    this.conf.set(HConstants.HBASE_DIR, "file://" + dataTestDir);
348    LOG.debug("Setting {} to {}", HConstants.HBASE_DIR, dataTestDir);
349    this.conf.setBoolean(CommonFSUtils.UNSAFE_STREAM_CAPABILITY_ENFORCE, false);
350    // If the value for random ports isn't set set it to true, thus making
351    // tests opt-out for random port assignment
352    this.conf.setBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS,
353      this.conf.getBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS, true));
354  }
355
356  /**
357   * @deprecated since 2.0.0 and will be removed in 3.0.0. Use {@link #HBaseTestingUtility()}
358   *             instead.
359   * @return a normal HBaseTestingUtility
360   * @see #HBaseTestingUtility()
361   * @see <a href="https://issues.apache.org/jira/browse/HBASE-19841">HBASE-19841</a>
362   */
363  @Deprecated
364  public static HBaseTestingUtility createLocalHTU() {
365    return new HBaseTestingUtility();
366  }
367
368  /**
369   * @deprecated since 2.0.0 and will be removed in 3.0.0. Use
370   *             {@link #HBaseTestingUtility(Configuration)} instead.
371   * @return a normal HBaseTestingUtility
372   * @see #HBaseTestingUtility(Configuration)
373   * @see <a href="https://issues.apache.org/jira/browse/HBASE-19841">HBASE-19841</a>
374   */
375  @Deprecated
376  public static HBaseTestingUtility createLocalHTU(Configuration c) {
377    return new HBaseTestingUtility(c);
378  }
379
380  /**
381   * Close both the region {@code r} and it's underlying WAL. For use in tests.
382   */
383  public static void closeRegionAndWAL(final Region r) throws IOException {
384    closeRegionAndWAL((HRegion) r);
385  }
386
387  /**
388   * Close both the HRegion {@code r} and it's underlying WAL. For use in tests.
389   */
390  public static void closeRegionAndWAL(final HRegion r) throws IOException {
391    if (r == null) return;
392    r.close();
393    if (r.getWAL() == null) return;
394    r.getWAL().close();
395  }
396
397  /**
398   * Returns this classes's instance of {@link Configuration}. Be careful how you use the returned
399   * Configuration since {@link Connection} instances can be shared. The Map of Connections is keyed
400   * by the Configuration. If say, a Connection was being used against a cluster that had been
401   * shutdown, see {@link #shutdownMiniCluster()}, then the Connection will no longer be wholesome.
402   * Rather than use the return direct, its usually best to make a copy and use that. Do
403   * <code>Configuration c = new Configuration(INSTANCE.getConfiguration());</code>
404   * @return Instance of Configuration.
405   */
406  @Override
407  public Configuration getConfiguration() {
408    return super.getConfiguration();
409  }
410
411  public void setHBaseCluster(HBaseCluster hbaseCluster) {
412    this.hbaseCluster = hbaseCluster;
413  }
414
415  /**
416   * Home our data in a dir under {@link #DEFAULT_BASE_TEST_DIRECTORY}. Give it a random name so can
417   * have many concurrent tests running if we need to. It needs to amend the
418   * {@link #TEST_DIRECTORY_KEY} System property, as it's what minidfscluster bases it data dir on.
419   * Moding a System property is not the way to do concurrent instances -- another instance could
420   * grab the temporary value unintentionally -- but not anything can do about it at moment; single
421   * instance only is how the minidfscluster works. We also create the underlying directory names
422   * for hadoop.log.dir, mapreduce.cluster.local.dir and hadoop.tmp.dir, and set the values in the
423   * conf, and as a system property for hadoop.tmp.dir (We do not create them!).
424   * @return The calculated data test build directory, if newly-created.
425   */
426  @Override
427  protected Path setupDataTestDir() {
428    Path testPath = super.setupDataTestDir();
429    if (null == testPath) {
430      return null;
431    }
432
433    createSubDirAndSystemProperty("hadoop.log.dir", testPath, "hadoop-log-dir");
434
435    // This is defaulted in core-default.xml to /tmp/hadoop-${user.name}, but
436    // we want our own value to ensure uniqueness on the same machine
437    createSubDirAndSystemProperty("hadoop.tmp.dir", testPath, "hadoop-tmp-dir");
438
439    // Read and modified in org.apache.hadoop.mapred.MiniMRCluster
440    createSubDir("mapreduce.cluster.local.dir", testPath, "mapred-local-dir");
441    return testPath;
442  }
443
444  private void createSubDirAndSystemProperty(String propertyName, Path parent, String subDirName) {
445
446    String sysValue = System.getProperty(propertyName);
447
448    if (sysValue != null) {
449      // There is already a value set. So we do nothing but hope
450      // that there will be no conflicts
451      LOG.info("System.getProperty(\"" + propertyName + "\") already set to: " + sysValue
452        + " so I do NOT create it in " + parent);
453      String confValue = conf.get(propertyName);
454      if (confValue != null && !confValue.endsWith(sysValue)) {
455        LOG.warn(propertyName + " property value differs in configuration and system: "
456          + "Configuration=" + confValue + " while System=" + sysValue
457          + " Erasing configuration value by system value.");
458      }
459      conf.set(propertyName, sysValue);
460    } else {
461      // Ok, it's not set, so we create it as a subdirectory
462      createSubDir(propertyName, parent, subDirName);
463      System.setProperty(propertyName, conf.get(propertyName));
464    }
465  }
466
467  /**
468   * @return Where to write test data on the test filesystem; Returns working directory for the test
469   *         filesystem by default
470   * @see #setupDataTestDirOnTestFS()
471   * @see #getTestFileSystem()
472   */
473  private Path getBaseTestDirOnTestFS() throws IOException {
474    FileSystem fs = getTestFileSystem();
475    return new Path(fs.getWorkingDirectory(), "test-data");
476  }
477
478  /**
479   * @return META table descriptor
480   * @deprecated since 2.0 version and will be removed in 3.0 version. Currently for test only. use
481   *             {@link #getMetaTableDescriptorBuilder()}
482   */
483  @Deprecated
484  public HTableDescriptor getMetaTableDescriptor() {
485    return new ImmutableHTableDescriptor(getMetaTableDescriptorBuilder().build());
486  }
487
488  /**
489   * @return META table descriptor
490   * @deprecated Since 2.3.0. No one should be using this internal. Used in testing only.
491   */
492  @Deprecated
493  @InterfaceAudience.Private
494  public TableDescriptorBuilder getMetaTableDescriptorBuilder() {
495    try {
496      return FSTableDescriptors.createMetaTableDescriptorBuilder(conf);
497    } catch (IOException e) {
498      throw new RuntimeException("Unable to create META table descriptor", e);
499    }
500  }
501
502  /**
503   * Returns a Path in the test filesystem, obtained from {@link #getTestFileSystem()} to write
504   * temporary test data. Call this method after setting up the mini dfs cluster if the test relies
505   * on it.
506   * @return a unique path in the test filesystem
507   */
508  public Path getDataTestDirOnTestFS() throws IOException {
509    if (dataTestDirOnTestFS == null) {
510      setupDataTestDirOnTestFS();
511    }
512
513    return dataTestDirOnTestFS;
514  }
515
516  /**
517   * Returns a Path in the test filesystem, obtained from {@link #getTestFileSystem()} to write
518   * temporary test data. Call this method after setting up the mini dfs cluster if the test relies
519   * on it.
520   * @return a unique path in the test filesystem
521   * @param subdirName name of the subdir to create under the base test dir
522   */
523  public Path getDataTestDirOnTestFS(final String subdirName) throws IOException {
524    return new Path(getDataTestDirOnTestFS(), subdirName);
525  }
526
527  /**
528   * Sets up a path in test filesystem to be used by tests. Creates a new directory if not already
529   * setup.
530   */
531  private void setupDataTestDirOnTestFS() throws IOException {
532    if (dataTestDirOnTestFS != null) {
533      LOG.warn("Data test on test fs dir already setup in " + dataTestDirOnTestFS.toString());
534      return;
535    }
536    dataTestDirOnTestFS = getNewDataTestDirOnTestFS();
537  }
538
539  /**
540   * Sets up a new path in test filesystem to be used by tests.
541   */
542  private Path getNewDataTestDirOnTestFS() throws IOException {
543    // The file system can be either local, mini dfs, or if the configuration
544    // is supplied externally, it can be an external cluster FS. If it is a local
545    // file system, the tests should use getBaseTestDir, otherwise, we can use
546    // the working directory, and create a unique sub dir there
547    FileSystem fs = getTestFileSystem();
548    Path newDataTestDir;
549    String randomStr = getRandomUUID().toString();
550    if (fs.getUri().getScheme().equals(FileSystem.getLocal(conf).getUri().getScheme())) {
551      newDataTestDir = new Path(getDataTestDir(), randomStr);
552      File dataTestDir = new File(newDataTestDir.toString());
553      if (deleteOnExit()) dataTestDir.deleteOnExit();
554    } else {
555      Path base = getBaseTestDirOnTestFS();
556      newDataTestDir = new Path(base, randomStr);
557      if (deleteOnExit()) fs.deleteOnExit(newDataTestDir);
558    }
559    return newDataTestDir;
560  }
561
562  /**
563   * Cleans the test data directory on the test filesystem.
564   * @return True if we removed the test dirs
565   */
566  public boolean cleanupDataTestDirOnTestFS() throws IOException {
567    boolean ret = getTestFileSystem().delete(dataTestDirOnTestFS, true);
568    if (ret) dataTestDirOnTestFS = null;
569    return ret;
570  }
571
572  /**
573   * Cleans a subdirectory under the test data directory on the test filesystem.
574   * @return True if we removed child
575   */
576  public boolean cleanupDataTestDirOnTestFS(String subdirName) throws IOException {
577    Path cpath = getDataTestDirOnTestFS(subdirName);
578    return getTestFileSystem().delete(cpath, true);
579  }
580
581  // Workaround to avoid IllegalThreadStateException
582  // See HBASE-27148 for more details
583  private static final class FsDatasetAsyncDiskServiceFixer extends Thread {
584
585    private volatile boolean stopped = false;
586
587    private final MiniDFSCluster cluster;
588
589    FsDatasetAsyncDiskServiceFixer(MiniDFSCluster cluster) {
590      super("FsDatasetAsyncDiskServiceFixer");
591      setDaemon(true);
592      this.cluster = cluster;
593    }
594
595    @Override
596    public void run() {
597      while (!stopped) {
598        try {
599          Thread.sleep(30000);
600        } catch (InterruptedException e) {
601          Thread.currentThread().interrupt();
602          continue;
603        }
604        // we could add new datanodes during tests, so here we will check every 30 seconds, as the
605        // timeout of the thread pool executor is 60 seconds by default.
606        try {
607          for (DataNode dn : cluster.getDataNodes()) {
608            FsDatasetSpi<?> dataset = dn.getFSDataset();
609            Field service = dataset.getClass().getDeclaredField("asyncDiskService");
610            service.setAccessible(true);
611            Object asyncDiskService = service.get(dataset);
612            Field group = asyncDiskService.getClass().getDeclaredField("threadGroup");
613            group.setAccessible(true);
614            ThreadGroup threadGroup = (ThreadGroup) group.get(asyncDiskService);
615            if (threadGroup.isDaemon()) {
616              threadGroup.setDaemon(false);
617            }
618          }
619        } catch (NoSuchFieldException e) {
620          LOG.debug("NoSuchFieldException: " + e.getMessage()
621            + "; It might because your Hadoop version > 3.2.3 or 3.3.4, "
622            + "See HBASE-27595 for details.");
623        } catch (Exception e) {
624          LOG.warn("failed to reset thread pool timeout for FsDatasetAsyncDiskService", e);
625        }
626      }
627    }
628
629    void shutdown() {
630      stopped = true;
631      interrupt();
632    }
633  }
634
635  /**
636   * Start a minidfscluster.
637   * @param servers How many DNs to start.
638   * @see #shutdownMiniDFSCluster()
639   * @return The mini dfs cluster created.
640   */
641  public MiniDFSCluster startMiniDFSCluster(int servers) throws Exception {
642    return startMiniDFSCluster(servers, null);
643  }
644
645  /**
646   * Start a minidfscluster. This is useful if you want to run datanode on distinct hosts for things
647   * like HDFS block location verification. If you start MiniDFSCluster without host names, all
648   * instances of the datanodes will have the same host name.
649   * @param hosts hostnames DNs to run on.
650   * @see #shutdownMiniDFSCluster()
651   * @return The mini dfs cluster created.
652   */
653  public MiniDFSCluster startMiniDFSCluster(final String hosts[]) throws Exception {
654    if (hosts != null && hosts.length != 0) {
655      return startMiniDFSCluster(hosts.length, hosts);
656    } else {
657      return startMiniDFSCluster(1, null);
658    }
659  }
660
661  /**
662   * Start a minidfscluster. Can only create one.
663   * @param servers How many DNs to start.
664   * @param hosts   hostnames DNs to run on.
665   * @see #shutdownMiniDFSCluster()
666   * @return The mini dfs cluster created.
667   */
668  public MiniDFSCluster startMiniDFSCluster(int servers, final String hosts[]) throws Exception {
669    return startMiniDFSCluster(servers, null, hosts);
670  }
671
672  private void setFs() throws IOException {
673    if (this.dfsCluster == null) {
674      LOG.info("Skipping setting fs because dfsCluster is null");
675      return;
676    }
677    FileSystem fs = this.dfsCluster.getFileSystem();
678    CommonFSUtils.setFsDefault(this.conf, new Path(fs.getUri()));
679
680    // re-enable this check with dfs
681    conf.unset(CommonFSUtils.UNSAFE_STREAM_CAPABILITY_ENFORCE);
682  }
683
684  public MiniDFSCluster startMiniDFSCluster(int servers, final String racks[], String hosts[])
685    throws Exception {
686    createDirsAndSetProperties();
687    EditLogFileOutputStream.setShouldSkipFsyncForTesting(true);
688
689    this.dfsCluster =
690      new MiniDFSCluster(0, this.conf, servers, true, true, true, null, racks, hosts, null);
691    this.dfsClusterFixer = new FsDatasetAsyncDiskServiceFixer(dfsCluster);
692    this.dfsClusterFixer.start();
693    // Set this just-started cluster as our filesystem.
694    setFs();
695
696    // Wait for the cluster to be totally up
697    this.dfsCluster.waitClusterUp();
698
699    // reset the test directory for test file system
700    dataTestDirOnTestFS = null;
701    String dataTestDir = getDataTestDir().toString();
702    conf.set(HConstants.HBASE_DIR, dataTestDir);
703    LOG.debug("Setting {} to {}", HConstants.HBASE_DIR, dataTestDir);
704
705    return this.dfsCluster;
706  }
707
708  public MiniDFSCluster startMiniDFSClusterForTestWAL(int namenodePort) throws IOException {
709    createDirsAndSetProperties();
710    dfsCluster =
711      new MiniDFSCluster(namenodePort, conf, 5, false, true, true, null, null, null, null);
712    this.dfsClusterFixer = new FsDatasetAsyncDiskServiceFixer(dfsCluster);
713    this.dfsClusterFixer.start();
714    return dfsCluster;
715  }
716
717  /**
718   * This is used before starting HDFS and map-reduce mini-clusters Run something like the below to
719   * check for the likes of '/tmp' references -- i.e. references outside of the test data dir -- in
720   * the conf.
721   *
722   * <pre>
723   * Configuration conf = TEST_UTIL.getConfiguration();
724   * for (Iterator&lt;Map.Entry&lt;String, String&gt;&gt; i = conf.iterator(); i.hasNext();) {
725   *   Map.Entry&lt;String, String&gt; e = i.next();
726   *   assertFalse(e.getKey() + " " + e.getValue(), e.getValue().contains("/tmp"));
727   * }
728   * </pre>
729   */
730  private void createDirsAndSetProperties() throws IOException {
731    setupClusterTestDir();
732    conf.set(TEST_DIRECTORY_KEY, clusterTestDir.getPath());
733    System.setProperty(TEST_DIRECTORY_KEY, clusterTestDir.getPath());
734    createDirAndSetProperty("test.cache.data");
735    createDirAndSetProperty("hadoop.tmp.dir");
736    hadoopLogDir = createDirAndSetProperty("hadoop.log.dir");
737    createDirAndSetProperty("mapreduce.cluster.local.dir");
738    createDirAndSetProperty("mapreduce.cluster.temp.dir");
739    enableShortCircuit();
740
741    Path root = getDataTestDirOnTestFS("hadoop");
742    conf.set(MapreduceTestingShim.getMROutputDirProp(),
743      new Path(root, "mapred-output-dir").toString());
744    conf.set("mapreduce.jobtracker.system.dir", new Path(root, "mapred-system-dir").toString());
745    conf.set("mapreduce.jobtracker.staging.root.dir",
746      new Path(root, "mapreduce-jobtracker-staging-root-dir").toString());
747    conf.set("mapreduce.job.working.dir", new Path(root, "mapred-working-dir").toString());
748    conf.set("yarn.app.mapreduce.am.staging-dir",
749      new Path(root, "mapreduce-am-staging-root-dir").toString());
750
751    // Frustrate yarn's and hdfs's attempts at writing /tmp.
752    // Below is fragile. Make it so we just interpolate any 'tmp' reference.
753    createDirAndSetProperty("yarn.node-labels.fs-store.root-dir");
754    createDirAndSetProperty("yarn.node-attribute.fs-store.root-dir");
755    createDirAndSetProperty("yarn.nodemanager.log-dirs");
756    createDirAndSetProperty("yarn.nodemanager.remote-app-log-dir");
757    createDirAndSetProperty("yarn.timeline-service.entity-group-fs-store.active-dir");
758    createDirAndSetProperty("yarn.timeline-service.entity-group-fs-store.done-dir");
759    createDirAndSetProperty("yarn.nodemanager.remote-app-log-dir");
760    createDirAndSetProperty("dfs.journalnode.edits.dir");
761    createDirAndSetProperty("dfs.datanode.shared.file.descriptor.paths");
762    createDirAndSetProperty("nfs.dump.dir");
763    createDirAndSetProperty("java.io.tmpdir");
764    createDirAndSetProperty("dfs.journalnode.edits.dir");
765    createDirAndSetProperty("dfs.provided.aliasmap.inmemory.leveldb.dir");
766    createDirAndSetProperty("fs.s3a.committer.staging.tmp.path");
767  }
768
769  /**
770   * Check whether the tests should assume NEW_VERSION_BEHAVIOR when creating new column families.
771   * Default to false.
772   */
773  public boolean isNewVersionBehaviorEnabled() {
774    final String propName = "hbase.tests.new.version.behavior";
775    String v = System.getProperty(propName);
776    if (v != null) {
777      return Boolean.parseBoolean(v);
778    }
779    return false;
780  }
781
782  /**
783   * Get the HBase setting for dfs.client.read.shortcircuit from the conf or a system property. This
784   * allows to specify this parameter on the command line. If not set, default is true.
785   */
786  public boolean isReadShortCircuitOn() {
787    final String propName = "hbase.tests.use.shortcircuit.reads";
788    String readOnProp = System.getProperty(propName);
789    if (readOnProp != null) {
790      return Boolean.parseBoolean(readOnProp);
791    } else {
792      return conf.getBoolean(propName, false);
793    }
794  }
795
796  /**
797   * Enable the short circuit read, unless configured differently. Set both HBase and HDFS settings,
798   * including skipping the hdfs checksum checks.
799   */
800  private void enableShortCircuit() {
801    if (isReadShortCircuitOn()) {
802      String curUser = System.getProperty("user.name");
803      LOG.info("read short circuit is ON for user " + curUser);
804      // read short circuit, for hdfs
805      conf.set("dfs.block.local-path-access.user", curUser);
806      // read short circuit, for hbase
807      conf.setBoolean("dfs.client.read.shortcircuit", true);
808      // Skip checking checksum, for the hdfs client and the datanode
809      conf.setBoolean("dfs.client.read.shortcircuit.skip.checksum", true);
810    } else {
811      LOG.info("read short circuit is OFF");
812    }
813  }
814
815  private String createDirAndSetProperty(String property) {
816    return createDirAndSetProperty(property, property);
817  }
818
819  private String createDirAndSetProperty(final String relPath, String property) {
820    String path = getDataTestDir(relPath).toString();
821    System.setProperty(property, path);
822    conf.set(property, path);
823    new File(path).mkdirs();
824    LOG.info("Setting " + property + " to " + path + " in system properties and HBase conf");
825    return path;
826  }
827
828  /**
829   * Shuts down instance created by call to {@link #startMiniDFSCluster(int)} or does nothing.
830   */
831  public void shutdownMiniDFSCluster() throws IOException {
832    if (this.dfsCluster != null) {
833      // The below throws an exception per dn, AsynchronousCloseException.
834      this.dfsCluster.shutdown();
835      dfsCluster = null;
836      // It is possible that the dfs cluster is set through setDFSCluster method, where we will not
837      // have a fixer
838      if (dfsClusterFixer != null) {
839        this.dfsClusterFixer.shutdown();
840        dfsClusterFixer = null;
841      }
842      dataTestDirOnTestFS = null;
843      CommonFSUtils.setFsDefault(this.conf, new Path("file:///"));
844    }
845  }
846
847  /**
848   * Start up a minicluster of hbase, dfs, and zookeeper where WAL's walDir is created separately.
849   * All other options will use default values, defined in {@link StartMiniClusterOption.Builder}.
850   * @param createWALDir Whether to create a new WAL directory.
851   * @return The mini HBase cluster created.
852   * @see #shutdownMiniCluster()
853   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
854   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
855   * @see #startMiniCluster(StartMiniClusterOption)
856   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
857   */
858  @Deprecated
859  public MiniHBaseCluster startMiniCluster(boolean createWALDir) throws Exception {
860    StartMiniClusterOption option =
861      StartMiniClusterOption.builder().createWALDir(createWALDir).build();
862    return startMiniCluster(option);
863  }
864
865  /**
866   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
867   * defined in {@link StartMiniClusterOption.Builder}.
868   * @param numSlaves     Slave node number, for both HBase region server and HDFS data node.
869   * @param createRootDir Whether to create a new root or data directory path.
870   * @return The mini HBase cluster created.
871   * @see #shutdownMiniCluster()
872   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
873   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
874   * @see #startMiniCluster(StartMiniClusterOption)
875   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
876   */
877  @Deprecated
878  public MiniHBaseCluster startMiniCluster(int numSlaves, boolean createRootDir) throws Exception {
879    StartMiniClusterOption option = StartMiniClusterOption.builder().numRegionServers(numSlaves)
880      .numDataNodes(numSlaves).createRootDir(createRootDir).build();
881    return startMiniCluster(option);
882  }
883
884  /**
885   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
886   * defined in {@link StartMiniClusterOption.Builder}.
887   * @param numSlaves     Slave node number, for both HBase region server and HDFS data node.
888   * @param createRootDir Whether to create a new root or data directory path.
889   * @param createWALDir  Whether to create a new WAL directory.
890   * @return The mini HBase cluster created.
891   * @see #shutdownMiniCluster()
892   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
893   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
894   * @see #startMiniCluster(StartMiniClusterOption)
895   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
896   */
897  @Deprecated
898  public MiniHBaseCluster startMiniCluster(int numSlaves, boolean createRootDir,
899    boolean createWALDir) throws Exception {
900    StartMiniClusterOption option = StartMiniClusterOption.builder().numRegionServers(numSlaves)
901      .numDataNodes(numSlaves).createRootDir(createRootDir).createWALDir(createWALDir).build();
902    return startMiniCluster(option);
903  }
904
905  /**
906   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
907   * defined in {@link StartMiniClusterOption.Builder}.
908   * @param numMasters    Master node number.
909   * @param numSlaves     Slave node number, for both HBase region server and HDFS data node.
910   * @param createRootDir Whether to create a new root or data directory path.
911   * @return The mini HBase cluster created.
912   * @see #shutdownMiniCluster()
913   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
914   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
915   * @see #startMiniCluster(StartMiniClusterOption)
916   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
917   */
918  @Deprecated
919  public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, boolean createRootDir)
920    throws Exception {
921    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
922      .numRegionServers(numSlaves).createRootDir(createRootDir).numDataNodes(numSlaves).build();
923    return startMiniCluster(option);
924  }
925
926  /**
927   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
928   * defined in {@link StartMiniClusterOption.Builder}.
929   * @param numMasters Master node number.
930   * @param numSlaves  Slave node number, for both HBase region server and HDFS data node.
931   * @return The mini HBase cluster created.
932   * @see #shutdownMiniCluster()
933   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
934   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
935   * @see #startMiniCluster(StartMiniClusterOption)
936   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
937   */
938  @Deprecated
939  public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves) throws Exception {
940    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
941      .numRegionServers(numSlaves).numDataNodes(numSlaves).build();
942    return startMiniCluster(option);
943  }
944
945  /**
946   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
947   * defined in {@link StartMiniClusterOption.Builder}.
948   * @param numMasters    Master node number.
949   * @param numSlaves     Slave node number, for both HBase region server and HDFS data node.
950   * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will overwrite
951   *                      HDFS data node number.
952   * @param createRootDir Whether to create a new root or data directory path.
953   * @return The mini HBase cluster created.
954   * @see #shutdownMiniCluster()
955   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
956   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
957   * @see #startMiniCluster(StartMiniClusterOption)
958   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
959   */
960  @Deprecated
961  public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, String[] dataNodeHosts,
962    boolean createRootDir) throws Exception {
963    StartMiniClusterOption option =
964      StartMiniClusterOption.builder().numMasters(numMasters).numRegionServers(numSlaves)
965        .createRootDir(createRootDir).numDataNodes(numSlaves).dataNodeHosts(dataNodeHosts).build();
966    return startMiniCluster(option);
967  }
968
969  /**
970   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
971   * defined in {@link StartMiniClusterOption.Builder}.
972   * @param numMasters    Master node number.
973   * @param numSlaves     Slave node number, for both HBase region server and HDFS data node.
974   * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will overwrite
975   *                      HDFS data node number.
976   * @return The mini HBase cluster created.
977   * @see #shutdownMiniCluster()
978   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
979   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
980   * @see #startMiniCluster(StartMiniClusterOption)
981   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
982   */
983  @Deprecated
984  public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, String[] dataNodeHosts)
985    throws Exception {
986    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
987      .numRegionServers(numSlaves).numDataNodes(numSlaves).dataNodeHosts(dataNodeHosts).build();
988    return startMiniCluster(option);
989  }
990
991  /**
992   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
993   * defined in {@link StartMiniClusterOption.Builder}.
994   * @param numMasters       Master node number.
995   * @param numRegionServers Number of region servers.
996   * @param numDataNodes     Number of datanodes.
997   * @return The mini HBase cluster created.
998   * @see #shutdownMiniCluster()
999   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1000   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
1001   * @see #startMiniCluster(StartMiniClusterOption)
1002   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1003   */
1004  @Deprecated
1005  public MiniHBaseCluster startMiniCluster(int numMasters, int numRegionServers, int numDataNodes)
1006    throws Exception {
1007    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1008      .numRegionServers(numRegionServers).numDataNodes(numDataNodes).build();
1009    return startMiniCluster(option);
1010  }
1011
1012  /**
1013   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
1014   * defined in {@link StartMiniClusterOption.Builder}.
1015   * @param numMasters    Master node number.
1016   * @param numSlaves     Slave node number, for both HBase region server and HDFS data node.
1017   * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will overwrite
1018   *                      HDFS data node number.
1019   * @param masterClass   The class to use as HMaster, or null for default.
1020   * @param rsClass       The class to use as HRegionServer, or null for default.
1021   * @return The mini HBase cluster created.
1022   * @see #shutdownMiniCluster()
1023   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1024   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
1025   * @see #startMiniCluster(StartMiniClusterOption)
1026   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1027   */
1028  @Deprecated
1029  public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, String[] dataNodeHosts,
1030    Class<? extends HMaster> masterClass,
1031    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass) throws Exception {
1032    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1033      .masterClass(masterClass).numRegionServers(numSlaves).rsClass(rsClass).numDataNodes(numSlaves)
1034      .dataNodeHosts(dataNodeHosts).build();
1035    return startMiniCluster(option);
1036  }
1037
1038  /**
1039   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
1040   * defined in {@link StartMiniClusterOption.Builder}.
1041   * @param numMasters       Master node number.
1042   * @param numRegionServers Number of region servers.
1043   * @param numDataNodes     Number of datanodes.
1044   * @param dataNodeHosts    The hostnames of DataNodes to run on. If not null, its size will
1045   *                         overwrite HDFS data node number.
1046   * @param masterClass      The class to use as HMaster, or null for default.
1047   * @param rsClass          The class to use as HRegionServer, or null for default.
1048   * @return The mini HBase cluster created.
1049   * @see #shutdownMiniCluster()
1050   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1051   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
1052   * @see #startMiniCluster(StartMiniClusterOption)
1053   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1054   */
1055  @Deprecated
1056  public MiniHBaseCluster startMiniCluster(int numMasters, int numRegionServers, int numDataNodes,
1057    String[] dataNodeHosts, Class<? extends HMaster> masterClass,
1058    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass) throws Exception {
1059    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1060      .masterClass(masterClass).numRegionServers(numRegionServers).rsClass(rsClass)
1061      .numDataNodes(numDataNodes).dataNodeHosts(dataNodeHosts).build();
1062    return startMiniCluster(option);
1063  }
1064
1065  /**
1066   * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values,
1067   * defined in {@link StartMiniClusterOption.Builder}.
1068   * @param numMasters       Master node number.
1069   * @param numRegionServers Number of region servers.
1070   * @param numDataNodes     Number of datanodes.
1071   * @param dataNodeHosts    The hostnames of DataNodes to run on. If not null, its size will
1072   *                         overwrite HDFS data node number.
1073   * @param masterClass      The class to use as HMaster, or null for default.
1074   * @param rsClass          The class to use as HRegionServer, or null for default.
1075   * @param createRootDir    Whether to create a new root or data directory path.
1076   * @param createWALDir     Whether to create a new WAL directory.
1077   * @return The mini HBase cluster created.
1078   * @see #shutdownMiniCluster()
1079   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1080   *             {@link #startMiniCluster(StartMiniClusterOption)} instead.
1081   * @see #startMiniCluster(StartMiniClusterOption)
1082   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1083   */
1084  @Deprecated
1085  public MiniHBaseCluster startMiniCluster(int numMasters, int numRegionServers, int numDataNodes,
1086    String[] dataNodeHosts, Class<? extends HMaster> masterClass,
1087    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass, boolean createRootDir,
1088    boolean createWALDir) throws Exception {
1089    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1090      .masterClass(masterClass).numRegionServers(numRegionServers).rsClass(rsClass)
1091      .numDataNodes(numDataNodes).dataNodeHosts(dataNodeHosts).createRootDir(createRootDir)
1092      .createWALDir(createWALDir).build();
1093    return startMiniCluster(option);
1094  }
1095
1096  /**
1097   * Start up a minicluster of hbase, dfs and zookeeper clusters with given slave node number. All
1098   * other options will use default values, defined in {@link StartMiniClusterOption.Builder}.
1099   * @param numSlaves slave node number, for both HBase region server and HDFS data node.
1100   * @see #startMiniCluster(StartMiniClusterOption option)
1101   * @see #shutdownMiniDFSCluster()
1102   */
1103  public MiniHBaseCluster startMiniCluster(int numSlaves) throws Exception {
1104    StartMiniClusterOption option =
1105      StartMiniClusterOption.builder().numRegionServers(numSlaves).numDataNodes(numSlaves).build();
1106    return startMiniCluster(option);
1107  }
1108
1109  /**
1110   * Start up a minicluster of hbase, dfs and zookeeper all using default options. Option default
1111   * value can be found in {@link StartMiniClusterOption.Builder}.
1112   * @see #startMiniCluster(StartMiniClusterOption option)
1113   * @see #shutdownMiniDFSCluster()
1114   */
1115  public MiniHBaseCluster startMiniCluster() throws Exception {
1116    return startMiniCluster(StartMiniClusterOption.builder().build());
1117  }
1118
1119  /**
1120   * Start up a mini cluster of hbase, optionally dfs and zookeeper if needed. It modifies
1121   * Configuration. It homes the cluster data directory under a random subdirectory in a directory
1122   * under System property test.build.data, to be cleaned up on exit.
1123   * @see #shutdownMiniDFSCluster()
1124   */
1125  public MiniHBaseCluster startMiniCluster(StartMiniClusterOption option) throws Exception {
1126    LOG.info("Starting up minicluster with option: {}", option);
1127
1128    // If we already put up a cluster, fail.
1129    if (miniClusterRunning) {
1130      throw new IllegalStateException("A mini-cluster is already running");
1131    }
1132    miniClusterRunning = true;
1133
1134    setupClusterTestDir();
1135    System.setProperty(TEST_DIRECTORY_KEY, this.clusterTestDir.getPath());
1136
1137    // Bring up mini dfs cluster. This spews a bunch of warnings about missing
1138    // scheme. Complaints are 'Scheme is undefined for build/test/data/dfs/name1'.
1139    if (dfsCluster == null) {
1140      LOG.info("STARTING DFS");
1141      dfsCluster = startMiniDFSCluster(option.getNumDataNodes(), option.getDataNodeHosts());
1142    } else {
1143      LOG.info("NOT STARTING DFS");
1144    }
1145
1146    // Start up a zk cluster.
1147    if (getZkCluster() == null) {
1148      startMiniZKCluster(option.getNumZkServers());
1149    }
1150
1151    // Start the MiniHBaseCluster
1152    return startMiniHBaseCluster(option);
1153  }
1154
1155  /**
1156   * Starts up mini hbase cluster. Usually you won't want this. You'll usually want
1157   * {@link #startMiniCluster()}. This is useful when doing stepped startup of clusters.
1158   * @return Reference to the hbase mini hbase cluster.
1159   * @see #startMiniCluster(StartMiniClusterOption)
1160   * @see #shutdownMiniHBaseCluster()
1161   */
1162  public MiniHBaseCluster startMiniHBaseCluster(StartMiniClusterOption option)
1163    throws IOException, InterruptedException {
1164    // Now do the mini hbase cluster. Set the hbase.rootdir in config.
1165    createRootDir(option.isCreateRootDir());
1166    if (option.isCreateWALDir()) {
1167      createWALRootDir();
1168    }
1169    // Set the hbase.fs.tmp.dir config to make sure that we have some default value. This is
1170    // for tests that do not read hbase-defaults.xml
1171    setHBaseFsTmpDir();
1172
1173    // These settings will make the server waits until this exact number of
1174    // regions servers are connected.
1175    if (conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1) == -1) {
1176      conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, option.getNumRegionServers());
1177    }
1178    if (conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, -1) == -1) {
1179      conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, option.getNumRegionServers());
1180    }
1181
1182    Configuration c = new Configuration(this.conf);
1183    this.hbaseCluster = new MiniHBaseCluster(c, option.getNumMasters(),
1184      option.getNumAlwaysStandByMasters(), option.getNumRegionServers(), option.getRsPorts(),
1185      option.getMasterClass(), option.getRsClass());
1186    // Populate the master address configuration from mini cluster configuration.
1187    conf.set(HConstants.MASTER_ADDRS_KEY, MasterRegistry.getMasterAddr(c));
1188    // Don't leave here till we've done a successful scan of the hbase:meta
1189    try (Table t = getConnection().getTable(TableName.META_TABLE_NAME);
1190      ResultScanner s = t.getScanner(new Scan())) {
1191      for (;;) {
1192        if (s.next() == null) {
1193          break;
1194        }
1195      }
1196    }
1197
1198    getAdmin(); // create immediately the hbaseAdmin
1199    LOG.info("Minicluster is up; activeMaster={}", getHBaseCluster().getMaster());
1200
1201    return (MiniHBaseCluster) hbaseCluster;
1202  }
1203
1204  /**
1205   * Starts up mini hbase cluster using default options. Default options can be found in
1206   * {@link StartMiniClusterOption.Builder}.
1207   * @see #startMiniHBaseCluster(StartMiniClusterOption)
1208   * @see #shutdownMiniHBaseCluster()
1209   */
1210  public MiniHBaseCluster startMiniHBaseCluster() throws IOException, InterruptedException {
1211    return startMiniHBaseCluster(StartMiniClusterOption.builder().build());
1212  }
1213
1214  /**
1215   * Starts up mini hbase cluster. Usually you won't want this. You'll usually want
1216   * {@link #startMiniCluster()}. All other options will use default values, defined in
1217   * {@link StartMiniClusterOption.Builder}.
1218   * @param numMasters       Master node number.
1219   * @param numRegionServers Number of region servers.
1220   * @return The mini HBase cluster created.
1221   * @see #shutdownMiniHBaseCluster()
1222   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1223   *             {@link #startMiniHBaseCluster(StartMiniClusterOption)} instead.
1224   * @see #startMiniHBaseCluster(StartMiniClusterOption)
1225   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1226   */
1227  @Deprecated
1228  public MiniHBaseCluster startMiniHBaseCluster(int numMasters, int numRegionServers)
1229    throws IOException, InterruptedException {
1230    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1231      .numRegionServers(numRegionServers).build();
1232    return startMiniHBaseCluster(option);
1233  }
1234
1235  /**
1236   * Starts up mini hbase cluster. Usually you won't want this. You'll usually want
1237   * {@link #startMiniCluster()}. All other options will use default values, defined in
1238   * {@link StartMiniClusterOption.Builder}.
1239   * @param numMasters       Master node number.
1240   * @param numRegionServers Number of region servers.
1241   * @param rsPorts          Ports that RegionServer should use.
1242   * @return The mini HBase cluster created.
1243   * @see #shutdownMiniHBaseCluster()
1244   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1245   *             {@link #startMiniHBaseCluster(StartMiniClusterOption)} instead.
1246   * @see #startMiniHBaseCluster(StartMiniClusterOption)
1247   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1248   */
1249  @Deprecated
1250  public MiniHBaseCluster startMiniHBaseCluster(int numMasters, int numRegionServers,
1251    List<Integer> rsPorts) throws IOException, InterruptedException {
1252    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1253      .numRegionServers(numRegionServers).rsPorts(rsPorts).build();
1254    return startMiniHBaseCluster(option);
1255  }
1256
1257  /**
1258   * Starts up mini hbase cluster. Usually you won't want this. You'll usually want
1259   * {@link #startMiniCluster()}. All other options will use default values, defined in
1260   * {@link StartMiniClusterOption.Builder}.
1261   * @param numMasters       Master node number.
1262   * @param numRegionServers Number of region servers.
1263   * @param rsPorts          Ports that RegionServer should use.
1264   * @param masterClass      The class to use as HMaster, or null for default.
1265   * @param rsClass          The class to use as HRegionServer, or null for default.
1266   * @param createRootDir    Whether to create a new root or data directory path.
1267   * @param createWALDir     Whether to create a new WAL directory.
1268   * @return The mini HBase cluster created.
1269   * @see #shutdownMiniHBaseCluster()
1270   * @deprecated since 2.2.0 and will be removed in 4.0.0. Use
1271   *             {@link #startMiniHBaseCluster(StartMiniClusterOption)} instead.
1272   * @see #startMiniHBaseCluster(StartMiniClusterOption)
1273   * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a>
1274   */
1275  @Deprecated
1276  public MiniHBaseCluster startMiniHBaseCluster(int numMasters, int numRegionServers,
1277    List<Integer> rsPorts, Class<? extends HMaster> masterClass,
1278    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass, boolean createRootDir,
1279    boolean createWALDir) throws IOException, InterruptedException {
1280    StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters)
1281      .masterClass(masterClass).numRegionServers(numRegionServers).rsClass(rsClass).rsPorts(rsPorts)
1282      .createRootDir(createRootDir).createWALDir(createWALDir).build();
1283    return startMiniHBaseCluster(option);
1284  }
1285
1286  /**
1287   * Starts the hbase cluster up again after shutting it down previously in a test. Use this if you
1288   * want to keep dfs/zk up and just stop/start hbase.
1289   * @param servers number of region servers
1290   */
1291  public void restartHBaseCluster(int servers) throws IOException, InterruptedException {
1292    this.restartHBaseCluster(servers, null);
1293  }
1294
1295  public void restartHBaseCluster(int servers, List<Integer> ports)
1296    throws IOException, InterruptedException {
1297    StartMiniClusterOption option =
1298      StartMiniClusterOption.builder().numRegionServers(servers).rsPorts(ports).build();
1299    restartHBaseCluster(option);
1300    invalidateConnection();
1301  }
1302
1303  public void restartHBaseCluster(StartMiniClusterOption option)
1304    throws IOException, InterruptedException {
1305    closeConnection();
1306    this.hbaseCluster = new MiniHBaseCluster(this.conf, option.getNumMasters(),
1307      option.getNumAlwaysStandByMasters(), option.getNumRegionServers(), option.getRsPorts(),
1308      option.getMasterClass(), option.getRsClass());
1309    // Don't leave here till we've done a successful scan of the hbase:meta
1310    Connection conn = ConnectionFactory.createConnection(this.conf);
1311    Table t = conn.getTable(TableName.META_TABLE_NAME);
1312    ResultScanner s = t.getScanner(new Scan());
1313    while (s.next() != null) {
1314      // do nothing
1315    }
1316    LOG.info("HBase has been restarted");
1317    s.close();
1318    t.close();
1319    conn.close();
1320  }
1321
1322  /**
1323   * @return Current mini hbase cluster. Only has something in it after a call to
1324   *         {@link #startMiniCluster()}.
1325   * @see #startMiniCluster()
1326   */
1327  public MiniHBaseCluster getMiniHBaseCluster() {
1328    if (this.hbaseCluster == null || this.hbaseCluster instanceof MiniHBaseCluster) {
1329      return (MiniHBaseCluster) this.hbaseCluster;
1330    }
1331    throw new RuntimeException(
1332      hbaseCluster + " not an instance of " + MiniHBaseCluster.class.getName());
1333  }
1334
1335  /**
1336   * Stops mini hbase, zk, and hdfs clusters.
1337   * @see #startMiniCluster(int)
1338   */
1339  public void shutdownMiniCluster() throws IOException {
1340    LOG.info("Shutting down minicluster");
1341    shutdownMiniHBaseCluster();
1342    shutdownMiniDFSCluster();
1343    shutdownMiniZKCluster();
1344
1345    cleanupTestDir();
1346    miniClusterRunning = false;
1347    LOG.info("Minicluster is down");
1348  }
1349
1350  /**
1351   * Shutdown HBase mini cluster.Does not shutdown zk or dfs if running.
1352   * @throws java.io.IOException in case command is unsuccessful
1353   */
1354  public void shutdownMiniHBaseCluster() throws IOException {
1355    cleanup();
1356    if (this.hbaseCluster != null) {
1357      this.hbaseCluster.shutdown();
1358      // Wait till hbase is down before going on to shutdown zk.
1359      this.hbaseCluster.waitUntilShutDown();
1360      this.hbaseCluster = null;
1361    }
1362    if (zooKeeperWatcher != null) {
1363      zooKeeperWatcher.close();
1364      zooKeeperWatcher = null;
1365    }
1366  }
1367
1368  /**
1369   * Abruptly Shutdown HBase mini cluster. Does not shutdown zk or dfs if running.
1370   * @throws java.io.IOException throws in case command is unsuccessful
1371   */
1372  public void killMiniHBaseCluster() throws IOException {
1373    cleanup();
1374    if (this.hbaseCluster != null) {
1375      getMiniHBaseCluster().killAll();
1376      this.hbaseCluster = null;
1377    }
1378    if (zooKeeperWatcher != null) {
1379      zooKeeperWatcher.close();
1380      zooKeeperWatcher = null;
1381    }
1382  }
1383
1384  // close hbase admin, close current connection and reset MIN MAX configs for RS.
1385  private void cleanup() throws IOException {
1386    closeConnection();
1387    // unset the configuration for MIN and MAX RS to start
1388    conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1);
1389    conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, -1);
1390  }
1391
1392  /**
1393   * Returns the path to the default root dir the minicluster uses. If <code>create</code> is true,
1394   * a new root directory path is fetched irrespective of whether it has been fetched before or not.
1395   * If false, previous path is used. Note: this does not cause the root dir to be created.
1396   * @return Fully qualified path for the default hbase root dir
1397   */
1398  public Path getDefaultRootDirPath(boolean create) throws IOException {
1399    if (!create) {
1400      return getDataTestDirOnTestFS();
1401    } else {
1402      return getNewDataTestDirOnTestFS();
1403    }
1404  }
1405
1406  /**
1407   * Same as {{@link HBaseTestingUtility#getDefaultRootDirPath(boolean create)} except that
1408   * <code>create</code> flag is false. Note: this does not cause the root dir to be created.
1409   * @return Fully qualified path for the default hbase root dir
1410   */
1411  public Path getDefaultRootDirPath() throws IOException {
1412    return getDefaultRootDirPath(false);
1413  }
1414
1415  /**
1416   * Creates an hbase rootdir in user home directory. Also creates hbase version file. Normally you
1417   * won't make use of this method. Root hbasedir is created for you as part of mini cluster
1418   * startup. You'd only use this method if you were doing manual operation.
1419   * @param create This flag decides whether to get a new root or data directory path or not, if it
1420   *               has been fetched already. Note : Directory will be made irrespective of whether
1421   *               path has been fetched or not. If directory already exists, it will be overwritten
1422   * @return Fully qualified path to hbase root dir
1423   */
1424  public Path createRootDir(boolean create) throws IOException {
1425    FileSystem fs = FileSystem.get(this.conf);
1426    Path hbaseRootdir = getDefaultRootDirPath(create);
1427    CommonFSUtils.setRootDir(this.conf, hbaseRootdir);
1428    fs.mkdirs(hbaseRootdir);
1429    FSUtils.setVersion(fs, hbaseRootdir);
1430    return hbaseRootdir;
1431  }
1432
1433  /**
1434   * Same as {@link HBaseTestingUtility#createRootDir(boolean create)} except that
1435   * <code>create</code> flag is false.
1436   * @return Fully qualified path to hbase root dir
1437   */
1438  public Path createRootDir() throws IOException {
1439    return createRootDir(false);
1440  }
1441
1442  /**
1443   * Creates a hbase walDir in the user's home directory. Normally you won't make use of this
1444   * method. Root hbaseWALDir is created for you as part of mini cluster startup. You'd only use
1445   * this method if you were doing manual operation.
1446   * @return Fully qualified path to hbase root dir
1447   */
1448  public Path createWALRootDir() throws IOException {
1449    FileSystem fs = FileSystem.get(this.conf);
1450    Path walDir = getNewDataTestDirOnTestFS();
1451    CommonFSUtils.setWALRootDir(this.conf, walDir);
1452    fs.mkdirs(walDir);
1453    return walDir;
1454  }
1455
1456  private void setHBaseFsTmpDir() throws IOException {
1457    String hbaseFsTmpDirInString = this.conf.get("hbase.fs.tmp.dir");
1458    if (hbaseFsTmpDirInString == null) {
1459      this.conf.set("hbase.fs.tmp.dir", getDataTestDirOnTestFS("hbase-staging").toString());
1460      LOG.info("Setting hbase.fs.tmp.dir to " + this.conf.get("hbase.fs.tmp.dir"));
1461    } else {
1462      LOG.info("The hbase.fs.tmp.dir is set to " + hbaseFsTmpDirInString);
1463    }
1464  }
1465
1466  /**
1467   * Flushes all caches in the mini hbase cluster
1468   */
1469  public void flush() throws IOException {
1470    getMiniHBaseCluster().flushcache();
1471  }
1472
1473  /**
1474   * Flushes all caches in the mini hbase cluster
1475   */
1476  public void flush(TableName tableName) throws IOException {
1477    getMiniHBaseCluster().flushcache(tableName);
1478  }
1479
1480  /**
1481   * Compact all regions in the mini hbase cluster
1482   */
1483  public void compact(boolean major) throws IOException {
1484    getMiniHBaseCluster().compact(major);
1485  }
1486
1487  /**
1488   * Compact all of a table's reagion in the mini hbase cluster
1489   */
1490  public void compact(TableName tableName, boolean major) throws IOException {
1491    getMiniHBaseCluster().compact(tableName, major);
1492  }
1493
1494  /**
1495   * Create a table.
1496   * @return A Table instance for the created table.
1497   */
1498  public Table createTable(TableName tableName, String family) throws IOException {
1499    return createTable(tableName, new String[] { family });
1500  }
1501
1502  /**
1503   * Create a table.
1504   * @return A Table instance for the created table.
1505   */
1506  public Table createTable(TableName tableName, String[] families) throws IOException {
1507    List<byte[]> fams = new ArrayList<>(families.length);
1508    for (String family : families) {
1509      fams.add(Bytes.toBytes(family));
1510    }
1511    return createTable(tableName, fams.toArray(new byte[0][]));
1512  }
1513
1514  /**
1515   * Create a table.
1516   * @return A Table instance for the created table.
1517   */
1518  public Table createTable(TableName tableName, byte[] family) throws IOException {
1519    return createTable(tableName, new byte[][] { family });
1520  }
1521
1522  /**
1523   * Create a table with multiple regions.
1524   * @return A Table instance for the created table.
1525   */
1526  public Table createMultiRegionTable(TableName tableName, byte[] family, int numRegions)
1527    throws IOException {
1528    if (numRegions < 3) throw new IOException("Must create at least 3 regions");
1529    byte[] startKey = Bytes.toBytes("aaaaa");
1530    byte[] endKey = Bytes.toBytes("zzzzz");
1531    byte[][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
1532
1533    return createTable(tableName, new byte[][] { family }, splitKeys);
1534  }
1535
1536  /**
1537   * Create a table.
1538   * @return A Table instance for the created table.
1539   */
1540  public Table createTable(TableName tableName, byte[][] families) throws IOException {
1541    return createTable(tableName, families, (byte[][]) null);
1542  }
1543
1544  /**
1545   * Create a table with multiple regions.
1546   * @return A Table instance for the created table.
1547   */
1548  public Table createMultiRegionTable(TableName tableName, byte[][] families) throws IOException {
1549    return createTable(tableName, families, KEYS_FOR_HBA_CREATE_TABLE);
1550  }
1551
1552  /**
1553   * Create a table with multiple regions.
1554   * @param replicaCount replica count.
1555   * @return A Table instance for the created table.
1556   */
1557  public Table createMultiRegionTable(TableName tableName, int replicaCount, byte[][] families)
1558    throws IOException {
1559    return createTable(tableName, families, KEYS_FOR_HBA_CREATE_TABLE, replicaCount);
1560  }
1561
1562  /**
1563   * Create a table.
1564   * @return A Table instance for the created table.
1565   */
1566  public Table createTable(TableName tableName, byte[][] families, byte[][] splitKeys)
1567    throws IOException {
1568    return createTable(tableName, families, splitKeys, 1, new Configuration(getConfiguration()));
1569  }
1570
1571  /**
1572   * Create a table.
1573   * @param tableName    the table name
1574   * @param families     the families
1575   * @param splitKeys    the splitkeys
1576   * @param replicaCount the region replica count
1577   * @return A Table instance for the created table.
1578   * @throws IOException throws IOException
1579   */
1580  public Table createTable(TableName tableName, byte[][] families, byte[][] splitKeys,
1581    int replicaCount) throws IOException {
1582    return createTable(tableName, families, splitKeys, replicaCount,
1583      new Configuration(getConfiguration()));
1584  }
1585
1586  public Table createTable(TableName tableName, byte[][] families, int numVersions, byte[] startKey,
1587    byte[] endKey, int numRegions) throws IOException {
1588    HTableDescriptor desc = createTableDescriptor(tableName, families, numVersions);
1589
1590    getAdmin().createTable(desc, startKey, endKey, numRegions);
1591    // HBaseAdmin only waits for regions to appear in hbase:meta we
1592    // should wait until they are assigned
1593    waitUntilAllRegionsAssigned(tableName);
1594    return getConnection().getTable(tableName);
1595  }
1596
1597  /**
1598   * Create a table.
1599   * @param c Configuration to use
1600   * @return A Table instance for the created table.
1601   */
1602  public Table createTable(TableDescriptor htd, byte[][] families, Configuration c)
1603    throws IOException {
1604    return createTable(htd, families, null, c);
1605  }
1606
1607  /**
1608   * Create a table.
1609   * @param htd       table descriptor
1610   * @param families  array of column families
1611   * @param splitKeys array of split keys
1612   * @param c         Configuration to use
1613   * @return A Table instance for the created table.
1614   * @throws IOException if getAdmin or createTable fails
1615   */
1616  public Table createTable(TableDescriptor htd, byte[][] families, byte[][] splitKeys,
1617    Configuration c) throws IOException {
1618    // Disable blooms (they are on by default as of 0.95) but we disable them here because
1619    // tests have hard coded counts of what to expect in block cache, etc., and blooms being
1620    // on is interfering.
1621    return createTable(htd, families, splitKeys, BloomType.NONE, HConstants.DEFAULT_BLOCKSIZE, c);
1622  }
1623
1624  /**
1625   * Create a table.
1626   * @param htd       table descriptor
1627   * @param families  array of column families
1628   * @param splitKeys array of split keys
1629   * @param type      Bloom type
1630   * @param blockSize block size
1631   * @param c         Configuration to use
1632   * @return A Table instance for the created table.
1633   * @throws IOException if getAdmin or createTable fails
1634   */
1635
1636  public Table createTable(TableDescriptor htd, byte[][] families, byte[][] splitKeys,
1637    BloomType type, int blockSize, Configuration c) throws IOException {
1638    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(htd);
1639    for (byte[] family : families) {
1640      ColumnFamilyDescriptorBuilder cfdb = ColumnFamilyDescriptorBuilder.newBuilder(family)
1641        .setBloomFilterType(type).setBlocksize(blockSize);
1642      if (isNewVersionBehaviorEnabled()) {
1643        cfdb.setNewVersionBehavior(true);
1644      }
1645      builder.setColumnFamily(cfdb.build());
1646    }
1647    TableDescriptor td = builder.build();
1648    getAdmin().createTable(td, splitKeys);
1649    // HBaseAdmin only waits for regions to appear in hbase:meta
1650    // we should wait until they are assigned
1651    waitUntilAllRegionsAssigned(td.getTableName());
1652    return getConnection().getTable(td.getTableName());
1653  }
1654
1655  /**
1656   * Create a table.
1657   * @param htd       table descriptor
1658   * @param splitRows array of split keys
1659   * @return A Table instance for the created table.
1660   */
1661  public Table createTable(TableDescriptor htd, byte[][] splitRows) throws IOException {
1662    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(htd);
1663    if (isNewVersionBehaviorEnabled()) {
1664      for (ColumnFamilyDescriptor family : htd.getColumnFamilies()) {
1665        builder.setColumnFamily(
1666          ColumnFamilyDescriptorBuilder.newBuilder(family).setNewVersionBehavior(true).build());
1667      }
1668    }
1669    getAdmin().createTable(builder.build(), splitRows);
1670    // HBaseAdmin only waits for regions to appear in hbase:meta
1671    // we should wait until they are assigned
1672    waitUntilAllRegionsAssigned(htd.getTableName());
1673    return getConnection().getTable(htd.getTableName());
1674  }
1675
1676  /**
1677   * Create a table.
1678   * @param tableName    the table name
1679   * @param families     the families
1680   * @param splitKeys    the split keys
1681   * @param replicaCount the replica count
1682   * @param c            Configuration to use
1683   * @return A Table instance for the created table.
1684   */
1685  public Table createTable(TableName tableName, byte[][] families, byte[][] splitKeys,
1686    int replicaCount, final Configuration c) throws IOException {
1687    HTableDescriptor htd = new HTableDescriptor(tableName);
1688    htd.setRegionReplication(replicaCount);
1689    return createTable(htd, families, splitKeys, c);
1690  }
1691
1692  /**
1693   * Create a table.
1694   * @return A Table instance for the created table.
1695   */
1696  public Table createTable(TableName tableName, byte[] family, int numVersions) throws IOException {
1697    return createTable(tableName, new byte[][] { family }, numVersions);
1698  }
1699
1700  /**
1701   * Create a table.
1702   * @return A Table instance for the created table.
1703   */
1704  public Table createTable(TableName tableName, byte[][] families, int numVersions)
1705    throws IOException {
1706    return createTable(tableName, families, numVersions, (byte[][]) null);
1707  }
1708
1709  /**
1710   * Create a table.
1711   * @return A Table instance for the created table.
1712   */
1713  public Table createTable(TableName tableName, byte[][] families, int numVersions,
1714    byte[][] splitKeys) throws IOException {
1715    HTableDescriptor desc = new HTableDescriptor(tableName);
1716    for (byte[] family : families) {
1717      HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(numVersions);
1718      if (isNewVersionBehaviorEnabled()) {
1719        hcd.setNewVersionBehavior(true);
1720      }
1721      desc.addFamily(hcd);
1722    }
1723    getAdmin().createTable(desc, splitKeys);
1724    // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are
1725    // assigned
1726    waitUntilAllRegionsAssigned(tableName);
1727    return getConnection().getTable(tableName);
1728  }
1729
1730  /**
1731   * Create a table with multiple regions.
1732   * @return A Table instance for the created table.
1733   */
1734  public Table createMultiRegionTable(TableName tableName, byte[][] families, int numVersions)
1735    throws IOException {
1736    return createTable(tableName, families, numVersions, KEYS_FOR_HBA_CREATE_TABLE);
1737  }
1738
1739  /**
1740   * Create a table.
1741   * @return A Table instance for the created table.
1742   */
1743  public Table createTable(TableName tableName, byte[][] families, int numVersions, int blockSize)
1744    throws IOException {
1745    HTableDescriptor desc = new HTableDescriptor(tableName);
1746    for (byte[] family : families) {
1747      HColumnDescriptor hcd =
1748        new HColumnDescriptor(family).setMaxVersions(numVersions).setBlocksize(blockSize);
1749      if (isNewVersionBehaviorEnabled()) {
1750        hcd.setNewVersionBehavior(true);
1751      }
1752      desc.addFamily(hcd);
1753    }
1754    getAdmin().createTable(desc);
1755    // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are
1756    // assigned
1757    waitUntilAllRegionsAssigned(tableName);
1758    return getConnection().getTable(tableName);
1759  }
1760
1761  public Table createTable(TableName tableName, byte[][] families, int numVersions, int blockSize,
1762    String cpName) throws IOException {
1763    HTableDescriptor desc = new HTableDescriptor(tableName);
1764    for (byte[] family : families) {
1765      HColumnDescriptor hcd =
1766        new HColumnDescriptor(family).setMaxVersions(numVersions).setBlocksize(blockSize);
1767      if (isNewVersionBehaviorEnabled()) {
1768        hcd.setNewVersionBehavior(true);
1769      }
1770      desc.addFamily(hcd);
1771    }
1772    if (cpName != null) {
1773      desc.addCoprocessor(cpName);
1774    }
1775    getAdmin().createTable(desc);
1776    // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are
1777    // assigned
1778    waitUntilAllRegionsAssigned(tableName);
1779    return getConnection().getTable(tableName);
1780  }
1781
1782  /**
1783   * Create a table.
1784   * @return A Table instance for the created table.
1785   */
1786  public Table createTable(TableName tableName, byte[][] families, int[] numVersions)
1787    throws IOException {
1788    HTableDescriptor desc = new HTableDescriptor(tableName);
1789    int i = 0;
1790    for (byte[] family : families) {
1791      HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(numVersions[i]);
1792      if (isNewVersionBehaviorEnabled()) {
1793        hcd.setNewVersionBehavior(true);
1794      }
1795      desc.addFamily(hcd);
1796      i++;
1797    }
1798    getAdmin().createTable(desc);
1799    // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are
1800    // assigned
1801    waitUntilAllRegionsAssigned(tableName);
1802    return getConnection().getTable(tableName);
1803  }
1804
1805  /**
1806   * Create a table.
1807   * @return A Table instance for the created table.
1808   */
1809  public Table createTable(TableName tableName, byte[] family, byte[][] splitRows)
1810    throws IOException {
1811    HTableDescriptor desc = new HTableDescriptor(tableName);
1812    HColumnDescriptor hcd = new HColumnDescriptor(family);
1813    if (isNewVersionBehaviorEnabled()) {
1814      hcd.setNewVersionBehavior(true);
1815    }
1816    desc.addFamily(hcd);
1817    getAdmin().createTable(desc, splitRows);
1818    // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are
1819    // assigned
1820    waitUntilAllRegionsAssigned(tableName);
1821    return getConnection().getTable(tableName);
1822  }
1823
1824  /**
1825   * Create a table with multiple regions.
1826   * @return A Table instance for the created table.
1827   */
1828  public Table createMultiRegionTable(TableName tableName, byte[] family) throws IOException {
1829    return createTable(tableName, family, KEYS_FOR_HBA_CREATE_TABLE);
1830  }
1831
1832  /**
1833   * Modify a table, synchronous. Waiting logic similar to that of {@code admin.rb#alter_status}.
1834   */
1835  @SuppressWarnings("serial")
1836  public static void modifyTableSync(Admin admin, TableDescriptor desc)
1837    throws IOException, InterruptedException {
1838    admin.modifyTable(desc);
1839    Pair<Integer, Integer> status = new Pair<Integer, Integer>() {
1840      {
1841        setFirst(0);
1842        setSecond(0);
1843      }
1844    };
1845    int i = 0;
1846    do {
1847      status = admin.getAlterStatus(desc.getTableName());
1848      if (status.getSecond() != 0) {
1849        LOG.debug(
1850          status.getSecond() - status.getFirst() + "/" + status.getSecond() + " regions updated.");
1851        Thread.sleep(1 * 1000L);
1852      } else {
1853        LOG.debug("All regions updated.");
1854        break;
1855      }
1856    } while (status.getFirst() != 0 && i++ < 500);
1857    if (status.getFirst() != 0) {
1858      throw new IOException("Failed to update all regions even after 500 seconds.");
1859    }
1860  }
1861
1862  /**
1863   * Set the number of Region replicas.
1864   */
1865  public static void setReplicas(Admin admin, TableName table, int replicaCount)
1866    throws IOException, InterruptedException {
1867    TableDescriptor desc = TableDescriptorBuilder.newBuilder(admin.getDescriptor(table))
1868      .setRegionReplication(replicaCount).build();
1869    admin.modifyTable(desc);
1870  }
1871
1872  /**
1873   * Set the number of Region replicas.
1874   */
1875  public static void setReplicas(AsyncAdmin admin, TableName table, int replicaCount)
1876    throws ExecutionException, IOException, InterruptedException {
1877    TableDescriptor desc = TableDescriptorBuilder.newBuilder(admin.getDescriptor(table).get())
1878      .setRegionReplication(replicaCount).build();
1879    admin.modifyTable(desc).get();
1880  }
1881
1882  /**
1883   * Drop an existing table
1884   * @param tableName existing table
1885   */
1886  public void deleteTable(TableName tableName) throws IOException {
1887    try {
1888      getAdmin().disableTable(tableName);
1889    } catch (TableNotEnabledException e) {
1890      LOG.debug("Table: " + tableName + " already disabled, so just deleting it.");
1891    }
1892    getAdmin().deleteTable(tableName);
1893  }
1894
1895  /**
1896   * Drop an existing table
1897   * @param tableName existing table
1898   */
1899  public void deleteTableIfAny(TableName tableName) throws IOException {
1900    try {
1901      deleteTable(tableName);
1902    } catch (TableNotFoundException e) {
1903      // ignore
1904    }
1905  }
1906
1907  // ==========================================================================
1908  // Canned table and table descriptor creation
1909  // TODO replace HBaseTestCase
1910
1911  public final static byte[] fam1 = Bytes.toBytes("colfamily11");
1912  public final static byte[] fam2 = Bytes.toBytes("colfamily21");
1913  public final static byte[] fam3 = Bytes.toBytes("colfamily31");
1914  public static final byte[][] COLUMNS = { fam1, fam2, fam3 };
1915  private static final int MAXVERSIONS = 3;
1916
1917  public static final char FIRST_CHAR = 'a';
1918  public static final char LAST_CHAR = 'z';
1919  public static final byte[] START_KEY_BYTES = { FIRST_CHAR, FIRST_CHAR, FIRST_CHAR };
1920  public static final String START_KEY = new String(START_KEY_BYTES, HConstants.UTF8_CHARSET);
1921
1922  public TableDescriptorBuilder.ModifyableTableDescriptor
1923    createModifyableTableDescriptor(final String name) {
1924    return createModifyableTableDescriptor(TableName.valueOf(name),
1925      ColumnFamilyDescriptorBuilder.DEFAULT_MIN_VERSIONS, MAXVERSIONS, HConstants.FOREVER,
1926      ColumnFamilyDescriptorBuilder.DEFAULT_KEEP_DELETED);
1927  }
1928
1929  public TableDescriptorBuilder.ModifyableTableDescriptor createModifyableTableDescriptor(
1930    final TableName name, final int minVersions, final int versions, final int ttl,
1931    KeepDeletedCells keepDeleted) {
1932    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(name);
1933    for (byte[] cfName : new byte[][] { fam1, fam2, fam3 }) {
1934      ColumnFamilyDescriptorBuilder cfBuilder = ColumnFamilyDescriptorBuilder.newBuilder(cfName)
1935        .setMinVersions(minVersions).setMaxVersions(versions).setKeepDeletedCells(keepDeleted)
1936        .setBlockCacheEnabled(false).setTimeToLive(ttl);
1937      if (isNewVersionBehaviorEnabled()) {
1938        cfBuilder.setNewVersionBehavior(true);
1939      }
1940      builder.setColumnFamily(cfBuilder.build());
1941    }
1942    return new TableDescriptorBuilder.ModifyableTableDescriptor(name, builder.build());
1943  }
1944
1945  /**
1946   * @deprecated since 2.0.0 and will be removed in 3.0.0. Use
1947   *             {@link #createTableDescriptor(TableName, int, int, int, KeepDeletedCells)} instead.
1948   * @see #createTableDescriptor(TableName, int, int, int, KeepDeletedCells)
1949   * @see <a href="https://issues.apache.org/jira/browse/HBASE-13893">HBASE-13893</a>
1950   */
1951  @Deprecated
1952  public HTableDescriptor createTableDescriptor(final String name, final int minVersions,
1953    final int versions, final int ttl, KeepDeletedCells keepDeleted) {
1954    return this.createTableDescriptor(TableName.valueOf(name), minVersions, versions, ttl,
1955      keepDeleted);
1956  }
1957
1958  /**
1959   * Create a table of name <code>name</code>.
1960   * @param name Name to give table.
1961   * @return Column descriptor.
1962   * @deprecated since 2.0.0 and will be removed in 3.0.0. Use
1963   *             {@link #createTableDescriptor(TableName, int, int, int, KeepDeletedCells)} instead.
1964   * @see #createTableDescriptor(TableName, int, int, int, KeepDeletedCells)
1965   * @see <a href="https://issues.apache.org/jira/browse/HBASE-13893">HBASE-13893</a>
1966   */
1967  @Deprecated
1968  public HTableDescriptor createTableDescriptor(final String name) {
1969    return createTableDescriptor(TableName.valueOf(name), HColumnDescriptor.DEFAULT_MIN_VERSIONS,
1970      MAXVERSIONS, HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED);
1971  }
1972
1973  public HTableDescriptor createTableDescriptor(final TableName name, final int minVersions,
1974    final int versions, final int ttl, KeepDeletedCells keepDeleted) {
1975    HTableDescriptor htd = new HTableDescriptor(name);
1976    for (byte[] cfName : new byte[][] { fam1, fam2, fam3 }) {
1977      HColumnDescriptor hcd =
1978        new HColumnDescriptor(cfName).setMinVersions(minVersions).setMaxVersions(versions)
1979          .setKeepDeletedCells(keepDeleted).setBlockCacheEnabled(false).setTimeToLive(ttl);
1980      if (isNewVersionBehaviorEnabled()) {
1981        hcd.setNewVersionBehavior(true);
1982      }
1983      htd.addFamily(hcd);
1984    }
1985    return htd;
1986  }
1987
1988  /**
1989   * Create a table of name <code>name</code>.
1990   * @param name Name to give table.
1991   * @return Column descriptor.
1992   */
1993  public HTableDescriptor createTableDescriptor(final TableName name) {
1994    return createTableDescriptor(name, HColumnDescriptor.DEFAULT_MIN_VERSIONS, MAXVERSIONS,
1995      HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED);
1996  }
1997
1998  public HTableDescriptor createTableDescriptor(final TableName tableName, byte[] family) {
1999    return createTableDescriptor(tableName, new byte[][] { family }, 1);
2000  }
2001
2002  public HTableDescriptor createTableDescriptor(final TableName tableName, byte[][] families,
2003    int maxVersions) {
2004    HTableDescriptor desc = new HTableDescriptor(tableName);
2005    for (byte[] family : families) {
2006      HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(maxVersions);
2007      if (isNewVersionBehaviorEnabled()) {
2008        hcd.setNewVersionBehavior(true);
2009      }
2010      desc.addFamily(hcd);
2011    }
2012    return desc;
2013  }
2014
2015  /**
2016   * Create an HRegion that writes to the local tmp dirs
2017   * @param desc     a table descriptor indicating which table the region belongs to
2018   * @param startKey the start boundary of the region
2019   * @param endKey   the end boundary of the region
2020   * @return a region that writes to local dir for testing
2021   */
2022  public HRegion createLocalHRegion(TableDescriptor desc, byte[] startKey, byte[] endKey)
2023    throws IOException {
2024    HRegionInfo hri = new HRegionInfo(desc.getTableName(), startKey, endKey);
2025    return createLocalHRegion(hri, desc);
2026  }
2027
2028  /**
2029   * Create an HRegion that writes to the local tmp dirs. Creates the WAL for you. Be sure to call
2030   * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} when you're finished with it.
2031   */
2032  public HRegion createLocalHRegion(RegionInfo info, TableDescriptor desc) throws IOException {
2033    return createRegionAndWAL(info, getDataTestDir(), getConfiguration(), desc);
2034  }
2035
2036  /**
2037   * Create an HRegion that writes to the local tmp dirs with specified wal
2038   * @param info regioninfo
2039   * @param conf configuration
2040   * @param desc table descriptor
2041   * @param wal  wal for this region.
2042   * @return created hregion
2043   */
2044  public HRegion createLocalHRegion(RegionInfo info, Configuration conf, TableDescriptor desc,
2045    WAL wal) throws IOException {
2046    ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null,
2047      MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT);
2048    return HRegion.createHRegion(info, getDataTestDir(), conf, desc, wal);
2049  }
2050
2051  /**
2052   * Create an HRegion that writes to the local tmp dirs with specified wal
2053   * @param info regioninfo
2054   * @param info configuration
2055   * @param desc table descriptor
2056   * @param wal  wal for this region.
2057   * @return created hregion
2058   */
2059  public HRegion createLocalHRegion(HRegionInfo info, Configuration conf, HTableDescriptor desc,
2060    WAL wal) throws IOException {
2061    ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null,
2062      MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT);
2063    return HRegion.createHRegion(info, getDataTestDir(), conf, desc, wal);
2064  }
2065
2066  /**
2067   * @param tableName     the name of the table
2068   * @param startKey      the start key of the region
2069   * @param stopKey       the stop key of the region
2070   * @param callingMethod the name of the calling method probably a test method
2071   * @param conf          the configuration to use
2072   * @param isReadOnly    {@code true} if the table is read only, {@code false} otherwise
2073   * @param families      the column families to use
2074   * @throws IOException if an IO problem is encountered
2075   * @return A region on which you must call {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)}
2076   *         when done.
2077   * @deprecated since 2.0.0 and will be removed in 3.0.0. Use {@link #createLocalHRegion(TableName,
2078   *             byte[], byte[], boolean, Durability, WAL, byte[]...)} instead.
2079   * @see #createLocalHRegion(TableName, byte[], byte[], boolean, Durability, WAL, byte[]...)
2080   * @see <a href="https://issues.apache.org/jira/browse/HBASE-13893">HBASE-13893</a>
2081   */
2082  @Deprecated
2083  public HRegion createLocalHRegion(byte[] tableName, byte[] startKey, byte[] stopKey,
2084    String callingMethod, Configuration conf, boolean isReadOnly, Durability durability, WAL wal,
2085    byte[]... families) throws IOException {
2086    return createLocalHRegion(TableName.valueOf(tableName), startKey, stopKey, conf, isReadOnly,
2087      durability, wal, families);
2088  }
2089
2090  /**
2091   * Return a region on which you must call {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)}
2092   * when done.
2093   */
2094  public HRegion createLocalHRegion(TableName tableName, byte[] startKey, byte[] stopKey,
2095    Configuration conf, boolean isReadOnly, Durability durability, WAL wal, byte[]... families)
2096    throws IOException {
2097    return createLocalHRegionWithInMemoryFlags(tableName, startKey, stopKey, conf, isReadOnly,
2098      durability, wal, null, families);
2099  }
2100
2101  public HRegion createLocalHRegionWithInMemoryFlags(TableName tableName, byte[] startKey,
2102    byte[] stopKey, Configuration conf, boolean isReadOnly, Durability durability, WAL wal,
2103    boolean[] compactedMemStore, byte[]... families) throws IOException {
2104    HTableDescriptor htd = new HTableDescriptor(tableName);
2105    htd.setReadOnly(isReadOnly);
2106    int i = 0;
2107    for (byte[] family : families) {
2108      HColumnDescriptor hcd = new HColumnDescriptor(family);
2109      if (compactedMemStore != null && i < compactedMemStore.length) {
2110        hcd.setInMemoryCompaction(MemoryCompactionPolicy.BASIC);
2111      } else {
2112        hcd.setInMemoryCompaction(MemoryCompactionPolicy.NONE);
2113
2114      }
2115      i++;
2116      // Set default to be three versions.
2117      hcd.setMaxVersions(Integer.MAX_VALUE);
2118      htd.addFamily(hcd);
2119    }
2120    htd.setDurability(durability);
2121    HRegionInfo info = new HRegionInfo(htd.getTableName(), startKey, stopKey, false);
2122    return createLocalHRegion(info, conf, htd, wal);
2123  }
2124
2125  //
2126  // ==========================================================================
2127
2128  /**
2129   * Provide an existing table name to truncate. Scans the table and issues a delete for each row
2130   * read.
2131   * @param tableName existing table
2132   * @return HTable to that new table
2133   */
2134  public Table deleteTableData(TableName tableName) throws IOException {
2135    Table table = getConnection().getTable(tableName);
2136    Scan scan = new Scan();
2137    ResultScanner resScan = table.getScanner(scan);
2138    for (Result res : resScan) {
2139      Delete del = new Delete(res.getRow());
2140      table.delete(del);
2141    }
2142    resScan = table.getScanner(scan);
2143    resScan.close();
2144    return table;
2145  }
2146
2147  /**
2148   * Truncate a table using the admin command. Effectively disables, deletes, and recreates the
2149   * table.
2150   * @param tableName       table which must exist.
2151   * @param preserveRegions keep the existing split points
2152   * @return HTable for the new table
2153   */
2154  public Table truncateTable(final TableName tableName, final boolean preserveRegions)
2155    throws IOException {
2156    Admin admin = getAdmin();
2157    if (!admin.isTableDisabled(tableName)) {
2158      admin.disableTable(tableName);
2159    }
2160    admin.truncateTable(tableName, preserveRegions);
2161    return getConnection().getTable(tableName);
2162  }
2163
2164  /**
2165   * Truncate a table using the admin command. Effectively disables, deletes, and recreates the
2166   * table. For previous behavior of issuing row deletes, see deleteTableData. Expressly does not
2167   * preserve regions of existing table.
2168   * @param tableName table which must exist.
2169   * @return HTable for the new table
2170   */
2171  public Table truncateTable(final TableName tableName) throws IOException {
2172    return truncateTable(tableName, false);
2173  }
2174
2175  /**
2176   * Load table with rows from 'aaa' to 'zzz'.
2177   * @param t Table
2178   * @param f Family
2179   * @return Count of rows loaded.
2180   */
2181  public int loadTable(final Table t, final byte[] f) throws IOException {
2182    return loadTable(t, new byte[][] { f });
2183  }
2184
2185  /**
2186   * Load table with rows from 'aaa' to 'zzz'.
2187   * @param t Table
2188   * @param f Family
2189   * @return Count of rows loaded.
2190   */
2191  public int loadTable(final Table t, final byte[] f, boolean writeToWAL) throws IOException {
2192    return loadTable(t, new byte[][] { f }, null, writeToWAL);
2193  }
2194
2195  /**
2196   * Load table of multiple column families with rows from 'aaa' to 'zzz'.
2197   * @param t Table
2198   * @param f Array of Families to load
2199   * @return Count of rows loaded.
2200   */
2201  public int loadTable(final Table t, final byte[][] f) throws IOException {
2202    return loadTable(t, f, null);
2203  }
2204
2205  /**
2206   * Load table of multiple column families with rows from 'aaa' to 'zzz'.
2207   * @param t     Table
2208   * @param f     Array of Families to load
2209   * @param value the values of the cells. If null is passed, the row key is used as value
2210   * @return Count of rows loaded.
2211   */
2212  public int loadTable(final Table t, final byte[][] f, byte[] value) throws IOException {
2213    return loadTable(t, f, value, true);
2214  }
2215
2216  /**
2217   * Load table of multiple column families with rows from 'aaa' to 'zzz'.
2218   * @param t     Table
2219   * @param f     Array of Families to load
2220   * @param value the values of the cells. If null is passed, the row key is used as value
2221   * @return Count of rows loaded.
2222   */
2223  public int loadTable(final Table t, final byte[][] f, byte[] value, boolean writeToWAL)
2224    throws IOException {
2225    List<Put> puts = new ArrayList<>();
2226    for (byte[] row : HBaseTestingUtility.ROWS) {
2227      Put put = new Put(row);
2228      put.setDurability(writeToWAL ? Durability.USE_DEFAULT : Durability.SKIP_WAL);
2229      for (int i = 0; i < f.length; i++) {
2230        byte[] value1 = value != null ? value : row;
2231        put.addColumn(f[i], f[i], value1);
2232      }
2233      puts.add(put);
2234    }
2235    t.put(puts);
2236    return puts.size();
2237  }
2238
2239  /**
2240   * A tracker for tracking and validating table rows generated with
2241   * {@link HBaseTestingUtility#loadTable(Table, byte[])}
2242   */
2243  public static class SeenRowTracker {
2244    int dim = 'z' - 'a' + 1;
2245    int[][][] seenRows = new int[dim][dim][dim]; // count of how many times the row is seen
2246    byte[] startRow;
2247    byte[] stopRow;
2248
2249    public SeenRowTracker(byte[] startRow, byte[] stopRow) {
2250      this.startRow = startRow;
2251      this.stopRow = stopRow;
2252    }
2253
2254    void reset() {
2255      for (byte[] row : ROWS) {
2256        seenRows[i(row[0])][i(row[1])][i(row[2])] = 0;
2257      }
2258    }
2259
2260    int i(byte b) {
2261      return b - 'a';
2262    }
2263
2264    public void addRow(byte[] row) {
2265      seenRows[i(row[0])][i(row[1])][i(row[2])]++;
2266    }
2267
2268    /**
2269     * Validate that all the rows between startRow and stopRow are seen exactly once, and all other
2270     * rows none
2271     */
2272    public void validate() {
2273      for (byte b1 = 'a'; b1 <= 'z'; b1++) {
2274        for (byte b2 = 'a'; b2 <= 'z'; b2++) {
2275          for (byte b3 = 'a'; b3 <= 'z'; b3++) {
2276            int count = seenRows[i(b1)][i(b2)][i(b3)];
2277            int expectedCount = 0;
2278            if (
2279              Bytes.compareTo(new byte[] { b1, b2, b3 }, startRow) >= 0
2280                && Bytes.compareTo(new byte[] { b1, b2, b3 }, stopRow) < 0
2281            ) {
2282              expectedCount = 1;
2283            }
2284            if (count != expectedCount) {
2285              String row = new String(new byte[] { b1, b2, b3 }, StandardCharsets.UTF_8);
2286              throw new RuntimeException("Row:" + row + " has a seen count of " + count + " "
2287                + "instead of " + expectedCount);
2288            }
2289          }
2290        }
2291      }
2292    }
2293  }
2294
2295  public int loadRegion(final HRegion r, final byte[] f) throws IOException {
2296    return loadRegion(r, f, false);
2297  }
2298
2299  public int loadRegion(final Region r, final byte[] f) throws IOException {
2300    return loadRegion((HRegion) r, f);
2301  }
2302
2303  /**
2304   * Load region with rows from 'aaa' to 'zzz'.
2305   * @param r     Region
2306   * @param f     Family
2307   * @param flush flush the cache if true
2308   * @return Count of rows loaded.
2309   */
2310  public int loadRegion(final HRegion r, final byte[] f, final boolean flush) throws IOException {
2311    byte[] k = new byte[3];
2312    int rowCount = 0;
2313    for (byte b1 = 'a'; b1 <= 'z'; b1++) {
2314      for (byte b2 = 'a'; b2 <= 'z'; b2++) {
2315        for (byte b3 = 'a'; b3 <= 'z'; b3++) {
2316          k[0] = b1;
2317          k[1] = b2;
2318          k[2] = b3;
2319          Put put = new Put(k);
2320          put.setDurability(Durability.SKIP_WAL);
2321          put.addColumn(f, null, k);
2322          if (r.getWAL() == null) {
2323            put.setDurability(Durability.SKIP_WAL);
2324          }
2325          int preRowCount = rowCount;
2326          int pause = 10;
2327          int maxPause = 1000;
2328          while (rowCount == preRowCount) {
2329            try {
2330              r.put(put);
2331              rowCount++;
2332            } catch (RegionTooBusyException e) {
2333              pause = (pause * 2 >= maxPause) ? maxPause : pause * 2;
2334              Threads.sleep(pause);
2335            }
2336          }
2337        }
2338      }
2339      if (flush) {
2340        r.flush(true);
2341      }
2342    }
2343    return rowCount;
2344  }
2345
2346  public void loadNumericRows(final Table t, final byte[] f, int startRow, int endRow)
2347    throws IOException {
2348    for (int i = startRow; i < endRow; i++) {
2349      byte[] data = Bytes.toBytes(String.valueOf(i));
2350      Put put = new Put(data);
2351      put.addColumn(f, null, data);
2352      t.put(put);
2353    }
2354  }
2355
2356  public void loadRandomRows(final Table t, final byte[] f, int rowSize, int totalRows)
2357    throws IOException {
2358    byte[] row = new byte[rowSize];
2359    for (int i = 0; i < totalRows; i++) {
2360      Bytes.random(row);
2361      Put put = new Put(row);
2362      put.addColumn(f, new byte[] { 0 }, new byte[] { 0 });
2363      t.put(put);
2364    }
2365  }
2366
2367  public void verifyNumericRows(Table table, final byte[] f, int startRow, int endRow,
2368    int replicaId) throws IOException {
2369    for (int i = startRow; i < endRow; i++) {
2370      String failMsg = "Failed verification of row :" + i;
2371      byte[] data = Bytes.toBytes(String.valueOf(i));
2372      Get get = new Get(data);
2373      get.setReplicaId(replicaId);
2374      get.setConsistency(Consistency.TIMELINE);
2375      Result result = table.get(get);
2376      assertTrue(failMsg, result.containsColumn(f, null));
2377      assertEquals(failMsg, 1, result.getColumnCells(f, null).size());
2378      Cell cell = result.getColumnLatestCell(f, null);
2379      assertTrue(failMsg, Bytes.equals(data, 0, data.length, cell.getValueArray(),
2380        cell.getValueOffset(), cell.getValueLength()));
2381    }
2382  }
2383
2384  public void verifyNumericRows(Region region, final byte[] f, int startRow, int endRow)
2385    throws IOException {
2386    verifyNumericRows((HRegion) region, f, startRow, endRow);
2387  }
2388
2389  public void verifyNumericRows(HRegion region, final byte[] f, int startRow, int endRow)
2390    throws IOException {
2391    verifyNumericRows(region, f, startRow, endRow, true);
2392  }
2393
2394  public void verifyNumericRows(Region region, final byte[] f, int startRow, int endRow,
2395    final boolean present) throws IOException {
2396    verifyNumericRows((HRegion) region, f, startRow, endRow, present);
2397  }
2398
2399  public void verifyNumericRows(HRegion region, final byte[] f, int startRow, int endRow,
2400    final boolean present) throws IOException {
2401    for (int i = startRow; i < endRow; i++) {
2402      String failMsg = "Failed verification of row :" + i;
2403      byte[] data = Bytes.toBytes(String.valueOf(i));
2404      Result result = region.get(new Get(data));
2405
2406      boolean hasResult = result != null && !result.isEmpty();
2407      assertEquals(failMsg + result, present, hasResult);
2408      if (!present) continue;
2409
2410      assertTrue(failMsg, result.containsColumn(f, null));
2411      assertEquals(failMsg, 1, result.getColumnCells(f, null).size());
2412      Cell cell = result.getColumnLatestCell(f, null);
2413      assertTrue(failMsg, Bytes.equals(data, 0, data.length, cell.getValueArray(),
2414        cell.getValueOffset(), cell.getValueLength()));
2415    }
2416  }
2417
2418  public void deleteNumericRows(final Table t, final byte[] f, int startRow, int endRow)
2419    throws IOException {
2420    for (int i = startRow; i < endRow; i++) {
2421      byte[] data = Bytes.toBytes(String.valueOf(i));
2422      Delete delete = new Delete(data);
2423      delete.addFamily(f);
2424      t.delete(delete);
2425    }
2426  }
2427
2428  /**
2429   * Return the number of rows in the given table.
2430   * @param table to count rows
2431   * @return count of rows
2432   */
2433  public int countRows(final Table table) throws IOException {
2434    return countRows(table, new Scan());
2435  }
2436
2437  public int countRows(final Table table, final Scan scan) throws IOException {
2438    try (ResultScanner results = table.getScanner(scan)) {
2439      int count = 0;
2440      while (results.next() != null) {
2441        count++;
2442      }
2443      return count;
2444    }
2445  }
2446
2447  public int countRows(final Table table, final byte[]... families) throws IOException {
2448    Scan scan = new Scan();
2449    for (byte[] family : families) {
2450      scan.addFamily(family);
2451    }
2452    return countRows(table, scan);
2453  }
2454
2455  /**
2456   * Return the number of rows in the given table.
2457   */
2458  public int countRows(final TableName tableName) throws IOException {
2459    Table table = getConnection().getTable(tableName);
2460    try {
2461      return countRows(table);
2462    } finally {
2463      table.close();
2464    }
2465  }
2466
2467  public int countRows(final Region region) throws IOException {
2468    return countRows(region, new Scan());
2469  }
2470
2471  public int countRows(final Region region, final Scan scan) throws IOException {
2472    InternalScanner scanner = region.getScanner(scan);
2473    try {
2474      return countRows(scanner);
2475    } finally {
2476      scanner.close();
2477    }
2478  }
2479
2480  public int countRows(final InternalScanner scanner) throws IOException {
2481    int scannedCount = 0;
2482    List<Cell> results = new ArrayList<>();
2483    boolean hasMore = true;
2484    while (hasMore) {
2485      hasMore = scanner.next(results);
2486      scannedCount += results.size();
2487      results.clear();
2488    }
2489    return scannedCount;
2490  }
2491
2492  /**
2493   * Return an md5 digest of the entire contents of a table.
2494   */
2495  public String checksumRows(final Table table) throws Exception {
2496
2497    Scan scan = new Scan();
2498    ResultScanner results = table.getScanner(scan);
2499    MessageDigest digest = MessageDigest.getInstance("MD5");
2500    for (Result res : results) {
2501      digest.update(res.getRow());
2502    }
2503    results.close();
2504    return digest.toString();
2505  }
2506
2507  /** All the row values for the data loaded by {@link #loadTable(Table, byte[])} */
2508  public static final byte[][] ROWS = new byte[(int) Math.pow('z' - 'a' + 1, 3)][3]; // ~52KB
2509  static {
2510    int i = 0;
2511    for (byte b1 = 'a'; b1 <= 'z'; b1++) {
2512      for (byte b2 = 'a'; b2 <= 'z'; b2++) {
2513        for (byte b3 = 'a'; b3 <= 'z'; b3++) {
2514          ROWS[i][0] = b1;
2515          ROWS[i][1] = b2;
2516          ROWS[i][2] = b3;
2517          i++;
2518        }
2519      }
2520    }
2521  }
2522
2523  public static final byte[][] KEYS = { HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes("bbb"),
2524    Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"), Bytes.toBytes("fff"),
2525    Bytes.toBytes("ggg"), Bytes.toBytes("hhh"), Bytes.toBytes("iii"), Bytes.toBytes("jjj"),
2526    Bytes.toBytes("kkk"), Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"),
2527    Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"), Bytes.toBytes("rrr"),
2528    Bytes.toBytes("sss"), Bytes.toBytes("ttt"), Bytes.toBytes("uuu"), Bytes.toBytes("vvv"),
2529    Bytes.toBytes("www"), Bytes.toBytes("xxx"), Bytes.toBytes("yyy") };
2530
2531  public static final byte[][] KEYS_FOR_HBA_CREATE_TABLE = { Bytes.toBytes("bbb"),
2532    Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"), Bytes.toBytes("fff"),
2533    Bytes.toBytes("ggg"), Bytes.toBytes("hhh"), Bytes.toBytes("iii"), Bytes.toBytes("jjj"),
2534    Bytes.toBytes("kkk"), Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"),
2535    Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"), Bytes.toBytes("rrr"),
2536    Bytes.toBytes("sss"), Bytes.toBytes("ttt"), Bytes.toBytes("uuu"), Bytes.toBytes("vvv"),
2537    Bytes.toBytes("www"), Bytes.toBytes("xxx"), Bytes.toBytes("yyy"), Bytes.toBytes("zzz") };
2538
2539  /**
2540   * Create rows in hbase:meta for regions of the specified table with the specified start keys. The
2541   * first startKey should be a 0 length byte array if you want to form a proper range of regions.
2542   * @return list of region info for regions added to meta
2543   * @deprecated since 2.0 version and will be removed in 3.0 version. use
2544   *             {@link #createMultiRegionsInMeta(Configuration, TableDescriptor, byte[][])}
2545   */
2546  @Deprecated
2547  public List<HRegionInfo> createMultiRegionsInMeta(final Configuration conf,
2548    final HTableDescriptor htd, byte[][] startKeys) throws IOException {
2549    return createMultiRegionsInMeta(conf, (TableDescriptor) htd, startKeys).stream()
2550      .map(ImmutableHRegionInfo::new).collect(Collectors.toList());
2551  }
2552
2553  /**
2554   * Create rows in hbase:meta for regions of the specified table with the specified start keys. The
2555   * first startKey should be a 0 length byte array if you want to form a proper range of regions.
2556   * @return list of region info for regions added to meta
2557   */
2558  public List<RegionInfo> createMultiRegionsInMeta(final Configuration conf,
2559    final TableDescriptor htd, byte[][] startKeys) throws IOException {
2560    Table meta = getConnection().getTable(TableName.META_TABLE_NAME);
2561    Arrays.sort(startKeys, Bytes.BYTES_COMPARATOR);
2562    List<RegionInfo> newRegions = new ArrayList<>(startKeys.length);
2563    MetaTableAccessor.updateTableState(getConnection(), htd.getTableName(),
2564      TableState.State.ENABLED);
2565    // add custom ones
2566    for (int i = 0; i < startKeys.length; i++) {
2567      int j = (i + 1) % startKeys.length;
2568      RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).setStartKey(startKeys[i])
2569        .setEndKey(startKeys[j]).build();
2570      MetaTableAccessor.addRegionsToMeta(getConnection(), Collections.singletonList(hri), 1);
2571      newRegions.add(hri);
2572    }
2573
2574    meta.close();
2575    return newRegions;
2576  }
2577
2578  /**
2579   * Create an unmanaged WAL. Be sure to close it when you're through.
2580   */
2581  public static WAL createWal(final Configuration conf, final Path rootDir, final RegionInfo hri)
2582    throws IOException {
2583    // The WAL subsystem will use the default rootDir rather than the passed in rootDir
2584    // unless I pass along via the conf.
2585    Configuration confForWAL = new Configuration(conf);
2586    confForWAL.set(HConstants.HBASE_DIR, rootDir.toString());
2587    return new WALFactory(confForWAL, "hregion-" + RandomStringUtils.randomNumeric(8)).getWAL(hri);
2588  }
2589
2590  /**
2591   * Create a region with it's own WAL. Be sure to call
2592   * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources.
2593   */
2594  public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir,
2595    final Configuration conf, final TableDescriptor htd) throws IOException {
2596    return createRegionAndWAL(info, rootDir, conf, htd, true);
2597  }
2598
2599  /**
2600   * Create a region with it's own WAL. Be sure to call
2601   * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources.
2602   */
2603  public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir,
2604    final Configuration conf, final TableDescriptor htd, BlockCache blockCache) throws IOException {
2605    HRegion region = createRegionAndWAL(info, rootDir, conf, htd, false);
2606    region.setBlockCache(blockCache);
2607    region.initialize();
2608    return region;
2609  }
2610
2611  /**
2612   * Create a region with it's own WAL. Be sure to call
2613   * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources.
2614   */
2615  public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir,
2616    final Configuration conf, final TableDescriptor htd, MobFileCache mobFileCache)
2617    throws IOException {
2618    HRegion region = createRegionAndWAL(info, rootDir, conf, htd, false);
2619    region.setMobFileCache(mobFileCache);
2620    region.initialize();
2621    return region;
2622  }
2623
2624  /**
2625   * Create a region with it's own WAL. Be sure to call
2626   * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources.
2627   */
2628  public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir,
2629    final Configuration conf, final TableDescriptor htd, boolean initialize) throws IOException {
2630    ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null,
2631      MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT);
2632    WAL wal = createWal(conf, rootDir, info);
2633    return HRegion.createHRegion(info, rootDir, conf, htd, wal, initialize);
2634  }
2635
2636  /**
2637   * Returns all rows from the hbase:meta table.
2638   * @throws IOException When reading the rows fails.
2639   */
2640  public List<byte[]> getMetaTableRows() throws IOException {
2641    // TODO: Redo using MetaTableAccessor class
2642    Table t = getConnection().getTable(TableName.META_TABLE_NAME);
2643    List<byte[]> rows = new ArrayList<>();
2644    ResultScanner s = t.getScanner(new Scan());
2645    for (Result result : s) {
2646      LOG.info("getMetaTableRows: row -> " + Bytes.toStringBinary(result.getRow()));
2647      rows.add(result.getRow());
2648    }
2649    s.close();
2650    t.close();
2651    return rows;
2652  }
2653
2654  /**
2655   * Returns all rows from the hbase:meta table for a given user table
2656   * @throws IOException When reading the rows fails.
2657   */
2658  public List<byte[]> getMetaTableRows(TableName tableName) throws IOException {
2659    // TODO: Redo using MetaTableAccessor.
2660    Table t = getConnection().getTable(TableName.META_TABLE_NAME);
2661    List<byte[]> rows = new ArrayList<>();
2662    ResultScanner s = t.getScanner(new Scan());
2663    for (Result result : s) {
2664      RegionInfo info = MetaTableAccessor.getRegionInfo(result);
2665      if (info == null) {
2666        LOG.error("No region info for row " + Bytes.toString(result.getRow()));
2667        // TODO figure out what to do for this new hosed case.
2668        continue;
2669      }
2670
2671      if (info.getTable().equals(tableName)) {
2672        LOG.info("getMetaTableRows: row -> " + Bytes.toStringBinary(result.getRow()) + info);
2673        rows.add(result.getRow());
2674      }
2675    }
2676    s.close();
2677    t.close();
2678    return rows;
2679  }
2680
2681  /**
2682   * Returns all regions of the specified table
2683   * @param tableName the table name
2684   * @return all regions of the specified table
2685   * @throws IOException when getting the regions fails.
2686   */
2687  private List<RegionInfo> getRegions(TableName tableName) throws IOException {
2688    try (Admin admin = getConnection().getAdmin()) {
2689      return admin.getRegions(tableName);
2690    }
2691  }
2692
2693  /**
2694   * Find any other region server which is different from the one identified by parameter
2695   * @return another region server
2696   */
2697  public HRegionServer getOtherRegionServer(HRegionServer rs) {
2698    for (JVMClusterUtil.RegionServerThread rst : getMiniHBaseCluster().getRegionServerThreads()) {
2699      if (!(rst.getRegionServer() == rs)) {
2700        return rst.getRegionServer();
2701      }
2702    }
2703    return null;
2704  }
2705
2706  /**
2707   * Tool to get the reference to the region server object that holds the region of the specified
2708   * user table.
2709   * @param tableName user table to lookup in hbase:meta
2710   * @return region server that holds it, null if the row doesn't exist
2711   */
2712  public HRegionServer getRSForFirstRegionInTable(TableName tableName)
2713    throws IOException, InterruptedException {
2714    List<RegionInfo> regions = getRegions(tableName);
2715    if (regions == null || regions.isEmpty()) {
2716      return null;
2717    }
2718    LOG.debug("Found " + regions.size() + " regions for table " + tableName);
2719
2720    byte[] firstRegionName =
2721      regions.stream().filter(r -> !r.isOffline()).map(RegionInfo::getRegionName).findFirst()
2722        .orElseThrow(() -> new IOException("online regions not found in table " + tableName));
2723
2724    LOG.debug("firstRegionName=" + Bytes.toString(firstRegionName));
2725    long pause = getConfiguration().getLong(HConstants.HBASE_CLIENT_PAUSE,
2726      HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
2727    int numRetries = getConfiguration().getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
2728      HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
2729    RetryCounter retrier = new RetryCounter(numRetries + 1, (int) pause, TimeUnit.MICROSECONDS);
2730    while (retrier.shouldRetry()) {
2731      int index = getMiniHBaseCluster().getServerWith(firstRegionName);
2732      if (index != -1) {
2733        return getMiniHBaseCluster().getRegionServerThreads().get(index).getRegionServer();
2734      }
2735      // Came back -1. Region may not be online yet. Sleep a while.
2736      retrier.sleepUntilNextRetry();
2737    }
2738    return null;
2739  }
2740
2741  /**
2742   * Starts a <code>MiniMRCluster</code> with a default number of <code>TaskTracker</code>'s.
2743   * @throws IOException When starting the cluster fails.
2744   */
2745  public MiniMRCluster startMiniMapReduceCluster() throws IOException {
2746    // Set a very high max-disk-utilization percentage to avoid the NodeManagers from failing.
2747    conf.setIfUnset("yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage",
2748      "99.0");
2749    startMiniMapReduceCluster(2);
2750    return mrCluster;
2751  }
2752
2753  /**
2754   * Tasktracker has a bug where changing the hadoop.log.dir system property will not change its
2755   * internal static LOG_DIR variable.
2756   */
2757  private void forceChangeTaskLogDir() {
2758    Field logDirField;
2759    try {
2760      logDirField = TaskLog.class.getDeclaredField("LOG_DIR");
2761      logDirField.setAccessible(true);
2762
2763      Field modifiersField = ReflectionUtils.getModifiersField();
2764      modifiersField.setAccessible(true);
2765      modifiersField.setInt(logDirField, logDirField.getModifiers() & ~Modifier.FINAL);
2766
2767      logDirField.set(null, new File(hadoopLogDir, "userlogs"));
2768    } catch (SecurityException e) {
2769      throw new RuntimeException(e);
2770    } catch (NoSuchFieldException e) {
2771      // TODO Auto-generated catch block
2772      throw new RuntimeException(e);
2773    } catch (IllegalArgumentException e) {
2774      throw new RuntimeException(e);
2775    } catch (IllegalAccessException e) {
2776      throw new RuntimeException(e);
2777    }
2778  }
2779
2780  /**
2781   * Starts a <code>MiniMRCluster</code>. Call {@link #setFileSystemURI(String)} to use a different
2782   * filesystem.
2783   * @param servers The number of <code>TaskTracker</code>'s to start.
2784   * @throws IOException When starting the cluster fails.
2785   */
2786  private void startMiniMapReduceCluster(final int servers) throws IOException {
2787    if (mrCluster != null) {
2788      throw new IllegalStateException("MiniMRCluster is already running");
2789    }
2790    LOG.info("Starting mini mapreduce cluster...");
2791    setupClusterTestDir();
2792    createDirsAndSetProperties();
2793
2794    forceChangeTaskLogDir();
2795
2796    //// hadoop2 specific settings
2797    // Tests were failing because this process used 6GB of virtual memory and was getting killed.
2798    // we up the VM usable so that processes don't get killed.
2799    conf.setFloat("yarn.nodemanager.vmem-pmem-ratio", 8.0f);
2800
2801    // Tests were failing due to MAPREDUCE-4880 / MAPREDUCE-4607 against hadoop 2.0.2-alpha and
2802    // this avoids the problem by disabling speculative task execution in tests.
2803    conf.setBoolean("mapreduce.map.speculative", false);
2804    conf.setBoolean("mapreduce.reduce.speculative", false);
2805    ////
2806
2807    // Yarn container runs in independent JVM. We need to pass the argument manually here if the
2808    // JDK version >= 17. Otherwise, the MiniMRCluster will fail.
2809    if (JVM.getJVMSpecVersion() >= 17) {
2810      String jvmOpts = conf.get("yarn.app.mapreduce.am.command-opts", "");
2811      conf.set("yarn.app.mapreduce.am.command-opts",
2812        jvmOpts + " --add-opens java.base/java.lang=ALL-UNNAMED");
2813    }
2814
2815    // Allow the user to override FS URI for this map-reduce cluster to use.
2816    mrCluster =
2817      new MiniMRCluster(servers, FS_URI != null ? FS_URI : FileSystem.get(conf).getUri().toString(),
2818        1, null, null, new JobConf(this.conf));
2819    JobConf jobConf = MapreduceTestingShim.getJobConf(mrCluster);
2820    if (jobConf == null) {
2821      jobConf = mrCluster.createJobConf();
2822    }
2823    // Hadoop MiniMR overwrites this while it should not
2824    jobConf.set("mapreduce.cluster.local.dir", conf.get("mapreduce.cluster.local.dir"));
2825    LOG.info("Mini mapreduce cluster started");
2826
2827    // In hadoop2, YARN/MR2 starts a mini cluster with its own conf instance and updates settings.
2828    // Our HBase MR jobs need several of these settings in order to properly run. So we copy the
2829    // necessary config properties here. YARN-129 required adding a few properties.
2830    conf.set("mapreduce.jobtracker.address", jobConf.get("mapreduce.jobtracker.address"));
2831    // this for mrv2 support; mr1 ignores this
2832    conf.set("mapreduce.framework.name", "yarn");
2833    conf.setBoolean("yarn.is.minicluster", true);
2834    String rmAddress = jobConf.get("yarn.resourcemanager.address");
2835    if (rmAddress != null) {
2836      conf.set("yarn.resourcemanager.address", rmAddress);
2837    }
2838    String historyAddress = jobConf.get("mapreduce.jobhistory.address");
2839    if (historyAddress != null) {
2840      conf.set("mapreduce.jobhistory.address", historyAddress);
2841    }
2842    String schedulerAddress = jobConf.get("yarn.resourcemanager.scheduler.address");
2843    if (schedulerAddress != null) {
2844      conf.set("yarn.resourcemanager.scheduler.address", schedulerAddress);
2845    }
2846    String mrJobHistoryWebappAddress = jobConf.get("mapreduce.jobhistory.webapp.address");
2847    if (mrJobHistoryWebappAddress != null) {
2848      conf.set("mapreduce.jobhistory.webapp.address", mrJobHistoryWebappAddress);
2849    }
2850    String yarnRMWebappAddress = jobConf.get("yarn.resourcemanager.webapp.address");
2851    if (yarnRMWebappAddress != null) {
2852      conf.set("yarn.resourcemanager.webapp.address", yarnRMWebappAddress);
2853    }
2854  }
2855
2856  /**
2857   * Stops the previously started <code>MiniMRCluster</code>.
2858   */
2859  public void shutdownMiniMapReduceCluster() {
2860    if (mrCluster != null) {
2861      LOG.info("Stopping mini mapreduce cluster...");
2862      mrCluster.shutdown();
2863      mrCluster = null;
2864      LOG.info("Mini mapreduce cluster stopped");
2865    }
2866    // Restore configuration to point to local jobtracker
2867    conf.set("mapreduce.jobtracker.address", "local");
2868  }
2869
2870  /**
2871   * Create a stubbed out RegionServerService, mainly for getting FS.
2872   */
2873  public RegionServerServices createMockRegionServerService() throws IOException {
2874    return createMockRegionServerService((ServerName) null);
2875  }
2876
2877  /**
2878   * Create a stubbed out RegionServerService, mainly for getting FS. This version is used by
2879   * TestTokenAuthentication
2880   */
2881  public RegionServerServices createMockRegionServerService(RpcServerInterface rpc)
2882    throws IOException {
2883    final MockRegionServerServices rss = new MockRegionServerServices(getZooKeeperWatcher());
2884    rss.setFileSystem(getTestFileSystem());
2885    rss.setRpcServer(rpc);
2886    return rss;
2887  }
2888
2889  /**
2890   * Create a stubbed out RegionServerService, mainly for getting FS. This version is used by
2891   * TestOpenRegionHandler
2892   */
2893  public RegionServerServices createMockRegionServerService(ServerName name) throws IOException {
2894    final MockRegionServerServices rss = new MockRegionServerServices(getZooKeeperWatcher(), name);
2895    rss.setFileSystem(getTestFileSystem());
2896    return rss;
2897  }
2898
2899  /**
2900   * Switches the logger for the given class to DEBUG level.
2901   * @param clazz The class for which to switch to debug logging.
2902   * @deprecated In 2.3.0, will be removed in 4.0.0. Only support changing log level on log4j now as
2903   *             HBase only uses log4j. You should do this by your own as it you know which log
2904   *             framework you are using then set the log level to debug is very easy.
2905   */
2906  @Deprecated
2907  public void enableDebug(Class<?> clazz) {
2908    Log4jUtils.enableDebug(clazz);
2909  }
2910
2911  /**
2912   * Expire the Master's session
2913   */
2914  public void expireMasterSession() throws Exception {
2915    HMaster master = getMiniHBaseCluster().getMaster();
2916    expireSession(master.getZooKeeper(), false);
2917  }
2918
2919  /**
2920   * Expire a region server's session
2921   * @param index which RS
2922   */
2923  public void expireRegionServerSession(int index) throws Exception {
2924    HRegionServer rs = getMiniHBaseCluster().getRegionServer(index);
2925    expireSession(rs.getZooKeeper(), false);
2926    decrementMinRegionServerCount();
2927  }
2928
2929  private void decrementMinRegionServerCount() {
2930    // decrement the count for this.conf, for newly spwaned master
2931    // this.hbaseCluster shares this configuration too
2932    decrementMinRegionServerCount(getConfiguration());
2933
2934    // each master thread keeps a copy of configuration
2935    for (MasterThread master : getHBaseCluster().getMasterThreads()) {
2936      decrementMinRegionServerCount(master.getMaster().getConfiguration());
2937    }
2938  }
2939
2940  private void decrementMinRegionServerCount(Configuration conf) {
2941    int currentCount = conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1);
2942    if (currentCount != -1) {
2943      conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, Math.max(currentCount - 1, 1));
2944    }
2945  }
2946
2947  public void expireSession(ZKWatcher nodeZK) throws Exception {
2948    expireSession(nodeZK, false);
2949  }
2950
2951  /**
2952   * Expire a ZooKeeper session as recommended in ZooKeeper documentation
2953   * http://hbase.apache.org/book.html#trouble.zookeeper There are issues when doing this: [1]
2954   * http://www.mail-archive.com/dev@zookeeper.apache.org/msg01942.html [2]
2955   * https://issues.apache.org/jira/browse/ZOOKEEPER-1105
2956   * @param nodeZK      - the ZK watcher to expire
2957   * @param checkStatus - true to check if we can create a Table with the current configuration.
2958   */
2959  public void expireSession(ZKWatcher nodeZK, boolean checkStatus) throws Exception {
2960    Configuration c = new Configuration(this.conf);
2961    String quorumServers = ZKConfig.getZKQuorumServersString(c);
2962    ZooKeeper zk = nodeZK.getRecoverableZooKeeper().getZooKeeper();
2963    byte[] password = zk.getSessionPasswd();
2964    long sessionID = zk.getSessionId();
2965
2966    // Expiry seems to be asynchronous (see comment from P. Hunt in [1]),
2967    // so we create a first watcher to be sure that the
2968    // event was sent. We expect that if our watcher receives the event
2969    // other watchers on the same machine will get is as well.
2970    // When we ask to close the connection, ZK does not close it before
2971    // we receive all the events, so don't have to capture the event, just
2972    // closing the connection should be enough.
2973    ZooKeeper monitor = new ZooKeeper(quorumServers, 1000, new org.apache.zookeeper.Watcher() {
2974      @Override
2975      public void process(WatchedEvent watchedEvent) {
2976        LOG.info("Monitor ZKW received event=" + watchedEvent);
2977      }
2978    }, sessionID, password);
2979
2980    // Making it expire
2981    ZooKeeper newZK =
2982      new ZooKeeper(quorumServers, 1000, EmptyWatcher.instance, sessionID, password);
2983
2984    // ensure that we have connection to the server before closing down, otherwise
2985    // the close session event will be eaten out before we start CONNECTING state
2986    long start = EnvironmentEdgeManager.currentTime();
2987    while (
2988      newZK.getState() != States.CONNECTED && EnvironmentEdgeManager.currentTime() - start < 1000
2989    ) {
2990      Thread.sleep(1);
2991    }
2992    newZK.close();
2993    LOG.info("ZK Closed Session 0x" + Long.toHexString(sessionID));
2994
2995    // Now closing & waiting to be sure that the clients get it.
2996    monitor.close();
2997
2998    if (checkStatus) {
2999      getConnection().getTable(TableName.META_TABLE_NAME).close();
3000    }
3001  }
3002
3003  /**
3004   * Get the Mini HBase cluster.
3005   * @return hbase cluster
3006   * @see #getHBaseClusterInterface()
3007   */
3008  public MiniHBaseCluster getHBaseCluster() {
3009    return getMiniHBaseCluster();
3010  }
3011
3012  /**
3013   * Returns the HBaseCluster instance.
3014   * <p>
3015   * Returned object can be any of the subclasses of HBaseCluster, and the tests referring this
3016   * should not assume that the cluster is a mini cluster or a distributed one. If the test only
3017   * works on a mini cluster, then specific method {@link #getMiniHBaseCluster()} can be used
3018   * instead w/o the need to type-cast.
3019   */
3020  public HBaseCluster getHBaseClusterInterface() {
3021    // implementation note: we should rename this method as #getHBaseCluster(),
3022    // but this would require refactoring 90+ calls.
3023    return hbaseCluster;
3024  }
3025
3026  /**
3027   * Resets the connections so that the next time getConnection() is called, a new connection is
3028   * created. This is needed in cases where the entire cluster / all the masters are shutdown and
3029   * the connection is not valid anymore. TODO: There should be a more coherent way of doing this.
3030   * Unfortunately the way tests are written, not all start() stop() calls go through this class.
3031   * Most tests directly operate on the underlying mini/local hbase cluster. That makes it difficult
3032   * for this wrapper class to maintain the connection state automatically. Cleaning this is a much
3033   * bigger refactor.
3034   */
3035  public void invalidateConnection() throws IOException {
3036    closeConnection();
3037    // Update the master addresses if they changed.
3038    final String masterConfigBefore = conf.get(HConstants.MASTER_ADDRS_KEY);
3039    final String masterConfAfter = getMiniHBaseCluster().conf.get(HConstants.MASTER_ADDRS_KEY);
3040    LOG.info("Invalidated connection. Updating master addresses before: {} after: {}",
3041      masterConfigBefore, masterConfAfter);
3042    conf.set(HConstants.MASTER_ADDRS_KEY,
3043      getMiniHBaseCluster().conf.get(HConstants.MASTER_ADDRS_KEY));
3044  }
3045
3046  /**
3047   * Get a shared Connection to the cluster. this method is threadsafe.
3048   * @return A Connection that can be shared. Don't close. Will be closed on shutdown of cluster.
3049   */
3050  public Connection getConnection() throws IOException {
3051    try {
3052      return this.connection.updateAndGet(connection -> {
3053        if (connection == null) {
3054          try {
3055            connection = ConnectionFactory.createConnection(this.conf);
3056          } catch (IOException ioe) {
3057            throw new UncheckedIOException("Failed to create connection", ioe);
3058          }
3059        }
3060        return connection;
3061      });
3062    } catch (UncheckedIOException exception) {
3063      throw exception.getCause();
3064    }
3065  }
3066
3067  /**
3068   * Returns a Admin instance. This instance is shared between HBaseTestingUtility instance users.
3069   * Closing it has no effect, it will be closed automatically when the cluster shutdowns
3070   * @return HBaseAdmin instance which is guaranteed to support only {@link Admin} interface.
3071   *         Functions in HBaseAdmin not provided by {@link Admin} interface can be changed/deleted
3072   *         anytime.
3073   * @deprecated Since 2.0. Will be removed in 3.0. Use {@link #getAdmin()} instead.
3074   */
3075  @Deprecated
3076  public synchronized HBaseAdmin getHBaseAdmin() throws IOException {
3077    if (hbaseAdmin == null) {
3078      this.hbaseAdmin = (HBaseAdmin) getConnection().getAdmin();
3079    }
3080    return hbaseAdmin;
3081  }
3082
3083  public void closeConnection() throws IOException {
3084    if (hbaseAdmin != null) {
3085      Closeables.close(hbaseAdmin, true);
3086      hbaseAdmin = null;
3087    }
3088    Connection connection = this.connection.getAndSet(null);
3089    if (connection != null) {
3090      Closeables.close(connection, true);
3091    }
3092  }
3093
3094  /**
3095   * Returns an Admin instance which is shared between HBaseTestingUtility instance users. Closing
3096   * it has no effect, it will be closed automatically when the cluster shutdowns
3097   */
3098  public synchronized Admin getAdmin() throws IOException {
3099    if (hbaseAdmin == null) {
3100      this.hbaseAdmin = (HBaseAdmin) getConnection().getAdmin();
3101    }
3102    return hbaseAdmin;
3103  }
3104
3105  private HBaseAdmin hbaseAdmin = null;
3106
3107  /**
3108   * Returns an {@link Hbck} instance. Needs be closed when done.
3109   */
3110  public Hbck getHbck() throws IOException {
3111    return getConnection().getHbck();
3112  }
3113
3114  /**
3115   * Unassign the named region.
3116   * @param regionName The region to unassign.
3117   */
3118  public void unassignRegion(String regionName) throws IOException {
3119    unassignRegion(Bytes.toBytes(regionName));
3120  }
3121
3122  /**
3123   * Unassign the named region.
3124   * @param regionName The region to unassign.
3125   */
3126  public void unassignRegion(byte[] regionName) throws IOException {
3127    getAdmin().unassign(regionName, true);
3128  }
3129
3130  /**
3131   * Closes the region containing the given row.
3132   * @param row   The row to find the containing region.
3133   * @param table The table to find the region.
3134   */
3135  public void unassignRegionByRow(String row, RegionLocator table) throws IOException {
3136    unassignRegionByRow(Bytes.toBytes(row), table);
3137  }
3138
3139  /**
3140   * Closes the region containing the given row.
3141   * @param row   The row to find the containing region.
3142   * @param table The table to find the region.
3143   */
3144  public void unassignRegionByRow(byte[] row, RegionLocator table) throws IOException {
3145    HRegionLocation hrl = table.getRegionLocation(row);
3146    unassignRegion(hrl.getRegionInfo().getRegionName());
3147  }
3148
3149  /**
3150   * Retrieves a splittable region randomly from tableName
3151   * @param tableName   name of table
3152   * @param maxAttempts maximum number of attempts, unlimited for value of -1
3153   * @return the HRegion chosen, null if none was found within limit of maxAttempts
3154   */
3155  public HRegion getSplittableRegion(TableName tableName, int maxAttempts) {
3156    List<HRegion> regions = getHBaseCluster().getRegions(tableName);
3157    int regCount = regions.size();
3158    Set<Integer> attempted = new HashSet<>();
3159    int idx;
3160    int attempts = 0;
3161    do {
3162      regions = getHBaseCluster().getRegions(tableName);
3163      if (regCount != regions.size()) {
3164        // if there was region movement, clear attempted Set
3165        attempted.clear();
3166      }
3167      regCount = regions.size();
3168      // There are chances that before we get the region for the table from an RS the region may
3169      // be going for CLOSE. This may be because online schema change is enabled
3170      if (regCount > 0) {
3171        idx = ThreadLocalRandom.current().nextInt(regCount);
3172        // if we have just tried this region, there is no need to try again
3173        if (attempted.contains(idx)) {
3174          continue;
3175        }
3176        HRegion region = regions.get(idx);
3177        if (region.checkSplit().isPresent()) {
3178          return region;
3179        }
3180        attempted.add(idx);
3181      }
3182      attempts++;
3183    } while (maxAttempts == -1 || attempts < maxAttempts);
3184    return null;
3185  }
3186
3187  public MiniDFSCluster getDFSCluster() {
3188    return dfsCluster;
3189  }
3190
3191  public void setDFSCluster(MiniDFSCluster cluster) throws IllegalStateException, IOException {
3192    setDFSCluster(cluster, true);
3193  }
3194
3195  /**
3196   * Set the MiniDFSCluster
3197   * @param cluster     cluster to use
3198   * @param requireDown require the that cluster not be "up" (MiniDFSCluster#isClusterUp) before it
3199   *                    is set.
3200   * @throws IllegalStateException if the passed cluster is up when it is required to be down
3201   * @throws IOException           if the FileSystem could not be set from the passed dfs cluster
3202   */
3203  public void setDFSCluster(MiniDFSCluster cluster, boolean requireDown)
3204    throws IllegalStateException, IOException {
3205    if (dfsCluster != null && requireDown && dfsCluster.isClusterUp()) {
3206      throw new IllegalStateException("DFSCluster is already running! Shut it down first.");
3207    }
3208    this.dfsCluster = cluster;
3209    this.setFs();
3210  }
3211
3212  public FileSystem getTestFileSystem() throws IOException {
3213    return HFileSystem.get(conf);
3214  }
3215
3216  /**
3217   * Wait until all regions in a table have been assigned. Waits default timeout before giving up
3218   * (30 seconds).
3219   * @param table Table to wait on.
3220   */
3221  public void waitTableAvailable(TableName table) throws InterruptedException, IOException {
3222    waitTableAvailable(table.getName(), 30000);
3223  }
3224
3225  public void waitTableAvailable(TableName table, long timeoutMillis)
3226    throws InterruptedException, IOException {
3227    waitFor(timeoutMillis, predicateTableAvailable(table));
3228  }
3229
3230  /**
3231   * Wait until all regions in a table have been assigned
3232   * @param table         Table to wait on.
3233   * @param timeoutMillis Timeout.
3234   */
3235  public void waitTableAvailable(byte[] table, long timeoutMillis)
3236    throws InterruptedException, IOException {
3237    waitFor(timeoutMillis, predicateTableAvailable(TableName.valueOf(table)));
3238  }
3239
3240  public String explainTableAvailability(TableName tableName) throws IOException {
3241    String msg = explainTableState(tableName, TableState.State.ENABLED) + ", ";
3242    if (getHBaseCluster().getMaster().isAlive()) {
3243      Map<RegionInfo, ServerName> assignments = getHBaseCluster().getMaster().getAssignmentManager()
3244        .getRegionStates().getRegionAssignments();
3245      final List<Pair<RegionInfo, ServerName>> metaLocations =
3246        MetaTableAccessor.getTableRegionsAndLocations(getConnection(), tableName);
3247      for (Pair<RegionInfo, ServerName> metaLocation : metaLocations) {
3248        RegionInfo hri = metaLocation.getFirst();
3249        ServerName sn = metaLocation.getSecond();
3250        if (!assignments.containsKey(hri)) {
3251          msg += ", region " + hri + " not assigned, but found in meta, it expected to be on " + sn;
3252
3253        } else if (sn == null) {
3254          msg += ",  region " + hri + " assigned,  but has no server in meta";
3255        } else if (!sn.equals(assignments.get(hri))) {
3256          msg += ",  region " + hri + " assigned,  but has different servers in meta and AM ( " + sn
3257            + " <> " + assignments.get(hri);
3258        }
3259      }
3260    }
3261    return msg;
3262  }
3263
3264  public String explainTableState(final TableName table, TableState.State state)
3265    throws IOException {
3266    TableState tableState = MetaTableAccessor.getTableState(getConnection(), table);
3267    if (tableState == null) {
3268      return "TableState in META: No table state in META for table " + table
3269        + " last state in meta (including deleted is " + findLastTableState(table) + ")";
3270    } else if (!tableState.inStates(state)) {
3271      return "TableState in META: Not " + state + " state, but " + tableState;
3272    } else {
3273      return "TableState in META: OK";
3274    }
3275  }
3276
3277  @Nullable
3278  public TableState findLastTableState(final TableName table) throws IOException {
3279    final AtomicReference<TableState> lastTableState = new AtomicReference<>(null);
3280    MetaTableAccessor.Visitor visitor = new MetaTableAccessor.Visitor() {
3281      @Override
3282      public boolean visit(Result r) throws IOException {
3283        if (!Arrays.equals(r.getRow(), table.getName())) return false;
3284        TableState state = MetaTableAccessor.getTableState(r);
3285        if (state != null) lastTableState.set(state);
3286        return true;
3287      }
3288    };
3289    MetaTableAccessor.scanMeta(getConnection(), null, null, MetaTableAccessor.QueryType.TABLE,
3290      Integer.MAX_VALUE, visitor);
3291    return lastTableState.get();
3292  }
3293
3294  /**
3295   * Waits for a table to be 'enabled'. Enabled means that table is set as 'enabled' and the regions
3296   * have been all assigned. Will timeout after default period (30 seconds) Tolerates nonexistent
3297   * table.
3298   * @param table the table to wait on.
3299   * @throws InterruptedException if interrupted while waiting
3300   * @throws IOException          if an IO problem is encountered
3301   */
3302  public void waitTableEnabled(TableName table) throws InterruptedException, IOException {
3303    waitTableEnabled(table, 30000);
3304  }
3305
3306  /**
3307   * Waits for a table to be 'enabled'. Enabled means that table is set as 'enabled' and the regions
3308   * have been all assigned.
3309   * @see #waitTableEnabled(TableName, long)
3310   * @param table         Table to wait on.
3311   * @param timeoutMillis Time to wait on it being marked enabled.
3312   */
3313  public void waitTableEnabled(byte[] table, long timeoutMillis)
3314    throws InterruptedException, IOException {
3315    waitTableEnabled(TableName.valueOf(table), timeoutMillis);
3316  }
3317
3318  public void waitTableEnabled(TableName table, long timeoutMillis) throws IOException {
3319    waitFor(timeoutMillis, predicateTableEnabled(table));
3320  }
3321
3322  /**
3323   * Waits for a table to be 'disabled'. Disabled means that table is set as 'disabled' Will timeout
3324   * after default period (30 seconds)
3325   * @param table Table to wait on.
3326   */
3327  public void waitTableDisabled(byte[] table) throws InterruptedException, IOException {
3328    waitTableDisabled(table, 30000);
3329  }
3330
3331  public void waitTableDisabled(TableName table, long millisTimeout)
3332    throws InterruptedException, IOException {
3333    waitFor(millisTimeout, predicateTableDisabled(table));
3334  }
3335
3336  /**
3337   * Waits for a table to be 'disabled'. Disabled means that table is set as 'disabled'
3338   * @param table         Table to wait on.
3339   * @param timeoutMillis Time to wait on it being marked disabled.
3340   */
3341  public void waitTableDisabled(byte[] table, long timeoutMillis)
3342    throws InterruptedException, IOException {
3343    waitTableDisabled(TableName.valueOf(table), timeoutMillis);
3344  }
3345
3346  /**
3347   * Make sure that at least the specified number of region servers are running
3348   * @param num minimum number of region servers that should be running
3349   * @return true if we started some servers
3350   */
3351  public boolean ensureSomeRegionServersAvailable(final int num) throws IOException {
3352    boolean startedServer = false;
3353    MiniHBaseCluster hbaseCluster = getMiniHBaseCluster();
3354    for (int i = hbaseCluster.getLiveRegionServerThreads().size(); i < num; ++i) {
3355      LOG.info("Started new server=" + hbaseCluster.startRegionServer());
3356      startedServer = true;
3357    }
3358
3359    return startedServer;
3360  }
3361
3362  /**
3363   * Make sure that at least the specified number of region servers are running. We don't count the
3364   * ones that are currently stopping or are stopped.
3365   * @param num minimum number of region servers that should be running
3366   * @return true if we started some servers
3367   */
3368  public boolean ensureSomeNonStoppedRegionServersAvailable(final int num) throws IOException {
3369    boolean startedServer = ensureSomeRegionServersAvailable(num);
3370
3371    int nonStoppedServers = 0;
3372    for (JVMClusterUtil.RegionServerThread rst : getMiniHBaseCluster().getRegionServerThreads()) {
3373
3374      HRegionServer hrs = rst.getRegionServer();
3375      if (hrs.isStopping() || hrs.isStopped()) {
3376        LOG.info("A region server is stopped or stopping:" + hrs);
3377      } else {
3378        nonStoppedServers++;
3379      }
3380    }
3381    for (int i = nonStoppedServers; i < num; ++i) {
3382      LOG.info("Started new server=" + getMiniHBaseCluster().startRegionServer());
3383      startedServer = true;
3384    }
3385    return startedServer;
3386  }
3387
3388  /**
3389   * This method clones the passed <code>c</code> configuration setting a new user into the clone.
3390   * Use it getting new instances of FileSystem. Only works for DistributedFileSystem w/o Kerberos.
3391   * @param c                     Initial configuration
3392   * @param differentiatingSuffix Suffix to differentiate this user from others.
3393   * @return A new configuration instance with a different user set into it.
3394   */
3395  public static User getDifferentUser(final Configuration c, final String differentiatingSuffix)
3396    throws IOException {
3397    FileSystem currentfs = FileSystem.get(c);
3398    if (!(currentfs instanceof DistributedFileSystem) || User.isHBaseSecurityEnabled(c)) {
3399      return User.getCurrent();
3400    }
3401    // Else distributed filesystem. Make a new instance per daemon. Below
3402    // code is taken from the AppendTestUtil over in hdfs.
3403    String username = User.getCurrent().getName() + differentiatingSuffix;
3404    User user = User.createUserForTesting(c, username, new String[] { "supergroup" });
3405    return user;
3406  }
3407
3408  public static NavigableSet<String> getAllOnlineRegions(MiniHBaseCluster cluster)
3409    throws IOException {
3410    NavigableSet<String> online = new TreeSet<>();
3411    for (RegionServerThread rst : cluster.getLiveRegionServerThreads()) {
3412      try {
3413        for (RegionInfo region : ProtobufUtil
3414          .getOnlineRegions(rst.getRegionServer().getRSRpcServices())) {
3415          online.add(region.getRegionNameAsString());
3416        }
3417      } catch (RegionServerStoppedException e) {
3418        // That's fine.
3419      }
3420    }
3421    for (MasterThread mt : cluster.getLiveMasterThreads()) {
3422      try {
3423        for (RegionInfo region : ProtobufUtil.getOnlineRegions(mt.getMaster().getRSRpcServices())) {
3424          online.add(region.getRegionNameAsString());
3425        }
3426      } catch (RegionServerStoppedException e) {
3427        // That's fine.
3428      } catch (ServerNotRunningYetException e) {
3429        // That's fine.
3430      }
3431    }
3432    return online;
3433  }
3434
3435  /**
3436   * Set maxRecoveryErrorCount in DFSClient. In 0.20 pre-append its hard-coded to 5 and makes tests
3437   * linger. Here is the exception you'll see:
3438   *
3439   * <pre>
3440   * 2010-06-15 11:52:28,511 WARN  [DataStreamer for file /hbase/.logs/wal.1276627923013 block
3441   * blk_928005470262850423_1021] hdfs.DFSClient$DFSOutputStream(2657): Error Recovery for block
3442   * blk_928005470262850423_1021 failed  because recovery from primary datanode 127.0.0.1:53683
3443   * failed 4 times.  Pipeline was 127.0.0.1:53687, 127.0.0.1:53683. Will retry...
3444   * </pre>
3445   *
3446   * @param stream A DFSClient.DFSOutputStream.
3447   */
3448  public static void setMaxRecoveryErrorCount(final OutputStream stream, final int max) {
3449    try {
3450      Class<?>[] clazzes = DFSClient.class.getDeclaredClasses();
3451      for (Class<?> clazz : clazzes) {
3452        String className = clazz.getSimpleName();
3453        if (className.equals("DFSOutputStream")) {
3454          if (clazz.isInstance(stream)) {
3455            Field maxRecoveryErrorCountField =
3456              stream.getClass().getDeclaredField("maxRecoveryErrorCount");
3457            maxRecoveryErrorCountField.setAccessible(true);
3458            maxRecoveryErrorCountField.setInt(stream, max);
3459            break;
3460          }
3461        }
3462      }
3463    } catch (Exception e) {
3464      LOG.info("Could not set max recovery field", e);
3465    }
3466  }
3467
3468  /**
3469   * Uses directly the assignment manager to assign the region. and waits until the specified region
3470   * has completed assignment.
3471   * @return true if the region is assigned false otherwise.
3472   */
3473  public boolean assignRegion(final RegionInfo regionInfo)
3474    throws IOException, InterruptedException {
3475    final AssignmentManager am = getHBaseCluster().getMaster().getAssignmentManager();
3476    am.assign(regionInfo);
3477    return AssignmentTestingUtil.waitForAssignment(am, regionInfo);
3478  }
3479
3480  /**
3481   * Move region to destination server and wait till region is completely moved and online
3482   * @param destRegion region to move
3483   * @param destServer destination server of the region
3484   */
3485  public void moveRegionAndWait(RegionInfo destRegion, ServerName destServer)
3486    throws InterruptedException, IOException {
3487    HMaster master = getMiniHBaseCluster().getMaster();
3488    // TODO: Here we start the move. The move can take a while.
3489    getAdmin().move(destRegion.getEncodedNameAsBytes(), destServer);
3490    while (true) {
3491      ServerName serverName =
3492        master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(destRegion);
3493      if (serverName != null && serverName.equals(destServer)) {
3494        assertRegionOnServer(destRegion, serverName, 2000);
3495        break;
3496      }
3497      Thread.sleep(10);
3498    }
3499  }
3500
3501  /**
3502   * Wait until all regions for a table in hbase:meta have a non-empty info:server, up to a
3503   * configuable timeout value (default is 60 seconds) This means all regions have been deployed,
3504   * master has been informed and updated hbase:meta with the regions deployed server.
3505   * @param tableName the table name
3506   */
3507  public void waitUntilAllRegionsAssigned(final TableName tableName) throws IOException {
3508    waitUntilAllRegionsAssigned(tableName,
3509      this.conf.getLong("hbase.client.sync.wait.timeout.msec", 60000));
3510  }
3511
3512  /**
3513   * Waith until all system table's regions get assigned
3514   */
3515  public void waitUntilAllSystemRegionsAssigned() throws IOException {
3516    waitUntilAllRegionsAssigned(TableName.META_TABLE_NAME);
3517    waitUntilAllRegionsAssigned(TableName.NAMESPACE_TABLE_NAME);
3518  }
3519
3520  /**
3521   * Wait until all regions for a table in hbase:meta have a non-empty info:server, or until
3522   * timeout. This means all regions have been deployed, master has been informed and updated
3523   * hbase:meta with the regions deployed server.
3524   * @param tableName the table name
3525   * @param timeout   timeout, in milliseconds
3526   */
3527  public void waitUntilAllRegionsAssigned(final TableName tableName, final long timeout)
3528    throws IOException {
3529    if (!TableName.isMetaTableName(tableName)) {
3530      try (final Table meta = getConnection().getTable(TableName.META_TABLE_NAME)) {
3531        LOG.debug("Waiting until all regions of table " + tableName + " get assigned. Timeout = "
3532          + timeout + "ms");
3533        waitFor(timeout, 200, true, new ExplainingPredicate<IOException>() {
3534          @Override
3535          public String explainFailure() throws IOException {
3536            return explainTableAvailability(tableName);
3537          }
3538
3539          @Override
3540          public boolean evaluate() throws IOException {
3541            Scan scan = new Scan();
3542            scan.addFamily(HConstants.CATALOG_FAMILY);
3543            boolean tableFound = false;
3544            try (ResultScanner s = meta.getScanner(scan)) {
3545              for (Result r; (r = s.next()) != null;) {
3546                byte[] b = r.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
3547                HRegionInfo info = HRegionInfo.parseFromOrNull(b);
3548                if (info != null && info.getTable().equals(tableName)) {
3549                  // Get server hosting this region from catalog family. Return false if no server
3550                  // hosting this region, or if the server hosting this region was recently killed
3551                  // (for fault tolerance testing).
3552                  tableFound = true;
3553                  byte[] server =
3554                    r.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
3555                  if (server == null) {
3556                    return false;
3557                  } else {
3558                    byte[] startCode =
3559                      r.getValue(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER);
3560                    ServerName serverName =
3561                      ServerName.valueOf(Bytes.toString(server).replaceFirst(":", ",") + ","
3562                        + Bytes.toLong(startCode));
3563                    if (
3564                      !getHBaseClusterInterface().isDistributedCluster()
3565                        && getHBaseCluster().isKilledRS(serverName)
3566                    ) {
3567                      return false;
3568                    }
3569                  }
3570                  if (RegionStateStore.getRegionState(r, info) != RegionState.State.OPEN) {
3571                    return false;
3572                  }
3573                }
3574              }
3575            }
3576            if (!tableFound) {
3577              LOG.warn(
3578                "Didn't find the entries for table " + tableName + " in meta, already deleted?");
3579            }
3580            return tableFound;
3581          }
3582        });
3583      }
3584    }
3585    LOG.info("All regions for table " + tableName + " assigned to meta. Checking AM states.");
3586    // check from the master state if we are using a mini cluster
3587    if (!getHBaseClusterInterface().isDistributedCluster()) {
3588      // So, all regions are in the meta table but make sure master knows of the assignments before
3589      // returning -- sometimes this can lag.
3590      HMaster master = getHBaseCluster().getMaster();
3591      final RegionStates states = master.getAssignmentManager().getRegionStates();
3592      waitFor(timeout, 200, new ExplainingPredicate<IOException>() {
3593        @Override
3594        public String explainFailure() throws IOException {
3595          return explainTableAvailability(tableName);
3596        }
3597
3598        @Override
3599        public boolean evaluate() throws IOException {
3600          List<RegionInfo> hris = states.getRegionsOfTable(tableName);
3601          return hris != null && !hris.isEmpty();
3602        }
3603      });
3604    }
3605    LOG.info("All regions for table " + tableName + " assigned.");
3606  }
3607
3608  /**
3609   * Do a small get/scan against one store. This is required because store has no actual methods of
3610   * querying itself, and relies on StoreScanner.
3611   */
3612  public static List<Cell> getFromStoreFile(HStore store, Get get) throws IOException {
3613    Scan scan = new Scan(get);
3614    InternalScanner scanner = (InternalScanner) store.getScanner(scan,
3615      scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()),
3616      // originally MultiVersionConcurrencyControl.resetThreadReadPoint() was called to set
3617      // readpoint 0.
3618      0);
3619
3620    List<Cell> result = new ArrayList<>();
3621    scanner.next(result);
3622    if (!result.isEmpty()) {
3623      // verify that we are on the row we want:
3624      Cell kv = result.get(0);
3625      if (!CellUtil.matchingRows(kv, get.getRow())) {
3626        result.clear();
3627      }
3628    }
3629    scanner.close();
3630    return result;
3631  }
3632
3633  /**
3634   * Create region split keys between startkey and endKey
3635   * @param numRegions the number of regions to be created. it has to be greater than 3.
3636   * @return resulting split keys
3637   */
3638  public byte[][] getRegionSplitStartKeys(byte[] startKey, byte[] endKey, int numRegions) {
3639    assertTrue(numRegions > 3);
3640    byte[][] tmpSplitKeys = Bytes.split(startKey, endKey, numRegions - 3);
3641    byte[][] result = new byte[tmpSplitKeys.length + 1][];
3642    System.arraycopy(tmpSplitKeys, 0, result, 1, tmpSplitKeys.length);
3643    result[0] = HConstants.EMPTY_BYTE_ARRAY;
3644    return result;
3645  }
3646
3647  /**
3648   * Do a small get/scan against one store. This is required because store has no actual methods of
3649   * querying itself, and relies on StoreScanner.
3650   */
3651  public static List<Cell> getFromStoreFile(HStore store, byte[] row, NavigableSet<byte[]> columns)
3652    throws IOException {
3653    Get get = new Get(row);
3654    Map<byte[], NavigableSet<byte[]>> s = get.getFamilyMap();
3655    s.put(store.getColumnFamilyDescriptor().getName(), columns);
3656
3657    return getFromStoreFile(store, get);
3658  }
3659
3660  public static void assertKVListsEqual(String additionalMsg, final List<? extends Cell> expected,
3661    final List<? extends Cell> actual) {
3662    final int eLen = expected.size();
3663    final int aLen = actual.size();
3664    final int minLen = Math.min(eLen, aLen);
3665
3666    int i;
3667    for (i = 0; i < minLen
3668      && CellComparator.getInstance().compare(expected.get(i), actual.get(i)) == 0; ++i) {
3669    }
3670
3671    if (additionalMsg == null) {
3672      additionalMsg = "";
3673    }
3674    if (!additionalMsg.isEmpty()) {
3675      additionalMsg = ". " + additionalMsg;
3676    }
3677
3678    if (eLen != aLen || i != minLen) {
3679      throw new AssertionError("Expected and actual KV arrays differ at position " + i + ": "
3680        + safeGetAsStr(expected, i) + " (length " + eLen + ") vs. " + safeGetAsStr(actual, i)
3681        + " (length " + aLen + ")" + additionalMsg);
3682    }
3683  }
3684
3685  public static <T> String safeGetAsStr(List<T> lst, int i) {
3686    if (0 <= i && i < lst.size()) {
3687      return lst.get(i).toString();
3688    } else {
3689      return "<out_of_range>";
3690    }
3691  }
3692
3693  public String getClusterKey() {
3694    return conf.get(HConstants.ZOOKEEPER_QUORUM) + ":" + conf.get(HConstants.ZOOKEEPER_CLIENT_PORT)
3695      + ":"
3696      + conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT, HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
3697  }
3698
3699  /** Creates a random table with the given parameters */
3700  public Table createRandomTable(TableName tableName, final Collection<String> families,
3701    final int maxVersions, final int numColsPerRow, final int numFlushes, final int numRegions,
3702    final int numRowsPerFlush) throws IOException, InterruptedException {
3703
3704    LOG.info("\n\nCreating random table " + tableName + " with " + numRegions + " regions, "
3705      + numFlushes + " storefiles per region, " + numRowsPerFlush + " rows per flush, maxVersions="
3706      + maxVersions + "\n");
3707
3708    final int numCF = families.size();
3709    final byte[][] cfBytes = new byte[numCF][];
3710    {
3711      int cfIndex = 0;
3712      for (String cf : families) {
3713        cfBytes[cfIndex++] = Bytes.toBytes(cf);
3714      }
3715    }
3716
3717    final int actualStartKey = 0;
3718    final int actualEndKey = Integer.MAX_VALUE;
3719    final int keysPerRegion = (actualEndKey - actualStartKey) / numRegions;
3720    final int splitStartKey = actualStartKey + keysPerRegion;
3721    final int splitEndKey = actualEndKey - keysPerRegion;
3722    final String keyFormat = "%08x";
3723    final Table table = createTable(tableName, cfBytes, maxVersions,
3724      Bytes.toBytes(String.format(keyFormat, splitStartKey)),
3725      Bytes.toBytes(String.format(keyFormat, splitEndKey)), numRegions);
3726
3727    if (hbaseCluster != null) {
3728      getMiniHBaseCluster().flushcache(TableName.META_TABLE_NAME);
3729    }
3730
3731    BufferedMutator mutator = getConnection().getBufferedMutator(tableName);
3732
3733    final Random rand = ThreadLocalRandom.current();
3734    for (int iFlush = 0; iFlush < numFlushes; ++iFlush) {
3735      for (int iRow = 0; iRow < numRowsPerFlush; ++iRow) {
3736        final byte[] row = Bytes.toBytes(
3737          String.format(keyFormat, actualStartKey + rand.nextInt(actualEndKey - actualStartKey)));
3738
3739        Put put = new Put(row);
3740        Delete del = new Delete(row);
3741        for (int iCol = 0; iCol < numColsPerRow; ++iCol) {
3742          final byte[] cf = cfBytes[rand.nextInt(numCF)];
3743          final long ts = rand.nextInt();
3744          final byte[] qual = Bytes.toBytes("col" + iCol);
3745          if (rand.nextBoolean()) {
3746            final byte[] value =
3747              Bytes.toBytes("value_for_row_" + iRow + "_cf_" + Bytes.toStringBinary(cf) + "_col_"
3748                + iCol + "_ts_" + ts + "_random_" + rand.nextLong());
3749            put.addColumn(cf, qual, ts, value);
3750          } else if (rand.nextDouble() < 0.8) {
3751            del.addColumn(cf, qual, ts);
3752          } else {
3753            del.addColumns(cf, qual, ts);
3754          }
3755        }
3756
3757        if (!put.isEmpty()) {
3758          mutator.mutate(put);
3759        }
3760
3761        if (!del.isEmpty()) {
3762          mutator.mutate(del);
3763        }
3764      }
3765      LOG.info("Initiating flush #" + iFlush + " for table " + tableName);
3766      mutator.flush();
3767      if (hbaseCluster != null) {
3768        getMiniHBaseCluster().flushcache(table.getName());
3769      }
3770    }
3771    mutator.close();
3772
3773    return table;
3774  }
3775
3776  public static int randomFreePort() {
3777    return HBaseCommonTestingUtility.randomFreePort();
3778  }
3779
3780  public static String randomMultiCastAddress() {
3781    return "226.1.1." + ThreadLocalRandom.current().nextInt(254);
3782  }
3783
3784  public static void waitForHostPort(String host, int port) throws IOException {
3785    final int maxTimeMs = 10000;
3786    final int maxNumAttempts = maxTimeMs / HConstants.SOCKET_RETRY_WAIT_MS;
3787    IOException savedException = null;
3788    LOG.info("Waiting for server at " + host + ":" + port);
3789    for (int attempt = 0; attempt < maxNumAttempts; ++attempt) {
3790      try {
3791        Socket sock = new Socket(InetAddress.getByName(host), port);
3792        sock.close();
3793        savedException = null;
3794        LOG.info("Server at " + host + ":" + port + " is available");
3795        break;
3796      } catch (UnknownHostException e) {
3797        throw new IOException("Failed to look up " + host, e);
3798      } catch (IOException e) {
3799        savedException = e;
3800      }
3801      Threads.sleepWithoutInterrupt(HConstants.SOCKET_RETRY_WAIT_MS);
3802    }
3803
3804    if (savedException != null) {
3805      throw savedException;
3806    }
3807  }
3808
3809  /**
3810   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3811   * continues.
3812   * @return the number of regions the table was split into
3813   */
3814  public static int createPreSplitLoadTestTable(Configuration conf, TableName tableName,
3815    byte[] columnFamily, Algorithm compression, DataBlockEncoding dataBlockEncoding)
3816    throws IOException {
3817    return createPreSplitLoadTestTable(conf, tableName, columnFamily, compression,
3818      dataBlockEncoding, DEFAULT_REGIONS_PER_SERVER, 1, Durability.USE_DEFAULT);
3819  }
3820
3821  /**
3822   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3823   * continues.
3824   * @return the number of regions the table was split into
3825   */
3826  public static int createPreSplitLoadTestTable(Configuration conf, TableName tableName,
3827    byte[] columnFamily, Algorithm compression, DataBlockEncoding dataBlockEncoding,
3828    int numRegionsPerServer, int regionReplication, Durability durability) throws IOException {
3829    HTableDescriptor desc = new HTableDescriptor(tableName);
3830    desc.setDurability(durability);
3831    desc.setRegionReplication(regionReplication);
3832    HColumnDescriptor hcd = new HColumnDescriptor(columnFamily);
3833    hcd.setDataBlockEncoding(dataBlockEncoding);
3834    hcd.setCompressionType(compression);
3835    return createPreSplitLoadTestTable(conf, desc, hcd, numRegionsPerServer);
3836  }
3837
3838  /**
3839   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3840   * continues.
3841   * @return the number of regions the table was split into
3842   */
3843  public static int createPreSplitLoadTestTable(Configuration conf, TableName tableName,
3844    byte[][] columnFamilies, Algorithm compression, DataBlockEncoding dataBlockEncoding,
3845    int numRegionsPerServer, int regionReplication, Durability durability) throws IOException {
3846    HTableDescriptor desc = new HTableDescriptor(tableName);
3847    desc.setDurability(durability);
3848    desc.setRegionReplication(regionReplication);
3849    HColumnDescriptor[] hcds = new HColumnDescriptor[columnFamilies.length];
3850    for (int i = 0; i < columnFamilies.length; i++) {
3851      HColumnDescriptor hcd = new HColumnDescriptor(columnFamilies[i]);
3852      hcd.setDataBlockEncoding(dataBlockEncoding);
3853      hcd.setCompressionType(compression);
3854      hcds[i] = hcd;
3855    }
3856    return createPreSplitLoadTestTable(conf, desc, hcds, numRegionsPerServer);
3857  }
3858
3859  /**
3860   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3861   * continues.
3862   * @return the number of regions the table was split into
3863   */
3864  public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor desc,
3865    ColumnFamilyDescriptor hcd) throws IOException {
3866    return createPreSplitLoadTestTable(conf, desc, hcd, DEFAULT_REGIONS_PER_SERVER);
3867  }
3868
3869  /**
3870   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3871   * continues.
3872   * @return the number of regions the table was split into
3873   */
3874  public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor desc,
3875    ColumnFamilyDescriptor hcd, int numRegionsPerServer) throws IOException {
3876    return createPreSplitLoadTestTable(conf, desc, new ColumnFamilyDescriptor[] { hcd },
3877      numRegionsPerServer);
3878  }
3879
3880  /**
3881   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3882   * continues.
3883   * @return the number of regions the table was split into
3884   */
3885  public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor desc,
3886    ColumnFamilyDescriptor[] hcds, int numRegionsPerServer) throws IOException {
3887    return createPreSplitLoadTestTable(conf, desc, hcds, new RegionSplitter.HexStringSplit(),
3888      numRegionsPerServer);
3889  }
3890
3891  /**
3892   * Creates a pre-split table for load testing. If the table already exists, logs a warning and
3893   * continues.
3894   * @return the number of regions the table was split into
3895   */
3896  public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor td,
3897    ColumnFamilyDescriptor[] cds, SplitAlgorithm splitter, int numRegionsPerServer)
3898    throws IOException {
3899    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(td);
3900    for (ColumnFamilyDescriptor cd : cds) {
3901      if (!td.hasColumnFamily(cd.getName())) {
3902        builder.setColumnFamily(cd);
3903      }
3904    }
3905    td = builder.build();
3906    int totalNumberOfRegions = 0;
3907    Connection unmanagedConnection = ConnectionFactory.createConnection(conf);
3908    Admin admin = unmanagedConnection.getAdmin();
3909
3910    try {
3911      // create a table a pre-splits regions.
3912      // The number of splits is set as:
3913      // region servers * regions per region server).
3914      int numberOfServers = admin.getRegionServers().size();
3915      if (numberOfServers == 0) {
3916        throw new IllegalStateException("No live regionservers");
3917      }
3918
3919      totalNumberOfRegions = numberOfServers * numRegionsPerServer;
3920      LOG.info("Number of live regionservers: " + numberOfServers + ", "
3921        + "pre-splitting table into " + totalNumberOfRegions + " regions " + "(regions per server: "
3922        + numRegionsPerServer + ")");
3923
3924      byte[][] splits = splitter.split(totalNumberOfRegions);
3925
3926      admin.createTable(td, splits);
3927    } catch (MasterNotRunningException e) {
3928      LOG.error("Master not running", e);
3929      throw new IOException(e);
3930    } catch (TableExistsException e) {
3931      LOG.warn("Table " + td.getTableName() + " already exists, continuing");
3932    } finally {
3933      admin.close();
3934      unmanagedConnection.close();
3935    }
3936    return totalNumberOfRegions;
3937  }
3938
3939  public static int getMetaRSPort(Connection connection) throws IOException {
3940    try (RegionLocator locator = connection.getRegionLocator(TableName.META_TABLE_NAME)) {
3941      return locator.getRegionLocation(Bytes.toBytes("")).getPort();
3942    }
3943  }
3944
3945  /**
3946   * Due to async racing issue, a region may not be in the online region list of a region server
3947   * yet, after the assignment znode is deleted and the new assignment is recorded in master.
3948   */
3949  public void assertRegionOnServer(final RegionInfo hri, final ServerName server,
3950    final long timeout) throws IOException, InterruptedException {
3951    long timeoutTime = EnvironmentEdgeManager.currentTime() + timeout;
3952    while (true) {
3953      List<RegionInfo> regions = getAdmin().getRegions(server);
3954      if (regions.stream().anyMatch(r -> RegionInfo.COMPARATOR.compare(r, hri) == 0)) return;
3955      long now = EnvironmentEdgeManager.currentTime();
3956      if (now > timeoutTime) break;
3957      Thread.sleep(10);
3958    }
3959    fail("Could not find region " + hri.getRegionNameAsString() + " on server " + server);
3960  }
3961
3962  /**
3963   * Check to make sure the region is open on the specified region server, but not on any other one.
3964   */
3965  public void assertRegionOnlyOnServer(final RegionInfo hri, final ServerName server,
3966    final long timeout) throws IOException, InterruptedException {
3967    long timeoutTime = EnvironmentEdgeManager.currentTime() + timeout;
3968    while (true) {
3969      List<RegionInfo> regions = getAdmin().getRegions(server);
3970      if (regions.stream().anyMatch(r -> RegionInfo.COMPARATOR.compare(r, hri) == 0)) {
3971        List<JVMClusterUtil.RegionServerThread> rsThreads =
3972          getHBaseCluster().getLiveRegionServerThreads();
3973        for (JVMClusterUtil.RegionServerThread rsThread : rsThreads) {
3974          HRegionServer rs = rsThread.getRegionServer();
3975          if (server.equals(rs.getServerName())) {
3976            continue;
3977          }
3978          Collection<HRegion> hrs = rs.getOnlineRegionsLocalContext();
3979          for (HRegion r : hrs) {
3980            assertTrue("Region should not be double assigned",
3981              r.getRegionInfo().getRegionId() != hri.getRegionId());
3982          }
3983        }
3984        return; // good, we are happy
3985      }
3986      long now = EnvironmentEdgeManager.currentTime();
3987      if (now > timeoutTime) break;
3988      Thread.sleep(10);
3989    }
3990    fail("Could not find region " + hri.getRegionNameAsString() + " on server " + server);
3991  }
3992
3993  public HRegion createTestRegion(String tableName, ColumnFamilyDescriptor cd) throws IOException {
3994    TableDescriptor td =
3995      TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName)).setColumnFamily(cd).build();
3996    RegionInfo info = RegionInfoBuilder.newBuilder(TableName.valueOf(tableName)).build();
3997    return createRegionAndWAL(info, getDataTestDir(), getConfiguration(), td);
3998  }
3999
4000  public HRegion createTestRegion(String tableName, ColumnFamilyDescriptor cd,
4001    BlockCache blockCache) throws IOException {
4002    TableDescriptor td =
4003      TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName)).setColumnFamily(cd).build();
4004    RegionInfo info = RegionInfoBuilder.newBuilder(TableName.valueOf(tableName)).build();
4005    return createRegionAndWAL(info, getDataTestDir(), getConfiguration(), td, blockCache);
4006  }
4007
4008  public void setFileSystemURI(String fsURI) {
4009    FS_URI = fsURI;
4010  }
4011
4012  /**
4013   * Returns a {@link Predicate} for checking that there are no regions in transition in master
4014   */
4015  public ExplainingPredicate<IOException> predicateNoRegionsInTransition() {
4016    return new ExplainingPredicate<IOException>() {
4017      @Override
4018      public String explainFailure() throws IOException {
4019        final RegionStates regionStates =
4020          getMiniHBaseCluster().getMaster().getAssignmentManager().getRegionStates();
4021        return "found in transition: " + regionStates.getRegionsInTransition().toString();
4022      }
4023
4024      @Override
4025      public boolean evaluate() throws IOException {
4026        HMaster master = getMiniHBaseCluster().getMaster();
4027        if (master == null) return false;
4028        AssignmentManager am = master.getAssignmentManager();
4029        if (am == null) return false;
4030        return !am.hasRegionsInTransition();
4031      }
4032    };
4033  }
4034
4035  /**
4036   * Returns a {@link Predicate} for checking that table is enabled
4037   */
4038  public Waiter.Predicate<IOException> predicateTableEnabled(final TableName tableName) {
4039    return new ExplainingPredicate<IOException>() {
4040      @Override
4041      public String explainFailure() throws IOException {
4042        return explainTableState(tableName, TableState.State.ENABLED);
4043      }
4044
4045      @Override
4046      public boolean evaluate() throws IOException {
4047        return getAdmin().tableExists(tableName) && getAdmin().isTableEnabled(tableName);
4048      }
4049    };
4050  }
4051
4052  /**
4053   * Returns a {@link Predicate} for checking that table is enabled
4054   */
4055  public Waiter.Predicate<IOException> predicateTableDisabled(final TableName tableName) {
4056    return new ExplainingPredicate<IOException>() {
4057      @Override
4058      public String explainFailure() throws IOException {
4059        return explainTableState(tableName, TableState.State.DISABLED);
4060      }
4061
4062      @Override
4063      public boolean evaluate() throws IOException {
4064        return getAdmin().isTableDisabled(tableName);
4065      }
4066    };
4067  }
4068
4069  /**
4070   * Returns a {@link Predicate} for checking that table is enabled
4071   */
4072  public Waiter.Predicate<IOException> predicateTableAvailable(final TableName tableName) {
4073    return new ExplainingPredicate<IOException>() {
4074      @Override
4075      public String explainFailure() throws IOException {
4076        return explainTableAvailability(tableName);
4077      }
4078
4079      @Override
4080      public boolean evaluate() throws IOException {
4081        boolean tableAvailable = getAdmin().isTableAvailable(tableName);
4082        if (tableAvailable) {
4083          try (Table table = getConnection().getTable(tableName)) {
4084            TableDescriptor htd = table.getDescriptor();
4085            for (HRegionLocation loc : getConnection().getRegionLocator(tableName)
4086              .getAllRegionLocations()) {
4087              Scan scan = new Scan().withStartRow(loc.getRegionInfo().getStartKey())
4088                .withStopRow(loc.getRegionInfo().getEndKey()).setOneRowLimit()
4089                .setMaxResultsPerColumnFamily(1).setCacheBlocks(false);
4090              for (byte[] family : htd.getColumnFamilyNames()) {
4091                scan.addFamily(family);
4092              }
4093              try (ResultScanner scanner = table.getScanner(scan)) {
4094                scanner.next();
4095              }
4096            }
4097          }
4098        }
4099        return tableAvailable;
4100      }
4101    };
4102  }
4103
4104  /**
4105   * Wait until no regions in transition.
4106   * @param timeout How long to wait.
4107   */
4108  public void waitUntilNoRegionsInTransition(final long timeout) throws IOException {
4109    waitFor(timeout, predicateNoRegionsInTransition());
4110  }
4111
4112  /**
4113   * Wait until no regions in transition. (time limit 15min)
4114   */
4115  public void waitUntilNoRegionsInTransition() throws IOException {
4116    waitUntilNoRegionsInTransition(15 * 60000);
4117  }
4118
4119  /**
4120   * Wait until labels is ready in VisibilityLabelsCache.
4121   */
4122  public void waitLabelAvailable(long timeoutMillis, final String... labels) {
4123    final VisibilityLabelsCache labelsCache = VisibilityLabelsCache.get();
4124    waitFor(timeoutMillis, new Waiter.ExplainingPredicate<RuntimeException>() {
4125
4126      @Override
4127      public boolean evaluate() {
4128        for (String label : labels) {
4129          if (labelsCache.getLabelOrdinal(label) == 0) {
4130            return false;
4131          }
4132        }
4133        return true;
4134      }
4135
4136      @Override
4137      public String explainFailure() {
4138        for (String label : labels) {
4139          if (labelsCache.getLabelOrdinal(label) == 0) {
4140            return label + " is not available yet";
4141          }
4142        }
4143        return "";
4144      }
4145    });
4146  }
4147
4148  /**
4149   * Create a set of column descriptors with the combination of compression, encoding, bloom codecs
4150   * available.
4151   * @return the list of column descriptors
4152   */
4153  public static List<HColumnDescriptor> generateColumnDescriptors() {
4154    return generateColumnDescriptors("");
4155  }
4156
4157  /**
4158   * Create a set of column descriptors with the combination of compression, encoding, bloom codecs
4159   * available.
4160   * @param prefix family names prefix
4161   * @return the list of column descriptors
4162   */
4163  public static List<HColumnDescriptor> generateColumnDescriptors(final String prefix) {
4164    List<HColumnDescriptor> htds = new ArrayList<>();
4165    long familyId = 0;
4166    for (Compression.Algorithm compressionType : getSupportedCompressionAlgorithms()) {
4167      for (DataBlockEncoding encodingType : DataBlockEncoding.values()) {
4168        for (BloomType bloomType : BloomType.values()) {
4169          String name = String.format("%s-cf-!@#&-%d!@#", prefix, familyId);
4170          HColumnDescriptor htd = new HColumnDescriptor(name);
4171          htd.setCompressionType(compressionType);
4172          htd.setDataBlockEncoding(encodingType);
4173          htd.setBloomFilterType(bloomType);
4174          htds.add(htd);
4175          familyId++;
4176        }
4177      }
4178    }
4179    return htds;
4180  }
4181
4182  /**
4183   * Get supported compression algorithms.
4184   * @return supported compression algorithms.
4185   */
4186  public static Compression.Algorithm[] getSupportedCompressionAlgorithms() {
4187    String[] allAlgos = HFile.getSupportedCompressionAlgorithms();
4188    List<Compression.Algorithm> supportedAlgos = new ArrayList<>();
4189    for (String algoName : allAlgos) {
4190      try {
4191        Compression.Algorithm algo = Compression.getCompressionAlgorithmByName(algoName);
4192        algo.getCompressor();
4193        supportedAlgos.add(algo);
4194      } catch (Throwable t) {
4195        // this algo is not available
4196      }
4197    }
4198    return supportedAlgos.toArray(new Algorithm[supportedAlgos.size()]);
4199  }
4200
4201  public Result getClosestRowBefore(Region r, byte[] row, byte[] family) throws IOException {
4202    Scan scan = new Scan(row);
4203    scan.setSmall(true);
4204    scan.setCaching(1);
4205    scan.setReversed(true);
4206    scan.addFamily(family);
4207    try (RegionScanner scanner = r.getScanner(scan)) {
4208      List<Cell> cells = new ArrayList<>(1);
4209      scanner.next(cells);
4210      if (r.getRegionInfo().isMetaRegion() && !isTargetTable(row, cells.get(0))) {
4211        return null;
4212      }
4213      return Result.create(cells);
4214    }
4215  }
4216
4217  private boolean isTargetTable(final byte[] inRow, Cell c) {
4218    String inputRowString = Bytes.toString(inRow);
4219    int i = inputRowString.indexOf(HConstants.DELIMITER);
4220    String outputRowString = Bytes.toString(c.getRowArray(), c.getRowOffset(), c.getRowLength());
4221    int o = outputRowString.indexOf(HConstants.DELIMITER);
4222    return inputRowString.substring(0, i).equals(outputRowString.substring(0, o));
4223  }
4224
4225  /**
4226   * Sets up {@link MiniKdc} for testing security. Uses {@link HBaseKerberosUtils} to set the given
4227   * keytab file as {@link HBaseKerberosUtils#KRB_KEYTAB_FILE}. FYI, there is also the easier-to-use
4228   * kerby KDC server and utility for using it,
4229   * {@link org.apache.hadoop.hbase.util.SimpleKdcServerUtil}. The kerby KDC server is preferred;
4230   * less baggage. It came in in HBASE-5291.
4231   */
4232  public MiniKdc setupMiniKdc(File keytabFile) throws Exception {
4233    Properties conf = MiniKdc.createConf();
4234    conf.put(MiniKdc.DEBUG, true);
4235    MiniKdc kdc = null;
4236    File dir = null;
4237    // There is time lag between selecting a port and trying to bind with it. It's possible that
4238    // another service captures the port in between which'll result in BindException.
4239    boolean bindException;
4240    int numTries = 0;
4241    do {
4242      try {
4243        bindException = false;
4244        dir = new File(getDataTestDir("kdc").toUri().getPath());
4245        kdc = new MiniKdc(conf, dir);
4246        kdc.start();
4247      } catch (BindException e) {
4248        FileUtils.deleteDirectory(dir); // clean directory
4249        numTries++;
4250        if (numTries == 3) {
4251          LOG.error("Failed setting up MiniKDC. Tried " + numTries + " times.");
4252          throw e;
4253        }
4254        LOG.error("BindException encountered when setting up MiniKdc. Trying again.");
4255        bindException = true;
4256      }
4257    } while (bindException);
4258    HBaseKerberosUtils.setKeytabFileForTesting(keytabFile.getAbsolutePath());
4259    return kdc;
4260  }
4261
4262  public int getNumHFiles(final TableName tableName, final byte[] family) {
4263    int numHFiles = 0;
4264    for (RegionServerThread regionServerThread : getMiniHBaseCluster().getRegionServerThreads()) {
4265      numHFiles += getNumHFilesForRS(regionServerThread.getRegionServer(), tableName, family);
4266    }
4267    return numHFiles;
4268  }
4269
4270  public int getNumHFilesForRS(final HRegionServer rs, final TableName tableName,
4271    final byte[] family) {
4272    int numHFiles = 0;
4273    for (Region region : rs.getRegions(tableName)) {
4274      numHFiles += region.getStore(family).getStorefilesCount();
4275    }
4276    return numHFiles;
4277  }
4278
4279  public void verifyTableDescriptorIgnoreTableName(TableDescriptor ltd, TableDescriptor rtd) {
4280    assertEquals(ltd.getValues().hashCode(), rtd.getValues().hashCode());
4281    Collection<ColumnFamilyDescriptor> ltdFamilies = Arrays.asList(ltd.getColumnFamilies());
4282    Collection<ColumnFamilyDescriptor> rtdFamilies = Arrays.asList(rtd.getColumnFamilies());
4283    assertEquals(ltdFamilies.size(), rtdFamilies.size());
4284    for (Iterator<ColumnFamilyDescriptor> it = ltdFamilies.iterator(),
4285        it2 = rtdFamilies.iterator(); it.hasNext();) {
4286      assertEquals(0, ColumnFamilyDescriptor.COMPARATOR.compare(it.next(), it2.next()));
4287    }
4288  }
4289
4290  /**
4291   * Await the successful return of {@code condition}, sleeping {@code sleepMillis} between
4292   * invocations.
4293   */
4294  public static void await(final long sleepMillis, final BooleanSupplier condition)
4295    throws InterruptedException {
4296    try {
4297      while (!condition.getAsBoolean()) {
4298        Thread.sleep(sleepMillis);
4299      }
4300    } catch (RuntimeException e) {
4301      if (e.getCause() instanceof AssertionError) {
4302        throw (AssertionError) e.getCause();
4303      }
4304      throw e;
4305    }
4306  }
4307}