/**
* 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.common;
import com.google.common.base.Joiner;
import io.hops.exception.StorageException;
import io.hops.metadata.HdfsVariables;
import io.hops.metadata.common.entity.Variable;
import io.hops.transaction.handler.HDFSOperationType;
import io.hops.transaction.handler.HopsTransactionalRequestHandler;
import io.hops.transaction.lock.LockFactory;
import io.hops.transaction.lock.TransactionLockTypes;
import io.hops.transaction.lock.TransactionLocks;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.DFSUtil;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.protocol.LayoutVersion;
import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature;
import org.apache.hadoop.net.DNS;
import org.apache.hadoop.util.Time;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.UUID;
/**
* Common class for storage information.
* <p/>
* TODO namespaceID should be long and computed as hash(address + port)
*/
@InterfaceAudience.Private
public class StorageInfo {
public static final Log LOG = LogFactory.getLog(StorageInfo.class);
public static final int DEFAULT_ROW_ID = 0;
// StorageInfo is stored as one row in the database.
protected String blockpoolID = "";
// id of the block pool. moved it from NNStorage.java to here. This is where it should have been
private static StorageInfo storageInfo = null;
public int layoutVersion; // layout version of the storage data
public int namespaceID; // id of the file system
public String clusterID; // id of the cluster
public long cTime; // creation time of the file system state
public StorageInfo() {
this(0, 0, "", 0L, "");
}
public StorageInfo(int layoutV, int nsID, String cid, long cT, String bpid) {
layoutVersion = layoutV;
clusterID = cid;
namespaceID = nsID;
cTime = cT;
blockpoolID = bpid;
}
public StorageInfo(StorageInfo from) {
setStorageInfo(from);
}
/**
* Layout version of the storage data.
*/
public int getLayoutVersion() {
return layoutVersion;
}
/**
* Namespace id of the file system.<p>
* Assigned to the file system at formatting and never changes after that.
* Shared by all file system components.
*/
public int getNamespaceID() {
return namespaceID;
}
/**
* cluster id of the file system.<p>
*/
public String getClusterID() {
return clusterID;
}
/**
* Creation time of the file system state.<p>
* Modified during upgrades.
*/
public long getCTime() {
return cTime;
}
public void setStorageInfo(StorageInfo from) {
layoutVersion = from.layoutVersion;
clusterID = from.clusterID;
namespaceID = from.namespaceID;
cTime = from.cTime;
}
public boolean versionSupportsFederation() {
return LayoutVersion.supports(Feature.FEDERATION, layoutVersion);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("lv=").append(layoutVersion).append(";cid=").append(clusterID)
.append(";nsid=").append(namespaceID).append(";c=").append(cTime);
return sb.toString();
}
public String toColonSeparatedString() {
return Joiner.on(":").join(layoutVersion, namespaceID, cTime, clusterID);
}
public static StorageInfo getStorageInfoFromDB() throws IOException {
if (storageInfo == null) {
storageInfo = (StorageInfo) new HopsTransactionalRequestHandler(
HDFSOperationType.GET_STORAGE_INFO) {
@Override
public void acquireLock(TransactionLocks locks) throws IOException {
LockFactory lf = LockFactory.getInstance();
locks.add(lf.getVariableLock(Variable.Finder.StorageInfo,
TransactionLockTypes.LockType.READ));
}
@Override
public Object performTask() throws StorageException, IOException {
return HdfsVariables.getStorageInfo();
}
}.handle();
}
return storageInfo;
}
public static void storeStorageInfoToDB(final String clusterId) throws
IOException { // should only be called by the format function once during the life time of the cluster.
// Solution. call format on only one namenode or every one puts the same values.
new HopsTransactionalRequestHandler(HDFSOperationType.ADD_STORAGE_INFO) {
@Override
public void acquireLock(TransactionLocks locks) throws IOException {
LockFactory lf = LockFactory.getInstance();
locks.add(lf.getVariableLock(Variable.Finder.StorageInfo,
TransactionLockTypes.LockType.WRITE));
}
@Override
public Object performTask() throws StorageException, IOException {
Configuration conf = new Configuration();
String bpid = newBlockPoolID();
storageInfo = new StorageInfo(HdfsConstants.LAYOUT_VERSION,
conf.getInt(DFSConfigKeys.DFS_NAME_SPACE_ID_KEY,
DFSConfigKeys.DFS_NAME_SPACE_ID_DEFAULT), clusterId, 0L, bpid);
HdfsVariables.setStorageInfo(storageInfo);
LOG.info("Added new entry to storage info. nsid:" +
DFSConfigKeys.DFS_NAME_SPACE_ID_KEY + " CID:" + clusterId +
" pbid:" + bpid);
return null;
}
}.handle();
}
public String getBlockPoolId() {
return blockpoolID;
}
static String newBlockPoolID() throws UnknownHostException {
String ip = "unknownIP";
try {
ip = DNS.getDefaultIP("default");
} catch (UnknownHostException e) {
System.out.println("Could not find ip address of \"default\" inteface.");
throw e;
}
int rand = DFSUtil.getSecureRandom().nextInt(Integer.MAX_VALUE);
String bpid = "BP-" + rand + "-" + ip + "-" + Time.now();
return bpid;
}
/**
* Generate new clusterID.
* <p/>
* clusterID is a persistent attribute of the cluster.
* It is generated when the cluster is created and remains the same
* during the life cycle of the cluster. When a new name node is formated,
* if
* this is a new cluster, a new clusterID is geneated and stored. Subsequent
* name node must be given the same ClusterID during its format to be in the
* same cluster.
* When a datanode register it receive the clusterID and stick with it.
* If at any point, name node or data node tries to join another cluster, it
* will be rejected.
*
* @return new clusterID
*/
public static String newClusterID() {
return "CID-" + UUID.randomUUID().toString();
}
public int getDefaultRowId() {
return this.DEFAULT_ROW_ID;
}
}