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