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.master;
20  
21  import java.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.net.InetAddress;
26  import java.net.InetSocketAddress;
27  import java.net.UnknownHostException;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Comparator;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Map.Entry;
37  import java.util.Set;
38  import java.util.concurrent.TimeUnit;
39  import java.util.concurrent.atomic.AtomicReference;
40  import java.util.regex.Pattern;
41  
42  import javax.servlet.ServletException;
43  import javax.servlet.http.HttpServlet;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  import org.apache.hadoop.conf.Configuration;
50  import org.apache.hadoop.fs.Path;
51  import org.apache.hadoop.hbase.ClusterStatus;
52  import org.apache.hadoop.hbase.CoordinatedStateException;
53  import org.apache.hadoop.hbase.CoordinatedStateManager;
54  import org.apache.hadoop.hbase.DoNotRetryIOException;
55  import org.apache.hadoop.hbase.HBaseIOException;
56  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
57  import org.apache.hadoop.hbase.HColumnDescriptor;
58  import org.apache.hadoop.hbase.HConstants;
59  import org.apache.hadoop.hbase.HRegionInfo;
60  import org.apache.hadoop.hbase.HTableDescriptor;
61  import org.apache.hadoop.hbase.MasterNotRunningException;
62  import org.apache.hadoop.hbase.MetaMigrationConvertingToPB;
63  import org.apache.hadoop.hbase.MetaTableAccessor;
64  import org.apache.hadoop.hbase.NamespaceDescriptor;
65  import org.apache.hadoop.hbase.NamespaceNotFoundException;
66  import org.apache.hadoop.hbase.PleaseHoldException;
67  import org.apache.hadoop.hbase.ProcedureInfo;
68  import org.apache.hadoop.hbase.RegionStateListener;
69  import org.apache.hadoop.hbase.ScheduledChore;
70  import org.apache.hadoop.hbase.Server;
71  import org.apache.hadoop.hbase.ServerLoad;
72  import org.apache.hadoop.hbase.ServerName;
73  import org.apache.hadoop.hbase.TableDescriptors;
74  import org.apache.hadoop.hbase.TableName;
75  import org.apache.hadoop.hbase.TableNotDisabledException;
76  import org.apache.hadoop.hbase.TableNotFoundException;
77  import org.apache.hadoop.hbase.UnknownRegionException;
78  import org.apache.hadoop.hbase.classification.InterfaceAudience;
79  import org.apache.hadoop.hbase.client.Admin;
80  import org.apache.hadoop.hbase.client.MetaScanner;
81  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
82  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
83  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
84  import org.apache.hadoop.hbase.client.Result;
85  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
86  import org.apache.hadoop.hbase.exceptions.DeserializationException;
87  import org.apache.hadoop.hbase.executor.ExecutorType;
88  import org.apache.hadoop.hbase.http.InfoServer;
89  import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
90  import org.apache.hadoop.hbase.ipc.RpcServer;
91  import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
92  import org.apache.hadoop.hbase.master.MasterRpcServices.BalanceSwitchMode;
93  import org.apache.hadoop.hbase.master.RegionState.State;
94  import org.apache.hadoop.hbase.master.balancer.BalancerChore;
95  import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer;
96  import org.apache.hadoop.hbase.master.balancer.ClusterStatusChore;
97  import org.apache.hadoop.hbase.master.balancer.LoadBalancerFactory;
98  import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
99  import org.apache.hadoop.hbase.master.cleaner.LogCleaner;
100 import org.apache.hadoop.hbase.master.cleaner.ReplicationZKLockCleanerChore;
101 import org.apache.hadoop.hbase.master.handler.DispatchMergingRegionHandler;
102 import org.apache.hadoop.hbase.master.normalizer.RegionNormalizer;
103 import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerChore;
104 import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerFactory;
105 import org.apache.hadoop.hbase.master.procedure.AddColumnFamilyProcedure;
106 import org.apache.hadoop.hbase.master.procedure.CreateNamespaceProcedure;
107 import org.apache.hadoop.hbase.master.procedure.CreateTableProcedure;
108 import org.apache.hadoop.hbase.master.procedure.DeleteColumnFamilyProcedure;
109 import org.apache.hadoop.hbase.master.procedure.DeleteNamespaceProcedure;
110 import org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure;
111 import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure;
112 import org.apache.hadoop.hbase.master.procedure.EnableTableProcedure;
113 import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants;
114 import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
115 import org.apache.hadoop.hbase.master.procedure.MasterProcedureScheduler.ProcedureEvent;
116 import org.apache.hadoop.hbase.master.procedure.ModifyColumnFamilyProcedure;
117 import org.apache.hadoop.hbase.master.procedure.ModifyNamespaceProcedure;
118 import org.apache.hadoop.hbase.master.procedure.MasterProcedureUtil;
119 import org.apache.hadoop.hbase.master.procedure.ModifyTableProcedure;
120 import org.apache.hadoop.hbase.master.procedure.ProcedurePrepareLatch;
121 import org.apache.hadoop.hbase.master.procedure.ProcedureSyncWait;
122 import org.apache.hadoop.hbase.master.procedure.TruncateTableProcedure;
123 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
124 import org.apache.hadoop.hbase.monitoring.MemoryBoundedLogMessageBuffer;
125 import org.apache.hadoop.hbase.monitoring.MonitoredTask;
126 import org.apache.hadoop.hbase.monitoring.TaskMonitor;
127 import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan;
128 import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan.PlanType;
129 import org.apache.hadoop.hbase.procedure.MasterProcedureManagerHost;
130 import org.apache.hadoop.hbase.procedure.flush.MasterFlushTableProcedureManager;
131 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
132 import org.apache.hadoop.hbase.procedure2.store.wal.WALProcedureStore;
133 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionServerInfo;
134 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
135 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.SplitLogTask.RecoveryMode;
136 import org.apache.hadoop.hbase.quotas.MasterQuotaManager;
137 import org.apache.hadoop.hbase.regionserver.DefaultStoreEngine;
138 import org.apache.hadoop.hbase.regionserver.HRegionServer;
139 import org.apache.hadoop.hbase.regionserver.HStore;
140 import org.apache.hadoop.hbase.regionserver.RSRpcServices;
141 import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
142 import org.apache.hadoop.hbase.regionserver.RegionSplitPolicy;
143 import org.apache.hadoop.hbase.regionserver.compactions.ExploringCompactionPolicy;
144 import org.apache.hadoop.hbase.regionserver.compactions.FIFOCompactionPolicy;
145 import org.apache.hadoop.hbase.replication.regionserver.Replication;
146 import org.apache.hadoop.hbase.security.User;
147 import org.apache.hadoop.hbase.security.UserProvider;
148 import org.apache.hadoop.hbase.util.Addressing;
149 import org.apache.hadoop.hbase.util.Bytes;
150 import org.apache.hadoop.hbase.util.CompressionTest;
151 import org.apache.hadoop.hbase.util.ConfigUtil;
152 import org.apache.hadoop.hbase.util.EncryptionTest;
153 import org.apache.hadoop.hbase.util.FSUtils;
154 import org.apache.hadoop.hbase.util.HFileArchiveUtil;
155 import org.apache.hadoop.hbase.util.HasThread;
156 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
157 import org.apache.hadoop.hbase.util.Pair;
158 import org.apache.hadoop.hbase.util.Threads;
159 import org.apache.hadoop.hbase.util.VersionInfo;
160 import org.apache.hadoop.hbase.zookeeper.DrainingServerTracker;
161 import org.apache.hadoop.hbase.zookeeper.LoadBalancerTracker;
162 import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
163 import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
164 import org.apache.hadoop.hbase.zookeeper.RegionNormalizerTracker;
165 import org.apache.hadoop.hbase.zookeeper.RegionServerTracker;
166 import org.apache.hadoop.hbase.zookeeper.SplitOrMergeTracker;
167 import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
168 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
169 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
170 import org.apache.zookeeper.KeeperException;
171 import org.mortbay.jetty.Connector;
172 import org.mortbay.jetty.nio.SelectChannelConnector;
173 import org.mortbay.jetty.servlet.Context;
174 import org.mortbay.jetty.servlet.ServletHolder;
175 
176 import com.google.common.annotations.VisibleForTesting;
177 import com.google.common.collect.Maps;
178 import com.google.protobuf.Descriptors;
179 import com.google.protobuf.Service;
180 
181 /**
182  * HMaster is the "master server" for HBase. An HBase cluster has one active
183  * master.  If many masters are started, all compete.  Whichever wins goes on to
184  * run the cluster.  All others park themselves in their constructor until
185  * master or cluster shutdown or until the active master loses its lease in
186  * zookeeper.  Thereafter, all running master jostle to take over master role.
187  *
188  * <p>The Master can be asked shutdown the cluster. See {@link #shutdown()}.  In
189  * this case it will tell all regionservers to go down and then wait on them
190  * all reporting in that they are down.  This master will then shut itself down.
191  *
192  * <p>You can also shutdown just this master.  Call {@link #stopMaster()}.
193  *
194  * @see org.apache.zookeeper.Watcher
195  */
196 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
197 @SuppressWarnings("deprecation")
198 public class HMaster extends HRegionServer implements MasterServices, Server {
199   private static final Log LOG = LogFactory.getLog(HMaster.class.getName());
200 
201   /**
202    * Protection against zombie master. Started once Master accepts active responsibility and
203    * starts taking over responsibilities. Allows a finite time window before giving up ownership.
204    */
205   private static class InitializationMonitor extends HasThread {
206     /** The amount of time in milliseconds to sleep before checking initialization status. */
207     public static final String TIMEOUT_KEY = "hbase.master.initializationmonitor.timeout";
208     public static final long TIMEOUT_DEFAULT = TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES);
209 
210     /**
211      * When timeout expired and initialization has not complete, call {@link System#exit(int)} when
212      * true, do nothing otherwise.
213      */
214     public static final String HALT_KEY = "hbase.master.initializationmonitor.haltontimeout";
215     public static final boolean HALT_DEFAULT = false;
216 
217     private final HMaster master;
218     private final long timeout;
219     private final boolean haltOnTimeout;
220 
221     /** Creates a Thread that monitors the {@link #isInitialized()} state. */
222     InitializationMonitor(HMaster master) {
223       super("MasterInitializationMonitor");
224       this.master = master;
225       this.timeout = master.getConfiguration().getLong(TIMEOUT_KEY, TIMEOUT_DEFAULT);
226       this.haltOnTimeout = master.getConfiguration().getBoolean(HALT_KEY, HALT_DEFAULT);
227       this.setDaemon(true);
228     }
229 
230     @Override
231     public void run() {
232       try {
233         while (!master.isStopped() && master.isActiveMaster()) {
234           Thread.sleep(timeout);
235           if (master.isInitialized()) {
236             LOG.debug("Initialization completed within allotted tolerance. Monitor exiting.");
237           } else {
238             LOG.error("Master failed to complete initialization after " + timeout + "ms. Please"
239                 + " consider submitting a bug report including a thread dump of this process.");
240             if (haltOnTimeout) {
241               LOG.error("Zombie Master exiting. Thread dump to stdout");
242               Threads.printThreadInfo(System.out, "Zombie HMaster");
243               System.exit(-1);
244             }
245           }
246         }
247       } catch (InterruptedException ie) {
248         LOG.trace("InitMonitor thread interrupted. Existing.");
249       }
250     }
251   }
252 
253   // MASTER is name of the webapp and the attribute name used stuffing this
254   //instance into web context.
255   public static final String MASTER = "master";
256 
257   // Manager and zk listener for master election
258   private final ActiveMasterManager activeMasterManager;
259   // Region server tracker
260   RegionServerTracker regionServerTracker;
261   // Draining region server tracker
262   private DrainingServerTracker drainingServerTracker;
263   // Tracker for load balancer state
264   LoadBalancerTracker loadBalancerTracker;
265 
266   // Tracker for split and merge state
267   private SplitOrMergeTracker splitOrMergeTracker;
268 
269   // Tracker for region normalizer state
270   private RegionNormalizerTracker regionNormalizerTracker;
271 
272   /** Namespace stuff */
273   private TableNamespaceManager tableNamespaceManager;
274 
275   // Metrics for the HMaster
276   final MetricsMaster metricsMaster;
277   // file system manager for the master FS operations
278   private MasterFileSystem fileSystemManager;
279 
280   // server manager to deal with region server info
281   volatile ServerManager serverManager;
282 
283   // manager of assignment nodes in zookeeper
284   AssignmentManager assignmentManager;
285 
286   // buffer for "fatal error" notices from region servers
287   // in the cluster. This is only used for assisting
288   // operations/debugging.
289   MemoryBoundedLogMessageBuffer rsFatals;
290 
291   // flag set after we become the active master (used for testing)
292   private volatile boolean isActiveMaster = false;
293 
294   // flag set after we complete initialization once active,
295   // it is not private since it's used in unit tests
296   private final ProcedureEvent initialized = new ProcedureEvent("master initialized");
297 
298   // flag set after master services are started,
299   // initialization may have not completed yet.
300   volatile boolean serviceStarted = false;
301 
302   // flag set after we complete assignMeta.
303   private final ProcedureEvent serverCrashProcessingEnabled =
304     new ProcedureEvent("server crash processing");
305 
306   LoadBalancer balancer;
307   private RegionNormalizer normalizer;
308   private BalancerChore balancerChore;
309   private RegionNormalizerChore normalizerChore;
310   private ClusterStatusChore clusterStatusChore;
311   private ClusterStatusPublisher clusterStatusPublisherChore = null;
312   private PeriodicDoMetrics periodicDoMetricsChore = null;
313 
314   CatalogJanitor catalogJanitorChore;
315   private ReplicationZKLockCleanerChore replicationZKLockCleanerChore;
316   private LogCleaner logCleaner;
317   private HFileCleaner hfileCleaner;
318 
319   MasterCoprocessorHost cpHost;
320 
321   private final boolean preLoadTableDescriptors;
322 
323   // Time stamps for when a hmaster became active
324   private long masterActiveTime;
325 
326   //should we check the compression codec type at master side, default true, HBASE-6370
327   private final boolean masterCheckCompression;
328 
329   //should we check encryption settings at master side, default true
330   private final boolean masterCheckEncryption;
331 
332   Map<String, Service> coprocessorServiceHandlers = Maps.newHashMap();
333 
334   // monitor for snapshot of hbase tables
335   SnapshotManager snapshotManager;
336   // monitor for distributed procedures
337   MasterProcedureManagerHost mpmHost;
338 
339   // it is assigned after 'initialized' guard set to true, so should be volatile
340   private volatile MasterQuotaManager quotaManager;
341 
342   private ProcedureExecutor<MasterProcedureEnv> procedureExecutor;
343   private WALProcedureStore procedureStore;
344 
345   private long splitPlanCount;
346   private long mergePlanCount;
347 
348   /** flag used in test cases in order to simulate RS failures during master initialization */
349   private volatile boolean initializationBeforeMetaAssignment = false;
350 
351   /** jetty server for master to redirect requests to regionserver infoServer */
352   private org.mortbay.jetty.Server masterJettyServer;
353 
354   public static class RedirectServlet extends HttpServlet {
355     private static final long serialVersionUID = 2894774810058302473L;
356     private final int regionServerInfoPort;
357     private final String regionServerHostname;
358 
359     /**
360      * @param infoServer that we're trying to send all requests to
361      * @param hostname may be null. if given, will be used for redirects instead of host from client.
362      */
363     public RedirectServlet(InfoServer infoServer, String hostname) {
364        regionServerInfoPort = infoServer.getPort();
365        regionServerHostname = hostname;
366     }
367 
368     @Override
369     public void doGet(HttpServletRequest request,
370         HttpServletResponse response) throws ServletException, IOException {
371       String redirectHost = regionServerHostname;
372       if(redirectHost == null) {
373         redirectHost = request.getServerName();
374         if(!Addressing.isLocalAddress(InetAddress.getByName(redirectHost))) {
375           LOG.warn("Couldn't resolve '" + redirectHost + "' as an address local to this node and '" +
376               MASTER_HOSTNAME_KEY + "' is not set; client will get a HTTP 400 response. If " +
377               "your HBase deployment relies on client accessible names that the region server process " +
378               "can't resolve locally, then you should set the previously mentioned configuration variable " +
379               "to an appropriate hostname.");
380           // no sending client provided input back to the client, so the goal host is just in the logs.
381           response.sendError(400, "Request was to a host that I can't resolve for any of the network interfaces on " +
382               "this node. If this is due to an intermediary such as an HTTP load balancer or other proxy, your HBase " +
383               "administrator can set '" + MASTER_HOSTNAME_KEY + "' to point to the correct hostname.");
384           return;
385         }
386       }
387       // TODO this scheme should come from looking at the scheme registered in the infoserver's http server for the
388       // host and port we're using, but it's buried way too deep to do that ATM.
389       String redirectUrl = request.getScheme() + "://"
390         + redirectHost + ":" + regionServerInfoPort
391         + request.getRequestURI();
392       response.sendRedirect(redirectUrl);
393     }
394   }
395 
396   private static class PeriodicDoMetrics extends ScheduledChore {
397     private final HMaster server;
398     public PeriodicDoMetrics(int doMetricsInterval, final HMaster server) {
399       super(server.getServerName() + "-DoMetricsChore", server, doMetricsInterval);
400       this.server = server;
401     }
402 
403     @Override
404     protected void chore() {
405       server.doMetrics();
406     }
407   }
408 
409   /**
410    * Initializes the HMaster. The steps are as follows:
411    * <p>
412    * <ol>
413    * <li>Initialize the local HRegionServer
414    * <li>Start the ActiveMasterManager.
415    * </ol>
416    * <p>
417    * Remaining steps of initialization occur in
418    * #finishActiveMasterInitialization(MonitoredTask) after
419    * the master becomes the active one.
420    *
421    * @throws InterruptedException
422    * @throws KeeperException
423    * @throws IOException
424    */
425   public HMaster(final Configuration conf, CoordinatedStateManager csm)
426       throws IOException, KeeperException, InterruptedException {
427     super(conf, csm);
428     this.rsFatals = new MemoryBoundedLogMessageBuffer(
429       conf.getLong("hbase.master.buffer.for.rs.fatals", 1*1024*1024));
430 
431     LOG.info("hbase.rootdir=" + FSUtils.getRootDir(this.conf) +
432       ", hbase.cluster.distributed=" + this.conf.getBoolean(HConstants.CLUSTER_DISTRIBUTED, false));
433 
434     // Disable usage of meta replicas in the master
435     this.conf.setBoolean(HConstants.USE_META_REPLICAS, false);
436 
437     Replication.decorateMasterConfiguration(this.conf);
438 
439     // Hack! Maps DFSClient => Master for logs.  HDFS made this
440     // config param for task trackers, but we can piggyback off of it.
441     if (this.conf.get("mapreduce.task.attempt.id") == null) {
442       this.conf.set("mapreduce.task.attempt.id", "hb_m_" + this.serverName.toString());
443     }
444 
445     // should we check the compression codec type at master side, default true, HBASE-6370
446     this.masterCheckCompression = conf.getBoolean("hbase.master.check.compression", true);
447 
448     // should we check encryption settings at master side, default true
449     this.masterCheckEncryption = conf.getBoolean("hbase.master.check.encryption", true);
450 
451     this.metricsMaster = new MetricsMaster(new MetricsMasterWrapperImpl(this));
452 
453     // preload table descriptor at startup
454     this.preLoadTableDescriptors = conf.getBoolean("hbase.master.preload.tabledescriptors", true);
455 
456     // Do we publish the status?
457 
458     boolean shouldPublish = conf.getBoolean(HConstants.STATUS_PUBLISHED,
459         HConstants.STATUS_PUBLISHED_DEFAULT);
460     Class<? extends ClusterStatusPublisher.Publisher> publisherClass =
461         conf.getClass(ClusterStatusPublisher.STATUS_PUBLISHER_CLASS,
462             ClusterStatusPublisher.DEFAULT_STATUS_PUBLISHER_CLASS,
463             ClusterStatusPublisher.Publisher.class);
464 
465     if (shouldPublish) {
466       if (publisherClass == null) {
467         LOG.warn(HConstants.STATUS_PUBLISHED + " is true, but " +
468             ClusterStatusPublisher.DEFAULT_STATUS_PUBLISHER_CLASS +
469             " is not set - not publishing status");
470       } else {
471         clusterStatusPublisherChore = new ClusterStatusPublisher(this, conf, publisherClass);
472         getChoreService().scheduleChore(clusterStatusPublisherChore);
473       }
474     }
475 
476     // Some unit tests don't need a cluster, so no zookeeper at all
477     if (!conf.getBoolean("hbase.testing.nocluster", false)) {
478       activeMasterManager = new ActiveMasterManager(zooKeeper, this.serverName, this);
479       int infoPort = putUpJettyServer();
480       startActiveMasterManager(infoPort);
481     } else {
482       activeMasterManager = null;
483     }
484   }
485 
486   // return the actual infoPort, -1 means disable info server.
487   private int putUpJettyServer() throws IOException {
488     if (!conf.getBoolean("hbase.master.infoserver.redirect", true)) {
489       return -1;
490     }
491     final int infoPort = conf.getInt("hbase.master.info.port.orig",
492       HConstants.DEFAULT_MASTER_INFOPORT);
493     // -1 is for disabling info server, so no redirecting
494     if (infoPort < 0 || infoServer == null) {
495       return -1;
496     }
497     if(infoPort == infoServer.getPort()) {
498       return infoPort;
499     }
500     final String addr = conf.get("hbase.master.info.bindAddress", "0.0.0.0");
501     if (!Addressing.isLocalAddress(InetAddress.getByName(addr))) {
502       String msg =
503           "Failed to start redirecting jetty server. Address " + addr
504               + " does not belong to this host. Correct configuration parameter: "
505               + "hbase.master.info.bindAddress";
506       LOG.error(msg);
507       throw new IOException(msg);
508     }
509 
510     // TODO I'm pretty sure we could just add another binding to the InfoServer run by
511     // the RegionServer and have it run the RedirectServlet instead of standing up
512     // a second entire stack here.
513     masterJettyServer = new org.mortbay.jetty.Server();
514     Connector connector = new SelectChannelConnector();
515     connector.setHost(addr);
516     connector.setPort(infoPort);
517     masterJettyServer.addConnector(connector);
518     masterJettyServer.setStopAtShutdown(true);
519 
520     final String redirectHostname = shouldUseThisHostnameInstead() ? useThisHostnameInstead : null;
521 
522     final RedirectServlet redirect = new RedirectServlet(infoServer, redirectHostname);
523     Context context = new Context(masterJettyServer, "/", Context.NO_SESSIONS);
524     context.addServlet(new ServletHolder(redirect), "/*");
525 
526     try {
527       masterJettyServer.start();
528     } catch (Exception e) {
529       throw new IOException("Failed to start redirecting jetty server", e);
530     }
531     return connector.getLocalPort();
532   }
533 
534   /**
535    * For compatibility, if failed with regionserver credentials, try the master one
536    */
537   @Override
538   protected void login(UserProvider user, String host) throws IOException {
539     try {
540       super.login(user, host);
541     } catch (IOException ie) {
542       user.login("hbase.master.keytab.file",
543         "hbase.master.kerberos.principal", host);
544     }
545   }
546 
547   /**
548    * If configured to put regions on active master,
549    * wait till a backup master becomes active.
550    * Otherwise, loop till the server is stopped or aborted.
551    */
552   @Override
553   protected void waitForMasterActive(){
554     boolean tablesOnMaster = BaseLoadBalancer.tablesOnMaster(conf);
555     while (!(tablesOnMaster && isActiveMaster)
556         && !isStopped() && !isAborted()) {
557       sleeper.sleep();
558     }
559   }
560 
561   @VisibleForTesting
562   public MasterRpcServices getMasterRpcServices() {
563     return (MasterRpcServices)rpcServices;
564   }
565 
566   public boolean balanceSwitch(final boolean b) throws IOException {
567     return getMasterRpcServices().switchBalancer(b, BalanceSwitchMode.ASYNC);
568   }
569 
570   @Override
571   protected String getProcessName() {
572     return MASTER;
573   }
574 
575   @Override
576   protected boolean canCreateBaseZNode() {
577     return true;
578   }
579 
580   @Override
581   protected boolean canUpdateTableDescriptor() {
582     return true;
583   }
584 
585   @Override
586   protected RSRpcServices createRpcServices() throws IOException {
587     return new MasterRpcServices(this);
588   }
589 
590   @Override
591   protected void configureInfoServer() {
592     infoServer.addServlet("master-status", "/master-status", MasterStatusServlet.class);
593     infoServer.setAttribute(MASTER, this);
594     if (BaseLoadBalancer.tablesOnMaster(conf)) {
595       super.configureInfoServer();
596     }
597   }
598 
599   @Override
600   protected Class<? extends HttpServlet> getDumpServlet() {
601     return MasterDumpServlet.class;
602   }
603 
604   /**
605    * Emit the HMaster metrics, such as region in transition metrics.
606    * Surrounding in a try block just to be sure metrics doesn't abort HMaster.
607    */
608   private void doMetrics() {
609     try {
610       if (assignmentManager != null) {
611         assignmentManager.updateRegionsInTransitionMetrics();
612       }
613     } catch (Throwable e) {
614       LOG.error("Couldn't update metrics: " + e.getMessage());
615     }
616   }
617 
618   MetricsMaster getMasterMetrics() {
619     return metricsMaster;
620   }
621 
622   /**
623    * Initialize all ZK based system trackers.
624    * @throws IOException
625    * @throws InterruptedException
626    * @throws KeeperException
627    * @throws CoordinatedStateException
628    */
629   void initializeZKBasedSystemTrackers() throws IOException,
630       InterruptedException, KeeperException, CoordinatedStateException {
631     this.balancer = LoadBalancerFactory.getLoadBalancer(conf);
632     this.normalizer = RegionNormalizerFactory.getRegionNormalizer(conf);
633     this.normalizer.setMasterServices(this);
634     this.normalizer.setMasterRpcServices((MasterRpcServices)rpcServices);
635     this.loadBalancerTracker = new LoadBalancerTracker(zooKeeper, this);
636     this.loadBalancerTracker.start();
637 
638     this.regionNormalizerTracker = new RegionNormalizerTracker(zooKeeper, this);
639     this.regionNormalizerTracker.start();
640 
641     this.splitOrMergeTracker = new SplitOrMergeTracker(zooKeeper, conf, this);
642     this.splitOrMergeTracker.start();
643 
644     this.assignmentManager = new AssignmentManager(this, serverManager,
645       this.balancer, this.service, this.metricsMaster,
646       this.tableLockManager);
647     zooKeeper.registerListenerFirst(assignmentManager);
648 
649     this.regionServerTracker = new RegionServerTracker(zooKeeper, this,
650         this.serverManager);
651     this.regionServerTracker.start();
652 
653     this.drainingServerTracker = new DrainingServerTracker(zooKeeper, this,
654       this.serverManager);
655     this.drainingServerTracker.start();
656 
657     // Set the cluster as up.  If new RSs, they'll be waiting on this before
658     // going ahead with their startup.
659     boolean wasUp = this.clusterStatusTracker.isClusterUp();
660     if (!wasUp) this.clusterStatusTracker.setClusterUp();
661 
662     LOG.info("Server active/primary master=" + this.serverName +
663         ", sessionid=0x" +
664         Long.toHexString(this.zooKeeper.getRecoverableZooKeeper().getSessionId()) +
665         ", setting cluster-up flag (Was=" + wasUp + ")");
666 
667     // create/initialize the snapshot manager and other procedure managers
668     this.snapshotManager = new SnapshotManager();
669     this.mpmHost = new MasterProcedureManagerHost();
670     this.mpmHost.register(this.snapshotManager);
671     this.mpmHost.register(new MasterFlushTableProcedureManager());
672     this.mpmHost.loadProcedures(conf);
673     this.mpmHost.initialize(this, this.metricsMaster);
674   }
675 
676   /**
677    * Finish initialization of HMaster after becoming the primary master.
678    *
679    * <ol>
680    * <li>Initialize master components - file system manager, server manager,
681    *     assignment manager, region server tracker, etc</li>
682    * <li>Start necessary service threads - balancer, catalog janior,
683    *     executor services, etc</li>
684    * <li>Set cluster as UP in ZooKeeper</li>
685    * <li>Wait for RegionServers to check-in</li>
686    * <li>Split logs and perform data recovery, if necessary</li>
687    * <li>Ensure assignment of meta/namespace regions<li>
688    * <li>Handle either fresh cluster start or master failover</li>
689    * </ol>
690    *
691    * @throws IOException
692    * @throws InterruptedException
693    * @throws KeeperException
694    * @throws CoordinatedStateException
695    */
696   private void finishActiveMasterInitialization(MonitoredTask status)
697       throws IOException, InterruptedException, KeeperException, CoordinatedStateException {
698 
699     isActiveMaster = true;
700     Thread zombieDetector = new Thread(new InitializationMonitor(this),
701         "ActiveMasterInitializationMonitor-" + System.currentTimeMillis());
702     zombieDetector.start();
703 
704     /*
705      * We are active master now... go initialize components we need to run.
706      * Note, there may be dross in zk from previous runs; it'll get addressed
707      * below after we determine if cluster startup or failover.
708      */
709 
710     status.setStatus("Initializing Master file system");
711 
712     this.masterActiveTime = System.currentTimeMillis();
713     // TODO: Do this using Dependency Injection, using PicoContainer, Guice or Spring.
714     this.fileSystemManager = new MasterFileSystem(this, this);
715 
716     // enable table descriptors cache
717     this.tableDescriptors.setCacheOn();
718     // set the META's descriptor to the correct replication
719     this.tableDescriptors.get(TableName.META_TABLE_NAME).setRegionReplication(
720         conf.getInt(HConstants.META_REPLICAS_NUM, HConstants.DEFAULT_META_REPLICA_NUM));
721     // warm-up HTDs cache on master initialization
722     if (preLoadTableDescriptors) {
723       status.setStatus("Pre-loading table descriptors");
724       this.tableDescriptors.getAll();
725     }
726 
727     // publish cluster ID
728     status.setStatus("Publishing Cluster ID in ZooKeeper");
729     ZKClusterId.setClusterId(this.zooKeeper, fileSystemManager.getClusterId());
730     this.serverManager = createServerManager(this, this);
731 
732     setupClusterConnection();
733 
734     // Invalidate all write locks held previously
735     this.tableLockManager.reapWriteLocks();
736 
737     status.setStatus("Initializing ZK system trackers");
738     initializeZKBasedSystemTrackers();
739 
740     // initialize master side coprocessors before we start handling requests
741     status.setStatus("Initializing master coprocessors");
742     this.cpHost = new MasterCoprocessorHost(this, this.conf);
743 
744     // start up all service threads.
745     status.setStatus("Initializing master service threads");
746     startServiceThreads();
747 
748     // Wake up this server to check in
749     sleeper.skipSleepCycle();
750 
751     // Wait for region servers to report in
752     this.serverManager.waitForRegionServers(status);
753     // Check zk for region servers that are up but didn't register
754     for (ServerName sn: this.regionServerTracker.getOnlineServers()) {
755       // The isServerOnline check is opportunistic, correctness is handled inside
756       if (!this.serverManager.isServerOnline(sn)
757           && serverManager.checkAndRecordNewServer(sn, ServerLoad.EMPTY_SERVERLOAD)) {
758         LOG.info("Registered server found up in zk but who has not yet reported in: " + sn);
759       }
760     }
761 
762     // get a list for previously failed RS which need log splitting work
763     // we recover hbase:meta region servers inside master initialization and
764     // handle other failed servers in SSH in order to start up master node ASAP
765     Set<ServerName> previouslyFailedServers =
766       this.fileSystemManager.getFailedServersFromLogFolders();
767 
768     // log splitting for hbase:meta server
769     ServerName oldMetaServerLocation = metaTableLocator.getMetaRegionLocation(this.getZooKeeper());
770     if (oldMetaServerLocation != null && previouslyFailedServers.contains(oldMetaServerLocation)) {
771       splitMetaLogBeforeAssignment(oldMetaServerLocation);
772       // Note: we can't remove oldMetaServerLocation from previousFailedServers list because it
773       // may also host user regions
774     }
775     Set<ServerName> previouslyFailedMetaRSs = getPreviouselyFailedMetaServersFromZK();
776     // need to use union of previouslyFailedMetaRSs recorded in ZK and previouslyFailedServers
777     // instead of previouslyFailedMetaRSs alone to address the following two situations:
778     // 1) the chained failure situation(recovery failed multiple times in a row).
779     // 2) master get killed right before it could delete the recovering hbase:meta from ZK while the
780     // same server still has non-meta wals to be replayed so that
781     // removeStaleRecoveringRegionsFromZK can't delete the stale hbase:meta region
782     // Passing more servers into splitMetaLog is all right. If a server doesn't have hbase:meta wal,
783     // there is no op for the server.
784     previouslyFailedMetaRSs.addAll(previouslyFailedServers);
785 
786     this.initializationBeforeMetaAssignment = true;
787 
788     // Wait for regionserver to finish initialization.
789     if (BaseLoadBalancer.tablesOnMaster(conf)) {
790       waitForServerOnline();
791     }
792 
793     //initialize load balancer
794     this.balancer.setClusterStatus(getClusterStatus());
795     this.balancer.setMasterServices(this);
796     this.balancer.initialize();
797 
798     // Check if master is shutting down because of some issue
799     // in initializing the regionserver or the balancer.
800     if (isStopped()) return;
801 
802     // Make sure meta assigned before proceeding.
803     status.setStatus("Assigning Meta Region");
804     assignMeta(status, previouslyFailedMetaRSs, HRegionInfo.DEFAULT_REPLICA_ID);
805     // check if master is shutting down because above assignMeta could return even hbase:meta isn't
806     // assigned when master is shutting down
807     if (isStopped()) return;
808 
809     status.setStatus("Submitting log splitting work for previously failed region servers");
810     // Master has recovered hbase:meta region server and we put
811     // other failed region servers in a queue to be handled later by SSH
812     for (ServerName tmpServer : previouslyFailedServers) {
813       this.serverManager.processDeadServer(tmpServer, true);
814     }
815 
816     // Update meta with new PB serialization if required. i.e migrate all HRI to PB serialization
817     // in meta. This must happen before we assign all user regions or else the assignment will fail.
818     if (this.conf.getBoolean("hbase.MetaMigrationConvertingToPB", true)) {
819       MetaMigrationConvertingToPB.updateMetaIfNecessary(this);
820     }
821 
822     // Fix up assignment manager status
823     status.setStatus("Starting assignment manager");
824     this.assignmentManager.joinCluster();
825 
826     // set cluster status again after user regions are assigned
827     this.balancer.setClusterStatus(getClusterStatus());
828 
829     // Start balancer and meta catalog janitor after meta and regions have been assigned.
830     status.setStatus("Starting balancer and catalog janitor");
831     this.clusterStatusChore = new ClusterStatusChore(this, balancer);
832     getChoreService().scheduleChore(clusterStatusChore);
833     this.balancerChore = new BalancerChore(this);
834     getChoreService().scheduleChore(balancerChore);
835     this.normalizerChore = new RegionNormalizerChore(this);
836     getChoreService().scheduleChore(normalizerChore);
837     this.catalogJanitorChore = new CatalogJanitor(this, this);
838     getChoreService().scheduleChore(catalogJanitorChore);
839 
840     // Do Metrics periodically
841     periodicDoMetricsChore = new PeriodicDoMetrics(msgInterval, this);
842     getChoreService().scheduleChore(periodicDoMetricsChore);
843 
844     status.setStatus("Starting namespace manager");
845     initNamespace();
846 
847     if (this.cpHost != null) {
848       try {
849         this.cpHost.preMasterInitialization();
850       } catch (IOException e) {
851         LOG.error("Coprocessor preMasterInitialization() hook failed", e);
852       }
853     }
854 
855     status.markComplete("Initialization successful");
856     LOG.info("Master has completed initialization");
857     configurationManager.registerObserver(this.balancer);
858 
859     // Set master as 'initialized'.
860     setInitialized(true);
861 
862     status.setStatus("Starting quota manager");
863     initQuotaManager();
864 
865     // assign the meta replicas
866     Set<ServerName> EMPTY_SET = new HashSet<ServerName>();
867     int numReplicas = conf.getInt(HConstants.META_REPLICAS_NUM,
868            HConstants.DEFAULT_META_REPLICA_NUM);
869     for (int i = 1; i < numReplicas; i++) {
870       assignMeta(status, EMPTY_SET, i);
871     }
872     unassignExcessMetaReplica(zooKeeper, numReplicas);
873 
874     // clear the dead servers with same host name and port of online server because we are not
875     // removing dead server with same hostname and port of rs which is trying to check in before
876     // master initialization. See HBASE-5916.
877     this.serverManager.clearDeadServersWithSameHostNameAndPortOfOnlineServer();
878 
879     // Check and set the znode ACLs if needed in case we are overtaking a non-secure configuration
880     status.setStatus("Checking ZNode ACLs");
881     zooKeeper.checkAndSetZNodeAcls();
882 
883     status.setStatus("Calling postStartMaster coprocessors");
884     if (this.cpHost != null) {
885       // don't let cp initialization errors kill the master
886       try {
887         this.cpHost.postStartMaster();
888       } catch (IOException ioe) {
889         LOG.error("Coprocessor postStartMaster() hook failed", ioe);
890       }
891     }
892 
893     zombieDetector.interrupt();
894   }
895 
896   private void initQuotaManager() throws IOException {
897     quotaManager = new MasterQuotaManager(this);
898     this.assignmentManager.setRegionStateListener((RegionStateListener) quotaManager);
899     quotaManager.start();
900   }
901 
902   /**
903    * Create a {@link ServerManager} instance.
904    * @param master
905    * @param services
906    * @return An instance of {@link ServerManager}
907    * @throws org.apache.hadoop.hbase.ZooKeeperConnectionException
908    * @throws IOException
909    */
910   ServerManager createServerManager(final Server master,
911       final MasterServices services)
912   throws IOException {
913     // We put this out here in a method so can do a Mockito.spy and stub it out
914     // w/ a mocked up ServerManager.
915     return new ServerManager(master, services);
916   }
917 
918   private void unassignExcessMetaReplica(ZooKeeperWatcher zkw, int numMetaReplicasConfigured) {
919     // unassign the unneeded replicas (for e.g., if the previous master was configured
920     // with a replication of 3 and now it is 2, we need to unassign the 1 unneeded replica)
921     try {
922       List<String> metaReplicaZnodes = zooKeeper.getMetaReplicaNodes();
923       for (String metaReplicaZnode : metaReplicaZnodes) {
924         int replicaId = zooKeeper.getMetaReplicaIdFromZnode(metaReplicaZnode);
925         if (replicaId >= numMetaReplicasConfigured) {
926           RegionState r = MetaTableLocator.getMetaRegionState(zkw, replicaId);
927           LOG.info("Closing excess replica of meta region " + r.getRegion());
928           // send a close and wait for a max of 30 seconds
929           ServerManager.closeRegionSilentlyAndWait(getConnection(), r.getServerName(),
930               r.getRegion(), 30000);
931           ZKUtil.deleteNode(zkw, zkw.getZNodeForReplica(replicaId));
932         }
933       }
934     } catch (Exception ex) {
935       // ignore the exception since we don't want the master to be wedged due to potential
936       // issues in the cleanup of the extra regions. We can do that cleanup via hbck or manually
937       LOG.warn("Ignoring exception " + ex);
938     }
939   }
940 
941   /**
942    * Check <code>hbase:meta</code> is assigned. If not, assign it.
943    * @param status MonitoredTask
944    * @param previouslyFailedMetaRSs
945    * @param replicaId
946    * @throws InterruptedException
947    * @throws IOException
948    * @throws KeeperException
949    */
950   void assignMeta(MonitoredTask status, Set<ServerName> previouslyFailedMetaRSs, int replicaId)
951       throws InterruptedException, IOException, KeeperException {
952     // Work on meta region
953     int assigned = 0;
954     long timeout = this.conf.getLong("hbase.catalog.verification.timeout", 1000);
955     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) {
956       status.setStatus("Assigning hbase:meta region");
957     } else {
958       status.setStatus("Assigning hbase:meta region, replicaId " + replicaId);
959     }
960     // Get current meta state from zk.
961     RegionStates regionStates = assignmentManager.getRegionStates();
962     RegionState metaState = MetaTableLocator.getMetaRegionState(getZooKeeper(), replicaId);
963     HRegionInfo hri = RegionReplicaUtil.getRegionInfoForReplica(HRegionInfo.FIRST_META_REGIONINFO,
964         replicaId);
965     ServerName currentMetaServer = metaState.getServerName();
966     if (!ConfigUtil.useZKForAssignment(conf)) {
967       regionStates.createRegionState(hri, metaState.getState(),
968         currentMetaServer, null);
969     } else {
970       regionStates.createRegionState(hri);
971     }
972     boolean rit = this.assignmentManager.
973       processRegionInTransitionAndBlockUntilAssigned(hri);
974     boolean metaRegionLocation = metaTableLocator.verifyMetaRegionLocation(
975       this.getConnection(), this.getZooKeeper(), timeout, replicaId);
976     if (!metaRegionLocation || !metaState.isOpened()) {
977       // Meta location is not verified. It should be in transition, or offline.
978       // We will wait for it to be assigned in enableSSHandWaitForMeta below.
979       assigned++;
980       if (!ConfigUtil.useZKForAssignment(conf)) {
981         assignMetaZkLess(regionStates, metaState, timeout, previouslyFailedMetaRSs);
982       } else if (!rit) {
983         // Assign meta since not already in transition
984         if (currentMetaServer != null) {
985           // If the meta server is not known to be dead or online,
986           // just split the meta log, and don't expire it since this
987           // could be a full cluster restart. Otherwise, we will think
988           // this is a failover and lose previous region locations.
989           // If it is really a failover case, AM will find out in rebuilding
990           // user regions. Otherwise, we are good since all logs are split
991           // or known to be replayed before user regions are assigned.
992           if (serverManager.isServerOnline(currentMetaServer)) {
993             LOG.info("Forcing expire of " + currentMetaServer);
994             serverManager.expireServer(currentMetaServer);
995           }
996           if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) {
997             splitMetaLogBeforeAssignment(currentMetaServer);
998             previouslyFailedMetaRSs.add(currentMetaServer);
999           }
1000         }
1001         assignmentManager.assignMeta(hri);
1002       }
1003     } else {
1004       // Region already assigned. We didn't assign it. Add to in-memory state.
1005       regionStates.updateRegionState(hri, State.OPEN, currentMetaServer);
1006       this.assignmentManager.regionOnline(hri, currentMetaServer);
1007     }
1008 
1009     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) enableMeta(TableName.META_TABLE_NAME);
1010 
1011     if ((RecoveryMode.LOG_REPLAY == this.getMasterFileSystem().getLogRecoveryMode())
1012         && (!previouslyFailedMetaRSs.isEmpty())) {
1013       // replay WAL edits mode need new hbase:meta RS is assigned firstly
1014       status.setStatus("replaying log for Meta Region");
1015       this.fileSystemManager.splitMetaLog(previouslyFailedMetaRSs);
1016     }
1017 
1018     // Make sure a hbase:meta location is set. We need to enable SSH here since
1019     // if the meta region server is died at this time, we need it to be re-assigned
1020     // by SSH so that system tables can be assigned.
1021     // No need to wait for meta is assigned = 0 when meta is just verified.
1022     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) enableCrashedServerProcessing(assigned != 0);
1023     LOG.info("hbase:meta with replicaId " + replicaId + " assigned=" + assigned + ", rit=" + rit +
1024       ", location=" + metaTableLocator.getMetaRegionLocation(this.getZooKeeper(), replicaId));
1025     status.setStatus("META assigned.");
1026   }
1027 
1028   private void assignMetaZkLess(RegionStates regionStates, RegionState regionState, long timeout,
1029       Set<ServerName> previouslyFailedRs) throws IOException, KeeperException {
1030     ServerName currentServer = regionState.getServerName();
1031     if (serverManager.isServerOnline(currentServer)) {
1032       LOG.info("Meta was in transition on " + currentServer);
1033       assignmentManager.processRegionInTransitionZkLess();
1034     } else {
1035       if (currentServer != null) {
1036         if (regionState.getRegion().getReplicaId() == HRegionInfo.DEFAULT_REPLICA_ID) {
1037           splitMetaLogBeforeAssignment(currentServer);
1038           regionStates.logSplit(HRegionInfo.FIRST_META_REGIONINFO);
1039           previouslyFailedRs.add(currentServer);
1040         }
1041       }
1042       LOG.info("Re-assigning hbase:meta, it was on " + currentServer);
1043       regionStates.updateRegionState(regionState.getRegion(), State.OFFLINE);
1044       assignmentManager.assignMeta(regionState.getRegion());
1045     }
1046   }
1047 
1048   void initNamespace() throws IOException {
1049     //create namespace manager
1050     tableNamespaceManager = new TableNamespaceManager(this);
1051     tableNamespaceManager.start();
1052   }
1053 
1054   boolean isCatalogJanitorEnabled() {
1055     return catalogJanitorChore != null ?
1056       catalogJanitorChore.getEnabled() : false;
1057   }
1058 
1059   private void splitMetaLogBeforeAssignment(ServerName currentMetaServer) throws IOException {
1060     if (RecoveryMode.LOG_REPLAY == this.getMasterFileSystem().getLogRecoveryMode()) {
1061       // In log replay mode, we mark hbase:meta region as recovering in ZK
1062       Set<HRegionInfo> regions = new HashSet<HRegionInfo>();
1063       regions.add(HRegionInfo.FIRST_META_REGIONINFO);
1064       this.fileSystemManager.prepareLogReplay(currentMetaServer, regions);
1065     } else {
1066       // In recovered.edits mode: create recovered edits file for hbase:meta server
1067       this.fileSystemManager.splitMetaLog(currentMetaServer);
1068     }
1069   }
1070 
1071   private void enableCrashedServerProcessing(final boolean waitForMeta)
1072   throws IOException, InterruptedException {
1073     // If crashed server processing is disabled, we enable it and expire those dead but not expired
1074     // servers. This is required so that if meta is assigning to a server which dies after
1075     // assignMeta starts assignment, ServerCrashProcedure can re-assign it. Otherwise, we will be
1076     // stuck here waiting forever if waitForMeta is specified.
1077     if (!isServerCrashProcessingEnabled()) {
1078       setServerCrashProcessingEnabled(true);
1079       this.serverManager.processQueuedDeadServers();
1080     }
1081 
1082     if (waitForMeta) {
1083       metaTableLocator.waitMetaRegionLocation(this.getZooKeeper());
1084       // Above check waits for general meta availability but this does not
1085       // guarantee that the transition has completed
1086       this.assignmentManager.waitForAssignment(HRegionInfo.FIRST_META_REGIONINFO);
1087     }
1088   }
1089 
1090   private void enableMeta(TableName metaTableName) {
1091     if (!this.assignmentManager.getTableStateManager().isTableState(metaTableName,
1092         ZooKeeperProtos.Table.State.ENABLED)) {
1093       this.assignmentManager.setEnabledTable(metaTableName);
1094     }
1095   }
1096 
1097   /**
1098    * This function returns a set of region server names under hbase:meta recovering region ZK node
1099    * @return Set of meta server names which were recorded in ZK
1100    * @throws KeeperException
1101    */
1102   private Set<ServerName> getPreviouselyFailedMetaServersFromZK() throws KeeperException {
1103     Set<ServerName> result = new HashSet<ServerName>();
1104     String metaRecoveringZNode = ZKUtil.joinZNode(zooKeeper.recoveringRegionsZNode,
1105       HRegionInfo.FIRST_META_REGIONINFO.getEncodedName());
1106     List<String> regionFailedServers = ZKUtil.listChildrenNoWatch(zooKeeper, metaRecoveringZNode);
1107     if (regionFailedServers == null) return result;
1108 
1109     for(String failedServer : regionFailedServers) {
1110       ServerName server = ServerName.parseServerName(failedServer);
1111       result.add(server);
1112     }
1113     return result;
1114   }
1115 
1116   @Override
1117   public TableDescriptors getTableDescriptors() {
1118     return this.tableDescriptors;
1119   }
1120 
1121   @Override
1122   public ServerManager getServerManager() {
1123     return this.serverManager;
1124   }
1125 
1126   @Override
1127   public MasterFileSystem getMasterFileSystem() {
1128     return this.fileSystemManager;
1129   }
1130 
1131   @Override
1132   public TableNamespaceManager getTableNamespaceManager() {
1133     return tableNamespaceManager;
1134   }
1135 
1136   /*
1137    * Start up all services. If any of these threads gets an unhandled exception
1138    * then they just die with a logged message.  This should be fine because
1139    * in general, we do not expect the master to get such unhandled exceptions
1140    *  as OOMEs; it should be lightly loaded. See what HRegionServer does if
1141    *  need to install an unexpected exception handler.
1142    */
1143   private void startServiceThreads() throws IOException{
1144    // Start the executor service pools
1145    this.service.startExecutorService(ExecutorType.MASTER_OPEN_REGION,
1146       conf.getInt("hbase.master.executor.openregion.threads", 5));
1147    this.service.startExecutorService(ExecutorType.MASTER_CLOSE_REGION,
1148       conf.getInt("hbase.master.executor.closeregion.threads", 5));
1149    this.service.startExecutorService(ExecutorType.MASTER_SERVER_OPERATIONS,
1150       conf.getInt("hbase.master.executor.serverops.threads", 5));
1151    this.service.startExecutorService(ExecutorType.MASTER_META_SERVER_OPERATIONS,
1152       conf.getInt("hbase.master.executor.serverops.threads", 5));
1153    this.service.startExecutorService(ExecutorType.M_LOG_REPLAY_OPS,
1154       conf.getInt("hbase.master.executor.logreplayops.threads", 10));
1155 
1156    // We depend on there being only one instance of this executor running
1157    // at a time.  To do concurrency, would need fencing of enable/disable of
1158    // tables.
1159    // Any time changing this maxThreads to > 1, pls see the comment at
1160    // AccessController#postCreateTableHandler
1161    this.service.startExecutorService(ExecutorType.MASTER_TABLE_OPERATIONS, 1);
1162    startProcedureExecutor();
1163 
1164    // Start log cleaner thread
1165    int cleanerInterval = conf.getInt("hbase.master.cleaner.interval", 60 * 1000);
1166    this.logCleaner =
1167       new LogCleaner(cleanerInterval,
1168          this, conf, getMasterFileSystem().getFileSystem(),
1169          getMasterFileSystem().getOldLogDir());
1170     getChoreService().scheduleChore(logCleaner);
1171 
1172    //start the hfile archive cleaner thread
1173     Path archiveDir = HFileArchiveUtil.getArchivePath(conf);
1174     this.hfileCleaner = new HFileCleaner(cleanerInterval, this, conf, getMasterFileSystem()
1175         .getFileSystem(), archiveDir);
1176     getChoreService().scheduleChore(hfileCleaner);
1177     serviceStarted = true;
1178     if (LOG.isTraceEnabled()) {
1179       LOG.trace("Started service threads");
1180     }
1181     if (!conf.getBoolean(HConstants.ZOOKEEPER_USEMULTI, true)) {
1182       try {
1183         replicationZKLockCleanerChore = new ReplicationZKLockCleanerChore(this, this,
1184             cleanerInterval, this.getZooKeeper(), this.conf);
1185         getChoreService().scheduleChore(replicationZKLockCleanerChore);
1186       } catch (Exception e) {
1187         LOG.error("start replicationZKLockCleanerChore failed", e);
1188       }
1189     }
1190   }
1191 
1192   @Override
1193   protected void sendShutdownInterrupt() {
1194     super.sendShutdownInterrupt();
1195     stopProcedureExecutor();
1196   }
1197 
1198   @Override
1199   protected void stopServiceThreads() {
1200     if (masterJettyServer != null) {
1201       LOG.info("Stopping master jetty server");
1202       try {
1203         masterJettyServer.stop();
1204       } catch (Exception e) {
1205         LOG.error("Failed to stop master jetty server", e);
1206       }
1207     }
1208     super.stopServiceThreads();
1209     stopChores();
1210 
1211     // Wait for all the remaining region servers to report in IFF we were
1212     // running a cluster shutdown AND we were NOT aborting.
1213     if (!isAborted() && this.serverManager != null &&
1214         this.serverManager.isClusterShutdown()) {
1215       this.serverManager.letRegionServersShutdown();
1216     }
1217     if (LOG.isDebugEnabled()) {
1218       LOG.debug("Stopping service threads");
1219     }
1220     // Clean up and close up shop
1221     if (this.logCleaner != null) this.logCleaner.cancel(true);
1222     if (this.hfileCleaner != null) this.hfileCleaner.cancel(true);
1223     if (this.replicationZKLockCleanerChore != null) this.replicationZKLockCleanerChore.cancel(true);
1224     if (this.quotaManager != null) this.quotaManager.stop();
1225     if (this.activeMasterManager != null) this.activeMasterManager.stop();
1226     if (this.serverManager != null) this.serverManager.stop();
1227     if (this.assignmentManager != null) this.assignmentManager.stop();
1228     if (this.fileSystemManager != null) this.fileSystemManager.stop();
1229     if (this.mpmHost != null) this.mpmHost.stop("server shutting down.");
1230   }
1231 
1232   private void startProcedureExecutor() throws IOException {
1233     final MasterProcedureEnv procEnv = new MasterProcedureEnv(this);
1234     final Path logDir = new Path(fileSystemManager.getRootDir(),
1235         MasterProcedureConstants.MASTER_PROCEDURE_LOGDIR);
1236 
1237     procedureStore = new WALProcedureStore(conf, fileSystemManager.getFileSystem(), logDir,
1238         new MasterProcedureEnv.WALStoreLeaseRecovery(this));
1239     procedureStore.registerListener(new MasterProcedureEnv.MasterProcedureStoreListener(this));
1240     procedureExecutor = new ProcedureExecutor(conf, procEnv, procedureStore,
1241         procEnv.getProcedureQueue());
1242 
1243     final int numThreads = conf.getInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS,
1244         Math.max(Runtime.getRuntime().availableProcessors(),
1245           MasterProcedureConstants.DEFAULT_MIN_MASTER_PROCEDURE_THREADS));
1246     final boolean abortOnCorruption = conf.getBoolean(
1247         MasterProcedureConstants.EXECUTOR_ABORT_ON_CORRUPTION,
1248         MasterProcedureConstants.DEFAULT_EXECUTOR_ABORT_ON_CORRUPTION);
1249     procedureStore.start(numThreads);
1250     procedureExecutor.start(numThreads, abortOnCorruption);
1251   }
1252 
1253   private void stopProcedureExecutor() {
1254     if (procedureExecutor != null) {
1255       procedureExecutor.stop();
1256     }
1257 
1258     if (procedureStore != null) {
1259       procedureStore.stop(isAborted());
1260     }
1261   }
1262 
1263   private void stopChores() {
1264     if (this.balancerChore != null) {
1265       this.balancerChore.cancel(true);
1266     }
1267     if (this.normalizerChore != null) {
1268       this.normalizerChore.cancel(true);
1269     }
1270     if (this.clusterStatusChore != null) {
1271       this.clusterStatusChore.cancel(true);
1272     }
1273     if (this.catalogJanitorChore != null) {
1274       this.catalogJanitorChore.cancel(true);
1275     }
1276     if (this.clusterStatusPublisherChore != null){
1277       clusterStatusPublisherChore.cancel(true);
1278     }
1279     if (this.periodicDoMetricsChore != null) {
1280       periodicDoMetricsChore.cancel();
1281     }
1282   }
1283 
1284   /**
1285    * @return Get remote side's InetAddress
1286    * @throws UnknownHostException
1287    */
1288   InetAddress getRemoteInetAddress(final int port,
1289       final long serverStartCode) throws UnknownHostException {
1290     // Do it out here in its own little method so can fake an address when
1291     // mocking up in tests.
1292     InetAddress ia = RpcServer.getRemoteIp();
1293 
1294     // The call could be from the local regionserver,
1295     // in which case, there is no remote address.
1296     if (ia == null && serverStartCode == startcode) {
1297       InetSocketAddress isa = rpcServices.getSocketAddress();
1298       if (isa != null && isa.getPort() == port) {
1299         ia = isa.getAddress();
1300       }
1301     }
1302     return ia;
1303   }
1304 
1305   /**
1306    * @return Maximum time we should run balancer for
1307    */
1308   private int getBalancerCutoffTime() {
1309     int balancerCutoffTime =
1310       getConfiguration().getInt("hbase.balancer.max.balancing", -1);
1311     if (balancerCutoffTime == -1) {
1312       // No time period set so create one
1313       int balancerPeriod =
1314         getConfiguration().getInt("hbase.balancer.period", 300000);
1315       balancerCutoffTime = balancerPeriod;
1316       // If nonsense period, set it to balancerPeriod
1317       if (balancerCutoffTime <= 0) balancerCutoffTime = balancerPeriod;
1318     }
1319     return balancerCutoffTime;
1320   }
1321 
1322   public boolean balance() throws IOException {
1323     return balance(false);
1324   }
1325 
1326   public boolean balance(boolean force) throws IOException {
1327     // if master not initialized, don't run balancer.
1328     if (!isInitialized()) {
1329       LOG.debug("Master has not been initialized, don't run balancer.");
1330       return false;
1331     }
1332     // Do this call outside of synchronized block.
1333     int maximumBalanceTime = getBalancerCutoffTime();
1334     synchronized (this.balancer) {
1335       // If balance not true, don't run balancer.
1336       if (!this.loadBalancerTracker.isBalancerOn()) return false;
1337       // Only allow one balance run at at time.
1338       if (this.assignmentManager.getRegionStates().isRegionsInTransition()) {
1339         Map<String, RegionState> regionsInTransition =
1340           this.assignmentManager.getRegionStates().getRegionsInTransition();
1341         // if hbase:meta region is in transition, result of assignment cannot be recorded
1342         // ignore the force flag in that case
1343         boolean metaInTransition = assignmentManager.getRegionStates().isMetaRegionInTransition();
1344         String prefix = force && !metaInTransition ? "R" : "Not r";
1345         LOG.debug(prefix + "unning balancer because " + regionsInTransition.size() +
1346           " region(s) in transition: " + org.apache.commons.lang.StringUtils.
1347             abbreviate(regionsInTransition.toString(), 256));
1348         if (!force || metaInTransition) return false;
1349       }
1350       if (this.serverManager.areDeadServersInProgress()) {
1351         LOG.debug("Not running balancer because processing dead regionserver(s): " +
1352           this.serverManager.getDeadServers());
1353         return false;
1354       }
1355 
1356       if (this.cpHost != null) {
1357         try {
1358           if (this.cpHost.preBalance()) {
1359             LOG.debug("Coprocessor bypassing balancer request");
1360             return false;
1361           }
1362         } catch (IOException ioe) {
1363           LOG.error("Error invoking master coprocessor preBalance()", ioe);
1364           return false;
1365         }
1366       }
1367 
1368       Map<TableName, Map<ServerName, List<HRegionInfo>>> assignmentsByTable =
1369         this.assignmentManager.getRegionStates().getAssignmentsByTable();
1370 
1371       List<RegionPlan> plans = new ArrayList<RegionPlan>();
1372 
1373       //Give the balancer the current cluster state.
1374       this.balancer.setClusterStatus(getClusterStatus());
1375       for (Entry<TableName, Map<ServerName, List<HRegionInfo>>> e : assignmentsByTable.entrySet()) {
1376         List<RegionPlan> partialPlans = this.balancer.balanceCluster(e.getKey(), e.getValue());
1377         if (partialPlans != null) plans.addAll(partialPlans);
1378       }
1379 
1380       long cutoffTime = System.currentTimeMillis() + maximumBalanceTime;
1381       int rpCount = 0;  // number of RegionPlans balanced so far
1382       long totalRegPlanExecTime = 0;
1383       if (plans != null && !plans.isEmpty()) {
1384         for (RegionPlan plan: plans) {
1385           LOG.info("balance " + plan);
1386           long balStartTime = System.currentTimeMillis();
1387           //TODO: bulk assign
1388           this.assignmentManager.balance(plan);
1389           totalRegPlanExecTime += System.currentTimeMillis()-balStartTime;
1390           rpCount++;
1391           if (rpCount < plans.size() &&
1392               // if performing next balance exceeds cutoff time, exit the loop
1393               (System.currentTimeMillis() + (totalRegPlanExecTime / rpCount)) > cutoffTime) {
1394             //TODO: After balance, there should not be a cutoff time (keeping it as a security net for now)
1395             LOG.debug("No more balancing till next balance run; maximumBalanceTime=" +
1396               maximumBalanceTime);
1397             break;
1398           }
1399         }
1400       }
1401       if (this.cpHost != null) {
1402         try {
1403           this.cpHost.postBalance(rpCount < plans.size() ? plans.subList(0, rpCount) : plans);
1404         } catch (IOException ioe) {
1405           // balancing already succeeded so don't change the result
1406           LOG.error("Error invoking master coprocessor postBalance()", ioe);
1407         }
1408       }
1409     }
1410     // If LoadBalancer did not generate any plans, it means the cluster is already balanced.
1411     // Return true indicating a success.
1412     return true;
1413   }
1414 
1415   /**
1416    * Perform normalization of cluster (invoked by {@link RegionNormalizerChore}).
1417    *
1418    * @return true if normalization step was performed successfully, false otherwise
1419    *   (specifically, if HMaster hasn't been initialized properly or normalization
1420    *   is globally disabled)
1421    * @throws IOException
1422    * @throws CoordinatedStateException
1423    */
1424   public boolean normalizeRegions() throws IOException, CoordinatedStateException {
1425     if (!isInitialized()) {
1426       LOG.debug("Master has not been initialized, don't run region normalizer.");
1427       return false;
1428     }
1429 
1430     if (!this.regionNormalizerTracker.isNormalizerOn()) {
1431       LOG.debug("Region normalization is disabled, don't run region normalizer.");
1432       return false;
1433     }
1434 
1435     synchronized (this.normalizer) {
1436       // Don't run the normalizer concurrently
1437       List<TableName> allEnabledTables = new ArrayList<>(
1438         this.assignmentManager.getTableStateManager().getTablesInStates(
1439           ZooKeeperProtos.Table.State.ENABLED));
1440 
1441       Collections.shuffle(allEnabledTables);
1442 
1443       for (TableName table : allEnabledTables) {
1444         if (quotaManager.getNamespaceQuotaManager() != null &&
1445             quotaManager.getNamespaceQuotaManager().getState(table.getNamespaceAsString()) != null){
1446           LOG.debug("Skipping normalizing " + table + " since its namespace has quota");
1447           continue;
1448         }
1449         if (table.isSystemTable() || (getTableDescriptors().get(table) != null &&
1450             !getTableDescriptors().get(table).isNormalizationEnabled())) {
1451           LOG.debug("Skipping normalization for table: " + table + ", as it's either system"
1452               + " table or doesn't have auto normalization turned on");
1453           continue;
1454         }
1455         List<NormalizationPlan> plans = this.normalizer.computePlanForTable(table);
1456         if (plans != null) {
1457           for (NormalizationPlan plan : plans) {
1458             plan.execute(clusterConnection.getAdmin());
1459             if (plan.getType() == PlanType.SPLIT) {
1460               splitPlanCount++;
1461             } else if (plan.getType() == PlanType.MERGE) {
1462               mergePlanCount++;
1463             }
1464           }
1465         }
1466       }
1467     }
1468     // If Region did not generate any plans, it means the cluster is already balanced.
1469     // Return true indicating a success.
1470     return true;
1471   }
1472 
1473   /**
1474    * @return Client info for use as prefix on an audit log string; who did an action
1475    */
1476   String getClientIdAuditPrefix() {
1477     return "Client=" + RpcServer.getRequestUserName() + "/" + RpcServer.getRemoteAddress();
1478   }
1479 
1480   /**
1481    * Switch for the background CatalogJanitor thread.
1482    * Used for testing.  The thread will continue to run.  It will just be a noop
1483    * if disabled.
1484    * @param b If false, the catalog janitor won't do anything.
1485    */
1486   public void setCatalogJanitorEnabled(final boolean b) {
1487     this.catalogJanitorChore.setEnabled(b);
1488   }
1489 
1490   @Override
1491   public void dispatchMergingRegions(final HRegionInfo region_a,
1492       final HRegionInfo region_b, final boolean forcible, final User user) throws IOException {
1493     checkInitialized();
1494     this.service.submit(new DispatchMergingRegionHandler(this,
1495       this.catalogJanitorChore, region_a, region_b, forcible, user));
1496   }
1497 
1498   void move(final byte[] encodedRegionName,
1499       final byte[] destServerName) throws HBaseIOException {
1500     RegionState regionState = assignmentManager.getRegionStates().
1501       getRegionState(Bytes.toString(encodedRegionName));
1502     if (regionState == null) {
1503       throw new UnknownRegionException(Bytes.toStringBinary(encodedRegionName));
1504     }
1505 
1506     HRegionInfo hri = regionState.getRegion();
1507     ServerName dest;
1508     if (destServerName == null || destServerName.length == 0) {
1509       LOG.info("Passed destination servername is null/empty so " +
1510         "choosing a server at random");
1511       final List<ServerName> destServers = this.serverManager.createDestinationServersList(
1512         regionState.getServerName());
1513       dest = balancer.randomAssignment(hri, destServers);
1514       if (dest == null) {
1515         LOG.debug("Unable to determine a plan to assign " + hri);
1516         return;
1517       }
1518     } else {
1519       dest = ServerName.valueOf(Bytes.toString(destServerName));
1520       if (dest.equals(serverName) && balancer instanceof BaseLoadBalancer
1521           && !((BaseLoadBalancer)balancer).shouldBeOnMaster(hri)) {
1522         // To avoid unnecessary region moving later by balancer. Don't put user
1523         // regions on master. Regions on master could be put on other region
1524         // server intentionally by test however.
1525         LOG.debug("Skipping move of region " + hri.getRegionNameAsString()
1526           + " to avoid unnecessary region moving later by load balancer,"
1527           + " because it should not be on master");
1528         return;
1529       }
1530     }
1531 
1532     if (dest.equals(regionState.getServerName())) {
1533       LOG.debug("Skipping move of region " + hri.getRegionNameAsString()
1534         + " because region already assigned to the same server " + dest + ".");
1535       return;
1536     }
1537 
1538     // Now we can do the move
1539     RegionPlan rp = new RegionPlan(hri, regionState.getServerName(), dest);
1540 
1541     try {
1542       checkInitialized();
1543       if (this.cpHost != null) {
1544         if (this.cpHost.preMove(hri, rp.getSource(), rp.getDestination())) {
1545           return;
1546         }
1547       }
1548       // warmup the region on the destination before initiating the move. this call
1549       // is synchronous and takes some time. doing it before the source region gets
1550       // closed
1551       serverManager.sendRegionWarmup(rp.getDestination(), hri);
1552 
1553       LOG.info(getClientIdAuditPrefix() + " move " + rp + ", running balancer");
1554       this.assignmentManager.balance(rp);
1555       if (this.cpHost != null) {
1556         this.cpHost.postMove(hri, rp.getSource(), rp.getDestination());
1557       }
1558     } catch (IOException ioe) {
1559       if (ioe instanceof HBaseIOException) {
1560         throw (HBaseIOException)ioe;
1561       }
1562       throw new HBaseIOException(ioe);
1563     }
1564   }
1565 
1566   @Override
1567   public long createTable(
1568       final HTableDescriptor hTableDescriptor,
1569       final byte [][] splitKeys,
1570       final long nonceGroup,
1571       final long nonce) throws IOException {
1572     if (isStopped()) {
1573       throw new MasterNotRunningException();
1574     }
1575 
1576     String namespace = hTableDescriptor.getTableName().getNamespaceAsString();
1577     ensureNamespaceExists(namespace);
1578 
1579     final HRegionInfo[] newRegions =
1580         ModifyRegionUtils.createHRegionInfos(hTableDescriptor, splitKeys);
1581     checkInitialized();
1582     sanityCheckTableDescriptor(hTableDescriptor);
1583 
1584     return MasterProcedureUtil.submitProcedure(
1585       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
1586       @Override
1587       protected void run() throws IOException {
1588         getMaster().getMasterCoprocessorHost().preCreateTable(hTableDescriptor, newRegions);
1589 
1590         LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor);
1591 
1592         // TODO: We can handle/merge duplicate requests, and differentiate the case of
1593         //       TableExistsException by saying if the schema is the same or not.
1594         ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch();
1595         submitProcedure(new CreateTableProcedure(
1596           procedureExecutor.getEnvironment(), hTableDescriptor, newRegions, latch));
1597         latch.await();
1598 
1599         getMaster().getMasterCoprocessorHost().postCreateTable(hTableDescriptor, newRegions);
1600       }
1601 
1602       @Override
1603       protected String getDescription() {
1604         return "CreateTableProcedure";
1605       }
1606     });
1607   }
1608 
1609   @Override
1610   public long createSystemTable(final HTableDescriptor hTableDescriptor) throws IOException {
1611     if (isStopped()) {
1612       throw new MasterNotRunningException();
1613     }
1614 
1615     TableName tableName = hTableDescriptor.getTableName();
1616     if (!(tableName.isSystemTable())) {
1617       throw new IllegalArgumentException(
1618         "Only system table creation can use this createSystemTable API");
1619     }
1620 
1621     HRegionInfo[] newRegions = ModifyRegionUtils.createHRegionInfos(hTableDescriptor, null);
1622 
1623     LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor);
1624 
1625     // This special create table is called locally to master.  Therefore, no RPC means no need
1626     // to use nonce to detect duplicated RPC call.
1627     long procId = this.procedureExecutor.submitProcedure(
1628       new CreateTableProcedure(procedureExecutor.getEnvironment(), hTableDescriptor, newRegions));
1629 
1630     return procId;
1631   }
1632 
1633   /**
1634    * Checks whether the table conforms to some sane limits, and configured
1635    * values (compression, etc) work. Throws an exception if something is wrong.
1636    * @throws IOException
1637    */
1638   private void sanityCheckTableDescriptor(final HTableDescriptor htd) throws IOException {
1639     final String CONF_KEY = "hbase.table.sanity.checks";
1640     boolean logWarn = false;
1641     if (!conf.getBoolean(CONF_KEY, true)) {
1642       logWarn = true;
1643     }
1644     String tableVal = htd.getConfigurationValue(CONF_KEY);
1645     if (tableVal != null && !Boolean.valueOf(tableVal)) {
1646       logWarn = true;
1647     }
1648 
1649     // check max file size
1650     long maxFileSizeLowerLimit = 2 * 1024 * 1024L; // 2M is the default lower limit
1651     long maxFileSize = htd.getMaxFileSize();
1652     if (maxFileSize < 0) {
1653       maxFileSize = conf.getLong(HConstants.HREGION_MAX_FILESIZE, maxFileSizeLowerLimit);
1654     }
1655     if (maxFileSize < conf.getLong("hbase.hregion.max.filesize.limit", maxFileSizeLowerLimit)) {
1656       String message = "MAX_FILESIZE for table descriptor or "
1657           + "\"hbase.hregion.max.filesize\" (" + maxFileSize
1658           + ") is too small, which might cause over splitting into unmanageable "
1659           + "number of regions.";
1660       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1661     }
1662 
1663     // check flush size
1664     long flushSizeLowerLimit = 1024 * 1024L; // 1M is the default lower limit
1665     long flushSize = htd.getMemStoreFlushSize();
1666     if (flushSize < 0) {
1667       flushSize = conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, flushSizeLowerLimit);
1668     }
1669     if (flushSize < conf.getLong("hbase.hregion.memstore.flush.size.limit", flushSizeLowerLimit)) {
1670       String message = "MEMSTORE_FLUSHSIZE for table descriptor or "
1671           + "\"hbase.hregion.memstore.flush.size\" ("+flushSize+") is too small, which might cause"
1672           + " very frequent flushing.";
1673       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1674     }
1675 
1676     // check that coprocessors and other specified plugin classes can be loaded
1677     try {
1678       checkClassLoading(conf, htd);
1679     } catch (Exception ex) {
1680       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, ex.getMessage(), null);
1681     }
1682 
1683     // check compression can be loaded
1684     try {
1685       checkCompression(htd);
1686     } catch (IOException e) {
1687       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, e.getMessage(), e);
1688     }
1689 
1690     // check encryption can be loaded
1691     try {
1692       checkEncryption(conf, htd);
1693     } catch (IOException e) {
1694       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, e.getMessage(), e);
1695     }
1696     // Verify compaction policy
1697     try{
1698       checkCompactionPolicy(conf, htd);
1699     } catch(IOException e){
1700       warnOrThrowExceptionForFailure(false, CONF_KEY, e.getMessage(), e);
1701     }
1702     // check that we have at least 1 CF
1703     if (htd.getColumnFamilies().length == 0) {
1704       String message = "Table should have at least one column family.";
1705       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1706     }
1707 
1708     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1709       if (hcd.getTimeToLive() <= 0) {
1710         String message = "TTL for column family " + hcd.getNameAsString() + " must be positive.";
1711         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1712       }
1713 
1714       // check blockSize
1715       if (hcd.getBlocksize() < 1024 || hcd.getBlocksize() > 16 * 1024 * 1024) {
1716         String message = "Block size for column family " + hcd.getNameAsString()
1717             + "  must be between 1K and 16MB.";
1718         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1719       }
1720 
1721       // check versions
1722       if (hcd.getMinVersions() < 0) {
1723         String message = "Min versions for column family " + hcd.getNameAsString()
1724           + "  must be positive.";
1725         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1726       }
1727       // max versions already being checked
1728 
1729       // HBASE-13776 Setting illegal versions for HColumnDescriptor
1730       //  does not throw IllegalArgumentException
1731       // check minVersions <= maxVerions
1732       if (hcd.getMinVersions() > hcd.getMaxVersions()) {
1733         String message = "Min versions for column family " + hcd.getNameAsString()
1734             + " must be less than the Max versions.";
1735         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1736       }
1737 
1738       // check replication scope
1739       if (hcd.getScope() < 0) {
1740         String message = "Replication scope for column family "
1741           + hcd.getNameAsString() + "  must be positive.";
1742         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1743       }
1744 
1745       // check data replication factor, it can be 0(default value) when user has not explicitly
1746       // set the value, in this case we use default replication factor set in the file system.
1747       if (hcd.getDFSReplication() < 0) {
1748         String message = "HFile Replication for column family " + hcd.getNameAsString()
1749             + "  must be greater than zero.";
1750         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1751       }
1752 
1753       // TODO: should we check coprocessors and encryption ?
1754     }
1755   }
1756 
1757   private void checkCompactionPolicy(Configuration conf, HTableDescriptor htd)
1758       throws IOException {
1759     // FIFO compaction has some requirements
1760     // Actually FCP ignores periodic major compactions
1761     String className =
1762         htd.getConfigurationValue(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY);
1763     if (className == null) {
1764       className =
1765           conf.get(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY,
1766             ExploringCompactionPolicy.class.getName());
1767     }
1768 
1769     int blockingFileCount = HStore.DEFAULT_BLOCKING_STOREFILE_COUNT;
1770     String sv = htd.getConfigurationValue(HStore.BLOCKING_STOREFILES_KEY);
1771     if (sv != null) {
1772       blockingFileCount = Integer.parseInt(sv);
1773     } else {
1774       blockingFileCount = conf.getInt(HStore.BLOCKING_STOREFILES_KEY, blockingFileCount);
1775     }
1776 
1777     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1778       String compactionPolicy =
1779           hcd.getConfigurationValue(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY);
1780       if (compactionPolicy == null) {
1781         compactionPolicy = className;
1782       }
1783       if (!compactionPolicy.equals(FIFOCompactionPolicy.class.getName())) {
1784         continue;
1785       }
1786       // FIFOCompaction
1787       String message = null;
1788 
1789       // 1. Check TTL
1790       if (hcd.getTimeToLive() == HColumnDescriptor.DEFAULT_TTL) {
1791         message = "Default TTL is not supported for FIFO compaction";
1792         throw new IOException(message);
1793       }
1794 
1795       // 2. Check min versions
1796       if (hcd.getMinVersions() > 0) {
1797         message = "MIN_VERSION > 0 is not supported for FIFO compaction";
1798         throw new IOException(message);
1799       }
1800 
1801       // 3. blocking file count
1802       String sbfc = htd.getConfigurationValue(HStore.BLOCKING_STOREFILES_KEY);
1803       if (sbfc != null) {
1804         blockingFileCount = Integer.parseInt(sbfc);
1805       }
1806       if (blockingFileCount < 1000) {
1807         message =
1808             "blocking file count '" + HStore.BLOCKING_STOREFILES_KEY + "' " + blockingFileCount
1809                 + " is below recommended minimum of 1000";
1810         throw new IOException(message);
1811       }
1812     }
1813   }
1814 
1815   // HBASE-13350 - Helper method to log warning on sanity check failures if checks disabled.
1816   private static void warnOrThrowExceptionForFailure(boolean logWarn, String confKey,
1817       String message, Exception cause) throws IOException {
1818     if (!logWarn) {
1819       throw new DoNotRetryIOException(message + " Set " + confKey +
1820           " to false at conf or table descriptor if you want to bypass sanity checks", cause);
1821     }
1822     LOG.warn(message);
1823   }
1824 
1825   private void startActiveMasterManager(int infoPort) throws KeeperException {
1826     String backupZNode = ZKUtil.joinZNode(
1827       zooKeeper.backupMasterAddressesZNode, serverName.toString());
1828     /*
1829     * Add a ZNode for ourselves in the backup master directory since we
1830     * may not become the active master. If so, we want the actual active
1831     * master to know we are backup masters, so that it won't assign
1832     * regions to us if so configured.
1833     *
1834     * If we become the active master later, ActiveMasterManager will delete
1835     * this node explicitly.  If we crash before then, ZooKeeper will delete
1836     * this node for us since it is ephemeral.
1837     */
1838     LOG.info("Adding backup master ZNode " + backupZNode);
1839     if (!MasterAddressTracker.setMasterAddress(zooKeeper, backupZNode,
1840         serverName, infoPort)) {
1841       LOG.warn("Failed create of " + backupZNode + " by " + serverName);
1842     }
1843 
1844     activeMasterManager.setInfoPort(infoPort);
1845     // Start a thread to try to become the active master, so we won't block here
1846     Threads.setDaemonThreadRunning(new Thread(new Runnable() {
1847       @Override
1848       public void run() {
1849         int timeout = conf.getInt(HConstants.ZK_SESSION_TIMEOUT,
1850           HConstants.DEFAULT_ZK_SESSION_TIMEOUT);
1851         // If we're a backup master, stall until a primary to writes his address
1852         if (conf.getBoolean(HConstants.MASTER_TYPE_BACKUP,
1853           HConstants.DEFAULT_MASTER_TYPE_BACKUP)) {
1854           LOG.debug("HMaster started in backup mode. "
1855             + "Stalling until master znode is written.");
1856           // This will only be a minute or so while the cluster starts up,
1857           // so don't worry about setting watches on the parent znode
1858           while (!activeMasterManager.hasActiveMaster()) {
1859             LOG.debug("Waiting for master address ZNode to be written "
1860               + "(Also watching cluster state node)");
1861             Threads.sleep(timeout);
1862           }
1863         }
1864         MonitoredTask status = TaskMonitor.get().createStatus("Master startup");
1865         status.setDescription("Master startup");
1866         try {
1867           if (activeMasterManager.blockUntilBecomingActiveMaster(timeout, status)) {
1868             finishActiveMasterInitialization(status);
1869           }
1870         } catch (Throwable t) {
1871           status.setStatus("Failed to become active: " + t.getMessage());
1872           LOG.fatal("Failed to become active master", t);
1873           // HBASE-5680: Likely hadoop23 vs hadoop 20.x/1.x incompatibility
1874           if (t instanceof NoClassDefFoundError &&
1875             t.getMessage()
1876               .contains("org/apache/hadoop/hdfs/protocol/HdfsConstants$SafeModeAction")) {
1877             // improved error message for this special case
1878             abort("HBase is having a problem with its Hadoop jars.  You may need to "
1879               + "recompile HBase against Hadoop version "
1880               + org.apache.hadoop.util.VersionInfo.getVersion()
1881               + " or change your hadoop jars to start properly", t);
1882           } else {
1883             abort("Unhandled exception. Starting shutdown.", t);
1884           }
1885         } finally {
1886           status.cleanup();
1887         }
1888       }
1889     }, getServerName().toShortString() + ".activeMasterManager"));
1890   }
1891 
1892   private void checkCompression(final HTableDescriptor htd)
1893   throws IOException {
1894     if (!this.masterCheckCompression) return;
1895     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1896       checkCompression(hcd);
1897     }
1898   }
1899 
1900   private void checkCompression(final HColumnDescriptor hcd)
1901   throws IOException {
1902     if (!this.masterCheckCompression) return;
1903     CompressionTest.testCompression(hcd.getCompression());
1904     CompressionTest.testCompression(hcd.getCompactionCompression());
1905   }
1906 
1907   private void checkEncryption(final Configuration conf, final HTableDescriptor htd)
1908   throws IOException {
1909     if (!this.masterCheckEncryption) return;
1910     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1911       checkEncryption(conf, hcd);
1912     }
1913   }
1914 
1915   private void checkEncryption(final Configuration conf, final HColumnDescriptor hcd)
1916   throws IOException {
1917     if (!this.masterCheckEncryption) return;
1918     EncryptionTest.testEncryption(conf, hcd.getEncryptionType(), hcd.getEncryptionKey());
1919   }
1920 
1921   private void checkClassLoading(final Configuration conf, final HTableDescriptor htd)
1922   throws IOException {
1923     RegionSplitPolicy.getSplitPolicyClass(htd, conf);
1924     RegionCoprocessorHost.testTableCoprocessorAttrs(conf, htd);
1925   }
1926 
1927   private static boolean isCatalogTable(final TableName tableName) {
1928     return tableName.equals(TableName.META_TABLE_NAME);
1929   }
1930 
1931   @Override
1932   public long deleteTable(
1933       final TableName tableName,
1934       final long nonceGroup,
1935       final long nonce) throws IOException {
1936     checkInitialized();
1937 
1938     return MasterProcedureUtil.submitProcedure(
1939       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
1940       @Override
1941       protected void run() throws IOException {
1942         getMaster().getMasterCoprocessorHost().preDeleteTable(tableName);
1943 
1944         LOG.info(getClientIdAuditPrefix() + " delete " + tableName);
1945 
1946         // TODO: We can handle/merge duplicate request
1947         ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch();
1948         submitProcedure(new DeleteTableProcedure(procedureExecutor.getEnvironment(),
1949             tableName, latch));
1950         latch.await();
1951 
1952         getMaster().getMasterCoprocessorHost().postDeleteTable(tableName);
1953       }
1954 
1955       @Override
1956       protected String getDescription() {
1957         return "DeleteTableProcedure";
1958       }
1959     });
1960   }
1961 
1962   @Override
1963   public void truncateTable(
1964       final TableName tableName,
1965       final boolean preserveSplits,
1966       final long nonceGroup,
1967       final long nonce) throws IOException {
1968     checkInitialized();
1969 
1970     MasterProcedureUtil.submitProcedure(
1971       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
1972       @Override
1973       protected void run() throws IOException {
1974         getMaster().getMasterCoprocessorHost().preTruncateTable(tableName);
1975 
1976         LOG.info(getClientIdAuditPrefix() + " truncate " + tableName);
1977 
1978         long procId = submitProcedure(new TruncateTableProcedure(procedureExecutor.getEnvironment(),
1979             tableName, preserveSplits));
1980         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
1981 
1982         getMaster().getMasterCoprocessorHost().postTruncateTable(tableName);
1983       }
1984 
1985       @Override
1986       protected String getDescription() {
1987         return "TruncateTableProcedure";
1988       }
1989     });
1990   }
1991 
1992   @Override
1993   public void addColumn(
1994       final TableName tableName,
1995       final HColumnDescriptor columnDescriptor,
1996       final long nonceGroup,
1997       final long nonce)
1998       throws IOException {
1999     checkInitialized();
2000     checkCompression(columnDescriptor);
2001     checkEncryption(conf, columnDescriptor);
2002 
2003     MasterProcedureUtil.submitProcedure(
2004       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2005       @Override
2006       protected void run() throws IOException {
2007         if (getMaster().getMasterCoprocessorHost().preAddColumn(tableName, columnDescriptor)) {
2008           return;
2009         }
2010 
2011         // Execute the operation synchronously, wait for the operation to complete before continuing
2012         long procId = submitProcedure(new AddColumnFamilyProcedure(
2013           procedureExecutor.getEnvironment(), tableName, columnDescriptor));
2014         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2015 
2016         getMaster().getMasterCoprocessorHost().postAddColumn(tableName, columnDescriptor);
2017       }
2018 
2019       @Override
2020       protected String getDescription() {
2021         return "AddColumnFamilyProcedure";
2022       }
2023     });
2024   }
2025 
2026   @Override
2027   public void modifyColumn(
2028       final TableName tableName,
2029       final HColumnDescriptor descriptor,
2030       final long nonceGroup,
2031       final long nonce)
2032       throws IOException {
2033     checkInitialized();
2034     checkCompression(descriptor);
2035     checkEncryption(conf, descriptor);
2036 
2037     MasterProcedureUtil.submitProcedure(
2038       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2039       @Override
2040       protected void run() throws IOException {
2041         if (getMaster().getMasterCoprocessorHost().preModifyColumn(tableName, descriptor)) {
2042           return;
2043         }
2044 
2045         LOG.info(getClientIdAuditPrefix() + " modify " + descriptor);
2046 
2047         // Execute the operation synchronously - wait for the operation to complete before continuing.
2048         long procId = submitProcedure(new ModifyColumnFamilyProcedure(
2049           procedureExecutor.getEnvironment(), tableName, descriptor));
2050         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2051 
2052         getMaster().getMasterCoprocessorHost().postModifyColumn(tableName, descriptor);
2053       }
2054 
2055       @Override
2056       protected String getDescription() {
2057         return "ModifyColumnFamilyProcedure";
2058       }
2059     });
2060   }
2061 
2062   @Override
2063   public void deleteColumn(
2064       final TableName tableName,
2065       final byte[] columnName,
2066       final long nonceGroup,
2067       final long nonce)
2068       throws IOException {
2069     checkInitialized();
2070 
2071     MasterProcedureUtil.submitProcedure(
2072       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2073       @Override
2074       protected void run() throws IOException {
2075         if (getMaster().getMasterCoprocessorHost().preDeleteColumn(tableName, columnName)) {
2076           return;
2077         }
2078 
2079         LOG.info(getClientIdAuditPrefix() + " delete " + Bytes.toString(columnName));
2080 
2081         // Execute the operation synchronously - wait for the operation to complete before
2082         // continuing.
2083         long procId = submitProcedure(new DeleteColumnFamilyProcedure(
2084           procedureExecutor.getEnvironment(), tableName, columnName));
2085         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2086 
2087         getMaster().getMasterCoprocessorHost().postDeleteColumn(tableName, columnName);
2088       }
2089 
2090       @Override
2091       protected String getDescription() {
2092         return "DeleteColumnFamilyProcedure";
2093       }
2094     });
2095   }
2096 
2097   @Override
2098   public long enableTable(final TableName tableName, final long nonceGroup, final long nonce)
2099       throws IOException {
2100     checkInitialized();
2101 
2102     return MasterProcedureUtil.submitProcedure(
2103         new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2104       @Override
2105       protected void run() throws IOException {
2106         getMaster().getMasterCoprocessorHost().preEnableTable(tableName);
2107 
2108         LOG.info(getClientIdAuditPrefix() + " enable " + tableName);
2109 
2110         // Execute the operation asynchronously - client will check the progress of the operation
2111         // In case the request is from a <1.1 client before returning,
2112         // we want to make sure that the table is prepared to be
2113         // enabled (the table is locked and the table state is set).
2114         // Note: if the procedure throws exception, we will catch it and rethrow.
2115         final ProcedurePrepareLatch prepareLatch = ProcedurePrepareLatch.createLatch();
2116         submitProcedure(new EnableTableProcedure(procedureExecutor.getEnvironment(),
2117             tableName, false, prepareLatch));
2118         prepareLatch.await();
2119 
2120         getMaster().getMasterCoprocessorHost().postEnableTable(tableName);
2121       }
2122 
2123       @Override
2124       protected String getDescription() {
2125         return "EnableTableProcedure";
2126       }
2127     });
2128   }
2129 
2130   @Override
2131   public long disableTable(final TableName tableName, final long nonceGroup, final long nonce)
2132       throws IOException {
2133     checkInitialized();
2134 
2135     return MasterProcedureUtil.submitProcedure(
2136         new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2137       @Override
2138       protected void run() throws IOException {
2139         getMaster().getMasterCoprocessorHost().preDisableTable(tableName);
2140 
2141         LOG.info(getClientIdAuditPrefix() + " disable " + tableName);
2142 
2143         // Execute the operation asynchronously - client will check the progress of the operation
2144         // In case the request is from a <1.1 client before returning,
2145         // we want to make sure that the table is prepared to be
2146         // enabled (the table is locked and the table state is set).
2147         // Note: if the procedure throws exception, we will catch it and rethrow.
2148         final ProcedurePrepareLatch prepareLatch = ProcedurePrepareLatch.createLatch();
2149         submitProcedure(new DisableTableProcedure(procedureExecutor.getEnvironment(),
2150             tableName, false, prepareLatch));
2151         prepareLatch.await();
2152 
2153         getMaster().getMasterCoprocessorHost().postDisableTable(tableName);
2154       }
2155 
2156       @Override
2157       protected String getDescription() {
2158         return "DisableTableProcedure";
2159       }
2160     });
2161   }
2162 
2163   /**
2164    * Return the region and current deployment for the region containing
2165    * the given row. If the region cannot be found, returns null. If it
2166    * is found, but not currently deployed, the second element of the pair
2167    * may be null.
2168    */
2169   @VisibleForTesting // Used by TestMaster.
2170   Pair<HRegionInfo, ServerName> getTableRegionForRow(
2171       final TableName tableName, final byte [] rowKey)
2172   throws IOException {
2173     final AtomicReference<Pair<HRegionInfo, ServerName>> result =
2174       new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
2175 
2176     MetaScannerVisitor visitor =
2177       new MetaScannerVisitorBase() {
2178         @Override
2179         public boolean processRow(Result data) throws IOException {
2180           if (data == null || data.size() <= 0) {
2181             return true;
2182           }
2183           Pair<HRegionInfo, ServerName> pair = HRegionInfo.getHRegionInfoAndServerName(data);
2184           if (pair == null) {
2185             return false;
2186           }
2187           if (!pair.getFirst().getTable().equals(tableName)) {
2188             return false;
2189           }
2190           result.set(pair);
2191           return true;
2192         }
2193     };
2194 
2195     MetaScanner.metaScan(clusterConnection, visitor, tableName, rowKey, 1);
2196     return result.get();
2197   }
2198 
2199   @Override
2200   public void modifyTable(
2201       final TableName tableName,
2202       final HTableDescriptor descriptor,
2203       final long nonceGroup,
2204       final long nonce)
2205       throws IOException {
2206     checkInitialized();
2207     sanityCheckTableDescriptor(descriptor);
2208 
2209     MasterProcedureUtil.submitProcedure(
2210       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2211       @Override
2212       protected void run() throws IOException {
2213         getMaster().getMasterCoprocessorHost().preModifyTable(tableName, descriptor);
2214 
2215         LOG.info(getClientIdAuditPrefix() + " modify " + tableName);
2216 
2217         // Execute the operation synchronously - wait for the operation completes before continuing.
2218         long procId = submitProcedure(new ModifyTableProcedure(
2219           procedureExecutor.getEnvironment(), descriptor));
2220         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2221 
2222         getMaster().getMasterCoprocessorHost().postModifyTable(tableName, descriptor);
2223       }
2224 
2225       @Override
2226       protected String getDescription() {
2227         return "ModifyTableProcedure";
2228       }
2229     });
2230   }
2231 
2232   @Override
2233   public void checkTableModifiable(final TableName tableName)
2234       throws IOException, TableNotFoundException, TableNotDisabledException {
2235     if (isCatalogTable(tableName)) {
2236       throw new IOException("Can't modify catalog tables");
2237     }
2238     if (!MetaTableAccessor.tableExists(getConnection(), tableName)) {
2239       throw new TableNotFoundException(tableName);
2240     }
2241     if (!getAssignmentManager().getTableStateManager().
2242         isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED)) {
2243       throw new TableNotDisabledException(tableName);
2244     }
2245   }
2246 
2247   /**
2248    * @return cluster status
2249    */
2250   public ClusterStatus getClusterStatus() throws InterruptedIOException {
2251     // Build Set of backup masters from ZK nodes
2252     List<String> backupMasterStrings;
2253     try {
2254       backupMasterStrings = ZKUtil.listChildrenNoWatch(this.zooKeeper,
2255         this.zooKeeper.backupMasterAddressesZNode);
2256     } catch (KeeperException e) {
2257       LOG.warn(this.zooKeeper.prefix("Unable to list backup servers"), e);
2258       backupMasterStrings = null;
2259     }
2260 
2261     List<ServerName> backupMasters = null;
2262     if (backupMasterStrings != null && !backupMasterStrings.isEmpty()) {
2263       backupMasters = new ArrayList<ServerName>(backupMasterStrings.size());
2264       for (String s: backupMasterStrings) {
2265         try {
2266           byte [] bytes;
2267           try {
2268             bytes = ZKUtil.getData(this.zooKeeper, ZKUtil.joinZNode(
2269                 this.zooKeeper.backupMasterAddressesZNode, s));
2270           } catch (InterruptedException e) {
2271             throw new InterruptedIOException();
2272           }
2273           if (bytes != null) {
2274             ServerName sn;
2275             try {
2276               sn = ServerName.parseFrom(bytes);
2277             } catch (DeserializationException e) {
2278               LOG.warn("Failed parse, skipping registering backup server", e);
2279               continue;
2280             }
2281             backupMasters.add(sn);
2282           }
2283         } catch (KeeperException e) {
2284           LOG.warn(this.zooKeeper.prefix("Unable to get information about " +
2285                    "backup servers"), e);
2286         }
2287       }
2288       Collections.sort(backupMasters, new Comparator<ServerName>() {
2289         @Override
2290         public int compare(ServerName s1, ServerName s2) {
2291           return s1.getServerName().compareTo(s2.getServerName());
2292         }});
2293     }
2294 
2295     String clusterId = fileSystemManager != null ?
2296       fileSystemManager.getClusterId().toString() : null;
2297     Map<String, RegionState> regionsInTransition = assignmentManager != null ?
2298       assignmentManager.getRegionStates().getRegionsInTransition() : null;
2299     String[] coprocessors = cpHost != null ? getMasterCoprocessors() : null;
2300     boolean balancerOn = loadBalancerTracker != null ?
2301       loadBalancerTracker.isBalancerOn() : false;
2302     Map<ServerName, ServerLoad> onlineServers = null;
2303     Set<ServerName> deadServers = null;
2304     if (serverManager != null) {
2305       deadServers = serverManager.getDeadServers().copyServerNames();
2306       onlineServers = serverManager.getOnlineServers();
2307     }
2308     return new ClusterStatus(VersionInfo.getVersion(), clusterId,
2309       onlineServers, deadServers, serverName, backupMasters,
2310       regionsInTransition, coprocessors, balancerOn);
2311   }
2312 
2313   /**
2314    * The set of loaded coprocessors is stored in a static set. Since it's
2315    * statically allocated, it does not require that HMaster's cpHost be
2316    * initialized prior to accessing it.
2317    * @return a String representation of the set of names of the loaded
2318    * coprocessors.
2319    */
2320   public static String getLoadedCoprocessors() {
2321     return CoprocessorHost.getLoadedCoprocessors().toString();
2322   }
2323 
2324   /**
2325    * @return timestamp in millis when HMaster was started.
2326    */
2327   public long getMasterStartTime() {
2328     return startcode;
2329   }
2330 
2331   /**
2332    * @return timestamp in millis when HMaster became the active master.
2333    */
2334   public long getMasterActiveTime() {
2335     return masterActiveTime;
2336   }
2337 
2338   public int getNumWALFiles() {
2339     return procedureStore != null ? procedureStore.getActiveLogs().size() : 0;
2340   }
2341 
2342   public WALProcedureStore getWalProcedureStore() {
2343     return procedureStore;
2344   }
2345 
2346   public int getRegionServerInfoPort(final ServerName sn) {
2347     RegionServerInfo info = this.regionServerTracker.getRegionServerInfo(sn);
2348     if (info == null || info.getInfoPort() == 0) {
2349       return conf.getInt(HConstants.REGIONSERVER_INFO_PORT,
2350         HConstants.DEFAULT_REGIONSERVER_INFOPORT);
2351     }
2352     return info.getInfoPort();
2353   }
2354 
2355   public String getRegionServerVersion(final ServerName sn) {
2356     RegionServerInfo info = this.regionServerTracker.getRegionServerInfo(sn);
2357     if (info != null && info.hasVersionInfo()) {
2358       return info.getVersionInfo().getVersion();
2359     }
2360     return "Unknown";
2361   }
2362 
2363   /**
2364    * @return array of coprocessor SimpleNames.
2365    */
2366   public String[] getMasterCoprocessors() {
2367     Set<String> masterCoprocessors = getMasterCoprocessorHost().getCoprocessors();
2368     return masterCoprocessors.toArray(new String[masterCoprocessors.size()]);
2369   }
2370 
2371   @Override
2372   public void abort(final String msg, final Throwable t) {
2373     if (isAborted() || isStopped()) {
2374       return;
2375     }
2376     if (cpHost != null) {
2377       // HBASE-4014: dump a list of loaded coprocessors.
2378       LOG.fatal("Master server abort: loaded coprocessors are: " +
2379           getLoadedCoprocessors());
2380     }
2381     if (t != null) LOG.fatal(msg, t);
2382     stop(msg);
2383   }
2384 
2385   @Override
2386   public ZooKeeperWatcher getZooKeeper() {
2387     return zooKeeper;
2388   }
2389 
2390   @Override
2391   public MasterCoprocessorHost getMasterCoprocessorHost() {
2392     return cpHost;
2393   }
2394 
2395   @Override
2396   public MasterQuotaManager getMasterQuotaManager() {
2397     return quotaManager;
2398   }
2399 
2400   @Override
2401   public ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
2402     return procedureExecutor;
2403   }
2404 
2405   @Override
2406   public ServerName getServerName() {
2407     return this.serverName;
2408   }
2409 
2410   @Override
2411   public AssignmentManager getAssignmentManager() {
2412     return this.assignmentManager;
2413   }
2414 
2415   public MemoryBoundedLogMessageBuffer getRegionServerFatalLogBuffer() {
2416     return rsFatals;
2417   }
2418 
2419   public void shutdown() throws IOException {
2420     if (cpHost != null) {
2421       cpHost.preShutdown();
2422     }
2423 
2424     if (this.serverManager != null) {
2425       this.serverManager.shutdownCluster();
2426     }
2427     if (this.clusterStatusTracker != null){
2428       try {
2429         this.clusterStatusTracker.setClusterDown();
2430       } catch (KeeperException e) {
2431         LOG.error("ZooKeeper exception trying to set cluster as down in ZK", e);
2432       }
2433     }
2434   }
2435 
2436   public void stopMaster() throws IOException {
2437     if (cpHost != null) {
2438       cpHost.preStopMaster();
2439     }
2440     stop("Stopped by " + Thread.currentThread().getName());
2441   }
2442 
2443   void checkServiceStarted() throws ServerNotRunningYetException {
2444     if (!serviceStarted) {
2445       throw new ServerNotRunningYetException("Server is not running yet");
2446     }
2447   }
2448 
2449   void checkInitialized() throws PleaseHoldException, ServerNotRunningYetException {
2450     checkServiceStarted();
2451     if (!isInitialized()) {
2452       throw new PleaseHoldException("Master is initializing");
2453     }
2454   }
2455 
2456   void checkNamespaceManagerReady() throws IOException {
2457     checkInitialized();
2458     if (tableNamespaceManager == null ||
2459         !tableNamespaceManager.isTableAvailableAndInitialized(true)) {
2460       throw new IOException("Table Namespace Manager not ready yet, try again later");
2461     }
2462   }
2463   /**
2464    * Report whether this master is currently the active master or not.
2465    * If not active master, we are parked on ZK waiting to become active.
2466    *
2467    * This method is used for testing.
2468    *
2469    * @return true if active master, false if not.
2470    */
2471   public boolean isActiveMaster() {
2472     return isActiveMaster;
2473   }
2474 
2475   /**
2476    * Report whether this master has completed with its initialization and is
2477    * ready.  If ready, the master is also the active master.  A standby master
2478    * is never ready.
2479    *
2480    * This method is used for testing.
2481    *
2482    * @return true if master is ready to go, false if not.
2483    */
2484   @Override
2485   public boolean isInitialized() {
2486     return initialized.isReady();
2487   }
2488 
2489   @VisibleForTesting
2490   public void setInitialized(boolean isInitialized) {
2491     procedureExecutor.getEnvironment().setEventReady(initialized, isInitialized);
2492   }
2493 
2494   public ProcedureEvent getInitializedEvent() {
2495     return initialized;
2496   }
2497 
2498   /**
2499    * ServerCrashProcessingEnabled is set false before completing assignMeta to prevent processing
2500    * of crashed servers.
2501    * @return true if assignMeta has completed;
2502    */
2503   @Override
2504   public boolean isServerCrashProcessingEnabled() {
2505     return serverCrashProcessingEnabled.isReady();
2506   }
2507 
2508   @VisibleForTesting
2509   public void setServerCrashProcessingEnabled(final boolean b) {
2510     procedureExecutor.getEnvironment().setEventReady(serverCrashProcessingEnabled, b);
2511   }
2512 
2513   public ProcedureEvent getServerCrashProcessingEnabledEvent() {
2514     return serverCrashProcessingEnabled;
2515   }
2516 
2517   /**
2518    * Report whether this master has started initialization and is about to do meta region assignment
2519    * @return true if master is in initialization &amp; about to assign hbase:meta regions
2520    */
2521   public boolean isInitializationStartsMetaRegionAssignment() {
2522     return this.initializationBeforeMetaAssignment;
2523   }
2524 
2525   public void assignRegion(HRegionInfo hri) {
2526     assignmentManager.assign(hri, true);
2527   }
2528 
2529   /**
2530    * Compute the average load across all region servers.
2531    * Currently, this uses a very naive computation - just uses the number of
2532    * regions being served, ignoring stats about number of requests.
2533    * @return the average load
2534    */
2535   public double getAverageLoad() {
2536     if (this.assignmentManager == null) {
2537       return 0;
2538     }
2539 
2540     RegionStates regionStates = this.assignmentManager.getRegionStates();
2541     if (regionStates == null) {
2542       return 0;
2543     }
2544     return regionStates.getAverageLoad();
2545   }
2546 
2547   /*
2548    * @return the count of region split plans executed
2549    */
2550   public long getSplitPlanCount() {
2551     return splitPlanCount;
2552   }
2553 
2554   /*
2555    * @return the count of region merge plans executed
2556    */
2557   public long getMergePlanCount() {
2558     return mergePlanCount;
2559   }
2560 
2561   @Override
2562   public boolean registerService(Service instance) {
2563     /*
2564      * No stacking of instances is allowed for a single service name
2565      */
2566     Descriptors.ServiceDescriptor serviceDesc = instance.getDescriptorForType();
2567     String serviceName = CoprocessorRpcUtils.getServiceName(serviceDesc);
2568     if (coprocessorServiceHandlers.containsKey(serviceName)) {
2569       LOG.error("Coprocessor service "+serviceName+
2570           " already registered, rejecting request from "+instance
2571       );
2572       return false;
2573     }
2574 
2575     coprocessorServiceHandlers.put(serviceName, instance);
2576     if (LOG.isDebugEnabled()) {
2577       LOG.debug("Registered master coprocessor service: service="+serviceName);
2578     }
2579     return true;
2580   }
2581 
2582   /**
2583    * Utility for constructing an instance of the passed HMaster class.
2584    * @param masterClass
2585    * @param conf
2586    * @return HMaster instance.
2587    */
2588   public static HMaster constructMaster(Class<? extends HMaster> masterClass,
2589       final Configuration conf, final CoordinatedStateManager cp)  {
2590     try {
2591       Constructor<? extends HMaster> c =
2592         masterClass.getConstructor(Configuration.class, CoordinatedStateManager.class);
2593       return c.newInstance(conf, cp);
2594     } catch(Exception e) {
2595       Throwable error = e;
2596       if (e instanceof InvocationTargetException &&
2597           ((InvocationTargetException)e).getTargetException() != null) {
2598         error = ((InvocationTargetException)e).getTargetException();
2599       }
2600       throw new RuntimeException("Failed construction of Master: " + masterClass.toString() + ". "
2601         , error);
2602     }
2603   }
2604 
2605   /**
2606    * @see org.apache.hadoop.hbase.master.HMasterCommandLine
2607    */
2608   public static void main(String [] args) {
2609     VersionInfo.logVersion();
2610     new HMasterCommandLine(HMaster.class).doMain(args);
2611   }
2612 
2613   public HFileCleaner getHFileCleaner() {
2614     return this.hfileCleaner;
2615   }
2616 
2617   /**
2618    * @return the underlying snapshot manager
2619    */
2620   public SnapshotManager getSnapshotManager() {
2621     return this.snapshotManager;
2622   }
2623 
2624   @Override
2625   public void createNamespace(
2626       final NamespaceDescriptor descriptor,
2627       final long nonceGroup,
2628       final long nonce) throws IOException {
2629     TableName.isLegalNamespaceName(Bytes.toBytes(descriptor.getName()));
2630     checkNamespaceManagerReady();
2631 
2632     createNamespaceSync(descriptor, nonceGroup, nonce, true);
2633   }
2634 
2635   @Override
2636   public void createNamespaceSync(
2637       final NamespaceDescriptor descriptor,
2638       final long nonceGroup,
2639       final long nonce,
2640       final boolean executeCoprocessor) throws IOException {
2641     MasterProcedureUtil.submitProcedure(
2642       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2643       @Override
2644       protected void run() throws IOException {
2645         if (executeCoprocessor &&
2646             getMaster().getMasterCoprocessorHost().preCreateNamespace(descriptor)) {
2647           return;
2648         }
2649         LOG.info(getClientIdAuditPrefix() + " creating " + descriptor);
2650         // Execute the operation synchronously - wait for the operation to complete before
2651         // continuing.
2652         long procId = submitProcedure(new CreateNamespaceProcedure(
2653           procedureExecutor.getEnvironment(), descriptor));
2654         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2655         if (executeCoprocessor) {
2656           getMaster().getMasterCoprocessorHost().postCreateNamespace(descriptor);
2657         }
2658       }
2659 
2660       @Override
2661       protected String getDescription() {
2662         return "CreateNamespaceProcedure";
2663       }
2664     });
2665   }
2666 
2667   @Override
2668   public void modifyNamespace(
2669       final NamespaceDescriptor descriptor,
2670       final long nonceGroup,
2671       final long nonce) throws IOException {
2672     TableName.isLegalNamespaceName(Bytes.toBytes(descriptor.getName()));
2673     checkNamespaceManagerReady();
2674 
2675     MasterProcedureUtil.submitProcedure(
2676       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2677       @Override
2678       protected void run() throws IOException {
2679         if (getMaster().getMasterCoprocessorHost().preModifyNamespace(descriptor)) {
2680           return;
2681         }
2682         LOG.info(getClientIdAuditPrefix() + " modify " + descriptor);
2683         // Execute the operation synchronously - wait for the operation to complete before
2684         // continuing.
2685         long procId = submitProcedure(new ModifyNamespaceProcedure(
2686           procedureExecutor.getEnvironment(), descriptor));
2687         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2688         getMaster().getMasterCoprocessorHost().postModifyNamespace(descriptor);
2689     }
2690 
2691     @Override
2692     protected String getDescription() {
2693       return "ModifyNamespace";
2694     }
2695   });
2696   }
2697 
2698   @Override
2699   public void deleteNamespace(
2700       final String name,
2701       final long nonceGroup,
2702       final long nonce) throws IOException {
2703     checkNamespaceManagerReady();
2704 
2705     MasterProcedureUtil.submitProcedure(
2706       new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2707       @Override
2708       protected void run() throws IOException {
2709         if (getMaster().getMasterCoprocessorHost().preDeleteNamespace(name)) {
2710           return;
2711         }
2712         LOG.info(getClientIdAuditPrefix() + " delete " + name);
2713         // Execute the operation synchronously - wait for the operation to complete before
2714         // continuing.
2715         long procId = submitProcedure(new DeleteNamespaceProcedure(
2716           procedureExecutor.getEnvironment(), name));
2717         ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2718         getMaster().getMasterCoprocessorHost().postDeleteNamespace(name);
2719       }
2720 
2721       @Override
2722       protected String getDescription() {
2723         return "DeleteNamespaceProcedure";
2724       }
2725     });
2726   }
2727 
2728   /**
2729    * Ensure that the specified namespace exists, otherwise throws a NamespaceNotFoundException
2730    *
2731    * @param name the namespace to check
2732    * @throws IOException if the namespace manager is not ready yet.
2733    * @throws NamespaceNotFoundException if the namespace does not exists
2734    */
2735   protected void ensureNamespaceExists(final String name)
2736       throws IOException, NamespaceNotFoundException {
2737     checkNamespaceManagerReady();
2738     NamespaceDescriptor nsd = tableNamespaceManager.get(name);
2739     if (nsd == null) {
2740       throw new NamespaceNotFoundException(name);
2741     }
2742   }
2743 
2744   @Override
2745   public NamespaceDescriptor getNamespaceDescriptor(String name) throws IOException {
2746     checkNamespaceManagerReady();
2747 
2748     if (cpHost != null) {
2749       cpHost.preGetNamespaceDescriptor(name);
2750     }
2751 
2752     NamespaceDescriptor nsd = tableNamespaceManager.get(name);
2753     if (nsd == null) {
2754       throw new NamespaceNotFoundException(name);
2755     }
2756 
2757     if (cpHost != null) {
2758       cpHost.postGetNamespaceDescriptor(nsd);
2759     }
2760 
2761     return nsd;
2762   }
2763 
2764   @Override
2765   public List<NamespaceDescriptor> listNamespaceDescriptors() throws IOException {
2766     checkNamespaceManagerReady();
2767 
2768     final List<NamespaceDescriptor> descriptors = new ArrayList<NamespaceDescriptor>();
2769     boolean bypass = false;
2770     if (cpHost != null) {
2771       bypass = cpHost.preListNamespaceDescriptors(descriptors);
2772     }
2773 
2774     if (!bypass) {
2775       descriptors.addAll(tableNamespaceManager.list());
2776 
2777       if (cpHost != null) {
2778         cpHost.postListNamespaceDescriptors(descriptors);
2779       }
2780     }
2781     return descriptors;
2782   }
2783 
2784   @Override
2785   public boolean abortProcedure(final long procId, final boolean mayInterruptIfRunning)
2786       throws IOException {
2787     if (cpHost != null) {
2788       cpHost.preAbortProcedure(this.procedureExecutor, procId);
2789     }
2790 
2791     final boolean result = this.procedureExecutor.abort(procId, mayInterruptIfRunning);
2792 
2793     if (cpHost != null) {
2794       cpHost.postAbortProcedure();
2795     }
2796 
2797     return result;
2798   }
2799 
2800   @Override
2801   public List<ProcedureInfo> listProcedures() throws IOException {
2802     if (cpHost != null) {
2803       cpHost.preListProcedures();
2804     }
2805 
2806     final List<ProcedureInfo> procInfoList = this.procedureExecutor.listProcedures();
2807 
2808     if (cpHost != null) {
2809       cpHost.postListProcedures(procInfoList);
2810     }
2811 
2812     return procInfoList;
2813   }
2814 
2815   @Override
2816   public List<HTableDescriptor> listTableDescriptorsByNamespace(String name) throws IOException {
2817     ensureNamespaceExists(name);
2818     return listTableDescriptors(name, null, null, true);
2819   }
2820 
2821   @Override
2822   public List<TableName> listTableNamesByNamespace(String name) throws IOException {
2823     ensureNamespaceExists(name);
2824     return listTableNames(name, null, true);
2825   }
2826 
2827   /**
2828    * Returns the list of table descriptors that match the specified request
2829    *
2830    * @param namespace the namespace to query, or null if querying for all
2831    * @param regex The regular expression to match against, or null if querying for all
2832    * @param tableNameList the list of table names, or null if querying for all
2833    * @param includeSysTables False to match only against userspace tables
2834    * @return the list of table descriptors
2835    */
2836   public List<HTableDescriptor> listTableDescriptors(final String namespace, final String regex,
2837       final List<TableName> tableNameList, final boolean includeSysTables)
2838       throws IOException {
2839     final List<HTableDescriptor> descriptors = new ArrayList<HTableDescriptor>();
2840 
2841     boolean bypass = false;
2842     if (cpHost != null) {
2843       bypass = cpHost.preGetTableDescriptors(tableNameList, descriptors);
2844       // method required for AccessController.
2845       bypass |= cpHost.preGetTableDescriptors(tableNameList, descriptors, regex);
2846     }
2847 
2848     if (!bypass) {
2849       if (tableNameList == null || tableNameList.size() == 0) {
2850         // request for all TableDescriptors
2851         Collection<HTableDescriptor> htds;
2852         if (namespace != null && namespace.length() > 0) {
2853           htds = tableDescriptors.getByNamespace(namespace).values();
2854         } else {
2855           htds = tableDescriptors.getAll().values();
2856         }
2857 
2858         for (HTableDescriptor desc: htds) {
2859           if (includeSysTables || !desc.getTableName().isSystemTable()) {
2860             descriptors.add(desc);
2861           }
2862         }
2863       } else {
2864         for (TableName s: tableNameList) {
2865           HTableDescriptor desc = tableDescriptors.get(s);
2866           if (desc != null) {
2867             descriptors.add(desc);
2868           }
2869         }
2870       }
2871 
2872       // Retains only those matched by regular expression.
2873       if (regex != null) {
2874         filterTablesByRegex(descriptors, Pattern.compile(regex));
2875       }
2876 
2877       if (cpHost != null) {
2878         cpHost.postGetTableDescriptors(descriptors);
2879         // method required for AccessController.
2880         cpHost.postGetTableDescriptors(tableNameList, descriptors, regex);
2881       }
2882     }
2883     return descriptors;
2884   }
2885 
2886   /**
2887    * Returns the list of table names that match the specified request
2888    * @param regex The regular expression to match against, or null if querying for all
2889    * @param namespace the namespace to query, or null if querying for all
2890    * @param includeSysTables False to match only against userspace tables
2891    * @return the list of table names
2892    */
2893   public List<TableName> listTableNames(final String namespace, final String regex,
2894       final boolean includeSysTables) throws IOException {
2895     final List<HTableDescriptor> descriptors = new ArrayList<HTableDescriptor>();
2896 
2897     boolean bypass = false;
2898     if (cpHost != null) {
2899       bypass = cpHost.preGetTableNames(descriptors, regex);
2900     }
2901 
2902     if (!bypass) {
2903       // get all descriptors
2904       Collection<HTableDescriptor> htds;
2905       if (namespace != null && namespace.length() > 0) {
2906         htds = tableDescriptors.getByNamespace(namespace).values();
2907       } else {
2908         htds = tableDescriptors.getAll().values();
2909       }
2910 
2911       for (HTableDescriptor htd: htds) {
2912         if (includeSysTables || !htd.getTableName().isSystemTable()) {
2913           descriptors.add(htd);
2914         }
2915       }
2916 
2917       // Retains only those matched by regular expression.
2918       if (regex != null) {
2919         filterTablesByRegex(descriptors, Pattern.compile(regex));
2920       }
2921 
2922       if (cpHost != null) {
2923         cpHost.postGetTableNames(descriptors, regex);
2924       }
2925     }
2926 
2927     List<TableName> result = new ArrayList<TableName>(descriptors.size());
2928     for (HTableDescriptor htd: descriptors) {
2929       result.add(htd.getTableName());
2930     }
2931     return result;
2932   }
2933 
2934 
2935   /**
2936    * Removes the table descriptors that don't match the pattern.
2937    * @param descriptors list of table descriptors to filter
2938    * @param pattern the regex to use
2939    */
2940   private static void filterTablesByRegex(final Collection<HTableDescriptor> descriptors,
2941       final Pattern pattern) {
2942     final String defaultNS = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
2943     Iterator<HTableDescriptor> itr = descriptors.iterator();
2944     while (itr.hasNext()) {
2945       HTableDescriptor htd = itr.next();
2946       String tableName = htd.getTableName().getNameAsString();
2947       boolean matched = pattern.matcher(tableName).matches();
2948       if (!matched && htd.getTableName().getNamespaceAsString().equals(defaultNS)) {
2949         matched = pattern.matcher(defaultNS + TableName.NAMESPACE_DELIM + tableName).matches();
2950       }
2951       if (!matched) {
2952         itr.remove();
2953       }
2954     }
2955   }
2956 
2957   @Override
2958   public long getLastMajorCompactionTimestamp(TableName table) throws IOException {
2959     return getClusterStatus().getLastMajorCompactionTsForTable(table);
2960   }
2961 
2962   @Override
2963   public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException {
2964     return getClusterStatus().getLastMajorCompactionTsForRegion(regionName);
2965   }
2966 
2967   /**
2968    * Queries the state of the {@link LoadBalancerTracker}. If the balancer is not initialized,
2969    * false is returned.
2970    *
2971    * @return The state of the load balancer, or false if the load balancer isn't defined.
2972    */
2973   public boolean isBalancerOn() {
2974     if (null == loadBalancerTracker) return false;
2975     return loadBalancerTracker.isBalancerOn();
2976   }
2977 
2978   /**
2979    * Queries the state of the {@link RegionNormalizerTracker}. If it's not initialized,
2980    * false is returned.
2981    */
2982    public boolean isNormalizerOn() {
2983     if (null == regionNormalizerTracker) {
2984       return false;
2985     }
2986     return regionNormalizerTracker.isNormalizerOn();
2987   }
2988 
2989 
2990   /**
2991    * Queries the state of the {@link SplitOrMergeTracker}. If it is not initialized,
2992    * false is returned. If switchType is illegal, false will return.
2993    * @param switchType see {@link org.apache.hadoop.hbase.client.Admin.MasterSwitchType}
2994    * @return The state of the switch
2995    */
2996   public boolean isSplitOrMergeEnabled(Admin.MasterSwitchType switchType) {
2997     if (null == splitOrMergeTracker) {
2998       return false;
2999     }
3000     return splitOrMergeTracker.isSplitOrMergeEnabled(switchType);
3001   }
3002 
3003   /**
3004    * Fetch the configured {@link LoadBalancer} class name. If none is set, a default is returned.
3005    *
3006    * @return The name of the {@link LoadBalancer} in use.
3007    */
3008   public String getLoadBalancerClassName() {
3009     return conf.get(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, LoadBalancerFactory
3010         .getDefaultLoadBalancerClass().getName());
3011   }
3012 
3013   /**
3014    * @return RegionNormalizerTracker instance
3015    */
3016   public RegionNormalizerTracker getRegionNormalizerTracker() {
3017     return regionNormalizerTracker;
3018   }
3019 
3020   public SplitOrMergeTracker getSplitOrMergeTracker() {
3021     return splitOrMergeTracker;
3022   }
3023 }