001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hdfs.tools.offlineEditsViewer;
019
020import org.apache.hadoop.classification.InterfaceAudience;
021import org.apache.hadoop.classification.InterfaceStability;
022
023import org.apache.hadoop.conf.Configured;
024import org.apache.hadoop.hdfs.tools.offlineEditsViewer.OfflineEditsLoader.OfflineEditsLoaderFactory;
025import org.apache.hadoop.util.Tool;
026import org.apache.hadoop.util.ToolRunner;
027
028import org.apache.commons.cli.CommandLine;
029import org.apache.commons.cli.CommandLineParser;
030import org.apache.commons.cli.OptionBuilder;
031import org.apache.commons.cli.Options;
032import org.apache.commons.cli.ParseException;
033import org.apache.commons.cli.PosixParser;
034
035/**
036 * This class implements an offline edits viewer, tool that
037 * can be used to view edit logs.
038 */
039@InterfaceAudience.Private
040@InterfaceStability.Unstable
041public class OfflineEditsViewer extends Configured implements Tool {
042  private static final String HELP_OPT = "-h";
043  private static final String HELP_LONGOPT = "--help";
044  private final static String defaultProcessor = "xml";
045
046  /**
047   * Print help.
048   */  
049  private void printHelp() {
050    String summary =
051      "Usage: bin/hdfs oev [OPTIONS] -i INPUT_FILE -o OUTPUT_FILE\n" +
052      "Offline edits viewer\n" +
053      "Parse a Hadoop edits log file INPUT_FILE and save results\n" +
054      "in OUTPUT_FILE.\n" +
055      "Required command line arguments:\n" +
056      "-i,--inputFile <arg>   edits file to process, xml (case\n" +
057      "                       insensitive) extension means XML format,\n" +
058      "                       any other filename means binary format\n" +
059      "-o,--outputFile <arg>  Name of output file. If the specified\n" +
060      "                       file exists, it will be overwritten,\n" +
061      "                       format of the file is determined\n" +
062      "                       by -p option\n" +
063      "\n" + 
064      "Optional command line arguments:\n" +
065      "-p,--processor <arg>   Select which type of processor to apply\n" +
066      "                       against image file, currently supported\n" +
067      "                       processors are: binary (native binary format\n" +
068      "                       that Hadoop uses), xml (default, XML\n" +
069      "                       format), stats (prints statistics about\n" +
070      "                       edits file)\n" +
071      "-h,--help              Display usage information and exit\n" +
072      "-f,--fix-txids         Renumber the transaction IDs in the input,\n" +
073      "                       so that there are no gaps or invalid\n" +
074      "                       transaction IDs.\n" +
075      "-r,--recover           When reading binary edit logs, use recovery \n" +
076      "                       mode.  This will give you the chance to skip \n" +
077      "                       corrupt parts of the edit log.\n" +
078      "-v,--verbose           More verbose output, prints the input and\n" +
079      "                       output filenames, for processors that write\n" +
080      "                       to a file, also output to screen. On large\n" +
081      "                       image files this will dramatically increase\n" +
082      "                       processing time (default is false).\n";
083
084
085    System.out.println(summary);
086    System.out.println();
087    ToolRunner.printGenericCommandUsage(System.out);
088  }
089
090  /**
091   * Build command-line options and descriptions
092   *
093   * @return command line options
094   */
095  public static Options buildOptions() {
096    Options options = new Options();
097
098    // Build in/output file arguments, which are required, but there is no 
099    // addOption method that can specify this
100    OptionBuilder.isRequired();
101    OptionBuilder.hasArgs();
102    OptionBuilder.withLongOpt("outputFilename");
103    options.addOption(OptionBuilder.create("o"));
104    
105    OptionBuilder.isRequired();
106    OptionBuilder.hasArgs();
107    OptionBuilder.withLongOpt("inputFilename");
108    options.addOption(OptionBuilder.create("i"));
109    
110    options.addOption("p", "processor", true, "");
111    options.addOption("v", "verbose", false, "");
112    options.addOption("f", "fix-txids", false, "");
113    options.addOption("r", "recover", false, "");
114    options.addOption("h", "help", false, "");
115
116    return options;
117  }
118
119  /** Process an edit log using the chosen processor or visitor.
120   * 
121   * @param inputFilename   The file to process
122   * @param outputFilename  The output file name
123   * @param processor       If visitor is null, the processor to use
124   * @param visitor         If non-null, the visitor to use.
125   * 
126   * @return                0 on success; error code otherwise
127   */
128  public int go(String inputFileName, String outputFileName, String processor,
129      Flags flags, OfflineEditsVisitor visitor)
130  {
131    if (flags.getPrintToScreen()) {
132      System.out.println("input  [" + inputFileName  + "]");
133      System.out.println("output [" + outputFileName + "]");
134    }
135    try {
136      if (visitor == null) {
137        visitor = OfflineEditsVisitorFactory.getEditsVisitor(
138            outputFileName, processor, flags.getPrintToScreen());
139      }
140      boolean xmlInput = inputFileName.toLowerCase().endsWith(".xml");
141      OfflineEditsLoader loader = OfflineEditsLoaderFactory.
142          createLoader(visitor, inputFileName, xmlInput, flags);
143      loader.loadEdits();
144    } catch(Exception e) {
145      System.err.println("Encountered exception. Exiting: " + e.getMessage());
146      e.printStackTrace(System.err);
147      return -1;
148    }
149    return 0;
150  }
151
152  public static class Flags {
153    private boolean printToScreen = false;
154    private boolean fixTxIds = false;
155    private boolean recoveryMode = false;
156    
157    public Flags() {
158    }
159    
160    public boolean getPrintToScreen() {
161      return printToScreen;
162    }
163    
164    public void setPrintToScreen() {
165      printToScreen = true;
166    }
167    
168    public boolean getFixTxIds() {
169      return fixTxIds;
170    }
171    
172    public void setFixTxIds() {
173      fixTxIds = true;
174    }
175    
176    public boolean getRecoveryMode() {
177      return recoveryMode;
178    }
179    
180    public void setRecoveryMode() {
181      recoveryMode = true;
182    }
183  }
184  
185  /**
186   * Main entry point for ToolRunner (see ToolRunner docs)
187   *
188   * @param argv The parameters passed to this program.
189   * @return 0 on success, non zero on error.
190   */
191  @Override
192  public int run(String[] argv) throws Exception {
193    Options options = buildOptions();
194    if(argv.length == 0) {
195      printHelp();
196      return 0;
197    }
198    // print help and exit with zero exit code
199    if (argv.length == 1 && isHelpOption(argv[0])) {
200      printHelp();
201      return 0;
202    }
203    CommandLineParser parser = new PosixParser();
204    CommandLine cmd;
205    try {
206      cmd = parser.parse(options, argv);
207    } catch (ParseException e) {
208      System.out.println(
209        "Error parsing command-line options: " + e.getMessage());
210      printHelp();
211      return -1;
212    }
213    
214    if (cmd.hasOption("h")) {
215      // print help and exit with non zero exit code since
216      // it is not expected to give help and other options together.
217      printHelp();
218      return -1;
219    }
220    String inputFileName = cmd.getOptionValue("i");
221    String outputFileName = cmd.getOptionValue("o");
222    String processor = cmd.getOptionValue("p");
223    if(processor == null) {
224      processor = defaultProcessor;
225    }
226    Flags flags = new Flags();
227    if (cmd.hasOption("r")) {
228      flags.setRecoveryMode();
229    }
230    if (cmd.hasOption("f")) {
231      flags.setFixTxIds();
232    }
233    if (cmd.hasOption("v")) {
234      flags.setPrintToScreen();
235    }
236    return go(inputFileName, outputFileName, processor, flags, null);
237  }
238
239  /**
240   * main() runs the offline edits viewer using ToolRunner
241   *
242   * @param argv Command line parameters.
243   */
244  public static void main(String[] argv) throws Exception {
245    int res = ToolRunner.run(new OfflineEditsViewer(), argv);
246    System.exit(res);
247  }
248
249  private static boolean isHelpOption(String arg) {
250    return arg.equalsIgnoreCase(HELP_OPT) ||
251        arg.equalsIgnoreCase(HELP_LONGOPT);
252  }
253}