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