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