001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import java.util.Arrays;
021import java.util.List;
022import org.apache.commons.lang3.StringUtils;
023import org.apache.hadoop.hbase.master.HMaster;
024import org.apache.yetus.audience.InterfaceAudience;
025
026/**
027 * Options for starting up a mini cluster (including an hbase, dfs and zookeeper clusters) in test.
028 * The options include HDFS options to build mini dfs cluster, Zookeeper options to build mini zk
029 * cluster, and mostly HBase options to build mini hbase cluster. To create an object, use a
030 * {@link Builder}. Example usage:
031 *
032 * <pre>
033 *    StartMiniClusterOption option = StartMiniClusterOption.builder().
034 *        .numMasters(3).rsClass(MyRegionServer.class).createWALDir(true).build();
035 * </pre>
036 *
037 * Default values can be found in {@link Builder}.
038 */
039@InterfaceAudience.Public
040public final class StartMiniClusterOption {
041  /**
042   * Number of masters to start up. We'll start this many hbase masters. If numMasters > 1, you can
043   * find the active/primary master with {@link MiniHBaseCluster#getMaster()}.
044   */
045  private final int numMasters;
046
047  /**
048   * Number of masters that always remain standby. These set of masters never transition to active
049   * even if an active master does not exist. These are needed for testing scenarios where there are
050   * no active masters in the cluster but the cluster connection (backed by master registry) should
051   * still work.
052   */
053  private final int numAlwaysStandByMasters;
054  /**
055   * The class to use as HMaster, or null for default.
056   */
057  private final Class<? extends HMaster> masterClass;
058
059  /**
060   * Number of region servers to start up. If this value is > 1, then make sure config
061   * "hbase.regionserver.info.port" is -1 (i.e. no ui per regionserver) otherwise bind errors.
062   */
063  private final int numRegionServers;
064  /**
065   * Ports that RegionServer should use. Pass ports if you want to test cluster restart where for
066   * sure the regionservers come up on same address+port (but just with different startcode); by
067   * default mini hbase clusters choose new arbitrary ports on each cluster start.
068   */
069  private final List<Integer> rsPorts;
070  /**
071   * The class to use as HRegionServer, or null for default.
072   */
073  private Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass;
074
075  /**
076   * Number of datanodes. Used to create mini DSF cluster. Surpassed by {@link #dataNodeHosts} size.
077   */
078  private final int numDataNodes;
079  /**
080   * The hostnames of DataNodes to run on. This is useful if you want to run datanode on distinct
081   * hosts for things like HDFS block location verification. If you start MiniDFSCluster without
082   * host names, all instances of the datanodes will have the same host name.
083   */
084  private final String[] dataNodeHosts;
085
086  /**
087   * Number of Zookeeper servers.
088   */
089  private final int numZkServers;
090
091  /**
092   * Whether to create a new root or data directory path. If true, the newly created data directory
093   * will be configured as HBase rootdir. This will overwrite existing root directory config.
094   */
095  private final boolean createRootDir;
096
097  /**
098   * Whether to create a new WAL directory. If true, the newly created directory will be configured
099   * as HBase wal.dir which is separate from HBase rootdir.
100   */
101  private final boolean createWALDir;
102
103  /**
104   * Private constructor. Use {@link Builder#build()}.
105   */
106  private StartMiniClusterOption(int numMasters, int numAlwaysStandByMasters,
107    Class<? extends HMaster> masterClass, int numRegionServers, List<Integer> rsPorts,
108    Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass, int numDataNodes,
109    String[] dataNodeHosts, int numZkServers, boolean createRootDir, boolean createWALDir) {
110    this.numMasters = numMasters;
111    this.numAlwaysStandByMasters = numAlwaysStandByMasters;
112    this.masterClass = masterClass;
113    this.numRegionServers = numRegionServers;
114    this.rsPorts = rsPorts;
115    this.rsClass = rsClass;
116    this.numDataNodes = numDataNodes;
117    this.dataNodeHosts = dataNodeHosts;
118    this.numZkServers = numZkServers;
119    this.createRootDir = createRootDir;
120    this.createWALDir = createWALDir;
121  }
122
123  public int getNumMasters() {
124    return numMasters;
125  }
126
127  public int getNumAlwaysStandByMasters() {
128    return numAlwaysStandByMasters;
129  }
130
131  public Class<? extends HMaster> getMasterClass() {
132    return masterClass;
133  }
134
135  public int getNumRegionServers() {
136    return numRegionServers;
137  }
138
139  public List<Integer> getRsPorts() {
140    return rsPorts;
141  }
142
143  public Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> getRsClass() {
144    return rsClass;
145  }
146
147  public int getNumDataNodes() {
148    return numDataNodes;
149  }
150
151  public String[] getDataNodeHosts() {
152    return dataNodeHosts;
153  }
154
155  public int getNumZkServers() {
156    return numZkServers;
157  }
158
159  public boolean isCreateRootDir() {
160    return createRootDir;
161  }
162
163  public boolean isCreateWALDir() {
164    return createWALDir;
165  }
166
167  @Override
168  public String toString() {
169    return "StartMiniClusterOption{" + "numMasters=" + numMasters + ", masterClass=" + masterClass
170      + ", numRegionServers=" + numRegionServers + ", rsPorts=" + StringUtils.join(rsPorts)
171      + ", rsClass=" + rsClass + ", numDataNodes=" + numDataNodes + ", dataNodeHosts="
172      + Arrays.toString(dataNodeHosts) + ", numZkServers=" + numZkServers + ", createRootDir="
173      + createRootDir + ", createWALDir=" + createWALDir + '}';
174  }
175
176  /** Returns a new builder. */
177  public static Builder builder() {
178    return new Builder();
179  }
180
181  /**
182   * Builder pattern for creating an {@link StartMiniClusterOption}. The default values of its
183   * fields should be considered public and constant. Changing the default values may cause other
184   * tests fail.
185   */
186  public static final class Builder {
187    private int numMasters = 1;
188    private int numAlwaysStandByMasters = 0;
189    private Class<? extends HMaster> masterClass = null;
190    private int numRegionServers = 1;
191    private List<Integer> rsPorts = null;
192    private Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass = null;
193    private int numDataNodes = 1;
194    private String[] dataNodeHosts = null;
195    private int numZkServers = 1;
196    private boolean createRootDir = false;
197    private boolean createWALDir = false;
198
199    private Builder() {
200    }
201
202    public StartMiniClusterOption build() {
203      if (dataNodeHosts != null && dataNodeHosts.length != 0) {
204        numDataNodes = dataNodeHosts.length;
205      }
206      return new StartMiniClusterOption(numMasters, numAlwaysStandByMasters, masterClass,
207        numRegionServers, rsPorts, rsClass, numDataNodes, dataNodeHosts, numZkServers,
208        createRootDir, createWALDir);
209    }
210
211    public Builder numMasters(int numMasters) {
212      this.numMasters = numMasters;
213      return this;
214    }
215
216    public Builder numAlwaysStandByMasters(int numAlwaysStandByMasters) {
217      this.numAlwaysStandByMasters = numAlwaysStandByMasters;
218      return this;
219    }
220
221    public Builder masterClass(Class<? extends HMaster> masterClass) {
222      this.masterClass = masterClass;
223      return this;
224    }
225
226    public Builder numRegionServers(int numRegionServers) {
227      this.numRegionServers = numRegionServers;
228      return this;
229    }
230
231    public Builder rsPorts(List<Integer> rsPorts) {
232      this.rsPorts = rsPorts;
233      return this;
234    }
235
236    public Builder rsClass(Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass) {
237      this.rsClass = rsClass;
238      return this;
239    }
240
241    public Builder numDataNodes(int numDataNodes) {
242      this.numDataNodes = numDataNodes;
243      return this;
244    }
245
246    public Builder dataNodeHosts(String[] dataNodeHosts) {
247      this.dataNodeHosts = dataNodeHosts;
248      return this;
249    }
250
251    public Builder numZkServers(int numZkServers) {
252      this.numZkServers = numZkServers;
253      return this;
254    }
255
256    public Builder numWorkers(int numWorkers) {
257      return numDataNodes(numWorkers).numRegionServers(numWorkers);
258    }
259
260    public Builder createRootDir(boolean createRootDir) {
261      this.createRootDir = createRootDir;
262      return this;
263    }
264
265    public Builder createWALDir(boolean createWALDir) {
266      this.createWALDir = createWALDir;
267      return this;
268    }
269  }
270
271}