blob: 1395a0073963945d68a991e254ddc0fee6cae9c7 [file] [log] [blame]
/**
* 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;
import static org.apache.hadoop.hdfs.server.common.HdfsConstants.NodeType.DATA_NODE;
import static org.apache.hadoop.hdfs.server.common.HdfsConstants.NodeType.NAME_NODE;
import java.io.File;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.protocol.FSConstants;
import org.apache.hadoop.hdfs.server.common.Storage;
import org.apache.hadoop.hdfs.server.common.StorageInfo;
import org.apache.hadoop.hdfs.server.common.HdfsConstants.NodeType;
import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption;
/**
* This test ensures the appropriate response (successful or failure) from
* a Datanode when the system is started with differing version combinations.
*/
public class TestDFSStartupVersions extends TestCase {
private static final Log LOG = LogFactory.getLog(
"org.apache.hadoop.hdfs.TestDFSStartupVersions");
private MiniDFSCluster cluster = null;
/**
* Writes an INFO log message containing the parameters.
*/
void log(String label, NodeType nodeType, Integer testCase, StorageInfo version) {
String testCaseLine = "";
if (testCase != null) {
testCaseLine = " testCase="+testCase;
}
LOG.info("============================================================");
LOG.info("***TEST*** " + label + ":"
+ testCaseLine
+ " nodeType="+nodeType
+ " layoutVersion="+version.getLayoutVersion()
+ " namespaceID="+version.getNamespaceID()
+ " fsscTime="+version.getCTime());
}
/**
* Initialize the versions array. This array stores all combinations
* of cross product:
* {oldLayoutVersion,currentLayoutVersion,futureLayoutVersion} X
* {currentNamespaceId,incorrectNamespaceId} X
* {pastFsscTime,currentFsscTime,futureFsscTime}
*/
private StorageInfo[] initializeVersions() throws Exception {
int layoutVersionOld = Storage.LAST_UPGRADABLE_LAYOUT_VERSION;
int layoutVersionCur = UpgradeUtilities.getCurrentLayoutVersion();
int layoutVersionNew = Integer.MIN_VALUE;
int namespaceIdCur = UpgradeUtilities.getCurrentNamespaceID(null);
int namespaceIdOld = Integer.MIN_VALUE;
long fsscTimeOld = Long.MIN_VALUE;
long fsscTimeCur = UpgradeUtilities.getCurrentFsscTime(null);
long fsscTimeNew = Long.MAX_VALUE;
return new StorageInfo[] {
new StorageInfo(layoutVersionOld, namespaceIdCur, fsscTimeOld), // 0
new StorageInfo(layoutVersionOld, namespaceIdCur, fsscTimeCur), // 1
new StorageInfo(layoutVersionOld, namespaceIdCur, fsscTimeNew), // 2
new StorageInfo(layoutVersionOld, namespaceIdOld, fsscTimeOld), // 3
new StorageInfo(layoutVersionOld, namespaceIdOld, fsscTimeCur), // 4
new StorageInfo(layoutVersionOld, namespaceIdOld, fsscTimeNew), // 5
new StorageInfo(layoutVersionCur, namespaceIdCur, fsscTimeOld), // 6
new StorageInfo(layoutVersionCur, namespaceIdCur, fsscTimeCur), // 7
new StorageInfo(layoutVersionCur, namespaceIdCur, fsscTimeNew), // 8
new StorageInfo(layoutVersionCur, namespaceIdOld, fsscTimeOld), // 9
new StorageInfo(layoutVersionCur, namespaceIdOld, fsscTimeCur), // 10
new StorageInfo(layoutVersionCur, namespaceIdOld, fsscTimeNew), // 11
new StorageInfo(layoutVersionNew, namespaceIdCur, fsscTimeOld), // 12
new StorageInfo(layoutVersionNew, namespaceIdCur, fsscTimeCur), // 13
new StorageInfo(layoutVersionNew, namespaceIdCur, fsscTimeNew), // 14
new StorageInfo(layoutVersionNew, namespaceIdOld, fsscTimeOld), // 15
new StorageInfo(layoutVersionNew, namespaceIdOld, fsscTimeCur), // 16
new StorageInfo(layoutVersionNew, namespaceIdOld, fsscTimeNew), // 17
};
}
/**
* Determines if the given Namenode version and Datanode version
* are compatible with each other. Compatibility in this case mean
* that the Namenode and Datanode will successfully start up and
* will work together. The rules for compatibility,
* taken from the DFS Upgrade Design, are as follows:
* <pre>
* 1. The data-node does regular startup (no matter which options
* it is started with) if
* softwareLV == storedLV AND
* DataNode.FSSCTime == NameNode.FSSCTime
* 2. The data-node performs an upgrade if it is started without any
* options and
* |softwareLV| > |storedLV| OR
* (softwareLV == storedLV AND
* DataNode.FSSCTime < NameNode.FSSCTime)
* 3. NOT TESTED: The data-node rolls back if it is started with
* the -rollback option and
* |softwareLV| >= |previous.storedLV| AND
* DataNode.previous.FSSCTime <= NameNode.FSSCTime
* 4. In all other cases the startup fails.
* </pre>
*/
boolean isVersionCompatible(StorageInfo namenodeVer, StorageInfo datanodeVer) {
// check #0
if (namenodeVer.getNamespaceID() != datanodeVer.getNamespaceID()) {
LOG.info("namespaceIDs are not equal: isVersionCompatible=false");
return false;
}
// check #1
int softwareLV = FSConstants.LAYOUT_VERSION; // will also be Namenode's LV
int storedLV = datanodeVer.getLayoutVersion();
if (softwareLV == storedLV &&
datanodeVer.getCTime() == namenodeVer.getCTime())
{
LOG.info("layoutVersions and cTimes are equal: isVersionCompatible=true");
return true;
}
// check #2
long absSoftwareLV = Math.abs((long)softwareLV);
long absStoredLV = Math.abs((long)storedLV);
if (absSoftwareLV > absStoredLV ||
(softwareLV == storedLV &&
datanodeVer.getCTime() < namenodeVer.getCTime()))
{
LOG.info("softwareLayoutVersion is newer OR namenode cTime is newer: isVersionCompatible=true");
return true;
}
// check #4
LOG.info("default case: isVersionCompatible=false");
return false;
}
/**
* This test ensures the appropriate response (successful or failure) from
* a Datanode when the system is started with differing version combinations.
* <pre>
* For each 3-tuple in the cross product
* ({oldLayoutVersion,currentLayoutVersion,futureLayoutVersion},
* {currentNamespaceId,incorrectNamespaceId},
* {pastFsscTime,currentFsscTime,futureFsscTime})
* 1. Startup Namenode with version file containing
* (currentLayoutVersion,currentNamespaceId,currentFsscTime)
* 2. Attempt to startup Datanode with version file containing
* this iterations version 3-tuple
* </pre>
*/
public void testVersions() throws Exception {
UpgradeUtilities.initialize();
Configuration conf = UpgradeUtilities.initializeStorageStateConf(1,
new HdfsConfiguration());
StorageInfo[] versions = initializeVersions();
UpgradeUtilities.createStorageDirs(
NAME_NODE, conf.getStrings(DFSConfigKeys.DFS_NAMENODE_NAME_DIR_KEY), "current");
cluster = new MiniDFSCluster(conf, 0, StartupOption.REGULAR);
StorageInfo nameNodeVersion = new StorageInfo(
UpgradeUtilities.getCurrentLayoutVersion(),
UpgradeUtilities.getCurrentNamespaceID(cluster),
UpgradeUtilities.getCurrentFsscTime(cluster));
log("NameNode version info", NAME_NODE, null, nameNodeVersion);
for (int i = 0; i < versions.length; i++) {
File[] storage = UpgradeUtilities.createStorageDirs(
DATA_NODE, conf.getStrings(DFSConfigKeys.DFS_DATANODE_DATA_DIR_KEY), "current");
log("DataNode version info", DATA_NODE, i, versions[i]);
UpgradeUtilities.createVersionFile(DATA_NODE, storage, versions[i]);
try {
cluster.startDataNodes(conf, 1, false, StartupOption.REGULAR, null);
} catch (Exception ignore) {
// Ignore. The asserts below will check for problems.
// ignore.printStackTrace();
}
assertTrue(cluster.getNameNode() != null);
assertEquals(isVersionCompatible(nameNodeVersion, versions[i]),
cluster.isDataNodeUp());
cluster.shutdownDataNodes();
}
}
protected void tearDown() throws Exception {
LOG.info("Shutting down MiniDFSCluster");
if (cluster != null) cluster.shutdown();
}
public static void main(String[] args) throws Exception {
new TestDFSStartupVersions().testVersions();
}
}