View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client.replication;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.conf.Configuration;
24  import org.apache.hadoop.hbase.Abortable;
25  import org.apache.hadoop.hbase.HConstants;
26  import org.apache.hadoop.hbase.client.HConnection;
27  import org.apache.hadoop.hbase.client.HConnectionManager;
28  import org.apache.hadoop.hbase.replication.ReplicationFactory;
29  import org.apache.hadoop.hbase.replication.ReplicationPeers;
30  import org.apache.hadoop.hbase.replication.ReplicationQueuesClient;
31  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
32  import org.apache.zookeeper.KeeperException;
33  import org.apache.hadoop.hbase.HTableDescriptor;
34  import org.apache.hadoop.hbase.HColumnDescriptor;
35  
36  import java.io.Closeable;
37  import java.io.IOException;
38  import java.util.Map;
39  import java.util.List;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.lang.Integer;
43  
44  /**
45   * <p>
46   * This class provides the administrative interface to HBase cluster
47   * replication. In order to use it, the cluster and the client using
48   * ReplicationAdmin must be configured with <code>hbase.replication</code>
49   * set to true.
50   * </p>
51   * <p>
52   * Adding a new peer results in creating new outbound connections from every
53   * region server to a subset of region servers on the slave cluster. Each
54   * new stream of replication will start replicating from the beginning of the
55   * current HLog, meaning that edits from that past will be replicated.
56   * </p>
57   * <p>
58   * Removing a peer is a destructive and irreversible operation that stops
59   * all the replication streams for the given cluster and deletes the metadata
60   * used to keep track of the replication state.
61   * </p>
62   * <p>
63   * To see which commands are available in the shell, type
64   * <code>replication</code>.
65   * </p>
66   */
67  public class ReplicationAdmin implements Closeable {
68    private static final Log LOG = LogFactory.getLog(ReplicationAdmin.class);
69  
70    public static final String TNAME = "tableName";
71    public static final String CFNAME = "columnFamlyName";
72  
73    // only Global for now, can add other type
74    // such as, 1) no global replication, or 2) the table is replicated to this cluster, etc.
75    public static final String REPLICATIONTYPE = "replicationType";
76    public static final String REPLICATIONGLOBAL = Integer
77        .toString(HConstants.REPLICATION_SCOPE_GLOBAL);
78  
79    private final HConnection connection;
80    private final ReplicationQueuesClient replicationQueuesClient;
81    private final ReplicationPeers replicationPeers;
82  
83    /**
84     * Constructor that creates a connection to the local ZooKeeper ensemble.
85     * @param conf Configuration to use
86     * @throws IOException if the connection to ZK cannot be made
87     * @throws RuntimeException if replication isn't enabled.
88     */
89    public ReplicationAdmin(Configuration conf) throws IOException {
90      if (!conf.getBoolean(HConstants.REPLICATION_ENABLE_KEY, false)) {
91        throw new RuntimeException("hbase.replication isn't true, please " +
92            "enable it in order to use replication");
93      }
94      this.connection = HConnectionManager.getConnection(conf);
95      ZooKeeperWatcher zkw = createZooKeeperWatcher();
96      try {
97        this.replicationPeers = ReplicationFactory.getReplicationPeers(zkw, conf, this.connection);
98        this.replicationPeers.init();
99        this.replicationQueuesClient =
100           ReplicationFactory.getReplicationQueuesClient(zkw, conf, this.connection);
101       this.replicationQueuesClient.init();
102 
103     } catch (KeeperException e) {
104       throw new IOException("Unable setup the ZooKeeper connection", e);
105     }
106   }
107 
108   private ZooKeeperWatcher createZooKeeperWatcher() throws IOException {
109     return new ZooKeeperWatcher(connection.getConfiguration(),
110       "Replication Admin", new Abortable() {
111       @Override
112       public void abort(String why, Throwable e) {
113         LOG.error(why, e);
114         System.exit(1);
115       }
116 
117       @Override
118       public boolean isAborted() {
119         return false;
120       }
121 
122     });
123   }
124 
125 
126   /**
127    * Add a new peer cluster to replicate to.
128    * @param id a short that identifies the cluster
129    * @param clusterKey the concatenation of the slave cluster's
130    * <code>hbase.zookeeper.quorum:hbase.zookeeper.property.clientPort:zookeeper.znode.parent</code>
131    * @throws IllegalStateException if there's already one slave since
132    * multi-slave isn't supported yet.
133    */
134   public void addPeer(String id, String clusterKey) throws IOException {
135     this.replicationPeers.addPeer(id, clusterKey);
136   }
137 
138   /**
139    * Removes a peer cluster and stops the replication to it.
140    * @param id a short that identifies the cluster
141    */
142   public void removePeer(String id) throws IOException {
143     this.replicationPeers.removePeer(id);
144   }
145 
146   /**
147    * Restart the replication stream to the specified peer.
148    * @param id a short that identifies the cluster
149    */
150   public void enablePeer(String id) throws IOException {
151     this.replicationPeers.enablePeer(id);
152   }
153 
154   /**
155    * Stop the replication stream to the specified peer.
156    * @param id a short that identifies the cluster
157    */
158   public void disablePeer(String id) throws IOException {
159     this.replicationPeers.disablePeer(id);
160   }
161 
162   /**
163    * Get the number of slave clusters the local cluster has.
164    * @return number of slave clusters
165    */
166   public int getPeersCount() {
167     return this.replicationPeers.getAllPeerIds().size();
168   }
169 
170   /**
171    * Map of this cluster's peers for display.
172    * @return A map of peer ids to peer cluster keys
173    */
174   public Map<String, String> listPeers() {
175     return this.replicationPeers.getAllPeerClusterKeys();
176   }
177 
178   /**
179    * Get the state of the specified peer cluster
180    * @param id String format of the Short that identifies the peer, an IllegalArgumentException
181    *           is thrown if it doesn't exist
182    * @return true if replication is enabled to that peer, false if it isn't
183    */
184   public boolean getPeerState(String id) throws IOException {
185     return this.replicationPeers.getStatusOfPeerFromBackingStore(id);
186   }
187 
188   @Override
189   public void close() throws IOException {
190     if (this.connection != null) {
191       this.connection.close();
192     }
193   }
194 
195   
196   /**
197    * Find all column families that are replicated from this cluster
198    * @return the full list of the replicated column families of this cluster as:
199    *        tableName, family name, replicationType
200    *
201    * Currently replicationType is Global. In the future, more replication
202    * types may be extended here. For example
203    *  1) the replication may only apply to selected peers instead of all peers
204    *  2) the replicationType may indicate the host Cluster servers as Slave
205    *     for the table:columnFam.         
206    */
207   public List<HashMap<String, String>> listReplicated() throws IOException {
208     List<HashMap<String, String>> replicationColFams = new ArrayList<HashMap<String, String>>();
209     HTableDescriptor[] tables = this.connection.listTables();
210 
211     for (HTableDescriptor table : tables) {
212       HColumnDescriptor[] columns = table.getColumnFamilies();
213       String tableName = table.getNameAsString();
214       for (HColumnDescriptor column : columns) {
215         if (column.getScope() != HConstants.REPLICATION_SCOPE_LOCAL) {
216           // At this moment, the columfam is replicated to all peers
217           HashMap<String, String> replicationEntry = new HashMap<String, String>();
218           replicationEntry.put(TNAME, tableName);
219           replicationEntry.put(CFNAME, column.getNameAsString());
220           replicationEntry.put(REPLICATIONTYPE, REPLICATIONGLOBAL);
221           replicationColFams.add(replicationEntry);
222         }
223       }
224     }
225 
226     return replicationColFams;
227   } 
228 }