View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.snapshot;
20  
21  import java.io.IOException;
22  import java.io.FileNotFoundException;
23  import java.net.URI;
24  import java.text.SimpleDateFormat;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.Date;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.concurrent.ConcurrentHashMap;
31  import java.util.concurrent.ExecutorService;
32  import java.util.concurrent.atomic.AtomicInteger;
33  import java.util.concurrent.atomic.AtomicLong;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  
38  import org.apache.hadoop.fs.Path;
39  import org.apache.hadoop.fs.FileStatus;
40  import org.apache.hadoop.fs.FileSystem;
41  import org.apache.hadoop.hbase.classification.InterfaceAudience;
42  import org.apache.hadoop.hbase.classification.InterfaceStability;
43  import org.apache.hadoop.conf.Configured;
44  import org.apache.hadoop.hbase.HRegionInfo;
45  import org.apache.hadoop.hbase.TableName;
46  import org.apache.hadoop.util.StringUtils;
47  import org.apache.hadoop.util.Tool;
48  import org.apache.hadoop.util.ToolRunner;
49  
50  import org.apache.hadoop.conf.Configuration;
51  import org.apache.hadoop.hbase.HBaseConfiguration;
52  import org.apache.hadoop.hbase.io.HFileLink;
53  import org.apache.hadoop.hbase.io.WALLink;
54  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
55  import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
56  import org.apache.hadoop.hbase.util.FSUtils;
57  
58  /**
59   * Tool for dumping snapshot information.
60   * <ol>
61   * <li> Table Descriptor
62   * <li> Snapshot creation time, type, format version, ...
63   * <li> List of hfiles and wals
64   * <li> Stats about hfiles and logs sizes, percentage of shared with the source table, ...
65   * </ol>
66   * @deprecated All the protos references will be changed to standalone POJOs from 2.0 onwards
67   */
68  @InterfaceAudience.Public
69  @InterfaceStability.Evolving
70  @Deprecated
71  public final class SnapshotInfo extends Configured implements Tool {
72    private static final Log LOG = LogFactory.getLog(SnapshotInfo.class);
73  
74    /**
75     * Statistics about the snapshot
76     * <ol>
77     * <li> How many store files and logs are in the archive
78     * <li> How many store files and logs are shared with the table
79     * <li> Total store files and logs size and shared amount
80     * </ol>
81     */
82    public static class SnapshotStats {
83      /** Information about the file referenced by the snapshot */
84      static class FileInfo {
85        private final boolean corrupted;
86        private final boolean inArchive;
87        private final long size;
88  
89        FileInfo(final boolean inArchive, final long size, final boolean corrupted) {
90          this.corrupted = corrupted;
91          this.inArchive = inArchive;
92          this.size = size;
93        }
94  
95        /** @return true if the file is in the archive */
96        public boolean inArchive() {
97          return this.inArchive;
98        }
99  
100       /** @return true if the file is corrupted */
101       public boolean isCorrupted() {
102         return this.corrupted;
103       }
104 
105       /** @return true if the file is missing */
106       public boolean isMissing() {
107         return this.size < 0;
108       }
109 
110       /** @return the file size */
111       public long getSize() {
112         return this.size;
113       }
114 
115       String getStateToString() {
116         if (isCorrupted()) return "CORRUPTED";
117         if (isMissing()) return "NOT FOUND";
118         if (inArchive()) return "archive";
119         return null;
120       }
121     }
122 
123     private AtomicInteger hfileArchiveCount = new AtomicInteger();
124     private AtomicInteger hfilesCorrupted = new AtomicInteger();
125     private AtomicInteger hfilesMissing = new AtomicInteger();
126     private AtomicInteger hfilesCount = new AtomicInteger();
127     private AtomicInteger logsMissing = new AtomicInteger();
128     private AtomicInteger logsCount = new AtomicInteger();
129     private AtomicLong hfileArchiveSize = new AtomicLong();
130     private AtomicLong hfileSize = new AtomicLong();
131     private AtomicLong nonSharedHfilesArchiveSize = new AtomicLong();
132     private AtomicLong logSize = new AtomicLong();
133 
134     private final SnapshotDescription snapshot;
135     private final TableName snapshotTable;
136     private final Configuration conf;
137     private final FileSystem fs;
138 
139     SnapshotStats(final Configuration conf, final FileSystem fs, final SnapshotDescription snapshot)
140     {
141       this.snapshot = snapshot;
142       this.snapshotTable = TableName.valueOf(snapshot.getTable());
143       this.conf = conf;
144       this.fs = fs;
145     }
146 
147     /** @return the snapshot descriptor */
148     public SnapshotDescription getSnapshotDescription() {
149       return this.snapshot;
150     }
151 
152     /** @return true if the snapshot is corrupted */
153     public boolean isSnapshotCorrupted() {
154       return hfilesMissing.get() > 0 ||
155              logsMissing.get() > 0 ||
156              hfilesCorrupted.get() > 0;
157     }
158 
159     /** @return the number of available store files */
160     public int getStoreFilesCount() {
161       return hfilesCount.get() + hfileArchiveCount.get();
162     }
163 
164     /** @return the number of available store files in the archive */
165     public int getArchivedStoreFilesCount() {
166       return hfileArchiveCount.get();
167     }
168 
169     /** @return the number of available log files */
170     public int getLogsCount() {
171       return logsCount.get();
172     }
173 
174     /** @return the number of missing store files */
175     public int getMissingStoreFilesCount() {
176       return hfilesMissing.get();
177     }
178 
179     /** @return the number of corrupted store files */
180     public int getCorruptedStoreFilesCount() {
181       return hfilesCorrupted.get();
182     }
183 
184     /** @return the number of missing log files */
185     public int getMissingLogsCount() {
186       return logsMissing.get();
187     }
188 
189     /** @return the total size of the store files referenced by the snapshot */
190     public long getStoreFilesSize() {
191       return hfileSize.get() + hfileArchiveSize.get();
192     }
193 
194     /** @return the total size of the store files shared */
195     public long getSharedStoreFilesSize() {
196       return hfileSize.get();
197     }
198 
199     /** @return the total size of the store files in the archive */
200     public long getArchivedStoreFileSize() {
201       return hfileArchiveSize.get();
202     }
203 
204     /** @return the total size of the store files in the archive which is not shared
205      *    with other snapshots and tables.
206      *    This is only calculated when getSnapshotStats(Configuration, SnapshotDescription, Map)
207      *    is called with a non-null Map
208      */
209     public long getNonSharedArchivedStoreFilesSize() {
210       return nonSharedHfilesArchiveSize.get();
211     }
212 
213     /** @return the percentage of the shared store files */
214     public float getSharedStoreFilePercentage() {
215       return ((float)hfileSize.get() / (hfileSize.get() + hfileArchiveSize.get())) * 100;
216     }
217 
218     /** @return the total log size */
219     public long getLogsSize() {
220       return logSize.get();
221     }
222 
223     /** Check if for a give file in archive, if there are other snapshots/tables still
224      * reference it.
225      * @param filePath file path in archive
226      * @param snapshotFilesMap a map for store files in snapshots about how many snapshots refer
227      *                         to it.
228      * @return true or false
229      */
230     private boolean isArchivedFileStillReferenced(final Path filePath,
231         final Map<Path, Integer> snapshotFilesMap) {
232 
233       Integer c = snapshotFilesMap.get(filePath);
234 
235       // Check if there are other snapshots or table from clone_snapshot() (via back-reference)
236       // still reference to it.
237       if ((c != null) && (c == 1)) {
238         Path parentDir = filePath.getParent();
239         Path backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName());
240         try {
241           if (FSUtils.listStatus(fs, backRefDir) == null) {
242             return false;
243           }
244         } catch (IOException e) {
245           // For the purpose of this function, IOException is ignored and treated as
246           // the file is still being referenced.
247         }
248       }
249       return true;
250     }
251 
252     /**
253      * Add the specified store file to the stats
254      * @param region region encoded Name
255      * @param family family name
256      * @param storeFile store file name
257      * @param filesMap store files map for all snapshots, it may be null
258      * @return the store file information
259      */
260     FileInfo addStoreFile(final HRegionInfo region, final String family,
261         final SnapshotRegionManifest.StoreFile storeFile,
262         final Map<Path, Integer> filesMap) throws IOException {
263       HFileLink link = HFileLink.build(conf, snapshotTable, region.getEncodedName(),
264               family, storeFile.getName());
265       boolean isCorrupted = false;
266       boolean inArchive = false;
267       long size = -1;
268       try {
269         if ((inArchive = fs.exists(link.getArchivePath()))) {
270           size = fs.getFileStatus(link.getArchivePath()).getLen();
271           hfileArchiveSize.addAndGet(size);
272           hfileArchiveCount.incrementAndGet();
273 
274           // If store file is not shared with other snapshots and tables,
275           // increase nonSharedHfilesArchiveSize
276           if ((filesMap != null) &&
277               !isArchivedFileStillReferenced(link.getArchivePath(), filesMap)) {
278             nonSharedHfilesArchiveSize.addAndGet(size);
279           }
280         } else {
281           size = link.getFileStatus(fs).getLen();
282           hfileSize.addAndGet(size);
283           hfilesCount.incrementAndGet();
284         }
285         isCorrupted = (storeFile.hasFileSize() && storeFile.getFileSize() != size);
286         if (isCorrupted) hfilesCorrupted.incrementAndGet();
287       } catch (FileNotFoundException e) {
288         hfilesMissing.incrementAndGet();
289       }
290       return new FileInfo(inArchive, size, isCorrupted);
291     }
292 
293     /**
294      * Add the specified log file to the stats
295      * @param server server name
296      * @param logfile log file name
297      * @return the log information
298      */
299     FileInfo addLogFile(final String server, final String logfile) throws IOException {
300       WALLink logLink = new WALLink(conf, server, logfile);
301       long size = -1;
302       try {
303         size = logLink.getFileStatus(fs).getLen();
304         logSize.addAndGet(size);
305         logsCount.incrementAndGet();
306       } catch (FileNotFoundException e) {
307         logsMissing.incrementAndGet();
308       }
309       return new FileInfo(false, size, false);
310     }
311   }
312 
313   private boolean printSizeInBytes = false;
314   private FileSystem fs;
315   private Path rootDir;
316 
317   private SnapshotManifest snapshotManifest;
318 
319   @Override
320   @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="REC_CATCH_EXCEPTION",
321     justification="Intentional")
322   public int run(String[] args) throws IOException, InterruptedException {
323     final Configuration conf = getConf();
324     boolean listSnapshots = false;
325     String snapshotName = null;
326     boolean showSchema = false;
327     boolean showFiles = false;
328     boolean showStats = false;
329 
330     // Process command line args
331     for (int i = 0; i < args.length; i++) {
332       String cmd = args[i];
333       try {
334         if (cmd.equals("-snapshot")) {
335           snapshotName = args[++i];
336         } else if (cmd.equals("-files")) {
337           showFiles = true;
338           showStats = true;
339         } else if (cmd.equals("-stats")) {
340           showStats = true;
341         } else if (cmd.equals("-schema")) {
342           showSchema = true;
343         } else if (cmd.equals("-remote-dir")) {
344           Path sourceDir = new Path(args[++i]);
345           URI defaultFs = sourceDir.getFileSystem(conf).getUri();
346           FSUtils.setFsDefault(conf, new Path(defaultFs));
347           FSUtils.setRootDir(conf, sourceDir);
348         } else if (cmd.equals("-list-snapshots")) {
349           listSnapshots = true;
350         } else if (cmd.equals("-size-in-bytes")) {
351           printSizeInBytes = true;
352         } else if (cmd.equals("-h") || cmd.equals("--help")) {
353           printUsageAndExit();
354         } else {
355           System.err.println("UNEXPECTED: " + cmd);
356           printUsageAndExit();
357         }
358       } catch (Exception e) {
359         printUsageAndExit(); // FindBugs: REC_CATCH_EXCEPTION
360       }
361     }
362 
363     // List Available Snapshots
364     if (listSnapshots) {
365       SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
366       System.out.printf("%-20s | %-20s | %s%n", "SNAPSHOT", "CREATION TIME", "TABLE NAME");
367       for (SnapshotDescription desc: getSnapshotList(conf)) {
368         System.out.printf("%-20s | %20s | %s%n",
369                           desc.getName(),
370                           df.format(new Date(desc.getCreationTime())),
371                           desc.getTable());
372       }
373       return 0;
374     }
375 
376     if (snapshotName == null) {
377       System.err.println("Missing snapshot name!");
378       printUsageAndExit();
379       return 1;
380     }
381 
382     rootDir = FSUtils.getRootDir(conf);
383     fs = FileSystem.get(rootDir.toUri(), conf);
384     LOG.debug("fs=" + fs.getUri().toString() + " root=" + rootDir);
385 
386     // Load snapshot information
387     if (!loadSnapshotInfo(snapshotName)) {
388       System.err.println("Snapshot '" + snapshotName + "' not found!");
389       return 1;
390     }
391 
392     printInfo();
393     if (showSchema) printSchema();
394     printFiles(showFiles, showStats);
395 
396     return 0;
397   }
398 
399   /**
400    * Load snapshot info and table descriptor for the specified snapshot
401    * @param snapshotName name of the snapshot to load
402    * @return false if snapshot is not found
403    */
404   private boolean loadSnapshotInfo(final String snapshotName) throws IOException {
405     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
406     if (!fs.exists(snapshotDir)) {
407       LOG.warn("Snapshot '" + snapshotName + "' not found in: " + snapshotDir);
408       return false;
409     }
410 
411     SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
412     snapshotManifest = SnapshotManifest.open(getConf(), fs, snapshotDir, snapshotDesc);
413     return true;
414   }
415 
416   /**
417    * Dump the {@link SnapshotDescription}
418    */
419   private void printInfo() {
420     SnapshotDescription snapshotDesc = snapshotManifest.getSnapshotDescription();
421     SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
422     System.out.println("Snapshot Info");
423     System.out.println("----------------------------------------");
424     System.out.println("   Name: " + snapshotDesc.getName());
425     System.out.println("   Type: " + snapshotDesc.getType());
426     System.out.println("  Table: " + snapshotDesc.getTable());
427     System.out.println(" Format: " + snapshotDesc.getVersion());
428     System.out.println("Created: " + df.format(new Date(snapshotDesc.getCreationTime())));
429     System.out.println();
430   }
431 
432   /**
433    * Dump the {@link HTableDescriptor}
434    */
435   private void printSchema() {
436     System.out.println("Table Descriptor");
437     System.out.println("----------------------------------------");
438     System.out.println(snapshotManifest.getTableDescriptor().toString());
439     System.out.println();
440   }
441 
442   /**
443    * Collect the hfiles and logs statistics of the snapshot and
444    * dump the file list if requested and the collected information.
445    */
446   private void printFiles(final boolean showFiles, final boolean showStats) throws IOException {
447     if (showFiles) {
448       System.out.println("Snapshot Files");
449       System.out.println("----------------------------------------");
450     }
451 
452     // Collect information about hfiles and logs in the snapshot
453     final SnapshotDescription snapshotDesc = snapshotManifest.getSnapshotDescription();
454     final String table = snapshotDesc.getTable();
455     final SnapshotStats stats = new SnapshotStats(this.getConf(), this.fs, snapshotDesc);
456     SnapshotReferenceUtil.concurrentVisitReferencedFiles(getConf(), fs, snapshotManifest,
457         "SnapshotInfo", new SnapshotReferenceUtil.SnapshotVisitor() {
458           @Override public void storeFile(final HRegionInfo regionInfo, final String family,
459               final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
460             if (storeFile.hasReference()) return;
461 
462             SnapshotStats.FileInfo info = stats.addStoreFile(regionInfo, family, storeFile, null);
463             if (showFiles) {
464               String state = info.getStateToString();
465               System.out.printf("%8s %s/%s/%s/%s %s%n",
466                   (info.isMissing() ? "-" : fileSizeToString(info.getSize())), table,
467                   regionInfo.getEncodedName(), family, storeFile.getName(),
468                   state == null ? "" : "(" + state + ")");
469             }
470           }
471         });
472 
473     // Dump the stats
474     System.out.println();
475     if (stats.isSnapshotCorrupted()) {
476       System.out.println("**************************************************************");
477       System.out.printf("BAD SNAPSHOT: %d hfile(s) and %d log(s) missing.%n",
478         stats.getMissingStoreFilesCount(), stats.getMissingLogsCount());
479       System.out.printf("              %d hfile(s) corrupted.%n",
480         stats.getCorruptedStoreFilesCount());
481       System.out.println("**************************************************************");
482     }
483 
484     if (showStats) {
485       System.out.printf("%d HFiles (%d in archive), total size %s (%.2f%% %s shared with the source table)%n",
486         stats.getStoreFilesCount(), stats.getArchivedStoreFilesCount(),
487         fileSizeToString(stats.getStoreFilesSize()),
488         stats.getSharedStoreFilePercentage(),
489         fileSizeToString(stats.getSharedStoreFilesSize())
490       );
491       System.out.printf("%d Logs, total size %s%n",
492         stats.getLogsCount(), fileSizeToString(stats.getLogsSize()));
493       System.out.println();
494     }
495   }
496 
497   private String fileSizeToString(long size) {
498     return printSizeInBytes ? Long.toString(size) : StringUtils.humanReadableInt(size);
499   }
500 
501   private void printUsageAndExit() {
502     System.err.printf("Usage: bin/hbase snapshot info [options]%n");
503     System.err.println(" where [options] are:");
504     System.err.println("  -h|-help                Show this help and exit.");
505     System.err.println("  -remote-dir             Root directory that contains the snapshots.");
506     System.err.println("  -list-snapshots         List all the available snapshots and exit.");
507     System.err.println("  -size-in-bytes          Print the size of the files in bytes.");
508     System.err.println("  -snapshot NAME          Snapshot to examine.");
509     System.err.println("  -files                  Files and logs list.");
510     System.err.println("  -stats                  Files and logs stats.");
511     System.err.println("  -schema                 Describe the snapshotted table.");
512     System.err.println();
513     System.err.println("Examples:");
514     System.err.println("  hbase snapshot info \\");
515     System.err.println("    -snapshot MySnapshot -files");
516     System.exit(1);
517   }
518 
519   /**
520    * Returns the snapshot stats
521    * @param conf the {@link Configuration} to use
522    * @param snapshot {@link SnapshotDescription} to get stats from
523    * @return the snapshot stats
524    */
525   public static SnapshotStats getSnapshotStats(final Configuration conf,
526       final SnapshotDescription snapshot) throws IOException {
527     return getSnapshotStats(conf, snapshot, null);
528   }
529 
530   /**
531    * Returns the snapshot stats
532    * @param conf the {@link Configuration} to use
533    * @param snapshot  {@link SnapshotDescription} to get stats from
534    * @param filesMap {@link Map} store files map for all snapshots, it may be null
535    * @return the snapshot stats
536    */
537   public static SnapshotStats getSnapshotStats(final Configuration conf,
538       final SnapshotDescription snapshot,
539       final Map<Path, Integer> filesMap) throws IOException {
540     Path rootDir = FSUtils.getRootDir(conf);
541     FileSystem fs = FileSystem.get(rootDir.toUri(), conf);
542     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
543     SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshot);
544     final SnapshotStats stats = new SnapshotStats(conf, fs, snapshot);
545     SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest,
546         "SnapshotsStatsAggregation", new SnapshotReferenceUtil.SnapshotVisitor() {
547           @Override
548           public void storeFile(final HRegionInfo regionInfo, final String family,
549               final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
550             if (!storeFile.hasReference()) {
551               stats.addStoreFile(regionInfo, family, storeFile, filesMap);
552             }
553           }});
554     return stats;
555   }
556 
557   /**
558    * Returns the list of available snapshots in the specified location
559    * @param conf the {@link Configuration} to use
560    * @return the list of snapshots
561    */
562   public static List<SnapshotDescription> getSnapshotList(final Configuration conf)
563       throws IOException {
564     Path rootDir = FSUtils.getRootDir(conf);
565     FileSystem fs = FileSystem.get(rootDir.toUri(), conf);
566     Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir);
567     FileStatus[] snapshots = fs.listStatus(snapshotDir,
568       new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
569     List<SnapshotDescription> snapshotLists =
570       new ArrayList<SnapshotDescription>(snapshots.length);
571     for (FileStatus snapshotDirStat: snapshots) {
572       snapshotLists.add(SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDirStat.getPath()));
573     }
574     return snapshotLists;
575   }
576 
577   /**
578    * Gets the store files map for snapshot
579    * @param conf the {@link Configuration} to use
580    * @param snapshot {@link SnapshotDescription} to get stats from
581    * @param exec the {@link ExecutorService} to use
582    * @param filesMap {@link Map} the map to put the mapping entries
583    * @param uniqueHFilesArchiveSize {@link AtomicLong} the accumulated store file size in archive
584    * @param uniqueHFilesSize {@link AtomicLong} the accumulated store file size shared
585    * @return the snapshot stats
586    */
587   private static void getSnapshotFilesMap(final Configuration conf,
588       final SnapshotDescription snapshot, final ExecutorService exec,
589       final ConcurrentHashMap<Path, Integer> filesMap,
590       final AtomicLong uniqueHFilesArchiveSize, final AtomicLong uniqueHFilesSize)
591       throws IOException {
592     Path rootDir = FSUtils.getRootDir(conf);
593     final FileSystem fs = FileSystem.get(rootDir.toUri(), conf);
594 
595     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
596     SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshot);
597     SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest, exec,
598         new SnapshotReferenceUtil.SnapshotVisitor() {
599           @Override public void storeFile(final HRegionInfo regionInfo, final String family,
600               final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
601             if (!storeFile.hasReference()) {
602               HFileLink link = HFileLink
603                   .build(conf, TableName.valueOf(snapshot.getTable()), regionInfo.getEncodedName(),
604                       family, storeFile.getName());
605               long size;
606               Integer count;
607               Path p;
608               AtomicLong al;
609               int c = 0;
610 
611               if (fs.exists(link.getArchivePath())) {
612                 p = link.getArchivePath();
613                 al = uniqueHFilesArchiveSize;
614                 size = fs.getFileStatus(p).getLen();
615               } else {
616                 p = link.getOriginPath();
617                 al = uniqueHFilesSize;
618                 size = link.getFileStatus(fs).getLen();
619               }
620 
621               // If it has been counted, do not double count
622               count = filesMap.get(p);
623               if (count != null) {
624                 c = count.intValue();
625               } else {
626                 al.addAndGet(size);
627               }
628 
629               filesMap.put(p, ++c);
630             }
631           }
632         });
633   }
634 
635   /**
636    * Returns the map of store files based on path for all snapshots
637    * @param conf the {@link Configuration} to use
638    * @param uniqueHFilesArchiveSize pass out the size for store files in archive
639    * @param uniqueHFilesSize pass out the size for store files shared
640    * @return the map of store files
641    */
642   public static Map<Path, Integer> getSnapshotsFilesMap(final Configuration conf,
643       AtomicLong uniqueHFilesArchiveSize, AtomicLong uniqueHFilesSize) throws IOException {
644     List<SnapshotDescription> snapshotList = getSnapshotList(conf);
645 
646     if (snapshotList.size() == 0) {
647       return Collections.emptyMap();
648     }
649 
650     ConcurrentHashMap<Path, Integer> fileMap = new ConcurrentHashMap<>();
651 
652     ExecutorService exec = SnapshotManifest.createExecutor(conf, "SnapshotsFilesMapping");
653 
654     try {
655       for (final SnapshotDescription snapshot : snapshotList) {
656         getSnapshotFilesMap(conf, snapshot, exec, fileMap, uniqueHFilesArchiveSize,
657             uniqueHFilesSize);
658       }
659     } finally {
660       exec.shutdown();
661     }
662 
663     return fileMap;
664   }
665 
666   /**
667    * The guts of the {@link #main} method.
668    * Call this method to avoid the {@link #main(String[])} System.exit.
669    * @param args
670    * @return errCode
671    * @throws Exception
672    */
673   static int innerMain(final String [] args) throws Exception {
674     return ToolRunner.run(HBaseConfiguration.create(), new SnapshotInfo(), args);
675   }
676 
677   public static void main(String[] args) throws Exception {
678      System.exit(innerMain(args));
679   }
680 }