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 package org.apache.hadoop.hdfs.server.namenode; 19 20 import java.io.File; 21 import java.io.FilenameFilter; 22 import java.io.IOException; 23 import java.nio.file.Files; 24 import java.util.List; 25 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 import org.apache.hadoop.conf.Configuration; 29 import org.apache.hadoop.hdfs.server.common.Storage; 30 import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory; 31 import org.apache.hadoop.hdfs.server.common.StorageInfo; 32 33 import com.google.common.base.Preconditions; 34 import org.apache.hadoop.io.IOUtils; 35 36 public abstract class NNUpgradeUtil { 37 38 private static final Log LOG = LogFactory.getLog(NNUpgradeUtil.class); 39 40 /** 41 * Return true if this storage dir can roll back to the previous storage 42 * state, false otherwise. The NN will refuse to run the rollback operation 43 * unless at least one JM or fsimage storage directory can roll back. 44 * 45 * @param storage the storage info for the current state 46 * @param prevStorage the storage info for the previous (unupgraded) state 47 * @param targetLayoutVersion the layout version we intend to roll back to 48 * @return true if this JM can roll back, false otherwise. 49 * @throws IOException in the event of error 50 */ canRollBack(StorageDirectory sd, StorageInfo storage, StorageInfo prevStorage, int targetLayoutVersion)51 static boolean canRollBack(StorageDirectory sd, StorageInfo storage, 52 StorageInfo prevStorage, int targetLayoutVersion) throws IOException { 53 File prevDir = sd.getPreviousDir(); 54 if (!prevDir.exists()) { // use current directory then 55 LOG.info("Storage directory " + sd.getRoot() 56 + " does not contain previous fs state."); 57 // read and verify consistency with other directories 58 storage.readProperties(sd); 59 return false; 60 } 61 62 // read and verify consistency of the prev dir 63 prevStorage.readPreviousVersionProperties(sd); 64 65 if (prevStorage.getLayoutVersion() != targetLayoutVersion) { 66 throw new IOException( 67 "Cannot rollback to storage version " + 68 prevStorage.getLayoutVersion() + 69 " using this version of the NameNode, which uses storage version " + 70 targetLayoutVersion + ". " + 71 "Please use the previous version of HDFS to perform the rollback."); 72 } 73 74 return true; 75 } 76 77 /** 78 * Finalize the upgrade. The previous dir, if any, will be renamed and 79 * removed. After this is completed, rollback is no longer allowed. 80 * 81 * @param sd the storage directory to finalize 82 * @throws IOException in the event of error 83 */ doFinalize(StorageDirectory sd)84 static void doFinalize(StorageDirectory sd) throws IOException { 85 File prevDir = sd.getPreviousDir(); 86 if (!prevDir.exists()) { // already discarded 87 LOG.info("Directory " + prevDir + " does not exist."); 88 LOG.info("Finalize upgrade for " + sd.getRoot()+ " is not required."); 89 return; 90 } 91 LOG.info("Finalizing upgrade of storage directory " + sd.getRoot()); 92 Preconditions.checkState(sd.getCurrentDir().exists(), 93 "Current directory must exist."); 94 final File tmpDir = sd.getFinalizedTmp(); 95 // rename previous to tmp and remove 96 NNStorage.rename(prevDir, tmpDir); 97 NNStorage.deleteDir(tmpDir); 98 LOG.info("Finalize upgrade for " + sd.getRoot()+ " is complete."); 99 } 100 101 /** 102 * Perform any steps that must succeed across all storage dirs/JournalManagers 103 * involved in an upgrade before proceeding onto the actual upgrade stage. If 104 * a call to any JM's or local storage dir's doPreUpgrade method fails, then 105 * doUpgrade will not be called for any JM. The existing current dir is 106 * renamed to previous.tmp, and then a new, empty current dir is created. 107 * 108 * @param conf configuration for creating {@link EditLogFileOutputStream} 109 * @param sd the storage directory to perform the pre-upgrade procedure. 110 * @throws IOException in the event of error 111 */ doPreUpgrade(Configuration conf, StorageDirectory sd)112 static void doPreUpgrade(Configuration conf, StorageDirectory sd) 113 throws IOException { 114 LOG.info("Starting upgrade of storage directory " + sd.getRoot()); 115 116 // rename current to tmp 117 renameCurToTmp(sd); 118 119 final File curDir = sd.getCurrentDir(); 120 final File tmpDir = sd.getPreviousTmp(); 121 List<String> fileNameList = IOUtils.listDirectory(tmpDir, new FilenameFilter() { 122 @Override 123 public boolean accept(File dir, String name) { 124 return dir.equals(tmpDir) 125 && name.startsWith(NNStorage.NameNodeFile.EDITS.getName()); 126 } 127 }); 128 129 for (String s : fileNameList) { 130 File prevFile = new File(tmpDir, s); 131 File newFile = new File(curDir, prevFile.getName()); 132 Files.createLink(newFile.toPath(), prevFile.toPath()); 133 } 134 } 135 136 /** 137 * Rename the existing current dir to previous.tmp, and create a new empty 138 * current dir. 139 */ renameCurToTmp(StorageDirectory sd)140 public static void renameCurToTmp(StorageDirectory sd) throws IOException { 141 File curDir = sd.getCurrentDir(); 142 File prevDir = sd.getPreviousDir(); 143 final File tmpDir = sd.getPreviousTmp(); 144 145 Preconditions.checkState(curDir.exists(), 146 "Current directory must exist for preupgrade."); 147 Preconditions.checkState(!prevDir.exists(), 148 "Previous directory must not exist for preupgrade."); 149 Preconditions.checkState(!tmpDir.exists(), 150 "Previous.tmp directory must not exist for preupgrade." 151 + "Consider restarting for recovery."); 152 153 // rename current to tmp 154 NNStorage.rename(curDir, tmpDir); 155 156 if (!curDir.mkdir()) { 157 throw new IOException("Cannot create directory " + curDir); 158 } 159 } 160 161 /** 162 * Perform the upgrade of the storage dir to the given storage info. The new 163 * storage info is written into the current directory, and the previous.tmp 164 * directory is renamed to previous. 165 * 166 * @param sd the storage directory to upgrade 167 * @param storage info about the new upgraded versions. 168 * @throws IOException in the event of error 169 */ doUpgrade(StorageDirectory sd, Storage storage)170 public static void doUpgrade(StorageDirectory sd, Storage storage) 171 throws IOException { 172 LOG.info("Performing upgrade of storage directory " + sd.getRoot()); 173 try { 174 // Write the version file, since saveFsImage only makes the 175 // fsimage_<txid>, and the directory is otherwise empty. 176 storage.writeProperties(sd); 177 178 File prevDir = sd.getPreviousDir(); 179 File tmpDir = sd.getPreviousTmp(); 180 Preconditions.checkState(!prevDir.exists(), 181 "previous directory must not exist for upgrade."); 182 Preconditions.checkState(tmpDir.exists(), 183 "previous.tmp directory must exist for upgrade."); 184 185 // rename tmp to previous 186 NNStorage.rename(tmpDir, prevDir); 187 } catch (IOException ioe) { 188 LOG.error("Unable to rename temp to previous for " + sd.getRoot(), ioe); 189 throw ioe; 190 } 191 } 192 193 /** 194 * Perform rollback of the storage dir to the previous state. The existing 195 * current dir is removed, and the previous dir is renamed to current. 196 * 197 * @param sd the storage directory to roll back. 198 * @throws IOException in the event of error 199 */ doRollBack(StorageDirectory sd)200 static void doRollBack(StorageDirectory sd) 201 throws IOException { 202 File prevDir = sd.getPreviousDir(); 203 if (!prevDir.exists()) { 204 return; 205 } 206 207 File tmpDir = sd.getRemovedTmp(); 208 Preconditions.checkState(!tmpDir.exists(), 209 "removed.tmp directory must not exist for rollback." 210 + "Consider restarting for recovery."); 211 // rename current to tmp 212 File curDir = sd.getCurrentDir(); 213 Preconditions.checkState(curDir.exists(), 214 "Current directory must exist for rollback."); 215 216 NNStorage.rename(curDir, tmpDir); 217 // rename previous to current 218 NNStorage.rename(prevDir, curDir); 219 220 // delete tmp dir 221 NNStorage.deleteDir(tmpDir); 222 LOG.info("Rollback of " + sd.getRoot() + " is complete."); 223 } 224 225 } 226