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