| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hdfs.server.namenode; |
| |
| import java.io.File; |
| import java.io.IOException; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.FileUtil; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.hdfs.HdfsConfiguration; |
| import org.apache.hadoop.hdfs.MiniDFSCluster; |
| import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption; |
| import org.apache.hadoop.hdfs.server.namenode.FSImage.CheckpointStates; |
| import org.apache.hadoop.hdfs.DFSConfigKeys; |
| |
| import junit.framework.TestCase; |
| |
| public class TestBackupNode extends TestCase { |
| public static final Log LOG = LogFactory.getLog(TestBackupNode.class); |
| |
| static final String BASE_DIR = MiniDFSCluster.getBaseDirectory(); |
| |
| protected void setUp() throws Exception { |
| super.setUp(); |
| File baseDir = new File(BASE_DIR); |
| if(baseDir.exists()) |
| if(!(FileUtil.fullyDelete(baseDir))) |
| throw new IOException("Cannot remove directory: " + baseDir); |
| File dirC = new File(getBackupNodeDir(StartupOption.CHECKPOINT, 1)); |
| dirC.mkdirs(); |
| File dirB = new File(getBackupNodeDir(StartupOption.BACKUP, 1)); |
| dirB.mkdirs(); |
| dirB = new File(getBackupNodeDir(StartupOption.BACKUP, 2)); |
| dirB.mkdirs(); |
| } |
| |
| protected void tearDown() throws Exception { |
| super.tearDown(); |
| File baseDir = new File(BASE_DIR); |
| if(!(FileUtil.fullyDelete(baseDir))) |
| throw new IOException("Cannot remove directory: " + baseDir); |
| } |
| |
| static void writeFile(FileSystem fileSys, Path name, int repl) |
| throws IOException { |
| TestCheckpoint.writeFile(fileSys, name, repl); |
| } |
| |
| |
| static void checkFile(FileSystem fileSys, Path name, int repl) |
| throws IOException { |
| TestCheckpoint.checkFile(fileSys, name, repl); |
| } |
| |
| void cleanupFile(FileSystem fileSys, Path name) |
| throws IOException { |
| TestCheckpoint.cleanupFile(fileSys, name); |
| } |
| |
| static String getBackupNodeDir(StartupOption t, int i) { |
| return BASE_DIR + "name" + t.getName() + i + "/"; |
| } |
| |
| BackupNode startBackupNode(Configuration conf, |
| StartupOption t, int i) throws IOException { |
| Configuration c = new HdfsConfiguration(conf); |
| String dirs = getBackupNodeDir(t, i); |
| c.set(DFSConfigKeys.DFS_NAMENODE_NAME_DIR_KEY, dirs); |
| c.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY, |
| "${" + DFSConfigKeys.DFS_NAMENODE_NAME_DIR_KEY + "}"); |
| return (BackupNode)NameNode.createNameNode(new String[]{t.getName()}, c); |
| } |
| |
| void waitCheckpointDone(BackupNode backup) { |
| do { |
| try { |
| LOG.info("Waiting checkpoint to complete..."); |
| Thread.sleep(1000); |
| } catch (Exception e) {} |
| } while(backup.getCheckpointState() != CheckpointStates.START); |
| } |
| |
| public void testCheckpoint() throws IOException { |
| testCheckpoint(StartupOption.CHECKPOINT); |
| testCheckpoint(StartupOption.BACKUP); |
| } |
| |
| void testCheckpoint(StartupOption op) throws IOException { |
| Path file1 = new Path("checkpoint.dat"); |
| Path file2 = new Path("checkpoint2.dat"); |
| |
| Configuration conf = new HdfsConfiguration(); |
| short replication = (short)conf.getInt(DFSConfigKeys.DFS_REPLICATION_KEY, 3); |
| conf.set(DFSConfigKeys.DFS_BLOCKREPORT_INITIAL_DELAY_KEY, "0"); |
| conf.setInt(DFSConfigKeys.DFS_DATANODE_SCAN_PERIOD_HOURS_KEY, -1); // disable block scanner |
| int numDatanodes = Math.max(3, replication); |
| MiniDFSCluster cluster = null; |
| FileSystem fileSys = null; |
| BackupNode backup = null; |
| |
| try { |
| cluster = new MiniDFSCluster.Builder(conf) |
| .numDataNodes(numDatanodes).build(); |
| fileSys = cluster.getFileSystem(); |
| // |
| // verify that 'format' really blew away all pre-existing files |
| // |
| assertTrue(!fileSys.exists(file1)); |
| assertTrue(!fileSys.exists(file2)); |
| |
| // |
| // Create file1 |
| // |
| writeFile(fileSys, file1, replication); |
| checkFile(fileSys, file1, replication); |
| |
| // |
| // Take a checkpoint |
| // |
| backup = startBackupNode(conf, op, 1); |
| waitCheckpointDone(backup); |
| } catch(IOException e) { |
| LOG.error("Error in TestBackupNode:", e); |
| assertTrue(e.getLocalizedMessage(), false); |
| } finally { |
| if(backup != null) backup.stop(); |
| if(fileSys != null) fileSys.close(); |
| if(cluster != null) cluster.shutdown(); |
| } |
| File imageFileNN = new File(BASE_DIR, "name1/current/fsimage"); |
| File imageFileBN = new File(getBackupNodeDir(op, 1), "/current/fsimage"); |
| LOG.info("NameNode fsimage length = " + imageFileNN.length()); |
| LOG.info("Backup Node fsimage length = " + imageFileBN.length()); |
| assertTrue(imageFileNN.length() == imageFileBN.length()); |
| |
| try { |
| // |
| // Restart cluster and verify that file1 still exist. |
| // |
| cluster = new MiniDFSCluster.Builder(conf).numDataNodes(numDatanodes) |
| .format(false).build(); |
| fileSys = cluster.getFileSystem(); |
| // check that file1 still exists |
| checkFile(fileSys, file1, replication); |
| cleanupFile(fileSys, file1); |
| |
| // create new file file2 |
| writeFile(fileSys, file2, replication); |
| checkFile(fileSys, file2, replication); |
| |
| // |
| // Take a checkpoint |
| // |
| backup = startBackupNode(conf, op, 1); |
| waitCheckpointDone(backup); |
| } catch(IOException e) { |
| LOG.error("Error in TestBackupNode:", e); |
| assertTrue(e.getLocalizedMessage(), false); |
| } finally { |
| if(backup != null) backup.stop(); |
| if(fileSys != null) fileSys.close(); |
| if(cluster != null) cluster.shutdown(); |
| } |
| LOG.info("NameNode fsimage length = " + imageFileNN.length()); |
| LOG.info("Backup Node fsimage length = " + imageFileBN.length()); |
| assertTrue(imageFileNN.length() == imageFileBN.length()); |
| |
| try { |
| // |
| // Restart cluster and verify that file2 exists and |
| // file1 does not exist. |
| // |
| cluster = new MiniDFSCluster.Builder(conf).numDataNodes(numDatanodes).format(false).build(); |
| fileSys = cluster.getFileSystem(); |
| |
| assertTrue(!fileSys.exists(file1)); |
| |
| // verify that file2 exists |
| checkFile(fileSys, file2, replication); |
| } catch(IOException e) { |
| LOG.error("Error in TestBackupNode:", e); |
| assertTrue(e.getLocalizedMessage(), false); |
| } finally { |
| fileSys.close(); |
| cluster.shutdown(); |
| } |
| } |
| |
| /** |
| * Test that only one backup node can register. |
| * @throws IOException |
| */ |
| public void testBackupRegistration() throws IOException { |
| Configuration conf1 = new HdfsConfiguration(); |
| Configuration conf2 = null; |
| MiniDFSCluster cluster = null; |
| BackupNode backup1 = null; |
| BackupNode backup2 = null; |
| try { |
| // start name-node and backup node 1 |
| cluster = new MiniDFSCluster.Builder(conf1).numDataNodes(0).build(); |
| conf1.set(DFSConfigKeys.DFS_NAMENODE_BACKUP_ADDRESS_KEY, "0.0.0.0:7771"); |
| conf1.set(DFSConfigKeys.DFS_NAMENODE_BACKUP_HTTP_ADDRESS_KEY, "0.0.0.0:7775"); |
| backup1 = startBackupNode(conf1, StartupOption.BACKUP, 1); |
| // try to start backup node 2 |
| conf2 = new HdfsConfiguration(conf1); |
| conf2.set(DFSConfigKeys.DFS_NAMENODE_BACKUP_ADDRESS_KEY, "0.0.0.0:7772"); |
| conf2.set(DFSConfigKeys.DFS_NAMENODE_BACKUP_HTTP_ADDRESS_KEY, "0.0.0.0:7776"); |
| try { |
| backup2 = startBackupNode(conf2, StartupOption.BACKUP, 2); |
| backup2.stop(); |
| backup2 = null; |
| assertTrue("Only one backup node should be able to start", false); |
| } catch(IOException e) { |
| assertTrue( |
| e.getLocalizedMessage().contains("Registration is not allowed")); |
| // should fail - doing good |
| } |
| // stop backup node 1; backup node 2 should be able to start |
| backup1.stop(); |
| backup1 = null; |
| try { |
| backup2 = startBackupNode(conf2, StartupOption.BACKUP, 2); |
| } catch(IOException e) { |
| assertTrue("Backup node 2 should be able to start", false); |
| } |
| } catch(IOException e) { |
| LOG.error("Error in TestBackupNode:", e); |
| assertTrue(e.getLocalizedMessage(), false); |
| } finally { |
| if(backup1 != null) backup1.stop(); |
| if(backup2 != null) backup2.stop(); |
| if(cluster != null) cluster.shutdown(); |
| } |
| } |
| } |