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