|
@@ -477,6 +477,8 @@ public class NameNode implements ClientProtocol, DatanodeProtocol,
|
|
* <li>{@link StartupOption#REGULAR REGULAR} - normal name node startup</li>
|
|
* <li>{@link StartupOption#REGULAR REGULAR} - normal name node startup</li>
|
|
* <li>{@link StartupOption#FORMAT FORMAT} - format name node</li>
|
|
* <li>{@link StartupOption#FORMAT FORMAT} - format name node</li>
|
|
* <li>{@link StartupOption#UPGRADE UPGRADE} - start the cluster
|
|
* <li>{@link StartupOption#UPGRADE UPGRADE} - start the cluster
|
|
|
|
+ * <li>{@link StartupOption#RECOVER RECOVER} - recover name node
|
|
|
|
+ * metadata</li>
|
|
* upgrade and create a snapshot of the current file system state</li>
|
|
* upgrade and create a snapshot of the current file system state</li>
|
|
* <li>{@link StartupOption#ROLLBACK ROLLBACK} - roll the
|
|
* <li>{@link StartupOption#ROLLBACK ROLLBACK} - roll the
|
|
* cluster back to the previous state</li>
|
|
* cluster back to the previous state</li>
|
|
@@ -1220,7 +1222,9 @@ public class NameNode implements ClientProtocol, DatanodeProtocol,
|
|
StartupOption.UPGRADE.getName() + "] | [" +
|
|
StartupOption.UPGRADE.getName() + "] | [" +
|
|
StartupOption.ROLLBACK.getName() + "] | [" +
|
|
StartupOption.ROLLBACK.getName() + "] | [" +
|
|
StartupOption.FINALIZE.getName() + "] | [" +
|
|
StartupOption.FINALIZE.getName() + "] | [" +
|
|
- StartupOption.IMPORT.getName() + "]");
|
|
|
|
|
|
+ StartupOption.IMPORT.getName() + "] | [" +
|
|
|
|
+ StartupOption.RECOVER.getName() +
|
|
|
|
+ " [ " + StartupOption.FORCE.getName() + " ] ]");
|
|
}
|
|
}
|
|
|
|
|
|
private static StartupOption parseArguments(String args[]) {
|
|
private static StartupOption parseArguments(String args[]) {
|
|
@@ -1234,6 +1238,21 @@ public class NameNode implements ClientProtocol, DatanodeProtocol,
|
|
startOpt = StartupOption.REGULAR;
|
|
startOpt = StartupOption.REGULAR;
|
|
} else if (StartupOption.UPGRADE.getName().equalsIgnoreCase(cmd)) {
|
|
} else if (StartupOption.UPGRADE.getName().equalsIgnoreCase(cmd)) {
|
|
startOpt = StartupOption.UPGRADE;
|
|
startOpt = StartupOption.UPGRADE;
|
|
|
|
+ } else if (StartupOption.RECOVER.getName().equalsIgnoreCase(cmd)) {
|
|
|
|
+ if (startOpt != StartupOption.REGULAR) {
|
|
|
|
+ throw new RuntimeException("Can't combine -recover with " +
|
|
|
|
+ "other startup options.");
|
|
|
|
+ }
|
|
|
|
+ startOpt = StartupOption.RECOVER;
|
|
|
|
+ while (++i < argsLen) {
|
|
|
|
+ if (args[i].equalsIgnoreCase(
|
|
|
|
+ StartupOption.FORCE.getName())) {
|
|
|
|
+ startOpt.setForce(MetaRecoveryContext.FORCE_FIRST_CHOICE);
|
|
|
|
+ } else {
|
|
|
|
+ throw new RuntimeException("Error parsing recovery options: " +
|
|
|
|
+ "can't understand option \"" + args[i] + "\"");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
} else if (StartupOption.ROLLBACK.getName().equalsIgnoreCase(cmd)) {
|
|
} else if (StartupOption.ROLLBACK.getName().equalsIgnoreCase(cmd)) {
|
|
startOpt = StartupOption.ROLLBACK;
|
|
startOpt = StartupOption.ROLLBACK;
|
|
} else if (StartupOption.FINALIZE.getName().equalsIgnoreCase(cmd)) {
|
|
} else if (StartupOption.FINALIZE.getName().equalsIgnoreCase(cmd)) {
|
|
@@ -1255,6 +1274,63 @@ public class NameNode implements ClientProtocol, DatanodeProtocol,
|
|
StartupOption.REGULAR.toString()));
|
|
StartupOption.REGULAR.toString()));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private static void doRecovery(StartupOption startOpt, Configuration conf)
|
|
|
|
+ throws IOException {
|
|
|
|
+ if (startOpt.getForce() < MetaRecoveryContext.FORCE_ALL) {
|
|
|
|
+ if (!confirmPrompt("You have selected Metadata Recovery mode. " +
|
|
|
|
+ "This mode is intended to recover lost metadata on a corrupt " +
|
|
|
|
+ "filesystem. Metadata recovery mode often permanently deletes " +
|
|
|
|
+ "data from your HDFS filesystem. Please back up your edit log " +
|
|
|
|
+ "and image before trying this!\n\n" +
|
|
|
|
+ "Are you ready to proceed? (Y/N)\n")) {
|
|
|
|
+ System.err.println("Recovery aborted at user request.\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ MetaRecoveryContext.LOG.info("starting recovery...");
|
|
|
|
+ Collection<File> namespaceDirs = FSNamesystem.getNamespaceDirs(conf);
|
|
|
|
+ Collection<File> editDirs =
|
|
|
|
+ FSNamesystem.getNamespaceEditsDirs(conf);
|
|
|
|
+ FSNamesystem fsn = null;
|
|
|
|
+ try {
|
|
|
|
+ fsn = new FSNamesystem(new FSImage(namespaceDirs, editDirs), conf);
|
|
|
|
+ fsn.dir.fsImage.loadFSImage(startOpt.createRecoveryContext());
|
|
|
|
+ fsn.dir.fsImage.saveNamespace(true);
|
|
|
|
+ MetaRecoveryContext.LOG.info("RECOVERY COMPLETE");
|
|
|
|
+ } finally {
|
|
|
|
+ if (fsn != null)
|
|
|
|
+ fsn.close();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Print out a prompt to the user, and return true if the user
|
|
|
|
+ * responds with "Y" or "yes".
|
|
|
|
+ */
|
|
|
|
+ static boolean confirmPrompt(String prompt) throws IOException {
|
|
|
|
+ while (true) {
|
|
|
|
+ System.err.print(prompt + " (Y or N) ");
|
|
|
|
+ StringBuilder responseBuilder = new StringBuilder();
|
|
|
|
+ while (true) {
|
|
|
|
+ int c = System.in.read();
|
|
|
|
+ if (c == -1 || c == '\r' || c == '\n') {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ responseBuilder.append((char)c);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String response = responseBuilder.toString();
|
|
|
|
+ if (response.equalsIgnoreCase("y") ||
|
|
|
|
+ response.equalsIgnoreCase("yes")) {
|
|
|
|
+ return true;
|
|
|
|
+ } else if (response.equalsIgnoreCase("n") ||
|
|
|
|
+ response.equalsIgnoreCase("no")) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ // else ask them again
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
public static NameNode createNameNode(String argv[],
|
|
public static NameNode createNameNode(String argv[],
|
|
Configuration conf) throws IOException {
|
|
Configuration conf) throws IOException {
|
|
if (conf == null)
|
|
if (conf == null)
|
|
@@ -1273,6 +1349,9 @@ public class NameNode implements ClientProtocol, DatanodeProtocol,
|
|
case FINALIZE:
|
|
case FINALIZE:
|
|
aborted = finalize(conf, true);
|
|
aborted = finalize(conf, true);
|
|
System.exit(aborted ? 1 : 0);
|
|
System.exit(aborted ? 1 : 0);
|
|
|
|
+ case RECOVER:
|
|
|
|
+ NameNode.doRecovery(startOpt, conf);
|
|
|
|
+ return null;
|
|
default:
|
|
default:
|
|
}
|
|
}
|
|
DefaultMetricsSystem.initialize("NameNode");
|
|
DefaultMetricsSystem.initialize("NameNode");
|