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