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