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.io.Closeable;
021import java.io.IOException;
022import org.apache.hadoop.conf.Configurable;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.util.Threads;
025import org.apache.yetus.audience.InterfaceAudience;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
030import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
031import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService;
032
033/**
034 * This class defines methods that can help with managing HBase clusters
035 * from unit tests and system tests. There are 3 types of cluster deployments:
036 * <ul>
037 * <li><b>MiniHBaseCluster:</b> each server is run in the same JVM in separate threads,
038 * used by unit tests</li>
039 * <li><b>DistributedHBaseCluster:</b> the cluster is pre-deployed, system and integration tests can
040 * interact with the cluster. </li>
041 * <li><b>ProcessBasedLocalHBaseCluster:</b> each server is deployed locally but in separate
042 * JVMs. </li>
043 * </ul>
044 * <p>
045 * HBaseCluster unifies the way tests interact with the cluster, so that the same test can
046 * be run against a mini-cluster during unit test execution, or a distributed cluster having
047 * tens/hundreds of nodes during execution of integration tests.
048 *
049 * <p>
050 * HBaseCluster exposes client-side public interfaces to tests, so that tests does not assume
051 * running in a particular mode. Not all the tests are suitable to be run on an actual cluster,
052 * and some tests will still need to mock stuff and introspect internal state. For those use
053 * cases from unit tests, or if more control is needed, you can use the subclasses directly.
054 * In that sense, this class does not abstract away <strong>every</strong> interface that
055 * MiniHBaseCluster or DistributedHBaseCluster provide.
056 */
057@InterfaceAudience.Private
058public abstract class HBaseCluster implements Closeable, Configurable {
059  // Log is being used in DistributedHBaseCluster class, hence keeping it as package scope
060  static final Logger LOG = LoggerFactory.getLogger(HBaseCluster.class.getName());
061  protected Configuration conf;
062
063  /** the status of the cluster before we begin */
064  protected ClusterMetrics initialClusterStatus;
065
066  /**
067   * Construct an HBaseCluster
068   * @param conf Configuration to be used for cluster
069   */
070  public HBaseCluster(Configuration conf) {
071    setConf(conf);
072  }
073
074  @Override
075  public void setConf(Configuration conf) {
076    this.conf = conf;
077  }
078
079  @Override
080  public Configuration getConf() {
081    return conf;
082  }
083
084  /**
085   * Returns a ClusterMetrics for this HBase cluster.
086   * @see #getInitialClusterMetrics()
087   */
088  public abstract ClusterMetrics getClusterMetrics() throws IOException;
089
090  /**
091   * Returns a ClusterStatus for this HBase cluster as observed at the
092   * starting of the HBaseCluster
093   */
094  public ClusterMetrics getInitialClusterMetrics() throws IOException {
095    return initialClusterStatus;
096  }
097
098  /**
099   * Returns an {@link MasterService.BlockingInterface} to the active master
100   */
101  public abstract MasterService.BlockingInterface getMasterAdminService()
102  throws IOException;
103
104  /**
105   * Returns an AdminProtocol interface to the regionserver
106   */
107  public abstract AdminService.BlockingInterface getAdminProtocol(ServerName serverName)
108  throws IOException;
109
110  /**
111   * Returns a ClientProtocol interface to the regionserver
112   */
113  public abstract ClientService.BlockingInterface getClientProtocol(ServerName serverName)
114  throws IOException;
115
116  /**
117   * Starts a new region server on the given hostname or if this is a mini/local cluster,
118   * starts a region server locally.
119   * @param hostname the hostname to start the regionserver on
120   * @throws IOException if something goes wrong
121   */
122  public abstract void startRegionServer(String hostname, int port) throws IOException;
123
124  /**
125   * Kills the region server process if this is a distributed cluster, otherwise
126   * this causes the region server to exit doing basic clean up only.
127   * @throws IOException if something goes wrong
128   */
129  public abstract void killRegionServer(ServerName serverName) throws IOException;
130
131  /**
132   * Keeping track of killed servers and being able to check if a particular server was killed makes
133   * it possible to do fault tolerance testing for dead servers in a deterministic way. A concrete
134   * example of such case is - killing servers and waiting for all regions of a particular table
135   * to be assigned. We can check for server column in META table and that its value is not one
136   * of the killed servers.
137   */
138  public abstract boolean isKilledRS(ServerName serverName);
139
140  /**
141   * Stops the given region server, by attempting a gradual stop.
142   * @return whether the operation finished with success
143   * @throws IOException if something goes wrong
144   */
145  public abstract void stopRegionServer(ServerName serverName) throws IOException;
146
147  /**
148   * Wait for the specified region server to join the cluster
149   * @return whether the operation finished with success
150   * @throws IOException if something goes wrong or timeout occurs
151   */
152  public void waitForRegionServerToStart(String hostname, int port, long timeout)
153      throws IOException {
154    long start = System.currentTimeMillis();
155    while ((System.currentTimeMillis() - start) < timeout) {
156      for (ServerName server : getClusterMetrics().getLiveServerMetrics().keySet()) {
157        if (server.getHostname().equals(hostname) && server.getPort() == port) {
158          return;
159        }
160      }
161      Threads.sleep(100);
162    }
163    throw new IOException("did timeout " + timeout + "ms waiting for region server to start: "
164        + hostname);
165  }
166
167  /**
168   * Wait for the specified region server to stop the thread / process.
169   * @return whether the operation finished with success
170   * @throws IOException if something goes wrong or timeout occurs
171   */
172  public abstract void waitForRegionServerToStop(ServerName serverName, long timeout)
173      throws IOException;
174
175  /**
176   * Starts a new zookeeper node on the given hostname or if this is a mini/local cluster,
177   * silently logs warning message.
178   * @param hostname the hostname to start the regionserver on
179   * @throws IOException if something goes wrong
180   */
181  public abstract void startZkNode(String hostname, int port) throws IOException;
182
183  /**
184   * Kills the zookeeper node process if this is a distributed cluster, otherwise,
185   * this causes master to exit doing basic clean up only.
186   * @throws IOException if something goes wrong
187   */
188  public abstract void killZkNode(ServerName serverName) throws IOException;
189
190  /**
191   * Stops the region zookeeper if this is a distributed cluster, otherwise
192   * silently logs warning message.
193   * @throws IOException if something goes wrong
194   */
195  public abstract void stopZkNode(ServerName serverName) throws IOException;
196
197  /**
198   * Wait for the specified zookeeper node to join the cluster
199   * @return whether the operation finished with success
200   * @throws IOException if something goes wrong or timeout occurs
201   */
202  public abstract void waitForZkNodeToStart(ServerName serverName, long timeout)
203    throws IOException;
204
205  /**
206   * Wait for the specified zookeeper node to stop the thread / process.
207   * @return whether the operation finished with success
208   * @throws IOException if something goes wrong or timeout occurs
209   */
210  public abstract void waitForZkNodeToStop(ServerName serverName, long timeout)
211    throws IOException;
212
213  /**
214   * Starts a new datanode on the given hostname or if this is a mini/local cluster,
215   * silently logs warning message.
216   * @throws IOException if something goes wrong
217   */
218  public abstract void startDataNode(ServerName serverName) throws IOException;
219
220  /**
221   * Kills the datanode process if this is a distributed cluster, otherwise,
222   * this causes master to exit doing basic clean up only.
223   * @throws IOException if something goes wrong
224   */
225  public abstract void killDataNode(ServerName serverName) throws IOException;
226
227  /**
228   * Stops the datanode if this is a distributed cluster, otherwise
229   * silently logs warning message.
230   * @throws IOException if something goes wrong
231   */
232  public abstract void stopDataNode(ServerName serverName) throws IOException;
233
234  /**
235   * Wait for the specified datanode to join the cluster
236   * @return whether the operation finished with success
237   * @throws IOException if something goes wrong or timeout occurs
238   */
239  public abstract void waitForDataNodeToStart(ServerName serverName, long timeout)
240    throws IOException;
241
242  /**
243   * Wait for the specified datanode to stop the thread / process.
244   * @return whether the operation finished with success
245   * @throws IOException if something goes wrong or timeout occurs
246   */
247  public abstract void waitForDataNodeToStop(ServerName serverName, long timeout)
248    throws IOException;
249
250  /**
251   * Starts a new master on the given hostname or if this is a mini/local cluster,
252   * starts a master locally.
253   * @param hostname the hostname to start the master on
254   * @return whether the operation finished with success
255   * @throws IOException if something goes wrong
256   */
257  public abstract void startMaster(String hostname, int port) throws IOException;
258
259  /**
260   * Kills the master process if this is a distributed cluster, otherwise,
261   * this causes master to exit doing basic clean up only.
262   * @throws IOException if something goes wrong
263   */
264  public abstract void killMaster(ServerName serverName) throws IOException;
265
266  /**
267   * Stops the given master, by attempting a gradual stop.
268   * @throws IOException if something goes wrong
269   */
270  public abstract void stopMaster(ServerName serverName) throws IOException;
271
272  /**
273   * Wait for the specified master to stop the thread / process.
274   * @throws IOException if something goes wrong or timeout occurs
275   */
276  public abstract void waitForMasterToStop(ServerName serverName, long timeout)
277      throws IOException;
278
279  /**
280   * Blocks until there is an active master and that master has completed
281   * initialization.
282   *
283   * @return true if an active master becomes available.  false if there are no
284   *         masters left.
285   * @throws IOException if something goes wrong or timeout occurs
286   */
287  public boolean waitForActiveAndReadyMaster()
288      throws IOException {
289    return waitForActiveAndReadyMaster(Long.MAX_VALUE);
290  }
291
292  /**
293   * Blocks until there is an active master and that master has completed
294   * initialization.
295   * @param timeout the timeout limit in ms
296   * @return true if an active master becomes available.  false if there are no
297   *         masters left.
298   */
299  public abstract boolean waitForActiveAndReadyMaster(long timeout)
300      throws IOException;
301
302  /**
303   * Wait for HBase Cluster to shut down.
304   */
305  public abstract void waitUntilShutDown() throws IOException;
306
307  /**
308   * Shut down the HBase cluster
309   */
310  public abstract void shutdown() throws IOException;
311
312  /**
313   * Restores the cluster to it's initial state if this is a real cluster,
314   * otherwise does nothing.
315   * This is a best effort restore. If the servers are not reachable, or insufficient
316   * permissions, etc. restoration might be partial.
317   * @return whether restoration is complete
318   */
319  public boolean restoreInitialStatus() throws IOException {
320    return restoreClusterMetrics(getInitialClusterMetrics());
321  }
322
323  /**
324   * Restores the cluster to given state if this is a real cluster,
325   * otherwise does nothing.
326   * This is a best effort restore. If the servers are not reachable, or insufficient
327   * permissions, etc. restoration might be partial.
328   * @return whether restoration is complete
329   */
330  public boolean restoreClusterMetrics(ClusterMetrics desiredStatus) throws IOException {
331    return true;
332  }
333
334  /**
335   * Get the ServerName of region server serving the first hbase:meta region
336   */
337  public ServerName getServerHoldingMeta() throws IOException {
338    return getServerHoldingRegion(TableName.META_TABLE_NAME,
339      HRegionInfo.FIRST_META_REGIONINFO.getRegionName());
340  }
341
342  /**
343   * Get the ServerName of region server serving the specified region
344   * @param regionName Name of the region in bytes
345   * @param tn Table name that has the region.
346   * @return ServerName that hosts the region or null
347   */
348  public abstract ServerName getServerHoldingRegion(final TableName tn, byte[] regionName)
349  throws IOException;
350
351  /**
352   * @return whether we are interacting with a distributed cluster as opposed to an
353   * in-process mini/local cluster.
354   */
355  public boolean isDistributedCluster() {
356    return false;
357  }
358
359  /**
360   * Closes all the resources held open for this cluster. Note that this call does not shutdown
361   * the cluster.
362   * @see #shutdown()
363   */
364  @Override
365  public abstract void close() throws IOException;
366
367  /**
368   * Wait for the namenode.
369   *
370   * @throws InterruptedException
371   */
372  public void waitForNamenodeAvailable() throws InterruptedException {
373  }
374
375  public void waitForDatanodesRegistered(int nbDN) throws Exception {
376  }
377}