| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import org.apache.hadoop.conf.Configurable; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.client.RegionInfoBuilder; |
| import org.apache.hadoop.hbase.util.Threads; |
| import org.apache.yetus.audience.InterfaceAudience; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This class defines methods that can help with managing HBase clusters |
| * from unit tests and system tests. There are 3 types of cluster deployments: |
| * <ul> |
| * <li><b>MiniHBaseCluster:</b> each server is run in the same JVM in separate threads, |
| * used by unit tests</li> |
| * <li><b>DistributedHBaseCluster:</b> the cluster is pre-deployed, system and integration tests can |
| * interact with the cluster. </li> |
| * <li><b>ProcessBasedLocalHBaseCluster:</b> each server is deployed locally but in separate |
| * JVMs. </li> |
| * </ul> |
| * <p> |
| * HBaseCluster unifies the way tests interact with the cluster, so that the same test can |
| * be run against a mini-cluster during unit test execution, or a distributed cluster having |
| * tens/hundreds of nodes during execution of integration tests. |
| * |
| * <p> |
| * HBaseCluster exposes client-side public interfaces to tests, so that tests does not assume |
| * running in a particular mode. Not all the tests are suitable to be run on an actual cluster, |
| * and some tests will still need to mock stuff and introspect internal state. For those use |
| * cases from unit tests, or if more control is needed, you can use the subclasses directly. |
| * In that sense, this class does not abstract away <strong>every</strong> interface that |
| * MiniHBaseCluster or DistributedHBaseCluster provide. |
| */ |
| @InterfaceAudience.Private |
| public abstract class HBaseCluster implements Closeable, Configurable { |
| // Log is being used in DistributedHBaseCluster class, hence keeping it as package scope |
| static final Logger LOG = LoggerFactory.getLogger(HBaseCluster.class.getName()); |
| protected Configuration conf; |
| |
| /** the status of the cluster before we begin */ |
| protected ClusterMetrics initialClusterStatus; |
| |
| /** |
| * Construct an HBaseCluster |
| * @param conf Configuration to be used for cluster |
| */ |
| public HBaseCluster(Configuration conf) { |
| setConf(conf); |
| } |
| |
| @Override |
| public void setConf(Configuration conf) { |
| this.conf = conf; |
| } |
| |
| @Override |
| public Configuration getConf() { |
| return conf; |
| } |
| |
| /** |
| * Returns a ClusterMetrics for this HBase cluster. |
| * @see #getInitialClusterMetrics() |
| */ |
| public abstract ClusterMetrics getClusterMetrics() throws IOException; |
| |
| /** |
| * Returns a ClusterStatus for this HBase cluster as observed at the |
| * starting of the HBaseCluster |
| */ |
| public ClusterMetrics getInitialClusterMetrics() throws IOException { |
| return initialClusterStatus; |
| } |
| |
| /** |
| * Starts a new region server on the given hostname or if this is a mini/local cluster, |
| * starts a region server locally. |
| * @param hostname the hostname to start the regionserver on |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void startRegionServer(String hostname, int port) throws IOException; |
| |
| /** |
| * Kills the region server process if this is a distributed cluster, otherwise |
| * this causes the region server to exit doing basic clean up only. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void killRegionServer(ServerName serverName) throws IOException; |
| |
| /** |
| * Keeping track of killed servers and being able to check if a particular server was killed makes |
| * it possible to do fault tolerance testing for dead servers in a deterministic way. A concrete |
| * example of such case is - killing servers and waiting for all regions of a particular table |
| * to be assigned. We can check for server column in META table and that its value is not one |
| * of the killed servers. |
| */ |
| public abstract boolean isKilledRS(ServerName serverName); |
| |
| /** |
| * Stops the given region server, by attempting a gradual stop. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void stopRegionServer(ServerName serverName) throws IOException; |
| |
| /** |
| * Wait for the specified region server to join the cluster |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public void waitForRegionServerToStart(String hostname, int port, long timeout) |
| throws IOException { |
| long start = System.currentTimeMillis(); |
| while ((System.currentTimeMillis() - start) < timeout) { |
| for (ServerName server : getClusterMetrics().getLiveServerMetrics().keySet()) { |
| if (server.getHostname().equals(hostname) && server.getPort() == port) { |
| return; |
| } |
| } |
| Threads.sleep(100); |
| } |
| throw new IOException("did timeout " + timeout + "ms waiting for region server to start: " |
| + hostname); |
| } |
| |
| /** |
| * Wait for the specified region server to stop the thread / process. |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForRegionServerToStop(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Suspend the region server |
| * @param serverName the hostname to suspend the regionserver on |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void suspendRegionServer(ServerName serverName) throws IOException; |
| |
| /** |
| * Resume the region server |
| * @param serverName the hostname to resume the regionserver on |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void resumeRegionServer(ServerName serverName) throws IOException; |
| |
| /** |
| * Starts a new zookeeper node on the given hostname or if this is a mini/local cluster, |
| * silently logs warning message. |
| * @param hostname the hostname to start the regionserver on |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void startZkNode(String hostname, int port) throws IOException; |
| |
| /** |
| * Kills the zookeeper node process if this is a distributed cluster, otherwise, |
| * this causes master to exit doing basic clean up only. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void killZkNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Stops the region zookeeper if this is a distributed cluster, otherwise |
| * silently logs warning message. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void stopZkNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Wait for the specified zookeeper node to join the cluster |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForZkNodeToStart(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Wait for the specified zookeeper node to stop the thread / process. |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForZkNodeToStop(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Starts a new datanode on the given hostname or if this is a mini/local cluster, |
| * silently logs warning message. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void startDataNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Kills the datanode process if this is a distributed cluster, otherwise, |
| * this causes master to exit doing basic clean up only. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void killDataNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Stops the datanode if this is a distributed cluster, otherwise |
| * silently logs warning message. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void stopDataNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Wait for the specified datanode to join the cluster |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForDataNodeToStart(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Wait for the specified datanode to stop the thread / process. |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForDataNodeToStop(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Starts a new namenode on the given hostname or if this is a mini/local cluster, silently logs |
| * warning message. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void startNameNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Kills the namenode process if this is a distributed cluster, otherwise, this causes master to |
| * exit doing basic clean up only. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void killNameNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Stops the namenode if this is a distributed cluster, otherwise silently logs warning message. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void stopNameNode(ServerName serverName) throws IOException; |
| |
| /** |
| * Wait for the specified namenode to join the cluster |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForNameNodeToStart(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Wait for the specified namenode to stop |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForNameNodeToStop(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Starts a new master on the given hostname or if this is a mini/local cluster, |
| * starts a master locally. |
| * @param hostname the hostname to start the master on |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void startMaster(String hostname, int port) throws IOException; |
| |
| /** |
| * Kills the master process if this is a distributed cluster, otherwise, |
| * this causes master to exit doing basic clean up only. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void killMaster(ServerName serverName) throws IOException; |
| |
| /** |
| * Stops the given master, by attempting a gradual stop. |
| * @throws IOException if something goes wrong |
| */ |
| public abstract void stopMaster(ServerName serverName) throws IOException; |
| |
| /** |
| * Wait for the specified master to stop the thread / process. |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public abstract void waitForMasterToStop(ServerName serverName, long timeout) |
| throws IOException; |
| |
| /** |
| * Blocks until there is an active master and that master has completed |
| * initialization. |
| * |
| * @return true if an active master becomes available. false if there are no |
| * masters left. |
| * @throws IOException if something goes wrong or timeout occurs |
| */ |
| public boolean waitForActiveAndReadyMaster() |
| throws IOException { |
| return waitForActiveAndReadyMaster(Long.MAX_VALUE); |
| } |
| |
| /** |
| * Blocks until there is an active master and that master has completed |
| * initialization. |
| * @param timeout the timeout limit in ms |
| * @return true if an active master becomes available. false if there are no |
| * masters left. |
| */ |
| public abstract boolean waitForActiveAndReadyMaster(long timeout) |
| throws IOException; |
| |
| /** |
| * Wait for HBase Cluster to shut down. |
| */ |
| public abstract void waitUntilShutDown() throws IOException; |
| |
| /** |
| * Shut down the HBase cluster |
| */ |
| public abstract void shutdown() throws IOException; |
| |
| /** |
| * Restores the cluster to it's initial state if this is a real cluster, |
| * otherwise does nothing. |
| * This is a best effort restore. If the servers are not reachable, or insufficient |
| * permissions, etc. restoration might be partial. |
| * @return whether restoration is complete |
| */ |
| public boolean restoreInitialStatus() throws IOException { |
| return restoreClusterMetrics(getInitialClusterMetrics()); |
| } |
| |
| /** |
| * Restores the cluster to given state if this is a real cluster, |
| * otherwise does nothing. |
| * This is a best effort restore. If the servers are not reachable, or insufficient |
| * permissions, etc. restoration might be partial. |
| * @return whether restoration is complete |
| */ |
| public boolean restoreClusterMetrics(ClusterMetrics desiredStatus) throws IOException { |
| return true; |
| } |
| |
| /** |
| * Get the ServerName of region server serving the first hbase:meta region |
| */ |
| public ServerName getServerHoldingMeta() throws IOException { |
| return getServerHoldingRegion(TableName.META_TABLE_NAME, |
| RegionInfoBuilder.FIRST_META_REGIONINFO.getRegionName()); |
| } |
| |
| /** |
| * Get the ServerName of region server serving the specified region |
| * @param regionName Name of the region in bytes |
| * @param tn Table name that has the region. |
| * @return ServerName that hosts the region or null |
| */ |
| public abstract ServerName getServerHoldingRegion(final TableName tn, byte[] regionName) |
| throws IOException; |
| |
| /** |
| * @return whether we are interacting with a distributed cluster as opposed to an |
| * in-process mini/local cluster. |
| */ |
| public boolean isDistributedCluster() { |
| return false; |
| } |
| |
| /** |
| * Closes all the resources held open for this cluster. Note that this call does not shutdown |
| * the cluster. |
| * @see #shutdown() |
| */ |
| @Override |
| public abstract void close() throws IOException; |
| |
| /** |
| * Wait for the namenode. |
| * |
| * @throws InterruptedException |
| */ |
| public void waitForNamenodeAvailable() throws InterruptedException { |
| } |
| |
| public void waitForDatanodesRegistered(int nbDN) throws Exception { |
| } |
| } |