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  
20  package org.apache.hadoop.hbase.tool;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.TreeSet;
30  import java.util.regex.Matcher;
31  import java.util.regex.Pattern;
32  
33  import org.apache.commons.lang.time.StopWatch;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.conf.Configuration;
37  import org.apache.hadoop.hbase.DoNotRetryIOException;
38  import org.apache.hadoop.hbase.HBaseConfiguration;
39  import org.apache.hadoop.hbase.HColumnDescriptor;
40  import org.apache.hadoop.hbase.HRegionInfo;
41  import org.apache.hadoop.hbase.HTableDescriptor;
42  import org.apache.hadoop.hbase.ServerName;
43  import org.apache.hadoop.hbase.TableName;
44  import org.apache.hadoop.hbase.TableNotEnabledException;
45  import org.apache.hadoop.hbase.TableNotFoundException;
46  import org.apache.hadoop.hbase.client.Get;
47  import org.apache.hadoop.hbase.client.HBaseAdmin;
48  import org.apache.hadoop.hbase.client.HTable;
49  import org.apache.hadoop.hbase.client.ResultScanner;
50  import org.apache.hadoop.hbase.client.Scan;
51  import org.apache.hadoop.util.Tool;
52  import org.apache.hadoop.util.ToolRunner;
53  
54  /**
55   * HBase Canary Tool, that that can be used to do
56   * "canary monitoring" of a running HBase cluster.
57   *
58   * Here are two modes
59   * 1. region mode - Foreach region tries to get one row per column family
60   * and outputs some information about failure or latency.
61   *
62   * 2. regionserver mode - Foreach regionserver tries to get one row from one table
63   * selected randomly and outputs some information about failure or latency.
64   */
65  public final class Canary implements Tool {
66    // Sink interface used by the canary to outputs information
67    public interface Sink {
68      public void publishReadFailure(HRegionInfo region, Exception e);
69      public void publishReadFailure(HRegionInfo region, HColumnDescriptor column, Exception e);
70      public void publishReadTiming(HRegionInfo region, HColumnDescriptor column, long msTime);
71    }
72    // new extended sink for output regionserver mode info
73    // do not change the Sink interface directly due to maintaining the API
74    public interface ExtendedSink extends Sink {
75      public void publishReadFailure(String table, String server);
76      public void publishReadTiming(String table, String server, long msTime);
77    }
78  
79    // Simple implementation of canary sink that allows to plot on
80    // file or standard output timings or failures.
81    public static class StdOutSink implements Sink {
82      @Override
83      public void publishReadFailure(HRegionInfo region, Exception e) {
84        LOG.error(String.format("read from region %s failed", region.getRegionNameAsString()), e);
85      }
86  
87      @Override
88      public void publishReadFailure(HRegionInfo region, HColumnDescriptor column, Exception e) {
89        LOG.error(String.format("read from region %s column family %s failed",
90                  region.getRegionNameAsString(), column.getNameAsString()), e);
91      }
92  
93      @Override
94      public void publishReadTiming(HRegionInfo region, HColumnDescriptor column, long msTime) {
95        LOG.info(String.format("read from region %s column family %s in %dms",
96                 region.getRegionNameAsString(), column.getNameAsString(), msTime));
97      }
98    }
99    // a ExtendedSink implementation
100   public static class RegionServerStdOutSink extends StdOutSink implements ExtendedSink {
101 
102     @Override
103     public void publishReadFailure(String table, String server) {
104       LOG.error(String.format("Read from table:%s on region server:%s", table, server));
105     }
106 
107     @Override
108     public void publishReadTiming(String table, String server, long msTime) {
109       LOG.info(String.format("Read from table:%s on region server:%s in %dms",
110           table, server, msTime));
111     }
112   }
113 
114   private static final int USAGE_EXIT_CODE = 1;
115   private static final int INIT_ERROR_EXIT_CODE = 2;
116   private static final int TIMEOUT_ERROR_EXIT_CODE = 3;
117   private static final int ERROR_EXIT_CODE = 4;
118 
119   private static final long DEFAULT_INTERVAL = 6000;
120 
121   private static final long DEFAULT_TIMEOUT = 600000; // 10 mins
122 
123   private static final Log LOG = LogFactory.getLog(Canary.class);
124 
125   private Configuration conf = null;
126   private long interval = 0;
127   private Sink sink = null;
128 
129   private boolean useRegExp;
130   private long timeout = DEFAULT_TIMEOUT;
131   private boolean failOnError = true;
132   private boolean regionServerMode = false;
133 
134   public Canary() {
135     this(new RegionServerStdOutSink());
136   }
137 
138   public Canary(Sink sink) {
139     this.sink = sink;
140   }
141 
142   @Override
143   public Configuration getConf() {
144     return conf;
145   }
146 
147   @Override
148   public void setConf(Configuration conf) {
149     this.conf = conf;
150   }
151 
152   @Override
153   public int run(String[] args) throws Exception {
154     int index = -1;
155 
156     // Process command line args
157     for (int i = 0; i < args.length; i++) {
158       String cmd = args[i];
159 
160       if (cmd.startsWith("-")) {
161         if (index >= 0) {
162           // command line args must be in the form: [opts] [table 1 [table 2 ...]]
163           System.err.println("Invalid command line options");
164           printUsageAndExit();
165         }
166 
167         if (cmd.equals("-help")) {
168           // user asked for help, print the help and quit.
169           printUsageAndExit();
170         } else if (cmd.equals("-daemon") && interval == 0) {
171           // user asked for daemon mode, set a default interval between checks
172           interval = DEFAULT_INTERVAL;
173         } else if (cmd.equals("-interval")) {
174           // user has specified an interval for canary breaths (-interval N)
175           i++;
176 
177           if (i == args.length) {
178             System.err.println("-interval needs a numeric value argument.");
179             printUsageAndExit();
180           }
181 
182           try {
183             interval = Long.parseLong(args[i]) * 1000;
184           } catch (NumberFormatException e) {
185             System.err.println("-interval needs a numeric value argument.");
186             printUsageAndExit();
187           }
188         } else if(cmd.equals("-regionserver")) {
189           this.regionServerMode = true;
190         } else if (cmd.equals("-e")) {
191           this.useRegExp = true;
192         } else if (cmd.equals("-t")) {
193           i++;
194 
195           if (i == args.length) {
196             System.err.println("-t needs a numeric value argument.");
197             printUsageAndExit();
198           }
199 
200           try {
201             this.timeout = Long.parseLong(args[i]);
202           } catch (NumberFormatException e) {
203             System.err.println("-t needs a numeric value argument.");
204             printUsageAndExit();
205           }
206 
207         } else if (cmd.equals("-f")) {
208           i++;
209 
210           if (i == args.length) {
211             System.err
212                 .println("-f needs a boolean value argument (true|false).");
213             printUsageAndExit();
214           }
215 
216           this.failOnError = Boolean.parseBoolean(args[i]);
217         } else {
218           // no options match
219           System.err.println(cmd + " options is invalid.");
220           printUsageAndExit();
221         }
222       } else if (index < 0) {
223         // keep track of first table name specified by the user
224         index = i;
225       }
226     }
227 
228     // start to prepare the stuffs
229     Monitor monitor = null;
230     Thread monitorThread = null;
231     long startTime = 0;
232     long currentTimeLength = 0;
233 
234     do {
235       // do monitor !!
236       monitor = this.newMonitor(index, args);
237       monitorThread = new Thread(monitor);
238       startTime = System.currentTimeMillis();
239       monitorThread.start();
240       while (!monitor.isDone()) {
241         // wait for 1 sec
242         Thread.sleep(1000);
243         // exit if any error occurs
244         if (this.failOnError && monitor.hasError()) {
245           monitorThread.interrupt();
246           if (monitor.initialized) {
247             System.exit(monitor.errorCode);
248           } else {
249             System.exit(INIT_ERROR_EXIT_CODE);
250           }
251         }
252         currentTimeLength = System.currentTimeMillis() - startTime;
253         if (currentTimeLength > this.timeout) {
254           LOG.error("The monitor is running too long (" + currentTimeLength
255               + ") after timeout limit:" + this.timeout
256               + " will be killed itself !!");
257           if (monitor.initialized) {
258             System.exit(TIMEOUT_ERROR_EXIT_CODE);
259           } else {
260             System.exit(INIT_ERROR_EXIT_CODE);
261           }
262           break;
263         }
264       }
265 
266       if (this.failOnError && monitor.hasError()) {
267         monitorThread.interrupt();
268         System.exit(monitor.errorCode);
269       }
270 
271       Thread.sleep(interval);
272     } while (interval > 0);
273 
274     return(monitor.errorCode);
275   }
276 
277   private void printUsageAndExit() {
278     System.err.printf(
279       "Usage: bin/hbase %s [opts] [table1 [table2]...] | [regionserver1 [regionserver2]..]%n",
280         getClass().getName());
281     System.err.println(" where [opts] are:");
282     System.err.println("   -help          Show this help and exit.");
283     System.err.println("   -regionserver  replace the table argument to regionserver,");
284     System.err.println("      which means to enable regionserver mode");
285     System.err.println("   -daemon        Continuous check at defined intervals.");
286     System.err.println("   -interval <N>  Interval between checks (sec)");
287     System.err.println("   -e             Use region/regionserver as regular expression");
288     System.err.println("      which means the region/regionserver is regular expression pattern");
289     System.err.println("   -f <B>         stop whole program if first error occurs," +
290         " default is true");
291     System.err.println("   -t <N>         timeout for a check, default is 600000 (milisecs)");
292     System.exit(USAGE_EXIT_CODE);
293   }
294 
295   /**
296    * a Factory method for {@link Monitor}.
297    * Can be overrided by user.
298    * @param index a start index for monitor target
299    * @param args args passed from user
300    * @return a Monitor instance
301    */
302   public Monitor newMonitor(int index, String[] args) {
303     Monitor monitor = null;
304     String[] monitorTargets = null;
305 
306     if(index >= 0) {
307       int length = args.length - index;
308       monitorTargets = new String[length];
309       System.arraycopy(args, index, monitorTargets, 0, length);
310     }
311 
312     if(this.regionServerMode) {
313       monitor = new RegionServerMonitor(
314           this.conf,
315           monitorTargets,
316           this.useRegExp,
317           (ExtendedSink)this.sink);
318     } else {
319       monitor = new RegionMonitor(this.conf, monitorTargets, this.useRegExp, this.sink);
320     }
321     return monitor;
322   }
323 
324   // a Monitor super-class can be extended by users
325   public static abstract class Monitor implements Runnable {
326 
327     protected Configuration config;
328     protected HBaseAdmin admin;
329     protected String[] targets;
330     protected boolean useRegExp;
331     protected boolean initialized = false;
332 
333     protected boolean done = false;
334     protected int errorCode = 0;
335     protected Sink sink;
336 
337     public boolean isDone() {
338       return done;
339     }
340 
341     public boolean hasError() {
342       return errorCode != 0;
343     }
344 
345     protected Monitor(Configuration config, String[] monitorTargets,
346         boolean useRegExp, Sink sink) {
347       if (null == config)
348         throw new IllegalArgumentException("config shall not be null");
349 
350       this.config = config;
351       this.targets = monitorTargets;
352       this.useRegExp = useRegExp;
353       this.sink = sink;
354     }
355 
356     public abstract void run();
357 
358     protected boolean initAdmin() {
359       if (null == this.admin) {
360         try {
361           this.admin = new HBaseAdmin(config);
362         } catch (Exception e) {
363           LOG.error("Initial HBaseAdmin failed...", e);
364           this.errorCode = INIT_ERROR_EXIT_CODE;
365         }
366       } else if (admin.isAborted()) {
367         LOG.error("HBaseAdmin aborted");
368         this.errorCode = INIT_ERROR_EXIT_CODE;
369       }
370       return !this.hasError();
371     }
372   }
373 
374   // a monitor for region mode
375   private static class RegionMonitor extends Monitor {
376 
377     public RegionMonitor(Configuration config, String[] monitorTargets,
378         boolean useRegExp, Sink sink) {
379       super(config, monitorTargets, useRegExp, sink);
380     }
381 
382     @Override
383     public void run() {
384       if(this.initAdmin()) {
385         try {
386           if (this.targets != null && this.targets.length > 0) {
387             String[] tables = generateMonitorTables(this.targets);
388             this.initialized = true;
389             for (String table : tables) {
390               Canary.sniff(admin, sink, table);
391             }
392           } else {
393             sniff();
394           }
395         } catch (Exception e) {
396           LOG.error("Run regionMonitor failed", e);
397           this.errorCode = ERROR_EXIT_CODE;
398         }
399       }
400       this.done = true;
401     }
402 
403     private String[] generateMonitorTables(String[] monitorTargets) throws IOException {
404       String[] returnTables = null;
405 
406       if(this.useRegExp) {
407         Pattern pattern = null;
408         HTableDescriptor[] tds = null;
409         Set<String> tmpTables = new TreeSet<String>();
410         try {
411           for (String monitorTarget : monitorTargets) {
412             pattern = Pattern.compile(monitorTarget);
413             tds = this.admin.listTables(pattern);
414             if (tds != null) {
415               for (HTableDescriptor td : tds) {
416                 tmpTables.add(td.getNameAsString());
417               }
418             }
419           }
420         } catch(IOException e) {
421           LOG.error("Communicate with admin failed", e);
422           throw e;
423         }
424 
425         if(tmpTables.size() > 0) {
426           returnTables = tmpTables.toArray(new String[tmpTables.size()]);
427         } else {
428           String msg = "No HTable found, tablePattern:"
429               + Arrays.toString(monitorTargets);
430           LOG.error(msg);
431           this.errorCode = INIT_ERROR_EXIT_CODE;
432           throw new TableNotFoundException(msg);
433         }
434       } else {
435         returnTables = monitorTargets;
436       }
437 
438       return returnTables;
439     }
440 
441     /*
442      * canary entry point to monitor all the tables.
443      */
444     private void sniff() throws Exception {
445       for (HTableDescriptor table : admin.listTables()) {
446         Canary.sniff(admin, sink, table);
447       }
448     }
449 
450   }
451 
452   /**
453    * Canary entry point for specified table.
454    * @throws Exception
455    */
456   public static void sniff(final HBaseAdmin admin, TableName tableName) throws Exception {
457     sniff(admin, new StdOutSink(), tableName.getNameAsString());
458   }
459 
460   /**
461    * Canary entry point for specified table.
462    * @throws Exception
463    */
464   private static void sniff(final HBaseAdmin admin, final Sink sink, String tableName)
465       throws Exception {
466     if (admin.isTableAvailable(tableName)) {
467       sniff(admin, sink, admin.getTableDescriptor(tableName.getBytes()));
468     } else {
469       LOG.warn(String.format("Table %s is not available", tableName));
470     }
471   }
472 
473   /*
474    * Loops over regions that owns this table, and output some information abouts the state.
475    */
476   private static void sniff(final HBaseAdmin admin, final Sink sink, HTableDescriptor tableDesc)
477       throws Exception {
478     HTable table = null;
479 
480     try {
481       table = new HTable(admin.getConfiguration(), tableDesc.getName());
482     } catch (TableNotFoundException e) {
483       return;
484     }
485 
486     try {
487       for (HRegionInfo region : admin.getTableRegions(tableDesc.getName())) {
488         try {
489           sniffRegion(admin, sink, region, table);
490         } catch (Exception e) {
491           sink.publishReadFailure(region, e);
492           LOG.debug("sniffRegion failed", e);
493         }
494       }
495     } finally {
496       table.close();
497     }
498   }
499 
500   /*
501    * For each column family of the region tries to get one row and outputs the latency, or the
502    * failure.
503    */
504   private static void sniffRegion(
505       final HBaseAdmin admin,
506       final Sink sink,
507       HRegionInfo region,
508       HTable table) throws Exception {
509     HTableDescriptor tableDesc = table.getTableDescriptor();
510     byte[] startKey = null;
511     Get get = null;
512     Scan scan = null;
513     ResultScanner rs = null;
514     StopWatch stopWatch = new StopWatch();
515     for (HColumnDescriptor column : tableDesc.getColumnFamilies()) {
516       stopWatch.reset();
517       startKey = region.getStartKey();
518       // Can't do a get on empty start row so do a Scan of first element if any instead.
519       if (startKey.length > 0) {
520         get = new Get(startKey);
521         get.addFamily(column.getName());
522       } else {
523         scan = new Scan();
524         scan.setCaching(1);
525         scan.addFamily(column.getName());
526         scan.setMaxResultSize(1L);
527       }
528 
529       try {
530         if (startKey.length > 0) {
531           stopWatch.start();
532           table.get(get);
533           stopWatch.stop();
534           sink.publishReadTiming(region, column, stopWatch.getTime());
535         } else {
536           stopWatch.start();
537           rs = table.getScanner(scan);
538           stopWatch.stop();
539           sink.publishReadTiming(region, column, stopWatch.getTime());
540         }
541       } catch (Exception e) {
542         sink.publishReadFailure(region, column, e);
543       } finally {
544         if (rs != null) {
545           rs.close();
546         }
547         scan = null;
548         get = null;
549         startKey = null;
550       }
551     }
552   }
553   //a monitor for regionserver mode
554   private static class RegionServerMonitor extends Monitor {
555 
556     public RegionServerMonitor(Configuration config, String[] monitorTargets,
557         boolean useRegExp, ExtendedSink sink) {
558       super(config, monitorTargets, useRegExp, sink);
559     }
560 
561     private ExtendedSink getSink() {
562       return (ExtendedSink) this.sink;
563     }
564 
565     @Override
566     public void run() {
567       if (this.initAdmin() && this.checkNoTableNames()) {
568         Map<String, List<HRegionInfo>> rsAndRMap = this.filterRegionServerByName();
569         this.initialized = true;
570         this.monitorRegionServers(rsAndRMap);
571       }
572       this.done = true;
573     }
574 
575     private boolean checkNoTableNames() {
576       List<String> foundTableNames = new ArrayList<String>();
577       TableName[] tableNames = null;
578 
579       try {
580         tableNames = this.admin.listTableNames();
581       } catch (IOException e) {
582         LOG.error("Get listTableNames failed", e);
583         this.errorCode = INIT_ERROR_EXIT_CODE;
584         return false;
585       }
586 
587       if (this.targets == null || this.targets.length == 0) return true;
588 
589       for (String target : this.targets) {
590         for (TableName tableName : tableNames) {
591           if (target.equals(tableName.getNameAsString())) {
592             foundTableNames.add(target);
593           }
594         }
595       }
596 
597       if (foundTableNames.size() > 0) {
598         System.err.println("Cannot pass a tablename when using the -regionserver " +
599             "option, tablenames:" + foundTableNames.toString());
600         this.errorCode = USAGE_EXIT_CODE;
601       }
602       return foundTableNames.size() == 0;
603     }
604 
605     private void monitorRegionServers(Map<String, List<HRegionInfo>> rsAndRMap) {
606       String serverName = null;
607       String tableName = null;
608       HRegionInfo region = null;
609       HTable table = null;
610       Get get = null;
611       byte[] startKey = null;
612       Scan scan = null;
613       StopWatch stopWatch = new StopWatch();
614       // monitor one region on every region server
615       for (Map.Entry<String, List<HRegionInfo>> entry : rsAndRMap.entrySet()) {
616         stopWatch.reset();
617         serverName = entry.getKey();
618         // always get the first region
619         region = entry.getValue().get(0);
620         try {
621           tableName = region.getTable().getNameAsString();
622           table = new HTable(this.admin.getConfiguration(), tableName);
623           startKey = region.getStartKey();
624           // Can't do a get on empty start row so do a Scan of first element if any instead.
625           if(startKey.length > 0) {
626             get = new Get(startKey);
627             stopWatch.start();
628             table.get(get);
629             stopWatch.stop();
630           } else {
631             scan = new Scan();
632             scan.setCaching(1);
633             scan.setMaxResultSize(1L);
634             stopWatch.start();
635             ResultScanner s = table.getScanner(scan);
636             s.close();
637             stopWatch.stop();
638           }
639           this.getSink().publishReadTiming(tableName, serverName, stopWatch.getTime());
640         } catch (TableNotFoundException tnfe) {
641           // This is ignored because it doesn't imply that the regionserver is dead
642         } catch (TableNotEnabledException tnee) {
643           // This is considered a success since we got a response.
644           LOG.debug("The targeted table was disabled.  Assuming success.");
645         } catch (DoNotRetryIOException dnrioe) {
646             this.getSink().publishReadFailure(tableName, serverName);
647             LOG.error(dnrioe);
648         } catch (IOException e) {
649           this.getSink().publishReadFailure(tableName, serverName);
650           LOG.error(e);
651           this.errorCode = ERROR_EXIT_CODE;
652         } finally {
653           if (table != null) {
654             try {
655               table.close();
656             } catch (IOException e) {/* DO NOTHING */
657             }
658           }
659           scan = null;
660           get = null;
661           startKey = null;
662         }
663       }
664     }
665 
666     private Map<String, List<HRegionInfo>> filterRegionServerByName() {
667       Map<String, List<HRegionInfo>> regionServerAndRegionsMap = this.getAllRegionServerByName();
668       regionServerAndRegionsMap = this.doFilterRegionServerByName(regionServerAndRegionsMap);
669       return regionServerAndRegionsMap;
670     }
671 
672     private Map<String, List<HRegionInfo>> getAllRegionServerByName() {
673       Map<String, List<HRegionInfo>> rsAndRMap = new HashMap<String, List<HRegionInfo>>();
674       HTable table = null;
675       try {
676         HTableDescriptor[] tableDescs = this.admin.listTables();
677         List<HRegionInfo> regions = null;
678         for (HTableDescriptor tableDesc : tableDescs) {
679           table = new HTable(this.admin.getConfiguration(), tableDesc.getName());
680 
681           for (Map.Entry<HRegionInfo, ServerName> entry : table
682               .getRegionLocations().entrySet()) {
683             ServerName rs = entry.getValue();
684             String rsName = rs.getHostname();
685             HRegionInfo r = entry.getKey();
686 
687             if (rsAndRMap.containsKey(rsName)) {
688               regions = rsAndRMap.get(rsName);
689             } else {
690               regions = new ArrayList<HRegionInfo>();
691               rsAndRMap.put(rsName, regions);
692             }
693             regions.add(r);
694           }
695           table.close();
696         }
697 
698       } catch (IOException e) {
699         String msg = "Get HTables info failed";
700         LOG.error(msg, e);
701         this.errorCode = INIT_ERROR_EXIT_CODE;
702       } finally {
703         if (table != null) {
704           try {
705             table.close();
706           } catch (IOException e) {
707             LOG.warn("Close table failed", e);
708           }
709         }
710       }
711 
712       return rsAndRMap;
713     }
714 
715     private Map<String, List<HRegionInfo>> doFilterRegionServerByName(
716         Map<String, List<HRegionInfo>> fullRsAndRMap) {
717 
718       Map<String, List<HRegionInfo>> filteredRsAndRMap = null;
719 
720       if (this.targets != null && this.targets.length > 0) {
721         filteredRsAndRMap = new HashMap<String, List<HRegionInfo>>();
722         Pattern pattern = null;
723         Matcher matcher = null;
724         boolean regExpFound = false;
725         for (String rsName : this.targets) {
726           if (this.useRegExp) {
727             regExpFound = false;
728             pattern = Pattern.compile(rsName);
729             for (Map.Entry<String,List<HRegionInfo>> entry : fullRsAndRMap.entrySet()) {
730               matcher = pattern.matcher(entry.getKey());
731               if (matcher.matches()) {
732                 filteredRsAndRMap.put(entry.getKey(), entry.getValue());
733                 regExpFound = true;
734               }
735             }
736             if (!regExpFound) {
737               LOG.info("No RegionServerInfo found, regionServerPattern:" + rsName);
738             }
739           } else {
740             if (fullRsAndRMap.containsKey(rsName)) {
741               filteredRsAndRMap.put(rsName, fullRsAndRMap.get(rsName));
742             } else {
743               LOG.info("No RegionServerInfo found, regionServerName:" + rsName);
744             }
745           }
746         }
747       } else {
748         filteredRsAndRMap = fullRsAndRMap;
749       }
750       return filteredRsAndRMap;
751     }
752   }
753 
754   public static void main(String[] args) throws Exception {
755     int exitCode = ToolRunner.run(HBaseConfiguration.create(), new Canary(), args);
756     System.exit(exitCode);
757   }
758 }