HADOOP-16654:Delete hadoop-ozone and hadoop-hdds subprojects from apache trunk

Signed-off-by: Dinesh Chitlangia <dineshchitlangia@gmail.com>
This commit is contained in:
Sandeep Nemuri 2019-10-24 20:13:44 +05:30 committed by Dinesh Chitlangia
parent b2cc8b6b4a
commit 9f0610fb83
2334 changed files with 0 additions and 349955 deletions

View File

@ -104,8 +104,6 @@ Maven main modules:
- hadoop-hdfs-project (Hadoop HDFS)
- hadoop-yarn-project (Hadoop YARN)
- hadoop-mapreduce-project (Hadoop MapReduce)
- hadoop-ozone (Hadoop Ozone)
- hadoop-hdds (Hadoop Distributed Data Store)
- hadoop-tools (Hadoop tools like Streaming, Distcp, etc.)
- hadoop-dist (Hadoop distribution assembler)
- hadoop-client-modules (Hadoop client modules)

View File

@ -56,8 +56,6 @@
<exclude>**/build/**</exclude>
<exclude>**/file:/**</exclude>
<exclude>**/SecurityAuth.audit*</exclude>
<exclude>hadoop-ozone/**</exclude>
<exclude>hadoop-hdds/**</exclude>
<exclude>hadoop-submarine/**</exclude>
</excludes>
</fileSet>

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed 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. See accompanying LICENSE file.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdds</artifactId>
<version>0.5.0-SNAPSHOT</version>
</parent>
<artifactId>hadoop-hdds-client</artifactId>
<version>0.5.0-SNAPSHOT</version>
<description>Apache Hadoop Distributed Data Store Client Library</description>
<name>Apache Hadoop HDDS Client</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdds-common</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,65 +0,0 @@
/*
* 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.hdds.scm;
import org.apache.ratis.thirdparty.io.grpc.CallOptions;
import org.apache.ratis.thirdparty.io.grpc.Channel;
import org.apache.ratis.thirdparty.io.grpc.ClientCall;
import org.apache.ratis.thirdparty.io.grpc.ClientInterceptor;
import org.apache.ratis.thirdparty.io.grpc.ForwardingClientCall;
import org.apache.ratis.thirdparty.io.grpc.Metadata;
import org.apache.ratis.thirdparty.io.grpc.MethodDescriptor;
import static org.apache.hadoop.ozone.OzoneConsts.OBT_METADATA_KEY;
import static org.apache.hadoop.ozone.OzoneConsts.USER_METADATA_KEY;
/**
* GRPC client interceptor for ozone block token.
*/
public class ClientCredentialInterceptor implements ClientInterceptor {
private final String user;
private final String token;
public ClientCredentialInterceptor(String user, String token) {
this.user = user;
this.token = token;
}
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions,
Channel next) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
next.newCall(method, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
if (token != null) {
headers.put(OBT_METADATA_KEY, token);
}
if (user != null) {
headers.put(USER_METADATA_KEY, user);
}
super.start(responseListener, headers);
}
};
}
}

View File

@ -1,466 +0,0 @@
/*
* 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.hdds.scm;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandRequestProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandResponseProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.XceiverClientProtocolServiceGrpc;
import org.apache.hadoop.hdds.protocol.datanode.proto.XceiverClientProtocolServiceGrpc.XceiverClientProtocolServiceStub;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.scm.storage.CheckedBiFunction;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.tracing.GrpcClientInterceptor;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.Time;
import io.opentracing.Scope;
import io.opentracing.util.GlobalTracer;
import org.apache.ratis.thirdparty.io.grpc.ManagedChannel;
import org.apache.ratis.thirdparty.io.grpc.Status;
import org.apache.ratis.thirdparty.io.grpc.netty.GrpcSslContexts;
import org.apache.ratis.thirdparty.io.grpc.netty.NettyChannelBuilder;
import org.apache.ratis.thirdparty.io.grpc.stub.StreamObserver;
import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A Client for the storageContainer protocol for read object data.
*/
public class XceiverClientGrpc extends XceiverClientSpi {
static final Logger LOG = LoggerFactory.getLogger(XceiverClientGrpc.class);
private static final String COMPONENT = "dn";
private final Pipeline pipeline;
private final Configuration config;
private Map<UUID, XceiverClientProtocolServiceStub> asyncStubs;
private XceiverClientMetrics metrics;
private Map<UUID, ManagedChannel> channels;
private final Semaphore semaphore;
private boolean closed = false;
private SecurityConfig secConfig;
private final boolean topologyAwareRead;
private X509Certificate caCert;
/**
* Constructs a client that can communicate with the Container framework on
* data nodes.
*
* @param pipeline - Pipeline that defines the machines.
* @param config -- Ozone Config
* @param caCert - SCM ca certificate.
*/
public XceiverClientGrpc(Pipeline pipeline, Configuration config,
X509Certificate caCert) {
super();
Preconditions.checkNotNull(pipeline);
Preconditions.checkNotNull(config);
this.pipeline = pipeline;
this.config = config;
this.secConfig = new SecurityConfig(config);
this.semaphore =
new Semaphore(HddsClientUtils.getMaxOutstandingRequests(config));
this.metrics = XceiverClientManager.getXceiverClientMetrics();
this.channels = new HashMap<>();
this.asyncStubs = new HashMap<>();
this.topologyAwareRead = config.getBoolean(
OzoneConfigKeys.OZONE_NETWORK_TOPOLOGY_AWARE_READ_KEY,
OzoneConfigKeys.OZONE_NETWORK_TOPOLOGY_AWARE_READ_DEFAULT);
this.caCert = caCert;
}
/**
* Constructs a client that can communicate with the Container framework on
* data nodes.
*
* @param pipeline - Pipeline that defines the machines.
* @param config -- Ozone Config
*/
public XceiverClientGrpc(Pipeline pipeline, Configuration config) {
this(pipeline, config, null);
}
/**
* To be used when grpc token is not enabled.
*/
@Override
public void connect() throws Exception {
// connect to the closest node, if closest node doesn't exist, delegate to
// first node, which is usually the leader in the pipeline.
DatanodeDetails dn = topologyAwareRead ? this.pipeline.getClosestNode() :
this.pipeline.getFirstNode();
// just make a connection to the picked datanode at the beginning
connectToDatanode(dn, null);
}
/**
* Passed encoded token to GRPC header when security is enabled.
*/
@Override
public void connect(String encodedToken) throws Exception {
// connect to the closest node, if closest node doesn't exist, delegate to
// first node, which is usually the leader in the pipeline.
DatanodeDetails dn = topologyAwareRead ? this.pipeline.getClosestNode() :
this.pipeline.getFirstNode();
// just make a connection to the picked datanode at the beginning
connectToDatanode(dn, encodedToken);
}
private void connectToDatanode(DatanodeDetails dn, String encodedToken)
throws IOException {
// read port from the data node, on failure use default configured
// port.
int port = dn.getPort(DatanodeDetails.Port.Name.STANDALONE).getValue();
if (port == 0) {
port = config.getInt(OzoneConfigKeys.DFS_CONTAINER_IPC_PORT,
OzoneConfigKeys.DFS_CONTAINER_IPC_PORT_DEFAULT);
}
// Add credential context to the client call
String userName = UserGroupInformation.getCurrentUser().getShortUserName();
if (LOG.isDebugEnabled()) {
LOG.debug("Nodes in pipeline : {}", pipeline.getNodes().toString());
LOG.debug("Connecting to server : {}", dn.getIpAddress());
}
NettyChannelBuilder channelBuilder =
NettyChannelBuilder.forAddress(dn.getIpAddress(), port).usePlaintext()
.maxInboundMessageSize(OzoneConsts.OZONE_SCM_CHUNK_MAX_SIZE)
.intercept(new ClientCredentialInterceptor(userName, encodedToken),
new GrpcClientInterceptor());
if (secConfig.isGrpcTlsEnabled()) {
SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
if (caCert != null) {
sslContextBuilder.trustManager(caCert);
}
if (secConfig.useTestCert()) {
channelBuilder.overrideAuthority("localhost");
}
channelBuilder.useTransportSecurity().
sslContext(sslContextBuilder.build());
} else {
channelBuilder.usePlaintext();
}
ManagedChannel channel = channelBuilder.build();
XceiverClientProtocolServiceStub asyncStub =
XceiverClientProtocolServiceGrpc.newStub(channel);
asyncStubs.put(dn.getUuid(), asyncStub);
channels.put(dn.getUuid(), channel);
}
/**
* Returns if the xceiver client connects to all servers in the pipeline.
*
* @return True if the connection is alive, false otherwise.
*/
@VisibleForTesting
public boolean isConnected(DatanodeDetails details) {
return isConnected(channels.get(details.getUuid()));
}
private boolean isConnected(ManagedChannel channel) {
return channel != null && !channel.isTerminated() && !channel.isShutdown();
}
@Override
public void close() {
closed = true;
for (ManagedChannel channel : channels.values()) {
channel.shutdownNow();
try {
channel.awaitTermination(60, TimeUnit.MINUTES);
} catch (Exception e) {
LOG.error("Unexpected exception while waiting for channel termination",
e);
}
}
}
@Override
public Pipeline getPipeline() {
return pipeline;
}
@Override
public ContainerCommandResponseProto sendCommand(
ContainerCommandRequestProto request) throws IOException {
try {
XceiverClientReply reply;
reply = sendCommandWithTraceIDAndRetry(request, null);
ContainerCommandResponseProto responseProto = reply.getResponse().get();
return responseProto;
} catch (ExecutionException | InterruptedException e) {
throw new IOException("Failed to execute command " + request, e);
}
}
@Override
public ContainerCommandResponseProto sendCommand(
ContainerCommandRequestProto request, List<CheckedBiFunction> validators)
throws IOException {
try {
XceiverClientReply reply;
reply = sendCommandWithTraceIDAndRetry(request, validators);
ContainerCommandResponseProto responseProto = reply.getResponse().get();
return responseProto;
} catch (ExecutionException | InterruptedException e) {
throw new IOException("Failed to execute command " + request, e);
}
}
private XceiverClientReply sendCommandWithTraceIDAndRetry(
ContainerCommandRequestProto request, List<CheckedBiFunction> validators)
throws IOException {
try (Scope scope = GlobalTracer.get()
.buildSpan("XceiverClientGrpc." + request.getCmdType().name())
.startActive(true)) {
ContainerCommandRequestProto finalPayload =
ContainerCommandRequestProto.newBuilder(request)
.setTraceID(TracingUtil.exportCurrentSpan()).build();
return sendCommandWithRetry(finalPayload, validators);
}
}
private XceiverClientReply sendCommandWithRetry(
ContainerCommandRequestProto request, List<CheckedBiFunction> validators)
throws IOException {
ContainerCommandResponseProto responseProto = null;
IOException ioException = null;
// In case of an exception or an error, we will try to read from the
// datanodes in the pipeline in a round robin fashion.
// TODO: cache the correct leader info in here, so that any subsequent calls
// should first go to leader
XceiverClientReply reply = new XceiverClientReply(null);
List<DatanodeDetails> datanodeList;
if ((request.getCmdType() == ContainerProtos.Type.ReadChunk ||
request.getCmdType() == ContainerProtos.Type.GetSmallFile) &&
topologyAwareRead) {
datanodeList = pipeline.getNodesInOrder();
} else {
datanodeList = pipeline.getNodes();
// Shuffle datanode list so that clients do not read in the same order
// every time.
Collections.shuffle(datanodeList);
}
for (DatanodeDetails dn : datanodeList) {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Executing command " + request + " on datanode " + dn);
}
// In case the command gets retried on a 2nd datanode,
// sendCommandAsyncCall will create a new channel and async stub
// in case these don't exist for the specific datanode.
reply.addDatanode(dn);
responseProto = sendCommandAsync(request, dn).getResponse().get();
if (validators != null && !validators.isEmpty()) {
for (CheckedBiFunction validator : validators) {
validator.apply(request, responseProto);
}
}
break;
} catch (ExecutionException | InterruptedException | IOException e) {
LOG.error("Failed to execute command " + request + " on datanode " + dn
.getUuidString(), e);
if (!(e instanceof IOException)) {
if (Status.fromThrowable(e.getCause()).getCode()
== Status.UNAUTHENTICATED.getCode()) {
throw new SCMSecurityException("Failed to authenticate with "
+ "GRPC XceiverServer with Ozone block token.");
}
ioException = new IOException(e);
} else {
ioException = (IOException) e;
}
responseProto = null;
}
}
if (responseProto != null) {
reply.setResponse(CompletableFuture.completedFuture(responseProto));
return reply;
} else {
Preconditions.checkNotNull(ioException);
LOG.error("Failed to execute command {} on the pipeline {}.", request,
pipeline);
throw ioException;
}
}
// TODO: for a true async API, once the waitable future while executing
// the command on one channel fails, it should be retried asynchronously
// on the future Task for all the remaining datanodes.
// Note: this Async api is not used currently used in any active I/O path.
// In case it gets used, the asynchronous retry logic needs to be plugged
// in here.
/**
* Sends a given command to server gets a waitable future back.
*
* @param request Request
* @return Response to the command
* @throws IOException
*/
@Override
public XceiverClientReply sendCommandAsync(
ContainerCommandRequestProto request)
throws IOException, ExecutionException, InterruptedException {
try (Scope scope = GlobalTracer.get()
.buildSpan("XceiverClientGrpc." + request.getCmdType().name())
.startActive(true)) {
ContainerCommandRequestProto finalPayload =
ContainerCommandRequestProto.newBuilder(request)
.setTraceID(TracingUtil.exportCurrentSpan())
.build();
XceiverClientReply asyncReply =
sendCommandAsync(finalPayload, pipeline.getFirstNode());
// TODO : for now make this API sync in nature as async requests are
// served out of order over XceiverClientGrpc. This needs to be fixed
// if this API is to be used for I/O path. Currently, this is not
// used for Read/Write Operation but for tests.
if (!HddsUtils.isReadOnly(request)) {
asyncReply.getResponse().get();
}
return asyncReply;
}
}
private XceiverClientReply sendCommandAsync(
ContainerCommandRequestProto request, DatanodeDetails dn)
throws IOException, ExecutionException, InterruptedException {
if (closed) {
throw new IOException("This channel is not connected.");
}
UUID dnId = dn.getUuid();
ManagedChannel channel = channels.get(dnId);
// If the channel doesn't exist for this specific datanode or the channel
// is closed, just reconnect
String token = request.getEncodedToken();
if (!isConnected(channel)) {
reconnect(dn, token);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Send command {} to datanode {}",
request.getCmdType().toString(), dn.getNetworkFullPath());
}
final CompletableFuture<ContainerCommandResponseProto> replyFuture =
new CompletableFuture<>();
semaphore.acquire();
long requestTime = Time.monotonicNowNanos();
metrics.incrPendingContainerOpsMetrics(request.getCmdType());
// create a new grpc stream for each non-async call.
// TODO: for async calls, we should reuse StreamObserver resources.
final StreamObserver<ContainerCommandRequestProto> requestObserver =
asyncStubs.get(dnId)
.send(new StreamObserver<ContainerCommandResponseProto>() {
@Override
public void onNext(ContainerCommandResponseProto value) {
replyFuture.complete(value);
metrics.decrPendingContainerOpsMetrics(request.getCmdType());
metrics.addContainerOpsLatency(request.getCmdType(),
Time.monotonicNowNanos() - requestTime);
semaphore.release();
}
@Override
public void onError(Throwable t) {
replyFuture.completeExceptionally(t);
metrics.decrPendingContainerOpsMetrics(request.getCmdType());
metrics.addContainerOpsLatency(request.getCmdType(),
Time.monotonicNowNanos() - requestTime);
semaphore.release();
}
@Override
public void onCompleted() {
if (!replyFuture.isDone()) {
replyFuture.completeExceptionally(new IOException(
"Stream completed but no reply for request " + request));
}
}
});
requestObserver.onNext(request);
requestObserver.onCompleted();
return new XceiverClientReply(replyFuture);
}
private void reconnect(DatanodeDetails dn, String encodedToken)
throws IOException {
ManagedChannel channel;
try {
connectToDatanode(dn, encodedToken);
channel = channels.get(dn.getUuid());
} catch (Exception e) {
LOG.error("Error while connecting: ", e);
throw new IOException(e);
}
if (channel == null || !isConnected(channel)) {
throw new IOException("This channel is not connected.");
}
}
@Override
public XceiverClientReply watchForCommit(long index, long timeout)
throws InterruptedException, ExecutionException, TimeoutException,
IOException {
// there is no notion of watch for commit index in standalone pipeline
return null;
};
public long getReplicatedMinCommitIndex() {
return 0;
}
/**
* Returns pipeline Type.
*
* @return - Stand Alone as the type.
*/
@Override
public HddsProtos.ReplicationType getPipelineType() {
return HddsProtos.ReplicationType.STAND_ALONE;
}
}

View File

@ -1,390 +0,0 @@
/*
* 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.hdds.scm;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.conf.Config;
import org.apache.hadoop.hdds.conf.ConfigGroup;
import org.apache.hadoop.hdds.conf.ConfigType;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.OzoneSecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.hadoop.hdds.conf.ConfigTag.OZONE;
import static org.apache.hadoop.hdds.conf.ConfigTag.PERFORMANCE;
/**
* XceiverClientManager is responsible for the lifecycle of XceiverClient
* instances. Callers use this class to acquire an XceiverClient instance
* connected to the desired container pipeline. When done, the caller also uses
* this class to release the previously acquired XceiverClient instance.
*
*
* This class caches connection to container for reuse purpose, such that
* accessing same container frequently will be through the same connection
* without reestablishing connection. But the connection will be closed if
* not being used for a period of time.
*/
public class XceiverClientManager implements Closeable {
private static final Logger LOG =
LoggerFactory.getLogger(XceiverClientManager.class);
//TODO : change this to SCM configuration class
private final Configuration conf;
private final Cache<String, XceiverClientSpi> clientCache;
private final boolean useRatis;
private X509Certificate caCert;
private static XceiverClientMetrics metrics;
private boolean isSecurityEnabled;
private final boolean topologyAwareRead;
/**
* Creates a new XceiverClientManager for non secured ozone cluster.
* For security enabled ozone cluster, client should use the other constructor
* with a valid ca certificate in pem string format.
*
* @param conf configuration
*/
public XceiverClientManager(Configuration conf) throws IOException {
this(conf, OzoneConfiguration.of(conf).getObject(ScmClientConfig.class),
null);
}
public XceiverClientManager(Configuration conf, ScmClientConfig clientConf,
String caCertPem) throws IOException {
Preconditions.checkNotNull(clientConf);
Preconditions.checkNotNull(conf);
long staleThresholdMs = clientConf.getStaleThreshold(MILLISECONDS);
this.useRatis = conf.getBoolean(
ScmConfigKeys.DFS_CONTAINER_RATIS_ENABLED_KEY,
ScmConfigKeys.DFS_CONTAINER_RATIS_ENABLED_DEFAULT);
this.conf = conf;
this.isSecurityEnabled = OzoneSecurityUtil.isSecurityEnabled(conf);
if (isSecurityEnabled) {
Preconditions.checkNotNull(caCertPem);
try {
this.caCert = CertificateCodec.getX509Cert(caCertPem);
} catch (CertificateException ex) {
throw new SCMSecurityException("Error: Fail to get SCM CA certificate",
ex);
}
}
this.clientCache = CacheBuilder.newBuilder()
.expireAfterAccess(staleThresholdMs, MILLISECONDS)
.maximumSize(clientConf.getMaxSize())
.removalListener(
new RemovalListener<String, XceiverClientSpi>() {
@Override
public void onRemoval(
RemovalNotification<String, XceiverClientSpi>
removalNotification) {
synchronized (clientCache) {
// Mark the entry as evicted
XceiverClientSpi info = removalNotification.getValue();
info.setEvicted();
}
}
}).build();
topologyAwareRead = conf.getBoolean(
OzoneConfigKeys.OZONE_NETWORK_TOPOLOGY_AWARE_READ_KEY,
OzoneConfigKeys.OZONE_NETWORK_TOPOLOGY_AWARE_READ_DEFAULT);
}
@VisibleForTesting
public Cache<String, XceiverClientSpi> getClientCache() {
return clientCache;
}
/**
* Acquires a XceiverClientSpi connected to a container capable of
* storing the specified key.
*
* If there is already a cached XceiverClientSpi, simply return
* the cached otherwise create a new one.
*
* @param pipeline the container pipeline for the client connection
* @return XceiverClientSpi connected to a container
* @throws IOException if a XceiverClientSpi cannot be acquired
*/
public XceiverClientSpi acquireClient(Pipeline pipeline)
throws IOException {
return acquireClient(pipeline, false);
}
/**
* Acquires a XceiverClientSpi connected to a container for read.
*
* If there is already a cached XceiverClientSpi, simply return
* the cached otherwise create a new one.
*
* @param pipeline the container pipeline for the client connection
* @return XceiverClientSpi connected to a container
* @throws IOException if a XceiverClientSpi cannot be acquired
*/
public XceiverClientSpi acquireClientForReadData(Pipeline pipeline)
throws IOException {
return acquireClient(pipeline, true);
}
private XceiverClientSpi acquireClient(Pipeline pipeline, boolean read)
throws IOException {
Preconditions.checkNotNull(pipeline);
Preconditions.checkArgument(pipeline.getNodes() != null);
Preconditions.checkArgument(!pipeline.getNodes().isEmpty());
synchronized (clientCache) {
XceiverClientSpi info = getClient(pipeline, read);
info.incrementReference();
return info;
}
}
/**
* Releases a XceiverClientSpi after use.
*
* @param client client to release
* @param invalidateClient if true, invalidates the client in cache
*/
public void releaseClient(XceiverClientSpi client, boolean invalidateClient) {
releaseClient(client, invalidateClient, false);
}
/**
* Releases a read XceiverClientSpi after use.
*
* @param client client to release
* @param invalidateClient if true, invalidates the client in cache
*/
public void releaseClientForReadData(XceiverClientSpi client,
boolean invalidateClient) {
releaseClient(client, invalidateClient, true);
}
private void releaseClient(XceiverClientSpi client, boolean invalidateClient,
boolean read) {
Preconditions.checkNotNull(client);
synchronized (clientCache) {
client.decrementReference();
if (invalidateClient) {
Pipeline pipeline = client.getPipeline();
String key = getPipelineCacheKey(pipeline, read);
XceiverClientSpi cachedClient = clientCache.getIfPresent(key);
if (cachedClient == client) {
clientCache.invalidate(key);
}
}
}
}
private XceiverClientSpi getClient(Pipeline pipeline, boolean forRead)
throws IOException {
HddsProtos.ReplicationType type = pipeline.getType();
try {
// create different client for read different pipeline node based on
// network topology
String key = getPipelineCacheKey(pipeline, forRead);
// Append user short name to key to prevent a different user
// from using same instance of xceiverClient.
key = isSecurityEnabled ?
key + UserGroupInformation.getCurrentUser().getShortUserName() : key;
return clientCache.get(key, new Callable<XceiverClientSpi>() {
@Override
public XceiverClientSpi call() throws Exception {
XceiverClientSpi client = null;
switch (type) {
case RATIS:
client = XceiverClientRatis.newXceiverClientRatis(pipeline, conf,
caCert);
client.connect();
break;
case STAND_ALONE:
client = new XceiverClientGrpc(pipeline, conf, caCert);
break;
case CHAINED:
default:
throw new IOException("not implemented" + pipeline.getType());
}
return client;
}
});
} catch (Exception e) {
throw new IOException(
"Exception getting XceiverClient: " + e.toString(), e);
}
}
private String getPipelineCacheKey(Pipeline pipeline, boolean forRead) {
String key = pipeline.getId().getId().toString() + pipeline.getType();
if (topologyAwareRead && forRead) {
try {
key += pipeline.getClosestNode().getHostName();
} catch (IOException e) {
LOG.error("Failed to get closest node to create pipeline cache key:" +
e.getMessage());
}
}
return key;
}
/**
* Close and remove all the cached clients.
*/
@Override
public void close() {
//closing is done through RemovalListener
clientCache.invalidateAll();
clientCache.cleanUp();
if (metrics != null) {
metrics.unRegister();
}
}
/**
* Tells us if Ratis is enabled for this cluster.
* @return True if Ratis is enabled.
*/
public boolean isUseRatis() {
return useRatis;
}
/**
* Returns hard coded 3 as replication factor.
* @return 3
*/
public HddsProtos.ReplicationFactor getFactor() {
if(isUseRatis()) {
return HddsProtos.ReplicationFactor.THREE;
}
return HddsProtos.ReplicationFactor.ONE;
}
/**
* Returns the default replication type.
* @return Ratis or Standalone
*/
public HddsProtos.ReplicationType getType() {
// TODO : Fix me and make Ratis default before release.
// TODO: Remove this as replication factor and type are pipeline properties
if(isUseRatis()) {
return HddsProtos.ReplicationType.RATIS;
}
return HddsProtos.ReplicationType.STAND_ALONE;
}
public Function<ByteBuffer, ByteString> byteBufferToByteStringConversion(){
return ByteStringConversion.createByteBufferConversion(conf);
}
/**
* Get xceiver client metric.
*/
public synchronized static XceiverClientMetrics getXceiverClientMetrics() {
if (metrics == null) {
metrics = XceiverClientMetrics.create();
}
return metrics;
}
/**
* Configuration for HDDS client.
*/
@ConfigGroup(prefix = "scm.container.client")
public static class ScmClientConfig {
private int maxSize;
private long staleThreshold;
private int maxOutstandingRequests;
public long getStaleThreshold(TimeUnit unit) {
return unit.convert(staleThreshold, MILLISECONDS);
}
@Config(key = "idle.threshold",
type = ConfigType.TIME, timeUnit = MILLISECONDS,
defaultValue = "10s",
tags = { OZONE, PERFORMANCE },
description =
"In the standalone pipelines, the SCM clients use netty to "
+ " communicate with the container. It also uses connection pooling"
+ " to reduce client side overheads. This allows a connection to"
+ " stay idle for a while before the connection is closed."
)
public void setStaleThreshold(long staleThreshold) {
this.staleThreshold = staleThreshold;
}
public int getMaxSize() {
return maxSize;
}
@Config(key = "max.size",
defaultValue = "256",
tags = { OZONE, PERFORMANCE },
description =
"Controls the maximum number of connections that are cached via"
+ " client connection pooling. If the number of connections"
+ " exceed this count, then the oldest idle connection is evicted."
)
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
public int getMaxOutstandingRequests() {
return maxOutstandingRequests;
}
@Config(key = "max.outstanding.requests",
defaultValue = "100",
tags = { OZONE, PERFORMANCE },
description =
"Controls the maximum number of outstanding async requests that can"
+ " be handled by the Standalone as well as Ratis client."
)
public void setMaxOutstandingRequests(int maxOutstandingRequests) {
this.maxOutstandingRequests = maxOutstandingRequests;
}
}
}

View File

@ -1,113 +0,0 @@
/**
* 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.hdds.scm;
import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.metrics2.MetricsSystem;
import org.apache.hadoop.metrics2.annotation.Metric;
import org.apache.hadoop.metrics2.annotation.Metrics;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.lib.MetricsRegistry;
import org.apache.hadoop.metrics2.lib.MutableCounterLong;
import org.apache.hadoop.metrics2.lib.MutableRate;
/**
* The client metrics for the Storage Container protocol.
*/
@InterfaceAudience.Private
@Metrics(about = "Storage Container Client Metrics", context = "dfs")
public class XceiverClientMetrics {
public static final String SOURCE_NAME = XceiverClientMetrics.class
.getSimpleName();
private @Metric MutableCounterLong pendingOps;
private @Metric MutableCounterLong totalOps;
private MutableCounterLong[] pendingOpsArray;
private MutableCounterLong[] opsArray;
private MutableRate[] containerOpsLatency;
private MetricsRegistry registry;
public XceiverClientMetrics() {
int numEnumEntries = ContainerProtos.Type.values().length;
this.registry = new MetricsRegistry(SOURCE_NAME);
this.pendingOpsArray = new MutableCounterLong[numEnumEntries];
this.opsArray = new MutableCounterLong[numEnumEntries];
this.containerOpsLatency = new MutableRate[numEnumEntries];
for (int i = 0; i < numEnumEntries; i++) {
pendingOpsArray[i] = registry.newCounter(
"numPending" + ContainerProtos.Type.forNumber(i + 1),
"number of pending" + ContainerProtos.Type.forNumber(i + 1) + " ops",
(long) 0);
opsArray[i] = registry
.newCounter("opCount" + ContainerProtos.Type.forNumber(i + 1),
"number of" + ContainerProtos.Type.forNumber(i + 1) + " ops",
(long) 0);
containerOpsLatency[i] = registry.newRate(
ContainerProtos.Type.forNumber(i + 1) + "Latency",
"latency of " + ContainerProtos.Type.forNumber(i + 1)
+ " ops");
}
}
public static XceiverClientMetrics create() {
DefaultMetricsSystem.initialize(SOURCE_NAME);
MetricsSystem ms = DefaultMetricsSystem.instance();
return ms.register(SOURCE_NAME, "Storage Container Client Metrics",
new XceiverClientMetrics());
}
public void incrPendingContainerOpsMetrics(ContainerProtos.Type type) {
pendingOps.incr();
totalOps.incr();
opsArray[type.ordinal()].incr();
pendingOpsArray[type.ordinal()].incr();
}
public void decrPendingContainerOpsMetrics(ContainerProtos.Type type) {
pendingOps.incr(-1);
pendingOpsArray[type.ordinal()].incr(-1);
}
public void addContainerOpsLatency(ContainerProtos.Type type,
long latencyNanos) {
containerOpsLatency[type.ordinal()].add(latencyNanos);
}
public long getContainerOpsMetrics(ContainerProtos.Type type) {
return pendingOpsArray[type.ordinal()].value();
}
@VisibleForTesting
public long getTotalOpCount() {
return totalOps.value();
}
@VisibleForTesting
public long getContainerOpCountMetrics(ContainerProtos.Type type) {
return opsArray[type.ordinal()].value();
}
public void unRegister() {
MetricsSystem ms = DefaultMetricsSystem.instance();
ms.unregisterSource(SOURCE_NAME);
}
}

View File

@ -1,367 +0,0 @@
/*
* 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.hdds.scm;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.OptionalLong;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandRequestProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandResponseProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.ratis.ContainerCommandRequestMessage;
import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.hadoop.util.Time;
import org.apache.hadoop.hdds.ratis.RatisHelper;
import org.apache.ratis.client.RaftClient;
import org.apache.ratis.grpc.GrpcTlsConfig;
import org.apache.ratis.proto.RaftProtos;
import org.apache.ratis.protocol.GroupMismatchException;
import org.apache.ratis.protocol.RaftClientReply;
import org.apache.ratis.protocol.RaftException;
import org.apache.ratis.retry.RetryPolicy;
import org.apache.ratis.rpc.RpcType;
import org.apache.ratis.rpc.SupportedRpcType;
import org.apache.ratis.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
import org.apache.ratis.util.TimeDuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import io.opentracing.Scope;
import io.opentracing.util.GlobalTracer;
/**
* An abstract implementation of {@link XceiverClientSpi} using Ratis.
* The underlying RPC mechanism can be chosen via the constructor.
*/
public final class XceiverClientRatis extends XceiverClientSpi {
public static final Logger LOG =
LoggerFactory.getLogger(XceiverClientRatis.class);
public static XceiverClientRatis newXceiverClientRatis(
org.apache.hadoop.hdds.scm.pipeline.Pipeline pipeline,
Configuration ozoneConf) {
return newXceiverClientRatis(pipeline, ozoneConf, null);
}
public static XceiverClientRatis newXceiverClientRatis(
org.apache.hadoop.hdds.scm.pipeline.Pipeline pipeline,
Configuration ozoneConf, X509Certificate caCert) {
final String rpcType = ozoneConf
.get(ScmConfigKeys.DFS_CONTAINER_RATIS_RPC_TYPE_KEY,
ScmConfigKeys.DFS_CONTAINER_RATIS_RPC_TYPE_DEFAULT);
final TimeDuration clientRequestTimeout =
RatisHelper.getClientRequestTimeout(ozoneConf);
final int maxOutstandingRequests =
HddsClientUtils.getMaxOutstandingRequests(ozoneConf);
final RetryPolicy retryPolicy = RatisHelper.createRetryPolicy(ozoneConf);
final GrpcTlsConfig tlsConfig = RatisHelper.createTlsClientConfig(new
SecurityConfig(ozoneConf), caCert);
return new XceiverClientRatis(pipeline,
SupportedRpcType.valueOfIgnoreCase(rpcType), maxOutstandingRequests,
retryPolicy, tlsConfig, clientRequestTimeout);
}
private final Pipeline pipeline;
private final RpcType rpcType;
private final AtomicReference<RaftClient> client = new AtomicReference<>();
private final int maxOutstandingRequests;
private final RetryPolicy retryPolicy;
private final GrpcTlsConfig tlsConfig;
private final TimeDuration clientRequestTimeout;
// Map to track commit index at every server
private final ConcurrentHashMap<UUID, Long> commitInfoMap;
private XceiverClientMetrics metrics;
/**
* Constructs a client.
*/
private XceiverClientRatis(Pipeline pipeline, RpcType rpcType,
int maxOutStandingChunks, RetryPolicy retryPolicy,
GrpcTlsConfig tlsConfig, TimeDuration timeout) {
super();
this.pipeline = pipeline;
this.rpcType = rpcType;
this.maxOutstandingRequests = maxOutStandingChunks;
this.retryPolicy = retryPolicy;
commitInfoMap = new ConcurrentHashMap<>();
this.tlsConfig = tlsConfig;
this.clientRequestTimeout = timeout;
metrics = XceiverClientManager.getXceiverClientMetrics();
}
private void updateCommitInfosMap(
Collection<RaftProtos.CommitInfoProto> commitInfoProtos) {
// if the commitInfo map is empty, just update the commit indexes for each
// of the servers
if (commitInfoMap.isEmpty()) {
commitInfoProtos.forEach(proto -> commitInfoMap
.put(RatisHelper.toDatanodeId(proto.getServer()),
proto.getCommitIndex()));
// In case the commit is happening 2 way, just update the commitIndex
// for the servers which have been successfully updating the commit
// indexes. This is important because getReplicatedMinCommitIndex()
// should always return the min commit index out of the nodes which have
// been replicating data successfully.
} else {
commitInfoProtos.forEach(proto -> commitInfoMap
.computeIfPresent(RatisHelper.toDatanodeId(proto.getServer()),
(address, index) -> {
index = proto.getCommitIndex();
return index;
}));
}
}
/**
* Returns Ratis as pipeline Type.
*
* @return - Ratis
*/
@Override
public HddsProtos.ReplicationType getPipelineType() {
return HddsProtos.ReplicationType.RATIS;
}
@Override
public Pipeline getPipeline() {
return pipeline;
}
@Override
public void connect() throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Connecting to pipeline:{} datanode:{}", getPipeline().getId(),
RatisHelper.toRaftPeerId(pipeline.getFirstNode()));
}
// TODO : XceiverClient ratis should pass the config value of
// maxOutstandingRequests so as to set the upper bound on max no of async
// requests to be handled by raft client
if (!client.compareAndSet(null,
RatisHelper.newRaftClient(rpcType, getPipeline(), retryPolicy,
maxOutstandingRequests, tlsConfig, clientRequestTimeout))) {
throw new IllegalStateException("Client is already connected.");
}
}
@Override
public void connect(String encodedToken) throws Exception {
throw new UnsupportedOperationException("Block tokens are not " +
"implemented for Ratis clients.");
}
@Override
public void close() {
final RaftClient c = client.getAndSet(null);
if (c != null) {
closeRaftClient(c);
}
}
private void closeRaftClient(RaftClient raftClient) {
try {
raftClient.close();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private RaftClient getClient() {
return Objects.requireNonNull(client.get(), "client is null");
}
@VisibleForTesting
public ConcurrentHashMap<UUID, Long> getCommitInfoMap() {
return commitInfoMap;
}
private CompletableFuture<RaftClientReply> sendRequestAsync(
ContainerCommandRequestProto request) {
try (Scope scope = GlobalTracer.get()
.buildSpan("XceiverClientRatis." + request.getCmdType().name())
.startActive(true)) {
final ContainerCommandRequestMessage message
= ContainerCommandRequestMessage.toMessage(
request, TracingUtil.exportCurrentSpan());
if (HddsUtils.isReadOnly(request)) {
if (LOG.isDebugEnabled()) {
LOG.debug("sendCommandAsync ReadOnly {}", message);
}
return getClient().sendReadOnlyAsync(message);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("sendCommandAsync {}", message);
}
return getClient().sendAsync(message);
}
}
}
// gets the minimum log index replicated to all servers
@Override
public long getReplicatedMinCommitIndex() {
OptionalLong minIndex =
commitInfoMap.values().parallelStream().mapToLong(v -> v).min();
return minIndex.isPresent() ? minIndex.getAsLong() : 0;
}
private void addDatanodetoReply(UUID address, XceiverClientReply reply) {
DatanodeDetails.Builder builder = DatanodeDetails.newBuilder();
builder.setUuid(address.toString());
reply.addDatanode(builder.build());
}
@Override
public XceiverClientReply watchForCommit(long index, long timeout)
throws InterruptedException, ExecutionException, TimeoutException,
IOException {
long commitIndex = getReplicatedMinCommitIndex();
XceiverClientReply clientReply = new XceiverClientReply(null);
if (commitIndex >= index) {
// return the min commit index till which the log has been replicated to
// all servers
clientReply.setLogIndex(commitIndex);
return clientReply;
}
if (LOG.isDebugEnabled()) {
LOG.debug("commit index : {} watch timeout : {}", index, timeout);
}
RaftClientReply reply;
try {
CompletableFuture<RaftClientReply> replyFuture = getClient()
.sendWatchAsync(index, RaftProtos.ReplicationLevel.ALL_COMMITTED);
replyFuture.get(timeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Throwable t = HddsClientUtils.checkForException(e);
LOG.warn("3 way commit failed on pipeline {}", pipeline, e);
if (t instanceof GroupMismatchException) {
throw e;
}
reply = getClient()
.sendWatchAsync(index, RaftProtos.ReplicationLevel.MAJORITY_COMMITTED)
.get(timeout, TimeUnit.MILLISECONDS);
List<RaftProtos.CommitInfoProto> commitInfoProtoList =
reply.getCommitInfos().stream()
.filter(i -> i.getCommitIndex() < index)
.collect(Collectors.toList());
commitInfoProtoList.parallelStream().forEach(proto -> {
UUID address = RatisHelper.toDatanodeId(proto.getServer());
addDatanodetoReply(address, clientReply);
// since 3 way commit has failed, the updated map from now on will
// only store entries for those datanodes which have had successful
// replication.
commitInfoMap.remove(address);
LOG.info(
"Could not commit index {} on pipeline {} to all the nodes. " +
"Server {} has failed. Committed by majority.",
index, pipeline, address);
});
}
clientReply.setLogIndex(index);
return clientReply;
}
/**
* Sends a given command to server gets a waitable future back.
*
* @param request Request
* @return Response to the command
*/
@Override
public XceiverClientReply sendCommandAsync(
ContainerCommandRequestProto request) {
XceiverClientReply asyncReply = new XceiverClientReply(null);
long requestTime = Time.monotonicNowNanos();
CompletableFuture<RaftClientReply> raftClientReply =
sendRequestAsync(request);
metrics.incrPendingContainerOpsMetrics(request.getCmdType());
CompletableFuture<ContainerCommandResponseProto> containerCommandResponse =
raftClientReply.whenComplete((reply, e) -> {
if (LOG.isDebugEnabled()) {
LOG.debug("received reply {} for request: cmdType={} containerID={}"
+ " pipelineID={} traceID={} exception: {}", reply,
request.getCmdType(), request.getContainerID(),
request.getPipelineID(), request.getTraceID(), e);
}
metrics.decrPendingContainerOpsMetrics(request.getCmdType());
metrics.addContainerOpsLatency(request.getCmdType(),
Time.monotonicNowNanos() - requestTime);
}).thenApply(reply -> {
try {
if (!reply.isSuccess()) {
// in case of raft retry failure, the raft client is
// not able to connect to the leader hence the pipeline
// can not be used but this instance of RaftClient will close
// and refreshed again. In case the client cannot connect to
// leader, getClient call will fail.
// No need to set the failed Server ID here. Ozone client
// will directly exclude this pipeline in next allocate block
// to SCM as in this case, it is the raft client which is not
// able to connect to leader in the pipeline, though the
// pipeline can still be functional.
RaftException exception = reply.getException();
Preconditions.checkNotNull(exception, "Raft reply failure but " +
"no exception propagated.");
throw new CompletionException(exception);
}
ContainerCommandResponseProto response =
ContainerCommandResponseProto
.parseFrom(reply.getMessage().getContent());
UUID serverId = RatisHelper.toDatanodeId(reply.getReplierId());
if (response.getResult() == ContainerProtos.Result.SUCCESS) {
updateCommitInfosMap(reply.getCommitInfos());
}
asyncReply.setLogIndex(reply.getLogIndex());
addDatanodetoReply(serverId, asyncReply);
return response;
} catch (InvalidProtocolBufferException e) {
throw new CompletionException(e);
}
});
asyncReply.setResponse(containerCommandResponse);
return asyncReply;
}
}

View File

@ -1,495 +0,0 @@
/*
* 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.hdds.scm.client;
import com.google.common.base.Preconditions;
import org.apache.hadoop.hdds.scm.XceiverClientManager;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.hdds.scm.container.common.helpers.ContainerWithPipeline;
import org.apache.hadoop.hdds.scm.container.ContainerInfo;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol;
import org.apache.hadoop.hdds.scm.storage.ContainerProtocolCalls;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos
.ContainerDataProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos
.ReadContainerResponseProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.protocol.proto
.StorageContainerLocationProtocolProtos.ObjectStageChangeRequestProto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
/**
* This class provides the client-facing APIs of container operations.
*/
public class ContainerOperationClient implements ScmClient {
private static final Logger LOG =
LoggerFactory.getLogger(ContainerOperationClient.class);
private static long containerSizeB = -1;
private final StorageContainerLocationProtocol
storageContainerLocationClient;
private final XceiverClientManager xceiverClientManager;
public ContainerOperationClient(
StorageContainerLocationProtocol
storageContainerLocationClient,
XceiverClientManager xceiverClientManager) {
this.storageContainerLocationClient = storageContainerLocationClient;
this.xceiverClientManager = xceiverClientManager;
}
/**
* Return the capacity of containers. The current assumption is that all
* containers have the same capacity. Therefore one static is sufficient for
* any container.
* @return The capacity of one container in number of bytes.
*/
public static long getContainerSizeB() {
return containerSizeB;
}
/**
* Set the capacity of container. Should be exactly once on system start.
* @param size Capacity of one container in number of bytes.
*/
public static void setContainerSizeB(long size) {
containerSizeB = size;
}
@Override
public ContainerWithPipeline createContainer(String owner)
throws IOException {
XceiverClientSpi client = null;
try {
ContainerWithPipeline containerWithPipeline =
storageContainerLocationClient.allocateContainer(
xceiverClientManager.getType(),
xceiverClientManager.getFactor(), owner);
Pipeline pipeline = containerWithPipeline.getPipeline();
client = xceiverClientManager.acquireClient(pipeline);
Preconditions.checkState(pipeline.isOpen(), String
.format("Unexpected state=%s for pipeline=%s, expected state=%s",
pipeline.getPipelineState(), pipeline.getId(),
Pipeline.PipelineState.OPEN));
createContainer(client,
containerWithPipeline.getContainerInfo().getContainerID());
return containerWithPipeline;
} finally {
if (client != null) {
xceiverClientManager.releaseClient(client, false);
}
}
}
/**
* Create a container over pipeline specified by the SCM.
*
* @param client - Client to communicate with Datanodes.
* @param containerId - Container ID.
* @throws IOException
*/
public void createContainer(XceiverClientSpi client,
long containerId) throws IOException {
ContainerProtocolCalls.createContainer(client, containerId, null);
// Let us log this info after we let SCM know that we have completed the
// creation state.
if (LOG.isDebugEnabled()) {
LOG.debug("Created container " + containerId
+ " machines:" + client.getPipeline().getNodes());
}
}
/**
* Creates a pipeline over the machines choosen by the SCM.
*
* @param client - Client
* @param pipeline - pipeline to be createdon Datanodes.
* @throws IOException
*/
private void createPipeline(XceiverClientSpi client, Pipeline pipeline)
throws IOException {
Preconditions.checkNotNull(pipeline.getId(), "Pipeline " +
"name cannot be null when client create flag is set.");
// Pipeline creation is a three step process.
//
// 1. Notify SCM that this client is doing a create pipeline on
// datanodes.
//
// 2. Talk to Datanodes to create the pipeline.
//
// 3. update SCM that pipeline creation was successful.
// TODO: this has not been fully implemented on server side
// SCMClientProtocolServer#notifyObjectStageChange
// TODO: when implement the pipeline state machine, change
// the pipeline name (string) to pipeline id (long)
//storageContainerLocationClient.notifyObjectStageChange(
// ObjectStageChangeRequestProto.Type.pipeline,
// pipeline.getPipelineName(),
// ObjectStageChangeRequestProto.Op.create,
// ObjectStageChangeRequestProto.Stage.begin);
// client.createPipeline();
// TODO: Use PipelineManager to createPipeline
//storageContainerLocationClient.notifyObjectStageChange(
// ObjectStageChangeRequestProto.Type.pipeline,
// pipeline.getPipelineName(),
// ObjectStageChangeRequestProto.Op.create,
// ObjectStageChangeRequestProto.Stage.complete);
// TODO : Should we change the state on the client side ??
// That makes sense, but it is not needed for the client to work.
if (LOG.isDebugEnabled()) {
LOG.debug("Pipeline creation successful. Pipeline: {}",
pipeline.toString());
}
}
@Override
public ContainerWithPipeline createContainer(HddsProtos.ReplicationType type,
HddsProtos.ReplicationFactor factor, String owner) throws IOException {
XceiverClientSpi client = null;
try {
// allocate container on SCM.
ContainerWithPipeline containerWithPipeline =
storageContainerLocationClient.allocateContainer(type, factor,
owner);
Pipeline pipeline = containerWithPipeline.getPipeline();
client = xceiverClientManager.acquireClient(pipeline);
// connect to pipeline leader and allocate container on leader datanode.
client = xceiverClientManager.acquireClient(pipeline);
createContainer(client,
containerWithPipeline.getContainerInfo().getContainerID());
return containerWithPipeline;
} finally {
if (client != null) {
xceiverClientManager.releaseClient(client, false);
}
}
}
/**
* Returns a set of Nodes that meet a query criteria.
*
* @param nodeStatuses - Criteria that we want the node to have.
* @param queryScope - Query scope - Cluster or pool.
* @param poolName - if it is pool, a pool name is required.
* @return A set of nodes that meet the requested criteria.
* @throws IOException
*/
@Override
public List<HddsProtos.Node> queryNode(HddsProtos.NodeState
nodeStatuses, HddsProtos.QueryScope queryScope, String poolName)
throws IOException {
return storageContainerLocationClient.queryNode(nodeStatuses, queryScope,
poolName);
}
/**
* Creates a specified replication pipeline.
*/
@Override
public Pipeline createReplicationPipeline(HddsProtos.ReplicationType type,
HddsProtos.ReplicationFactor factor, HddsProtos.NodePool nodePool)
throws IOException {
return storageContainerLocationClient.createReplicationPipeline(type,
factor, nodePool);
}
@Override
public List<Pipeline> listPipelines() throws IOException {
return storageContainerLocationClient.listPipelines();
}
@Override
public void activatePipeline(HddsProtos.PipelineID pipelineID)
throws IOException {
storageContainerLocationClient.activatePipeline(pipelineID);
}
@Override
public void deactivatePipeline(HddsProtos.PipelineID pipelineID)
throws IOException {
storageContainerLocationClient.deactivatePipeline(pipelineID);
}
@Override
public void closePipeline(HddsProtos.PipelineID pipelineID)
throws IOException {
storageContainerLocationClient.closePipeline(pipelineID);
}
@Override
public void close() {
try {
xceiverClientManager.close();
} catch (Exception ex) {
LOG.error("Can't close " + this.getClass().getSimpleName(), ex);
}
}
/**
* Deletes an existing container.
*
* @param containerId - ID of the container.
* @param pipeline - Pipeline that represents the container.
* @param force - true to forcibly delete the container.
* @throws IOException
*/
@Override
public void deleteContainer(long containerId, Pipeline pipeline,
boolean force) throws IOException {
XceiverClientSpi client = null;
try {
client = xceiverClientManager.acquireClient(pipeline);
ContainerProtocolCalls
.deleteContainer(client, containerId, force, null);
storageContainerLocationClient
.deleteContainer(containerId);
if (LOG.isDebugEnabled()) {
LOG.debug("Deleted container {}, machines: {} ", containerId,
pipeline.getNodes());
}
} finally {
if (client != null) {
xceiverClientManager.releaseClient(client, false);
}
}
}
/**
* Delete the container, this will release any resource it uses.
* @param containerID - containerID.
* @param force - True to forcibly delete the container.
* @throws IOException
*/
@Override
public void deleteContainer(long containerID, boolean force)
throws IOException {
ContainerWithPipeline info = getContainerWithPipeline(containerID);
deleteContainer(containerID, info.getPipeline(), force);
}
@Override
public List<ContainerInfo> listContainer(long startContainerID,
int count) throws IOException {
return storageContainerLocationClient.listContainer(
startContainerID, count);
}
/**
* Get meta data from an existing container.
*
* @param containerID - ID of the container.
* @param pipeline - Pipeline where the container is located.
* @return ContainerInfo
* @throws IOException
*/
@Override
public ContainerDataProto readContainer(long containerID,
Pipeline pipeline) throws IOException {
XceiverClientSpi client = null;
try {
client = xceiverClientManager.acquireClient(pipeline);
ReadContainerResponseProto response =
ContainerProtocolCalls.readContainer(client, containerID, null);
if (LOG.isDebugEnabled()) {
LOG.debug("Read container {}, machines: {} ", containerID,
pipeline.getNodes());
}
return response.getContainerData();
} finally {
if (client != null) {
xceiverClientManager.releaseClient(client, false);
}
}
}
/**
* Get meta data from an existing container.
* @param containerID - ID of the container.
* @return ContainerInfo - a message of protobuf which has basic info
* of a container.
* @throws IOException
*/
@Override
public ContainerDataProto readContainer(long containerID) throws IOException {
ContainerWithPipeline info = getContainerWithPipeline(containerID);
return readContainer(containerID, info.getPipeline());
}
/**
* Given an id, return the pipeline associated with the container.
* @param containerId - String Container ID
* @return Pipeline of the existing container, corresponding to the given id.
* @throws IOException
*/
@Override
public ContainerInfo getContainer(long containerId) throws
IOException {
return storageContainerLocationClient.getContainer(containerId);
}
/**
* Gets a container by Name -- Throws if the container does not exist.
*
* @param containerId - Container ID
* @return ContainerWithPipeline
* @throws IOException
*/
@Override
public ContainerWithPipeline getContainerWithPipeline(long containerId)
throws IOException {
return storageContainerLocationClient.getContainerWithPipeline(containerId);
}
/**
* Close a container.
*
* @param pipeline the container to be closed.
* @throws IOException
*/
@Override
public void closeContainer(long containerId, Pipeline pipeline)
throws IOException {
XceiverClientSpi client = null;
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Close container {}", pipeline);
}
/*
TODO: two orders here, revisit this later:
1. close on SCM first, then on data node
2. close on data node first, then on SCM
with 1: if client failed after closing on SCM, then there is a
container SCM thinks as closed, but is actually open. Then SCM will no
longer allocate block to it, which is fine. But SCM may later try to
replicate this "closed" container, which I'm not sure is safe.
with 2: if client failed after close on datanode, then there is a
container SCM thinks as open, but is actually closed. Then SCM will still
try to allocate block to it. Which will fail when actually doing the
write. No more data can be written, but at least the correctness and
consistency of existing data will maintain.
For now, take the #2 way.
*/
// Actually close the container on Datanode
client = xceiverClientManager.acquireClient(pipeline);
storageContainerLocationClient.notifyObjectStageChange(
ObjectStageChangeRequestProto.Type.container,
containerId,
ObjectStageChangeRequestProto.Op.close,
ObjectStageChangeRequestProto.Stage.begin);
ContainerProtocolCalls.closeContainer(client, containerId,
null);
// Notify SCM to close the container
storageContainerLocationClient.notifyObjectStageChange(
ObjectStageChangeRequestProto.Type.container,
containerId,
ObjectStageChangeRequestProto.Op.close,
ObjectStageChangeRequestProto.Stage.complete);
} finally {
if (client != null) {
xceiverClientManager.releaseClient(client, false);
}
}
}
/**
* Close a container.
*
* @throws IOException
*/
@Override
public void closeContainer(long containerId)
throws IOException {
ContainerWithPipeline info = getContainerWithPipeline(containerId);
Pipeline pipeline = info.getPipeline();
closeContainer(containerId, pipeline);
}
/**
* Get the the current usage information.
* @param containerID - ID of the container.
* @return the size of the given container.
* @throws IOException
*/
@Override
public long getContainerSize(long containerID) throws IOException {
// TODO : Fix this, it currently returns the capacity
// but not the current usage.
long size = getContainerSizeB();
if (size == -1) {
throw new IOException("Container size unknown!");
}
return size;
}
/**
* Check if SCM is in safe mode.
*
* @return Returns true if SCM is in safe mode else returns false.
* @throws IOException
*/
public boolean inSafeMode() throws IOException {
return storageContainerLocationClient.inSafeMode();
}
/**
* Force SCM out of safe mode.
*
* @return returns true if operation is successful.
* @throws IOException
*/
public boolean forceExitSafeMode() throws IOException {
return storageContainerLocationClient.forceExitSafeMode();
}
@Override
public void startReplicationManager() throws IOException {
storageContainerLocationClient.startReplicationManager();
}
@Override
public void stopReplicationManager() throws IOException {
storageContainerLocationClient.stopReplicationManager();
}
@Override
public boolean getReplicationManagerStatus() throws IOException {
return storageContainerLocationClient.getReplicationManagerStatus();
}
}

View File

@ -1,350 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.scm.client;
import com.google.common.base.Preconditions;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolPB;
import org.apache.hadoop.hdds.scm.XceiverClientManager.ScmClientConfig;
import org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
import org.apache.hadoop.hdds.scm.protocol.ScmBlockLocationProtocol;
import org.apache.hadoop.hdds.scm.protocolPB.ScmBlockLocationProtocolPB;
import org.apache.hadoop.io.retry.RetryPolicies;
import org.apache.hadoop.io.retry.RetryPolicy;
import org.apache.hadoop.ipc.Client;
import org.apache.hadoop.ipc.ProtobufRpcEngine;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.ratis.protocol.AlreadyClosedException;
import org.apache.ratis.protocol.GroupMismatchException;
import org.apache.ratis.protocol.NotReplicatedException;
import org.apache.ratis.protocol.RaftRetryFailureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.ParseException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Utility methods for Ozone and Container Clients.
*
* The methods to retrieve SCM service endpoints assume there is a single
* SCM service instance. This will change when we switch to replicated service
* instances for redundancy.
*/
@InterfaceAudience.Public
@InterfaceStability.Unstable
public final class HddsClientUtils {
private static final Logger LOG = LoggerFactory.getLogger(
HddsClientUtils.class);
private static final int NO_PORT = -1;
private HddsClientUtils() {
}
private static final List<Class<? extends Exception>> EXCEPTION_LIST =
new ArrayList<Class<? extends Exception>>() {{
add(TimeoutException.class);
add(StorageContainerException.class);
add(RaftRetryFailureException.class);
add(AlreadyClosedException.class);
add(GroupMismatchException.class);
// Not Replicated Exception will be thrown if watch For commit
// does not succeed
add(NotReplicatedException.class);
}};
/**
* Date format that used in ozone. Here the format is thread safe to use.
*/
private static final ThreadLocal<DateTimeFormatter> DATE_FORMAT =
ThreadLocal.withInitial(() -> {
DateTimeFormatter format =
DateTimeFormatter.ofPattern(OzoneConsts.OZONE_DATE_FORMAT);
return format.withZone(ZoneId.of(OzoneConsts.OZONE_TIME_ZONE));
});
/**
* Convert time in millisecond to a human readable format required in ozone.
* @return a human readable string for the input time
*/
public static String formatDateTime(long millis) {
ZonedDateTime dateTime = ZonedDateTime.ofInstant(
Instant.ofEpochMilli(millis), DATE_FORMAT.get().getZone());
return DATE_FORMAT.get().format(dateTime);
}
/**
* Convert time in ozone date format to millisecond.
* @return time in milliseconds
*/
public static long formatDateTime(String date) throws ParseException {
Preconditions.checkNotNull(date, "Date string should not be null.");
return ZonedDateTime.parse(date, DATE_FORMAT.get())
.toInstant().toEpochMilli();
}
/**
* verifies that bucket name / volume name is a valid DNS name.
*
* @param resName Bucket or volume Name to be validated
*
* @throws IllegalArgumentException
*/
public static void verifyResourceName(String resName)
throws IllegalArgumentException {
if (resName == null) {
throw new IllegalArgumentException("Bucket or Volume name is null");
}
if (resName.length() < OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH ||
resName.length() > OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH) {
throw new IllegalArgumentException(
"Bucket or Volume length is illegal, "
+ "valid length is 3-63 characters");
}
if (resName.charAt(0) == '.' || resName.charAt(0) == '-') {
throw new IllegalArgumentException(
"Bucket or Volume name cannot start with a period or dash");
}
if (resName.charAt(resName.length() - 1) == '.' ||
resName.charAt(resName.length() - 1) == '-') {
throw new IllegalArgumentException("Bucket or Volume name "
+ "cannot end with a period or dash");
}
boolean isIPv4 = true;
char prev = (char) 0;
for (int index = 0; index < resName.length(); index++) {
char currChar = resName.charAt(index);
if (currChar != '.') {
isIPv4 = ((currChar >= '0') && (currChar <= '9')) && isIPv4;
}
if (currChar > 'A' && currChar < 'Z') {
throw new IllegalArgumentException(
"Bucket or Volume name does not support uppercase characters");
}
if (currChar != '.' && currChar != '-') {
if (currChar < '0' || (currChar > '9' && currChar < 'a') ||
currChar > 'z') {
throw new IllegalArgumentException("Bucket or Volume name has an " +
"unsupported character : " +
currChar);
}
}
if (prev == '.' && currChar == '.') {
throw new IllegalArgumentException("Bucket or Volume name should not " +
"have two contiguous periods");
}
if (prev == '-' && currChar == '.') {
throw new IllegalArgumentException(
"Bucket or Volume name should not have period after dash");
}
if (prev == '.' && currChar == '-') {
throw new IllegalArgumentException(
"Bucket or Volume name should not have dash after period");
}
prev = currChar;
}
if (isIPv4) {
throw new IllegalArgumentException(
"Bucket or Volume name cannot be an IPv4 address or all numeric");
}
}
/**
* verifies that bucket / volume name is a valid DNS name.
*
* @param resourceNames Array of bucket / volume names to be verified.
*/
public static void verifyResourceName(String... resourceNames) {
for (String resourceName : resourceNames) {
HddsClientUtils.verifyResourceName(resourceName);
}
}
/**
* Checks that object parameters passed as reference is not null.
*
* @param references Array of object references to be checked.
* @param <T>
*/
public static <T> void checkNotNull(T... references) {
for (T ref: references) {
Preconditions.checkNotNull(ref);
}
}
/**
* Returns the cache value to be used for list calls.
* @param conf Configuration object
* @return list cache size
*/
public static int getListCacheSize(Configuration conf) {
return conf.getInt(OzoneConfigKeys.OZONE_CLIENT_LIST_CACHE_SIZE,
OzoneConfigKeys.OZONE_CLIENT_LIST_CACHE_SIZE_DEFAULT);
}
/**
* @return a default instance of {@link CloseableHttpClient}.
*/
public static CloseableHttpClient newHttpClient() {
return HddsClientUtils.newHttpClient(new Configuration());
}
/**
* Returns a {@link CloseableHttpClient} configured by given configuration.
* If conf is null, returns a default instance.
*
* @param conf configuration
* @return a {@link CloseableHttpClient} instance.
*/
public static CloseableHttpClient newHttpClient(Configuration conf) {
long socketTimeout = OzoneConfigKeys
.OZONE_CLIENT_SOCKET_TIMEOUT_DEFAULT;
long connectionTimeout = OzoneConfigKeys
.OZONE_CLIENT_CONNECTION_TIMEOUT_DEFAULT;
if (conf != null) {
socketTimeout = conf.getTimeDuration(
OzoneConfigKeys.OZONE_CLIENT_SOCKET_TIMEOUT,
OzoneConfigKeys.OZONE_CLIENT_SOCKET_TIMEOUT_DEFAULT,
TimeUnit.MILLISECONDS);
connectionTimeout = conf.getTimeDuration(
OzoneConfigKeys.OZONE_CLIENT_CONNECTION_TIMEOUT,
OzoneConfigKeys.OZONE_CLIENT_CONNECTION_TIMEOUT_DEFAULT,
TimeUnit.MILLISECONDS);
}
CloseableHttpClient client = HttpClients.custom()
.setDefaultRequestConfig(
RequestConfig.custom()
.setSocketTimeout(Math.toIntExact(socketTimeout))
.setConnectTimeout(Math.toIntExact(connectionTimeout))
.build())
.build();
return client;
}
/**
* Returns the maximum no of outstanding async requests to be handled by
* Standalone and Ratis client.
*/
public static int getMaxOutstandingRequests(Configuration config) {
return OzoneConfiguration.of(config)
.getObject(ScmClientConfig.class)
.getMaxOutstandingRequests();
}
/**
* Create a scm block client, used by putKey() and getKey().
*
* @return {@link ScmBlockLocationProtocol}
* @throws IOException
*/
public static SCMSecurityProtocol getScmSecurityClient(
OzoneConfiguration conf, UserGroupInformation ugi) throws IOException {
RPC.setProtocolEngine(conf, SCMSecurityProtocolPB.class,
ProtobufRpcEngine.class);
long scmVersion =
RPC.getProtocolVersion(ScmBlockLocationProtocolPB.class);
InetSocketAddress scmSecurityProtoAdd =
HddsUtils.getScmAddressForSecurityProtocol(conf);
SCMSecurityProtocolClientSideTranslatorPB scmSecurityClient =
new SCMSecurityProtocolClientSideTranslatorPB(
RPC.getProxy(SCMSecurityProtocolPB.class, scmVersion,
scmSecurityProtoAdd, ugi, conf,
NetUtils.getDefaultSocketFactory(conf),
Client.getRpcTimeout(conf)));
return scmSecurityClient;
}
public static Throwable checkForException(Exception e) {
Throwable t = e;
while (t != null) {
for (Class<? extends Exception> cls : getExceptionList()) {
if (cls.isInstance(t)) {
return t;
}
}
t = t.getCause();
}
return t;
}
public static RetryPolicy createRetryPolicy(int maxRetryCount,
long retryInterval) {
// retry with fixed sleep between retries
return RetryPolicies.retryUpToMaximumCountWithFixedSleep(
maxRetryCount, retryInterval, TimeUnit.MILLISECONDS);
}
public static Map<Class<? extends Throwable>,
RetryPolicy> getRetryPolicyByException(int maxRetryCount,
long retryInterval) {
Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<>();
for (Class<? extends Exception> ex : EXCEPTION_LIST) {
if (ex == TimeoutException.class
|| ex == RaftRetryFailureException.class) {
// retry without sleep
policyMap.put(ex, createRetryPolicy(maxRetryCount, 0));
} else {
// retry with fixed sleep between retries
policyMap.put(ex, createRetryPolicy(maxRetryCount, retryInterval));
}
}
// Default retry policy
policyMap
.put(Exception.class, createRetryPolicy(maxRetryCount, retryInterval));
return policyMap;
}
public static List<Class<? extends Exception>> getExceptionList() {
return EXCEPTION_LIST;
}
}

View File

@ -1,23 +0,0 @@
/**
* 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.hdds.scm.client;
/**
* Client facing classes for the container operations.
*/

View File

@ -1,23 +0,0 @@
/**
* 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.hdds.scm;
/**
* Classes for different type of container service client.
*/

View File

@ -1,388 +0,0 @@
/*
* 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.hdds.scm.storage;
import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.fs.Seekable;
import org.apache.hadoop.hdds.scm.XceiverClientManager;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChunkInfo;
import org.apache.hadoop.hdds.client.BlockID;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.DatanodeBlockID;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.GetBlockResponseProto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* An {@link InputStream} called from KeyInputStream to read a block from the
* container.
* This class encapsulates all state management for iterating
* through the sequence of chunks through {@link ChunkInputStream}.
*/
public class BlockInputStream extends InputStream implements Seekable {
private static final Logger LOG =
LoggerFactory.getLogger(BlockInputStream.class);
private static final int EOF = -1;
private final BlockID blockID;
private final long length;
private Pipeline pipeline;
private final Token<OzoneBlockTokenIdentifier> token;
private final boolean verifyChecksum;
private XceiverClientManager xceiverClientManager;
private XceiverClientSpi xceiverClient;
private boolean initialized = false;
// List of ChunkInputStreams, one for each chunk in the block
private List<ChunkInputStream> chunkStreams;
// chunkOffsets[i] stores the index of the first data byte in
// chunkStream i w.r.t the block data.
// Lets say we have chunk size as 40 bytes. And let's say the parent
// block stores data from index 200 and has length 400.
// The first 40 bytes of this block will be stored in chunk[0], next 40 in
// chunk[1] and so on. But since the chunkOffsets are w.r.t the block only
// and not the key, the values in chunkOffsets will be [0, 40, 80,....].
private long[] chunkOffsets = null;
// Index of the chunkStream corresponding to the current position of the
// BlockInputStream i.e offset of the data to be read next from this block
private int chunkIndex;
// Position of the BlockInputStream is maintainted by this variable till
// the stream is initialized. This position is w.r.t to the block only and
// not the key.
// For the above example, if we seek to position 240 before the stream is
// initialized, then value of blockPosition will be set to 40.
// Once, the stream is initialized, the position of the stream
// will be determined by the current chunkStream and its position.
private long blockPosition = 0;
// Tracks the chunkIndex corresponding to the last blockPosition so that it
// can be reset if a new position is seeked.
private int chunkIndexOfPrevPosition;
public BlockInputStream(BlockID blockId, long blockLen, Pipeline pipeline,
Token<OzoneBlockTokenIdentifier> token, boolean verifyChecksum,
XceiverClientManager xceiverClientManager) {
this.blockID = blockId;
this.length = blockLen;
this.pipeline = pipeline;
this.token = token;
this.verifyChecksum = verifyChecksum;
this.xceiverClientManager = xceiverClientManager;
}
/**
* Initialize the BlockInputStream. Get the BlockData (list of chunks) from
* the Container and create the ChunkInputStreams for each Chunk in the Block.
*/
public synchronized void initialize() throws IOException {
// Pre-check that the stream has not been intialized already
if (initialized) {
return;
}
List<ChunkInfo> chunks = getChunkInfos();
if (chunks != null && !chunks.isEmpty()) {
// For each chunk in the block, create a ChunkInputStream and compute
// its chunkOffset
this.chunkOffsets = new long[chunks.size()];
long tempOffset = 0;
this.chunkStreams = new ArrayList<>(chunks.size());
for (int i = 0; i < chunks.size(); i++) {
addStream(chunks.get(i));
chunkOffsets[i] = tempOffset;
tempOffset += chunks.get(i).getLen();
}
initialized = true;
this.chunkIndex = 0;
if (blockPosition > 0) {
// Stream was seeked to blockPosition before initialization. Seek to the
// blockPosition now.
seek(blockPosition);
}
}
}
/**
* Send RPC call to get the block info from the container.
* @return List of chunks in this block.
*/
protected List<ChunkInfo> getChunkInfos() throws IOException {
// irrespective of the container state, we will always read via Standalone
// protocol.
if (pipeline.getType() != HddsProtos.ReplicationType.STAND_ALONE) {
pipeline = Pipeline.newBuilder(pipeline)
.setType(HddsProtos.ReplicationType.STAND_ALONE).build();
}
xceiverClient = xceiverClientManager.acquireClientForReadData(pipeline);
boolean success = false;
List<ChunkInfo> chunks;
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Initializing BlockInputStream for get key to access {}",
blockID.getContainerID());
}
if (token != null) {
UserGroupInformation.getCurrentUser().addToken(token);
}
DatanodeBlockID datanodeBlockID = blockID
.getDatanodeBlockIDProtobuf();
GetBlockResponseProto response = ContainerProtocolCalls
.getBlock(xceiverClient, datanodeBlockID);
chunks = response.getBlockData().getChunksList();
success = true;
} finally {
if (!success) {
xceiverClientManager.releaseClientForReadData(xceiverClient, false);
}
}
return chunks;
}
/**
* Append another ChunkInputStream to the end of the list. Note that the
* ChunkInputStream is only created here. The chunk will be read from the
* Datanode only when a read operation is performed on for that chunk.
*/
protected synchronized void addStream(ChunkInfo chunkInfo) {
chunkStreams.add(new ChunkInputStream(chunkInfo, blockID,
xceiverClient, verifyChecksum));
}
public synchronized long getRemaining() throws IOException {
return length - getPos();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int read() throws IOException {
byte[] buf = new byte[1];
if (read(buf, 0, 1) == EOF) {
return EOF;
}
return Byte.toUnsignedInt(buf[0]);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
if (!initialized) {
initialize();
}
checkOpen();
int totalReadLen = 0;
while (len > 0) {
// if we are at the last chunk and have read the entire chunk, return
if (chunkStreams.size() == 0 ||
(chunkStreams.size() - 1 <= chunkIndex &&
chunkStreams.get(chunkIndex)
.getRemaining() == 0)) {
return totalReadLen == 0 ? EOF : totalReadLen;
}
// Get the current chunkStream and read data from it
ChunkInputStream current = chunkStreams.get(chunkIndex);
int numBytesToRead = Math.min(len, (int)current.getRemaining());
int numBytesRead = current.read(b, off, numBytesToRead);
if (numBytesRead != numBytesToRead) {
// This implies that there is either data loss or corruption in the
// chunk entries. Even EOF in the current stream would be covered in
// this case.
throw new IOException(String.format(
"Inconsistent read for chunkName=%s length=%d numBytesRead=%d",
current.getChunkName(), current.getLength(), numBytesRead));
}
totalReadLen += numBytesRead;
off += numBytesRead;
len -= numBytesRead;
if (current.getRemaining() <= 0 &&
((chunkIndex + 1) < chunkStreams.size())) {
chunkIndex += 1;
}
}
return totalReadLen;
}
/**
* Seeks the BlockInputStream to the specified position. If the stream is
* not initialized, save the seeked position via blockPosition. Otherwise,
* update the position in 2 steps:
* 1. Updating the chunkIndex to the chunkStream corresponding to the
* seeked position.
* 2. Seek the corresponding chunkStream to the adjusted position.
*
* Lets say we have chunk size as 40 bytes. And let's say the parent block
* stores data from index 200 and has length 400. If the key was seeked to
* position 90, then this block will be seeked to position 90.
* When seek(90) is called on this blockStream, then
* 1. chunkIndex will be set to 2 (as indices 80 - 120 reside in chunk[2]).
* 2. chunkStream[2] will be seeked to position 10
* (= 90 - chunkOffset[2] (= 80)).
*/
@Override
public synchronized void seek(long pos) throws IOException {
if (!initialized) {
// Stream has not been initialized yet. Save the position so that it
// can be seeked when the stream is initialized.
blockPosition = pos;
return;
}
checkOpen();
if (pos < 0 || pos >= length) {
if (pos == 0) {
// It is possible for length and pos to be zero in which case
// seek should return instead of throwing exception
return;
}
throw new EOFException(
"EOF encountered at pos: " + pos + " for block: " + blockID);
}
if (chunkIndex >= chunkStreams.size()) {
chunkIndex = Arrays.binarySearch(chunkOffsets, pos);
} else if (pos < chunkOffsets[chunkIndex]) {
chunkIndex =
Arrays.binarySearch(chunkOffsets, 0, chunkIndex, pos);
} else if (pos >= chunkOffsets[chunkIndex] + chunkStreams
.get(chunkIndex).getLength()) {
chunkIndex = Arrays.binarySearch(chunkOffsets,
chunkIndex + 1, chunkStreams.size(), pos);
}
if (chunkIndex < 0) {
// Binary search returns -insertionPoint - 1 if element is not present
// in the array. insertionPoint is the point at which element would be
// inserted in the sorted array. We need to adjust the chunkIndex
// accordingly so that chunkIndex = insertionPoint - 1
chunkIndex = -chunkIndex - 2;
}
// Reset the previous chunkStream's position
chunkStreams.get(chunkIndexOfPrevPosition).resetPosition();
// seek to the proper offset in the ChunkInputStream
chunkStreams.get(chunkIndex).seek(pos - chunkOffsets[chunkIndex]);
chunkIndexOfPrevPosition = chunkIndex;
}
@Override
public synchronized long getPos() throws IOException {
if (length == 0) {
return 0;
}
if (!initialized) {
// The stream is not initialized yet. Return the blockPosition
return blockPosition;
} else {
return chunkOffsets[chunkIndex] + chunkStreams.get(chunkIndex).getPos();
}
}
@Override
public boolean seekToNewSource(long targetPos) throws IOException {
return false;
}
@Override
public synchronized void close() {
if (xceiverClientManager != null && xceiverClient != null) {
xceiverClientManager.releaseClient(xceiverClient, false);
xceiverClientManager = null;
xceiverClient = null;
}
}
public synchronized void resetPosition() {
this.blockPosition = 0;
}
/**
* Checks if the stream is open. If not, throw an exception.
*
* @throws IOException if stream is closed
*/
protected synchronized void checkOpen() throws IOException {
if (xceiverClient == null) {
throw new IOException("BlockInputStream has been closed.");
}
}
public BlockID getBlockID() {
return blockID;
}
public long getLength() {
return length;
}
@VisibleForTesting
synchronized int getChunkIndex() {
return chunkIndex;
}
@VisibleForTesting
synchronized long getBlockPosition() {
return blockPosition;
}
@VisibleForTesting
synchronized List<ChunkInputStream> getChunkStreams() {
return chunkStreams;
}
}

View File

@ -1,640 +0,0 @@
/*
* 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.hdds.scm.storage;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.scm.XceiverClientReply;
import org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.ozone.common.Checksum;
import org.apache.hadoop.ozone.common.ChecksumData;
import org.apache.hadoop.ozone.common.OzoneChecksumException;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.apache.hadoop.hdds.scm.XceiverClientManager;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChecksumType;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChunkInfo;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.BlockData;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.KeyValue;
import org.apache.hadoop.hdds.client.BlockID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import static org.apache.hadoop.hdds.scm.storage.ContainerProtocolCalls
.putBlockAsync;
import static org.apache.hadoop.hdds.scm.storage.ContainerProtocolCalls
.writeChunkAsync;
/**
* An {@link OutputStream} used by the REST service in combination with the
* SCMClient to write the value of a key to a sequence
* of container chunks. Writes are buffered locally and periodically written to
* the container as a new chunk. In order to preserve the semantics that
* replacement of a pre-existing key is atomic, each instance of the stream has
* an internal unique identifier. This unique identifier and a monotonically
* increasing chunk index form a composite key that is used as the chunk name.
* After all data is written, a putKey call creates or updates the corresponding
* container key, and this call includes the full list of chunks that make up
* the key data. The list of chunks is updated all at once. Therefore, a
* concurrent reader never can see an intermediate state in which different
* chunks of data from different versions of the key data are interleaved.
* This class encapsulates all state management for buffering and writing
* through to the container.
*/
public class BlockOutputStream extends OutputStream {
public static final Logger LOG =
LoggerFactory.getLogger(BlockOutputStream.class);
private volatile BlockID blockID;
private final BlockData.Builder containerBlockData;
private XceiverClientManager xceiverClientManager;
private XceiverClientSpi xceiverClient;
private final ContainerProtos.ChecksumType checksumType;
private final int bytesPerChecksum;
private int chunkIndex;
private int chunkSize;
private final long streamBufferFlushSize;
private final long streamBufferMaxSize;
private BufferPool bufferPool;
// The IOException will be set by response handling thread in case there is an
// exception received in the response. If the exception is set, the next
// request will fail upfront.
private AtomicReference<IOException> ioException;
private ExecutorService responseExecutor;
// the effective length of data flushed so far
private long totalDataFlushedLength;
// effective data write attempted so far for the block
private long writtenDataLength;
// List containing buffers for which the putBlock call will
// update the length in the datanodes. This list will just maintain
// references to the buffers in the BufferPool which will be cleared
// when the watchForCommit acknowledges a putBlock logIndex has been
// committed on all datanodes. This list will be a place holder for buffers
// which got written between successive putBlock calls.
private List<ByteBuffer> bufferList;
// This object will maintain the commitIndexes and byteBufferList in order
// Also, corresponding to the logIndex, the corresponding list of buffers will
// be released from the buffer pool.
private final CommitWatcher commitWatcher;
private List<DatanodeDetails> failedServers;
/**
* Creates a new BlockOutputStream.
*
* @param blockID block ID
* @param xceiverClientManager client manager that controls client
* @param pipeline pipeline where block will be written
* @param chunkSize chunk size
* @param bufferPool pool of buffers
* @param streamBufferFlushSize flush size
* @param streamBufferMaxSize max size of the currentBuffer
* @param watchTimeout watch timeout
* @param checksumType checksum type
* @param bytesPerChecksum Bytes per checksum
*/
@SuppressWarnings("parameternumber")
public BlockOutputStream(BlockID blockID,
XceiverClientManager xceiverClientManager, Pipeline pipeline,
int chunkSize, long streamBufferFlushSize, long streamBufferMaxSize,
long watchTimeout, BufferPool bufferPool, ChecksumType checksumType,
int bytesPerChecksum)
throws IOException {
this.blockID = blockID;
this.chunkSize = chunkSize;
KeyValue keyValue =
KeyValue.newBuilder().setKey("TYPE").setValue("KEY").build();
this.containerBlockData =
BlockData.newBuilder().setBlockID(blockID.getDatanodeBlockIDProtobuf())
.addMetadata(keyValue);
this.xceiverClientManager = xceiverClientManager;
this.xceiverClient = xceiverClientManager.acquireClient(pipeline);
this.chunkIndex = 0;
this.streamBufferFlushSize = streamBufferFlushSize;
this.streamBufferMaxSize = streamBufferMaxSize;
this.bufferPool = bufferPool;
this.checksumType = checksumType;
this.bytesPerChecksum = bytesPerChecksum;
// A single thread executor handle the responses of async requests
responseExecutor = Executors.newSingleThreadExecutor();
commitWatcher = new CommitWatcher(bufferPool, xceiverClient, watchTimeout);
bufferList = null;
totalDataFlushedLength = 0;
writtenDataLength = 0;
failedServers = new ArrayList<>(0);
ioException = new AtomicReference<>(null);
}
public BlockID getBlockID() {
return blockID;
}
public long getTotalAckDataLength() {
return commitWatcher.getTotalAckDataLength();
}
public long getWrittenDataLength() {
return writtenDataLength;
}
public List<DatanodeDetails> getFailedServers() {
return failedServers;
}
@VisibleForTesting
public XceiverClientSpi getXceiverClient() {
return xceiverClient;
}
@VisibleForTesting
public long getTotalDataFlushedLength() {
return totalDataFlushedLength;
}
@VisibleForTesting
public BufferPool getBufferPool() {
return bufferPool;
}
public IOException getIoException() {
return ioException.get();
}
@VisibleForTesting
public Map<Long, List<ByteBuffer>> getCommitIndex2flushedDataMap() {
return commitWatcher.getCommitIndex2flushedDataMap();
}
@Override
public void write(int b) throws IOException {
checkOpen();
byte[] buf = new byte[1];
buf[0] = (byte) b;
write(buf, 0, 1);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
checkOpen();
if (b == null) {
throw new NullPointerException();
}
if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)
|| ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return;
}
while (len > 0) {
int writeLen;
// Allocate a buffer if needed. The buffer will be allocated only
// once as needed and will be reused again for multiple blockOutputStream
// entries.
ByteBuffer currentBuffer = bufferPool.allocateBufferIfNeeded();
int pos = currentBuffer.position();
writeLen =
Math.min(chunkSize - pos % chunkSize, len);
currentBuffer.put(b, off, writeLen);
if (!currentBuffer.hasRemaining()) {
writeChunk(currentBuffer);
}
off += writeLen;
len -= writeLen;
writtenDataLength += writeLen;
if (shouldFlush()) {
updateFlushLength();
executePutBlock();
}
// Data in the bufferPool can not exceed streamBufferMaxSize
if (isBufferPoolFull()) {
handleFullBuffer();
}
}
}
private boolean shouldFlush() {
return bufferPool.computeBufferData() % streamBufferFlushSize == 0;
}
private void updateFlushLength() {
totalDataFlushedLength += writtenDataLength - totalDataFlushedLength;
}
private boolean isBufferPoolFull() {
return bufferPool.computeBufferData() == streamBufferMaxSize;
}
/**
* Will be called on the retryPath in case closedContainerException/
* TimeoutException.
* @param len length of data to write
* @throws IOException if error occurred
*/
// In this case, the data is already cached in the currentBuffer.
public void writeOnRetry(long len) throws IOException {
if (len == 0) {
return;
}
int count = 0;
Preconditions.checkArgument(len <= streamBufferMaxSize);
while (len > 0) {
long writeLen;
writeLen = Math.min(chunkSize, len);
if (writeLen == chunkSize) {
writeChunk(bufferPool.getBuffer(count));
}
len -= writeLen;
count++;
writtenDataLength += writeLen;
// we should not call isBufferFull/shouldFlush here.
// The buffer might already be full as whole data is already cached in
// the buffer. We should just validate
// if we wrote data of size streamBufferMaxSize/streamBufferFlushSize to
// call for handling full buffer/flush buffer condition.
if (writtenDataLength % streamBufferFlushSize == 0) {
// reset the position to zero as now we will be reading the
// next buffer in the list
updateFlushLength();
executePutBlock();
}
if (writtenDataLength == streamBufferMaxSize) {
handleFullBuffer();
}
}
}
/**
* This is a blocking call. It will wait for the flush till the commit index
* at the head of the commitIndex2flushedDataMap gets replicated to all or
* majority.
* @throws IOException
*/
private void handleFullBuffer() throws IOException {
try {
checkOpen();
if (!commitWatcher.getFutureMap().isEmpty()) {
waitOnFlushFutures();
}
} catch (InterruptedException | ExecutionException e) {
setIoException(e);
adjustBuffersOnException();
throw getIoException();
}
watchForCommit(true);
}
// It may happen that once the exception is encountered , we still might
// have successfully flushed up to a certain index. Make sure the buffers
// only contain data which have not been sufficiently replicated
private void adjustBuffersOnException() {
commitWatcher.releaseBuffersOnException();
}
/**
* calls watchForCommit API of the Ratis Client. For Standalone client,
* it is a no op.
* @param bufferFull flag indicating whether bufferFull condition is hit or
* its called as part flush/close
* @return minimum commit index replicated to all nodes
* @throws IOException IOException in case watch gets timed out
*/
private void watchForCommit(boolean bufferFull) throws IOException {
checkOpen();
try {
XceiverClientReply reply = bufferFull ?
commitWatcher.watchOnFirstIndex() : commitWatcher.watchOnLastIndex();
if (reply != null) {
List<DatanodeDetails> dnList = reply.getDatanodes();
if (!dnList.isEmpty()) {
Pipeline pipe = xceiverClient.getPipeline();
LOG.warn("Failed to commit BlockId {} on {}. Failed nodes: {}",
blockID, pipe, dnList);
failedServers.addAll(dnList);
}
}
} catch (IOException ioe) {
setIoException(ioe);
throw getIoException();
}
}
private CompletableFuture<ContainerProtos.
ContainerCommandResponseProto> executePutBlock()
throws IOException {
checkOpen();
long flushPos = totalDataFlushedLength;
Preconditions.checkNotNull(bufferList);
List<ByteBuffer> byteBufferList = bufferList;
bufferList = null;
Preconditions.checkNotNull(byteBufferList);
CompletableFuture<ContainerProtos.
ContainerCommandResponseProto> flushFuture;
try {
XceiverClientReply asyncReply =
putBlockAsync(xceiverClient, containerBlockData.build());
CompletableFuture<ContainerProtos.ContainerCommandResponseProto> future =
asyncReply.getResponse();
flushFuture = future.thenApplyAsync(e -> {
try {
validateResponse(e);
} catch (IOException sce) {
throw new CompletionException(sce);
}
// if the ioException is not set, putBlock is successful
if (getIoException() == null) {
BlockID responseBlockID = BlockID.getFromProtobuf(
e.getPutBlock().getCommittedBlockLength().getBlockID());
Preconditions.checkState(blockID.getContainerBlockID()
.equals(responseBlockID.getContainerBlockID()));
// updates the bcsId of the block
blockID = responseBlockID;
if (LOG.isDebugEnabled()) {
LOG.debug(
"Adding index " + asyncReply.getLogIndex() + " commitMap size "
+ commitWatcher.getCommitInfoMapSize() + " flushLength "
+ flushPos + " numBuffers " + byteBufferList.size()
+ " blockID " + blockID + " bufferPool size" + bufferPool
.getSize() + " currentBufferIndex " + bufferPool
.getCurrentBufferIndex());
}
// for standalone protocol, logIndex will always be 0.
commitWatcher
.updateCommitInfoMap(asyncReply.getLogIndex(), byteBufferList);
}
return e;
}, responseExecutor).exceptionally(e -> {
if (LOG.isDebugEnabled()) {
LOG.debug(
"putBlock failed for blockID " + blockID + " with exception " + e
.getLocalizedMessage());
}
CompletionException ce = new CompletionException(e);
setIoException(ce);
throw ce;
});
} catch (IOException | InterruptedException | ExecutionException e) {
throw new IOException(
"Unexpected Storage Container Exception: " + e.toString(), e);
}
commitWatcher.getFutureMap().put(flushPos, flushFuture);
return flushFuture;
}
@Override
public void flush() throws IOException {
if (xceiverClientManager != null && xceiverClient != null
&& bufferPool != null && bufferPool.getSize() > 0) {
try {
handleFlush();
} catch (InterruptedException | ExecutionException e) {
// just set the exception here as well in order to maintain sanctity of
// ioException field
setIoException(e);
adjustBuffersOnException();
throw getIoException();
}
}
}
private void writeChunk(ByteBuffer buffer)
throws IOException {
// This data in the buffer will be pushed to datanode and a reference will
// be added to the bufferList. Once putBlock gets executed, this list will
// be marked null. Hence, during first writeChunk call after every putBlock
// call or during the first call to writeChunk here, the list will be null.
if (bufferList == null) {
bufferList = new ArrayList<>();
}
bufferList.add(buffer);
// Please note : We are not flipping the slice when we write since
// the slices are pointing the currentBuffer start and end as needed for
// the chunk write. Also please note, Duplicate does not create a
// copy of data, it only creates metadata that points to the data
// stream.
ByteBuffer chunk = buffer.duplicate();
chunk.position(0);
chunk.limit(buffer.position());
writeChunkToContainer(chunk);
}
private void handleFlush()
throws IOException, InterruptedException, ExecutionException {
checkOpen();
// flush the last chunk data residing on the currentBuffer
if (totalDataFlushedLength < writtenDataLength) {
ByteBuffer currentBuffer = bufferPool.getCurrentBuffer();
Preconditions.checkArgument(currentBuffer.position() > 0);
if (currentBuffer.position() != chunkSize) {
writeChunk(currentBuffer);
}
// This can be a partially filled chunk. Since we are flushing the buffer
// here, we just limit this buffer to the current position. So that next
// write will happen in new buffer
updateFlushLength();
executePutBlock();
}
waitOnFlushFutures();
watchForCommit(false);
// just check again if the exception is hit while waiting for the
// futures to ensure flush has indeed succeeded
// irrespective of whether the commitIndex2flushedDataMap is empty
// or not, ensure there is no exception set
checkOpen();
}
@Override
public void close() throws IOException {
if (xceiverClientManager != null && xceiverClient != null
&& bufferPool != null && bufferPool.getSize() > 0) {
try {
handleFlush();
} catch (InterruptedException | ExecutionException e) {
setIoException(e);
adjustBuffersOnException();
throw getIoException();
} finally {
cleanup(false);
}
// TODO: Turn the below buffer empty check on when Standalone pipeline
// is removed in the write path in tests
// Preconditions.checkArgument(buffer.position() == 0);
// bufferPool.checkBufferPoolEmpty();
}
}
private void waitOnFlushFutures()
throws InterruptedException, ExecutionException {
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(
commitWatcher.getFutureMap().values().toArray(
new CompletableFuture[commitWatcher.getFutureMap().size()]));
// wait for all the transactions to complete
combinedFuture.get();
}
private void validateResponse(
ContainerProtos.ContainerCommandResponseProto responseProto)
throws IOException {
try {
// if the ioException is already set, it means a prev request has failed
// just throw the exception. The current operation will fail with the
// original error
IOException exception = getIoException();
if (exception != null) {
throw exception;
}
ContainerProtocolCalls.validateContainerResponse(responseProto);
} catch (StorageContainerException sce) {
LOG.error("Unexpected Storage Container Exception: ", sce);
setIoException(sce);
throw sce;
}
}
private void setIoException(Exception e) {
if (getIoException() == null) {
IOException exception = new IOException(
"Unexpected Storage Container Exception: " + e.toString(), e);
ioException.compareAndSet(null, exception);
}
}
public void cleanup(boolean invalidateClient) {
if (xceiverClientManager != null) {
xceiverClientManager.releaseClient(xceiverClient, invalidateClient);
}
xceiverClientManager = null;
xceiverClient = null;
commitWatcher.cleanup();
if (bufferList != null) {
bufferList.clear();
}
bufferList = null;
responseExecutor.shutdown();
}
/**
* Checks if the stream is open or exception has occured.
* If not, throws an exception.
*
* @throws IOException if stream is closed
*/
private void checkOpen() throws IOException {
if (isClosed()) {
throw new IOException("BlockOutputStream has been closed.");
} else if (getIoException() != null) {
adjustBuffersOnException();
throw getIoException();
}
}
public boolean isClosed() {
return xceiverClient == null;
}
/**
* Writes buffered data as a new chunk to the container and saves chunk
* information to be used later in putKey call.
*
* @throws IOException if there is an I/O error while performing the call
* @throws OzoneChecksumException if there is an error while computing
* checksum
*/
private void writeChunkToContainer(ByteBuffer chunk) throws IOException {
int effectiveChunkSize = chunk.remaining();
ByteString data = bufferPool.byteStringConversion().apply(chunk);
Checksum checksum = new Checksum(checksumType, bytesPerChecksum);
ChecksumData checksumData = checksum.computeChecksum(chunk);
ChunkInfo chunkInfo = ChunkInfo.newBuilder()
.setChunkName(blockID.getLocalID() + "_chunk_" + ++chunkIndex)
.setOffset(0)
.setLen(effectiveChunkSize)
.setChecksumData(checksumData.getProtoBufMessage())
.build();
try {
XceiverClientReply asyncReply =
writeChunkAsync(xceiverClient, chunkInfo, blockID, data);
CompletableFuture<ContainerProtos.ContainerCommandResponseProto> future =
asyncReply.getResponse();
future.thenApplyAsync(e -> {
try {
validateResponse(e);
} catch (IOException sce) {
future.completeExceptionally(sce);
}
return e;
}, responseExecutor).exceptionally(e -> {
if (LOG.isDebugEnabled()) {
LOG.debug(
"writing chunk failed " + chunkInfo.getChunkName() + " blockID "
+ blockID + " with exception " + e.getLocalizedMessage());
}
CompletionException ce = new CompletionException(e);
setIoException(ce);
throw ce;
});
} catch (IOException | InterruptedException | ExecutionException e) {
throw new IOException(
"Unexpected Storage Container Exception: " + e.toString(), e);
}
if (LOG.isDebugEnabled()) {
LOG.debug(
"writing chunk " + chunkInfo.getChunkName() + " blockID " + blockID
+ " length " + effectiveChunkSize);
}
containerBlockData.addChunks(chunkInfo);
}
@VisibleForTesting
public void setXceiverClient(XceiverClientSpi xceiverClient) {
this.xceiverClient = xceiverClient;
}
}

View File

@ -1,129 +0,0 @@
/*
* 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.hdds.scm.storage;
import com.google.common.base.Preconditions;
import org.apache.hadoop.hdds.scm.ByteStringConversion;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
* This class creates and manages pool of n buffers.
*/
public class BufferPool {
private List<ByteBuffer> bufferList;
private int currentBufferIndex;
private final int bufferSize;
private final int capacity;
private final Function<ByteBuffer, ByteString> byteStringConversion;
public BufferPool(int bufferSize, int capacity) {
this(bufferSize, capacity,
ByteStringConversion.createByteBufferConversion(null));
}
public BufferPool(int bufferSize, int capacity,
Function<ByteBuffer, ByteString> byteStringConversion){
this.capacity = capacity;
this.bufferSize = bufferSize;
bufferList = new ArrayList<>(capacity);
currentBufferIndex = -1;
this.byteStringConversion = byteStringConversion;
}
public Function<ByteBuffer, ByteString> byteStringConversion(){
return byteStringConversion;
}
public ByteBuffer getCurrentBuffer() {
return currentBufferIndex == -1 ? null : bufferList.get(currentBufferIndex);
}
/**
* If the currentBufferIndex is less than the buffer size - 1,
* it means, the next buffer in the list has been freed up for
* rewriting. Reuse the next available buffer in such cases.
*
* In case, the currentBufferIndex == buffer.size and buffer size is still
* less than the capacity to be allocated, just allocate a buffer of size
* chunk size.
*
*/
public ByteBuffer allocateBufferIfNeeded() {
ByteBuffer buffer = getCurrentBuffer();
if (buffer != null && buffer.hasRemaining()) {
return buffer;
}
if (currentBufferIndex < bufferList.size() - 1) {
buffer = getBuffer(currentBufferIndex + 1);
} else {
buffer = ByteBuffer.allocate(bufferSize);
bufferList.add(buffer);
}
Preconditions.checkArgument(bufferList.size() <= capacity);
currentBufferIndex++;
// TODO: Turn the below precondition check on when Standalone pipeline
// is removed in the write path in tests
// Preconditions.checkArgument(buffer.position() == 0);
return buffer;
}
public void releaseBuffer(ByteBuffer byteBuffer) {
// always remove from head of the list and append at last
ByteBuffer buffer = bufferList.remove(0);
// Ensure the buffer to be removed is always at the head of the list.
Preconditions.checkArgument(buffer.equals(byteBuffer));
buffer.clear();
bufferList.add(buffer);
Preconditions.checkArgument(currentBufferIndex >= 0);
currentBufferIndex--;
}
public void clearBufferPool() {
bufferList.clear();
currentBufferIndex = -1;
}
public void checkBufferPoolEmpty() {
Preconditions.checkArgument(computeBufferData() == 0);
}
public long computeBufferData() {
return bufferList.stream().mapToInt(value -> value.position())
.sum();
}
public int getSize() {
return bufferList.size();
}
public ByteBuffer getBuffer(int index) {
return bufferList.get(index);
}
int getCurrentBufferIndex() {
return currentBufferIndex;
}
}

View File

@ -1,544 +0,0 @@
/*
* 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.hdds.scm.storage;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.fs.Seekable;
import org.apache.hadoop.hdds.client.BlockID;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandRequestProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandResponseProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChunkInfo;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ReadChunkResponseProto;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
import org.apache.hadoop.ozone.common.Checksum;
import org.apache.hadoop.ozone.common.ChecksumData;
import org.apache.hadoop.ozone.common.OzoneChecksumException;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.List;
/**
* An {@link InputStream} called from BlockInputStream to read a chunk from the
* container. Each chunk may contain multiple underlying {@link ByteBuffer}
* instances.
*/
public class ChunkInputStream extends InputStream implements Seekable {
private ChunkInfo chunkInfo;
private final long length;
private final BlockID blockID;
private XceiverClientSpi xceiverClient;
private boolean verifyChecksum;
private boolean allocated = false;
// Buffer to store the chunk data read from the DN container
private List<ByteBuffer> buffers;
// Index of the buffers corresponding to the current position of the buffers
private int bufferIndex;
// The offset of the current data residing in the buffers w.r.t the start
// of chunk data
private long bufferOffset;
// The number of bytes of chunk data residing in the buffers currently
private long bufferLength;
// Position of the ChunkInputStream is maintained by this variable (if a
// seek is performed. This position is w.r.t to the chunk only and not the
// block or key. This variable is set only if either the buffers are not
// yet allocated or the if the allocated buffers do not cover the seeked
// position. Once the chunk is read, this variable is reset.
private long chunkPosition = -1;
private static final int EOF = -1;
ChunkInputStream(ChunkInfo chunkInfo, BlockID blockId,
XceiverClientSpi xceiverClient, boolean verifyChecksum) {
this.chunkInfo = chunkInfo;
this.length = chunkInfo.getLen();
this.blockID = blockId;
this.xceiverClient = xceiverClient;
this.verifyChecksum = verifyChecksum;
}
public synchronized long getRemaining() throws IOException {
return length - getPos();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int read() throws IOException {
checkOpen();
int available = prepareRead(1);
int dataout = EOF;
if (available == EOF) {
// There is no more data in the chunk stream. The buffers should have
// been released by now
Preconditions.checkState(buffers == null);
} else {
dataout = Byte.toUnsignedInt(buffers.get(bufferIndex).get());
}
if (chunkStreamEOF()) {
// consumer might use getPos to determine EOF,
// so release buffers when serving the last byte of data
releaseBuffers();
}
return dataout;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
// According to the JavaDocs for InputStream, it is recommended that
// subclasses provide an override of bulk read if possible for performance
// reasons. In addition to performance, we need to do it for correctness
// reasons. The Ozone REST service uses PipedInputStream and
// PipedOutputStream to relay HTTP response data between a Jersey thread and
// a Netty thread. It turns out that PipedInputStream/PipedOutputStream
// have a subtle dependency (bug?) on the wrapped stream providing separate
// implementations of single-byte read and bulk read. Without this, get key
// responses might close the connection before writing all of the bytes
// advertised in the Content-Length.
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
checkOpen();
int total = 0;
while (len > 0) {
int available = prepareRead(len);
if (available == EOF) {
// There is no more data in the chunk stream. The buffers should have
// been released by now
Preconditions.checkState(buffers == null);
return total != 0 ? total : EOF;
}
buffers.get(bufferIndex).get(b, off + total, available);
len -= available;
total += available;
}
if (chunkStreamEOF()) {
// smart consumers determine EOF by calling getPos()
// so we release buffers when serving the final bytes of data
releaseBuffers();
}
return total;
}
/**
* Seeks the ChunkInputStream to the specified position. This is done by
* updating the chunkPosition to the seeked position in case the buffers
* are not allocated or buffers do not contain the data corresponding to
* the seeked position (determined by buffersHavePosition()). Otherwise,
* the buffers position is updated to the seeked position.
*/
@Override
public synchronized void seek(long pos) throws IOException {
if (pos < 0 || pos >= length) {
if (pos == 0) {
// It is possible for length and pos to be zero in which case
// seek should return instead of throwing exception
return;
}
throw new EOFException("EOF encountered at pos: " + pos + " for chunk: "
+ chunkInfo.getChunkName());
}
if (buffersHavePosition(pos)) {
// The bufferPosition is w.r.t the current chunk.
// Adjust the bufferIndex and position to the seeked position.
adjustBufferPosition(pos - bufferOffset);
} else {
chunkPosition = pos;
}
}
@Override
public synchronized long getPos() throws IOException {
if (chunkPosition >= 0) {
return chunkPosition;
}
if (chunkStreamEOF()) {
return length;
}
if (buffersHaveData()) {
return bufferOffset + buffers.get(bufferIndex).position();
}
if (buffersAllocated()) {
return bufferOffset + bufferLength;
}
return 0;
}
@Override
public boolean seekToNewSource(long targetPos) throws IOException {
return false;
}
@Override
public synchronized void close() {
if (xceiverClient != null) {
xceiverClient = null;
}
}
/**
* Checks if the stream is open. If not, throw an exception.
*
* @throws IOException if stream is closed
*/
protected synchronized void checkOpen() throws IOException {
if (xceiverClient == null) {
throw new IOException("BlockInputStream has been closed.");
}
}
/**
* Prepares to read by advancing through buffers or allocating new buffers,
* as needed until it finds data to return, or encounters EOF.
* @param len desired lenght of data to read
* @return length of data available to read, possibly less than desired length
*/
private synchronized int prepareRead(int len) throws IOException {
for (;;) {
if (chunkPosition >= 0) {
if (buffersHavePosition(chunkPosition)) {
// The current buffers have the seeked position. Adjust the buffer
// index and position to point to the chunkPosition.
adjustBufferPosition(chunkPosition - bufferOffset);
} else {
// Read a required chunk data to fill the buffers with seeked
// position data
readChunkFromContainer(len);
}
}
if (buffersHaveData()) {
// Data is available from buffers
ByteBuffer bb = buffers.get(bufferIndex);
return len > bb.remaining() ? bb.remaining() : len;
} else if (dataRemainingInChunk()) {
// There is more data in the chunk stream which has not
// been read into the buffers yet.
readChunkFromContainer(len);
} else {
// All available input from this chunk stream has been consumed.
return EOF;
}
}
}
/**
* Reads full or partial Chunk from DN Container based on the current
* position of the ChunkInputStream, the number of bytes of data to read
* and the checksum boundaries.
* If successful, then the read data in saved in the buffers so that
* subsequent read calls can utilize it.
* @param len number of bytes of data to be read
* @throws IOException if there is an I/O error while performing the call
* to Datanode
*/
private synchronized void readChunkFromContainer(int len) throws IOException {
// index of first byte to be read from the chunk
long startByteIndex;
if (chunkPosition >= 0) {
// If seek operation was called to advance the buffer position, the
// chunk should be read from that position onwards.
startByteIndex = chunkPosition;
} else {
// Start reading the chunk from the last chunkPosition onwards.
startByteIndex = bufferOffset + bufferLength;
}
if (verifyChecksum) {
// Update the bufferOffset and bufferLength as per the checksum
// boundary requirement.
computeChecksumBoundaries(startByteIndex, len);
} else {
// Read from the startByteIndex
bufferOffset = startByteIndex;
bufferLength = len;
}
// Adjust the chunkInfo so that only the required bytes are read from
// the chunk.
final ChunkInfo adjustedChunkInfo = ChunkInfo.newBuilder(chunkInfo)
.setOffset(bufferOffset)
.setLen(bufferLength)
.build();
ByteString byteString = readChunk(adjustedChunkInfo);
buffers = byteString.asReadOnlyByteBufferList();
bufferIndex = 0;
allocated = true;
// If the stream was seeked to position before, then the buffer
// position should be adjusted as the reads happen at checksum boundaries.
// The buffers position might need to be adjusted for the following
// scenarios:
// 1. Stream was seeked to a position before the chunk was read
// 2. Chunk was read from index < the current position to account for
// checksum boundaries.
adjustBufferPosition(startByteIndex - bufferOffset);
}
/**
* Send RPC call to get the chunk from the container.
*/
@VisibleForTesting
protected ByteString readChunk(ChunkInfo readChunkInfo) throws IOException {
ReadChunkResponseProto readChunkResponse;
try {
List<CheckedBiFunction> validators =
ContainerProtocolCalls.getValidatorList();
validators.add(validator);
readChunkResponse = ContainerProtocolCalls.readChunk(xceiverClient,
readChunkInfo, blockID, validators);
} catch (IOException e) {
if (e instanceof StorageContainerException) {
throw e;
}
throw new IOException("Unexpected OzoneException: " + e.toString(), e);
}
return readChunkResponse.getData();
}
private CheckedBiFunction<ContainerCommandRequestProto,
ContainerCommandResponseProto, IOException> validator =
(request, response) -> {
final ChunkInfo reqChunkInfo =
request.getReadChunk().getChunkData();
ReadChunkResponseProto readChunkResponse = response.getReadChunk();
ByteString byteString = readChunkResponse.getData();
if (byteString.size() != reqChunkInfo.getLen()) {
// Bytes read from chunk should be equal to chunk size.
throw new OzoneChecksumException(String
.format("Inconsistent read for chunk=%s len=%d bytesRead=%d",
reqChunkInfo.getChunkName(), reqChunkInfo.getLen(),
byteString.size()));
}
if (verifyChecksum) {
ChecksumData checksumData = ChecksumData.getFromProtoBuf(
chunkInfo.getChecksumData());
// ChecksumData stores checksum for each 'numBytesPerChecksum'
// number of bytes in a list. Compute the index of the first
// checksum to match with the read data
int checkumStartIndex = (int) (reqChunkInfo.getOffset() /
checksumData.getBytesPerChecksum());
Checksum.verifyChecksum(
byteString, checksumData, checkumStartIndex);
}
};
/**
* Return the offset and length of bytes that need to be read from the
* chunk file to cover the checksum boundaries covering the actual start and
* end of the chunk index to be read.
* For example, lets say the client is reading from index 120 to 450 in the
* chunk. And let's say checksum is stored for every 100 bytes in the chunk
* i.e. the first checksum is for bytes from index 0 to 99, the next for
* bytes from index 100 to 199 and so on. To verify bytes from 120 to 450,
* we would need to read from bytes 100 to 499 so that checksum
* verification can be done.
*
* @param startByteIndex the first byte index to be read by client
* @param dataLen number of bytes to be read from the chunk
*/
private void computeChecksumBoundaries(long startByteIndex, int dataLen) {
int bytesPerChecksum = chunkInfo.getChecksumData().getBytesPerChecksum();
// index of the last byte to be read from chunk, inclusively.
final long endByteIndex = startByteIndex + dataLen - 1;
bufferOffset = (startByteIndex / bytesPerChecksum)
* bytesPerChecksum; // inclusive
final long endIndex = ((endByteIndex / bytesPerChecksum) + 1)
* bytesPerChecksum; // exclusive
bufferLength = Math.min(endIndex, length) - bufferOffset;
}
/**
* Adjust the buffers position to account for seeked position and/ or checksum
* boundary reads.
* @param bufferPosition the position to which the buffers must be advanced
*/
private void adjustBufferPosition(long bufferPosition) {
// The bufferPosition is w.r.t the current chunk.
// Adjust the bufferIndex and position to the seeked chunkPosition.
long tempOffest = 0;
for (int i = 0; i < buffers.size(); i++) {
if (bufferPosition - tempOffest >= buffers.get(i).capacity()) {
tempOffest += buffers.get(i).capacity();
} else {
bufferIndex = i;
break;
}
}
buffers.get(bufferIndex).position((int) (bufferPosition - tempOffest));
// Reset the chunkPosition as chunk stream has been initialized i.e. the
// buffers have been allocated.
resetPosition();
}
/**
* Check if the buffers have been allocated data and false otherwise.
*/
private boolean buffersAllocated() {
return buffers != null && !buffers.isEmpty();
}
/**
* Check if the buffers have any data remaining between the current
* position and the limit.
*/
private boolean buffersHaveData() {
boolean hasData = false;
if (buffersAllocated()) {
while (bufferIndex < (buffers.size())) {
if (buffers.get(bufferIndex).hasRemaining()) {
// current buffer has data
hasData = true;
break;
} else {
if (buffersRemaining()) {
// move to next available buffer
++bufferIndex;
Preconditions.checkState(bufferIndex < buffers.size());
} else {
// no more buffers remaining
break;
}
}
}
}
return hasData;
}
private boolean buffersRemaining() {
return (bufferIndex < (buffers.size() - 1));
}
/**
* Check if curernt buffers have the data corresponding to the input position.
*/
private boolean buffersHavePosition(long pos) {
// Check if buffers have been allocated
if (buffersAllocated()) {
// Check if the current buffers cover the input position
return pos >= bufferOffset &&
pos < bufferOffset + bufferLength;
}
return false;
}
/**
* Check if there is more data in the chunk which has not yet been read
* into the buffers.
*/
private boolean dataRemainingInChunk() {
long bufferPos;
if (chunkPosition >= 0) {
bufferPos = chunkPosition;
} else {
bufferPos = bufferOffset + bufferLength;
}
return bufferPos < length;
}
/**
* Check if end of chunkStream has been reached.
*/
private boolean chunkStreamEOF() {
if (!allocated) {
// Chunk data has not been read yet
return false;
}
if (buffersHaveData() || dataRemainingInChunk()) {
return false;
} else {
Preconditions.checkState(bufferOffset + bufferLength == length,
"EOF detected, but not at the last byte of the chunk");
return true;
}
}
/**
* If EOF is reached, release the buffers.
*/
private void releaseBuffers() {
buffers = null;
bufferIndex = 0;
}
/**
* Reset the chunkPosition once the buffers are allocated.
*/
void resetPosition() {
this.chunkPosition = -1;
}
String getChunkName() {
return chunkInfo.getChunkName();
}
protected long getLength() {
return length;
}
@VisibleForTesting
protected long getChunkPosition() {
return chunkPosition;
}
}

View File

@ -1,240 +0,0 @@
/*
* 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.
*/
/**
* This class maintains the map of the commitIndexes to be watched for
* successful replication in the datanodes in a given pipeline. It also releases
* the buffers associated with the user data back to {@Link BufferPool} once
* minimum replication criteria is achieved during an ozone key write.
*/
package org.apache.hadoop.hdds.scm.storage;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.scm.XceiverClientReply;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.stream.Collectors;
/**
* This class executes watchForCommit on ratis pipeline and releases
* buffers once data successfully gets replicated.
*/
public class CommitWatcher {
private static final Logger LOG =
LoggerFactory.getLogger(CommitWatcher.class);
// A reference to the pool of buffers holding the data
private BufferPool bufferPool;
// The map should maintain the keys (logIndexes) in order so that while
// removing we always end up updating incremented data flushed length.
// Also, corresponding to the logIndex, the corresponding list of buffers will
// be released from the buffer pool.
private ConcurrentSkipListMap<Long, List<ByteBuffer>>
commitIndex2flushedDataMap;
// future Map to hold up all putBlock futures
private ConcurrentHashMap<Long,
CompletableFuture<ContainerProtos.ContainerCommandResponseProto>>
futureMap;
private XceiverClientSpi xceiverClient;
private final long watchTimeout;
// total data which has been successfully flushed and acknowledged
// by all servers
private long totalAckDataLength;
public CommitWatcher(BufferPool bufferPool, XceiverClientSpi xceiverClient,
long watchTimeout) {
this.bufferPool = bufferPool;
this.xceiverClient = xceiverClient;
this.watchTimeout = watchTimeout;
commitIndex2flushedDataMap = new ConcurrentSkipListMap<>();
totalAckDataLength = 0;
futureMap = new ConcurrentHashMap<>();
}
/**
* just update the totalAckDataLength. In case of failure,
* we will read the data starting from totalAckDataLength.
*/
private long releaseBuffers(List<Long> indexes) {
Preconditions.checkArgument(!commitIndex2flushedDataMap.isEmpty());
for (long index : indexes) {
Preconditions.checkState(commitIndex2flushedDataMap.containsKey(index));
List<ByteBuffer> buffers = commitIndex2flushedDataMap.remove(index);
long length = buffers.stream().mapToLong(value -> {
int pos = value.position();
return pos;
}).sum();
totalAckDataLength += length;
// clear the future object from the future Map
Preconditions.checkNotNull(futureMap.remove(totalAckDataLength));
for (ByteBuffer byteBuffer : buffers) {
bufferPool.releaseBuffer(byteBuffer);
}
}
return totalAckDataLength;
}
public void updateCommitInfoMap(long index, List<ByteBuffer> byteBufferList) {
commitIndex2flushedDataMap
.put(index, byteBufferList);
}
int getCommitInfoMapSize() {
return commitIndex2flushedDataMap.size();
}
/**
* Calls watch for commit for the first index in commitIndex2flushedDataMap to
* the Ratis client.
* @return reply reply from raft client
* @throws IOException in case watchForCommit fails
*/
public XceiverClientReply watchOnFirstIndex() throws IOException {
if (!commitIndex2flushedDataMap.isEmpty()) {
// wait for the first commit index in the commitIndex2flushedDataMap
// to get committed to all or majority of nodes in case timeout
// happens.
long index =
commitIndex2flushedDataMap.keySet().stream().mapToLong(v -> v).min()
.getAsLong();
if (LOG.isDebugEnabled()) {
LOG.debug("waiting for first index " + index + " to catch up");
}
return watchForCommit(index);
} else {
return null;
}
}
/**
* Calls watch for commit for the first index in commitIndex2flushedDataMap to
* the Ratis client.
* @return reply reply from raft client
* @throws IOException in case watchForCommit fails
*/
public XceiverClientReply watchOnLastIndex()
throws IOException {
if (!commitIndex2flushedDataMap.isEmpty()) {
// wait for the commit index in the commitIndex2flushedDataMap
// to get committed to all or majority of nodes in case timeout
// happens.
long index =
commitIndex2flushedDataMap.keySet().stream().mapToLong(v -> v).max()
.getAsLong();
if (LOG.isDebugEnabled()) {
LOG.debug("waiting for last flush Index " + index + " to catch up");
}
return watchForCommit(index);
} else {
return null;
}
}
private void adjustBuffers(long commitIndex) {
List<Long> keyList = commitIndex2flushedDataMap.keySet().stream()
.filter(p -> p <= commitIndex).collect(Collectors.toList());
if (keyList.isEmpty()) {
return;
} else {
releaseBuffers(keyList);
}
}
// It may happen that once the exception is encountered , we still might
// have successfully flushed up to a certain index. Make sure the buffers
// only contain data which have not been sufficiently replicated
void releaseBuffersOnException() {
adjustBuffers(xceiverClient.getReplicatedMinCommitIndex());
}
/**
* calls watchForCommit API of the Ratis Client. For Standalone client,
* it is a no op.
* @param commitIndex log index to watch for
* @return minimum commit index replicated to all nodes
* @throws IOException IOException in case watch gets timed out
*/
public XceiverClientReply watchForCommit(long commitIndex)
throws IOException {
long index;
try {
XceiverClientReply reply =
xceiverClient.watchForCommit(commitIndex, watchTimeout);
if (reply == null) {
index = 0;
} else {
index = reply.getLogIndex();
}
adjustBuffers(index);
return reply;
} catch (TimeoutException | InterruptedException | ExecutionException e) {
LOG.warn("watchForCommit failed for index " + commitIndex, e);
IOException ioException = new IOException(
"Unexpected Storage Container Exception: " + e.toString(), e);
releaseBuffersOnException();
throw ioException;
}
}
@VisibleForTesting
public ConcurrentSkipListMap<Long,
List<ByteBuffer>> getCommitIndex2flushedDataMap() {
return commitIndex2flushedDataMap;
}
public ConcurrentHashMap<Long,
CompletableFuture<ContainerProtos.
ContainerCommandResponseProto>> getFutureMap() {
return futureMap;
}
public long getTotalAckDataLength() {
return totalAckDataLength;
}
public void cleanup() {
if (commitIndex2flushedDataMap != null) {
commitIndex2flushedDataMap.clear();
}
if (futureMap != null) {
futureMap.clear();
}
commitIndex2flushedDataMap = null;
}
}

View File

@ -1,23 +0,0 @@
/**
* 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.hdds.scm.storage;
/**
* Low level IO streams to upload/download chunks from container service.
*/

View File

@ -1,234 +0,0 @@
/*
* 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.hdds.scm.storage;
import com.google.common.primitives.Bytes;
import org.apache.hadoop.hdds.client.BlockID;
import org.apache.hadoop.hdds.client.ContainerBlockID;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChecksumType;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChunkInfo;
import org.apache.hadoop.hdds.scm.XceiverClientManager;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
import org.apache.hadoop.ozone.common.Checksum;
import org.apache.hadoop.security.token.Token;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.EOFException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import static org.apache.hadoop.hdds.scm.storage.TestChunkInputStream.generateRandomData;
/**
* Tests for {@link BlockInputStream}'s functionality.
*/
public class TestBlockInputStream {
private static final int CHUNK_SIZE = 100;
private static Checksum checksum;
private BlockInputStream blockStream;
private byte[] blockData;
private int blockSize;
private List<ChunkInfo> chunks;
private Map<String, byte[]> chunkDataMap;
@Before
public void setup() throws Exception {
BlockID blockID = new BlockID(new ContainerBlockID(1, 1));
checksum = new Checksum(ChecksumType.NONE, CHUNK_SIZE);
createChunkList(5);
blockStream = new DummyBlockInputStream(blockID, blockSize, null, null,
false, null);
}
/**
* Create a mock list of chunks. The first n-1 chunks of length CHUNK_SIZE
* and the last chunk with length CHUNK_SIZE/2.
*/
private void createChunkList(int numChunks)
throws Exception {
chunks = new ArrayList<>(numChunks);
chunkDataMap = new HashMap<>();
blockData = new byte[0];
int i, chunkLen;
byte[] byteData;
String chunkName;
for (i = 0; i < numChunks; i++) {
chunkName = "chunk-" + i;
chunkLen = CHUNK_SIZE;
if (i == numChunks - 1) {
chunkLen = CHUNK_SIZE / 2;
}
byteData = generateRandomData(chunkLen);
ChunkInfo chunkInfo = ChunkInfo.newBuilder()
.setChunkName(chunkName)
.setOffset(0)
.setLen(chunkLen)
.setChecksumData(checksum.computeChecksum(
byteData, 0, chunkLen).getProtoBufMessage())
.build();
chunkDataMap.put(chunkName, byteData);
chunks.add(chunkInfo);
blockSize += chunkLen;
blockData = Bytes.concat(blockData, byteData);
}
}
/**
* A dummy BlockInputStream to mock read block call to DN.
*/
private class DummyBlockInputStream extends BlockInputStream {
DummyBlockInputStream(BlockID blockId,
long blockLen,
Pipeline pipeline,
Token<OzoneBlockTokenIdentifier> token,
boolean verifyChecksum,
XceiverClientManager xceiverClientManager) {
super(blockId, blockLen, pipeline, token, verifyChecksum,
xceiverClientManager);
}
@Override
protected List<ChunkInfo> getChunkInfos() {
return chunks;
}
@Override
protected void addStream(ChunkInfo chunkInfo) {
TestChunkInputStream testChunkInputStream = new TestChunkInputStream();
getChunkStreams().add(testChunkInputStream.new DummyChunkInputStream(
chunkInfo, null, null, false,
chunkDataMap.get(chunkInfo.getChunkName()).clone()));
}
@Override
protected synchronized void checkOpen() throws IOException {
// No action needed
}
}
private void seekAndVerify(int pos) throws Exception {
blockStream.seek(pos);
Assert.assertEquals("Current position of buffer does not match with the " +
"seeked position", pos, blockStream.getPos());
}
/**
* Match readData with the chunkData byte-wise.
* @param readData Data read through ChunkInputStream
* @param inputDataStartIndex first index (inclusive) in chunkData to compare
* with read data
* @param length the number of bytes of data to match starting from
* inputDataStartIndex
*/
private void matchWithInputData(byte[] readData, int inputDataStartIndex,
int length) {
for (int i = inputDataStartIndex; i < inputDataStartIndex + length; i++) {
Assert.assertEquals(blockData[i], readData[i - inputDataStartIndex]);
}
}
@Test
public void testSeek() throws Exception {
// Seek to position 0
int pos = 0;
seekAndVerify(pos);
Assert.assertEquals("ChunkIndex is incorrect", 0,
blockStream.getChunkIndex());
// Before BlockInputStream is initialized (initialization happens during
// read operation), seek should update the BlockInputStream#blockPosition
pos = CHUNK_SIZE;
seekAndVerify(pos);
Assert.assertEquals("ChunkIndex is incorrect", 0,
blockStream.getChunkIndex());
Assert.assertEquals(pos, blockStream.getBlockPosition());
// Initialize the BlockInputStream. After initializtion, the chunkIndex
// should be updated to correspond to the seeked position.
blockStream.initialize();
Assert.assertEquals("ChunkIndex is incorrect", 1,
blockStream.getChunkIndex());
pos = (CHUNK_SIZE * 4) + 5;
seekAndVerify(pos);
Assert.assertEquals("ChunkIndex is incorrect", 4,
blockStream.getChunkIndex());
try {
// Try seeking beyond the blockSize.
pos = blockSize + 10;
seekAndVerify(pos);
Assert.fail("Seek to position beyond block size should fail.");
} catch (EOFException e) {
System.out.println(e);
}
// Seek to random positions between 0 and the block size.
Random random = new Random();
for (int i = 0; i < 10; i++) {
pos = random.nextInt(blockSize);
seekAndVerify(pos);
}
}
@Test
public void testRead() throws Exception {
// read 200 bytes of data starting from position 50. Chunk0 contains
// indices 0 to 99, chunk1 from 100 to 199 and chunk3 from 200 to 299. So
// the read should result in 3 ChunkInputStream reads
seekAndVerify(50);
byte[] b = new byte[200];
blockStream.read(b, 0, 200);
matchWithInputData(b, 50, 200);
// The new position of the blockInputStream should be the last index read
// + 1.
Assert.assertEquals(250, blockStream.getPos());
Assert.assertEquals(2, blockStream.getChunkIndex());
}
@Test
public void testSeekAndRead() throws Exception {
// Seek to a position and read data
seekAndVerify(50);
byte[] b1 = new byte[100];
blockStream.read(b1, 0, 100);
matchWithInputData(b1, 50, 100);
// Next read should start from the position of the last read + 1 i.e. 100
byte[] b2 = new byte[100];
blockStream.read(b2, 0, 100);
matchWithInputData(b2, 150, 100);
}
}

View File

@ -1,222 +0,0 @@
/*
* 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.hdds.scm.storage;
import org.apache.hadoop.hdds.client.BlockID;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChecksumType;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ChunkInfo;
import org.apache.hadoop.hdds.scm.XceiverClientSpi;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.common.Checksum;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.EOFException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Tests for {@link ChunkInputStream}'s functionality.
*/
public class TestChunkInputStream {
private static final int CHUNK_SIZE = 100;
private static final int BYTES_PER_CHECKSUM = 20;
private static final String CHUNK_NAME = "dummyChunk";
private static final Random RANDOM = new Random();
private static Checksum checksum;
private DummyChunkInputStream chunkStream;
private ChunkInfo chunkInfo;
private byte[] chunkData;
@Before
public void setup() throws Exception {
checksum = new Checksum(ChecksumType.valueOf(
OzoneConfigKeys.OZONE_CLIENT_CHECKSUM_TYPE_DEFAULT),
BYTES_PER_CHECKSUM);
chunkData = generateRandomData(CHUNK_SIZE);
chunkInfo = ChunkInfo.newBuilder()
.setChunkName(CHUNK_NAME)
.setOffset(0)
.setLen(CHUNK_SIZE)
.setChecksumData(checksum.computeChecksum(
chunkData, 0, CHUNK_SIZE).getProtoBufMessage())
.build();
chunkStream = new DummyChunkInputStream(chunkInfo, null, null, true);
}
static byte[] generateRandomData(int length) {
byte[] bytes = new byte[length];
RANDOM.nextBytes(bytes);
return bytes;
}
/**
* A dummy ChunkInputStream to mock read chunk calls to DN.
*/
public class DummyChunkInputStream extends ChunkInputStream {
// Stores the read chunk data in each readChunk call
private List<ByteString> readByteBuffers = new ArrayList<>();
DummyChunkInputStream(ChunkInfo chunkInfo,
BlockID blockId,
XceiverClientSpi xceiverClient,
boolean verifyChecksum) {
super(chunkInfo, blockId, xceiverClient, verifyChecksum);
}
public DummyChunkInputStream(ChunkInfo chunkInfo,
BlockID blockId,
XceiverClientSpi xceiverClient,
boolean verifyChecksum,
byte[] data) {
super(chunkInfo, blockId, xceiverClient, verifyChecksum);
chunkData = data;
}
@Override
protected ByteString readChunk(ChunkInfo readChunkInfo) {
ByteString byteString = ByteString.copyFrom(chunkData,
(int) readChunkInfo.getOffset(),
(int) readChunkInfo.getLen());
readByteBuffers.add(byteString);
return byteString;
}
@Override
protected void checkOpen() {
// No action needed
}
}
/**
* Match readData with the chunkData byte-wise.
* @param readData Data read through ChunkInputStream
* @param inputDataStartIndex first index (inclusive) in chunkData to compare
* with read data
* @param length the number of bytes of data to match starting from
* inputDataStartIndex
*/
private void matchWithInputData(byte[] readData, int inputDataStartIndex,
int length) {
for (int i = inputDataStartIndex; i < inputDataStartIndex + length; i++) {
Assert.assertEquals(chunkData[i], readData[i - inputDataStartIndex]);
}
}
/**
* Seek to a position and verify through getPos().
*/
private void seekAndVerify(int pos) throws Exception {
chunkStream.seek(pos);
Assert.assertEquals("Current position of buffer does not match with the " +
"seeked position", pos, chunkStream.getPos());
}
@Test
public void testFullChunkRead() throws Exception {
byte[] b = new byte[CHUNK_SIZE];
chunkStream.read(b, 0, CHUNK_SIZE);
matchWithInputData(b, 0, CHUNK_SIZE);
}
@Test
public void testPartialChunkRead() throws Exception {
int len = CHUNK_SIZE / 2;
byte[] b = new byte[len];
chunkStream.read(b, 0, len);
matchWithInputData(b, 0, len);
// To read chunk data from index 0 to 49 (len = 50), we need to read
// chunk from offset 0 to 60 as the checksum boundary is at every 20
// bytes. Verify that 60 bytes of chunk data are read and stored in the
// buffers.
matchWithInputData(chunkStream.readByteBuffers.get(0).toByteArray(),
0, 60);
}
@Test
public void testSeek() throws Exception {
seekAndVerify(0);
try {
seekAndVerify(CHUNK_SIZE);
Assert.fail("Seeking to Chunk Length should fail.");
} catch (EOFException e) {
GenericTestUtils.assertExceptionContains("EOF encountered at pos: "
+ CHUNK_SIZE + " for chunk: " + CHUNK_NAME, e);
}
// Seek before read should update the ChunkInputStream#chunkPosition
seekAndVerify(25);
Assert.assertEquals(25, chunkStream.getChunkPosition());
// Read from the seeked position.
// Reading from index 25 to 54 should result in the ChunkInputStream
// copying chunk data from index 20 to 59 into the buffers (checksum
// boundaries).
byte[] b = new byte[30];
chunkStream.read(b, 0, 30);
matchWithInputData(b, 25, 30);
matchWithInputData(chunkStream.readByteBuffers.get(0).toByteArray(),
20, 40);
// After read, the position of the chunkStream is evaluated from the
// buffers and the chunkPosition should be reset to -1.
Assert.assertEquals(-1, chunkStream.getChunkPosition());
// Seek to a position within the current buffers. Current buffers contain
// data from index 20 to 59. ChunkPosition should still not be used to
// set the position.
seekAndVerify(35);
Assert.assertEquals(-1, chunkStream.getChunkPosition());
// Seek to a position outside the current buffers. In this case, the
// chunkPosition should be updated to the seeked position.
seekAndVerify(75);
Assert.assertEquals(75, chunkStream.getChunkPosition());
}
@Test
public void testSeekAndRead() throws Exception {
// Seek to a position and read data
seekAndVerify(50);
byte[] b1 = new byte[20];
chunkStream.read(b1, 0, 20);
matchWithInputData(b1, 50, 20);
// Next read should start from the position of the last read + 1 i.e. 70
byte[] b2 = new byte[20];
chunkStream.read(b2, 0, 20);
matchWithInputData(b2, 70, 20);
}
}

View File

@ -1,21 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*/
/**
* This package contains Ozone InputStream related tests.
*/
package org.apache.hadoop.hdds.scm.storage;

View File

@ -1,33 +0,0 @@
<!--
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.
-->
<FindBugsFilter>
<Match>
<Package name="org.apache.hadoop.hdds.protocol.proto"/>
</Match>
<Match>
<Package name="org.apache.hadoop.hdds.protocol.datanode.proto"/>
</Match>
<Match>
<Class name="org.apache.hadoop.hdds.cli.GenericCli"></Class>
<Bug pattern="DM_EXIT" />
</Match>
<Match>
<Class name="org.apache.hadoop.ozone.common.ChecksumByteBuffer$CrcIntTable" />
<Method name="update" />
<Bug pattern="SF_SWITCH_FALLTHROUGH,SF_SWITCH_NO_DEFAULT" />
</Match>
</FindBugsFilter>

View File

@ -1,285 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed 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. See accompanying LICENSE file.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdds</artifactId>
<version>0.5.0-SNAPSHOT</version>
</parent>
<artifactId>hadoop-hdds-common</artifactId>
<version>0.5.0-SNAPSHOT</version>
<description>Apache Hadoop Distributed Data Store Common</description>
<name>Apache Hadoop HDDS Common</name>
<packaging>jar</packaging>
<properties>
<hdds.version>0.5.0-SNAPSHOT</hdds.version>
<log4j2.version>2.11.0</log4j2.version>
<disruptor.version>3.4.2</disruptor.version>
<declared.hdds.version>${hdds.version}</declared.hdds.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdds-config</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.fusesource.leveldbjni</groupId>
<artifactId>leveldbjni-all</artifactId>
</dependency>
<dependency>
<artifactId>ratis-server</artifactId>
<groupId>org.apache.ratis</groupId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>ratis-netty</artifactId>
<groupId>org.apache.ratis</groupId>
</dependency>
<dependency>
<artifactId>ratis-grpc</artifactId>
<groupId>org.apache.ratis</groupId>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>2.2.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.rocksdb</groupId>
<artifactId>rocksdbjni</artifactId>
<version>6.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
<version>${jaeger.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
<version>0.31.0</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.16</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<excludes>
<exclude>hdds-version-info.properties</exclude>
</excludes>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>hdds-version-info.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>${os-maven-plugin.version}</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:${protobuf-compile.version}:exe:${os.detected.classifier}
</protocArtifact>
<protoSourceRoot>${basedir}/src/main/proto/</protoSourceRoot>
<includes>
<include>DatanodeContainerProtocol.proto</include>
</includes>
<outputDirectory>target/generated-sources/java</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<id>compile-protoc</id>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
<goal>compile-custom</goal>
<goal>test-compile-custom</goal>
</goals>
<configuration>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<tasks>
<replace token="com.google.protobuf" value="org.apache.ratis.thirdparty.com.google.protobuf"
dir="target/generated-sources/java/org/apache/hadoop/hdds/protocol/datanode/proto">
</replace>
<replace token="io.grpc" value="org.apache.ratis.thirdparty.io.grpc"
dir="target/generated-sources/java/org/apache/hadoop/hdds/protocol/datanode/proto">
</replace>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-maven-plugins</artifactId>
<executions>
<execution>
<id>version-info</id>
<phase>generate-resources</phase>
<goals>
<goal>version-info</goal>
</goals>
<configuration>
<source>
<directory>${basedir}/../</directory>
<includes>
<include>*/src/main/java/**/*.java</include>
<include>*/src/main/proto/*.proto</include>
</includes>
</source>
</configuration>
</execution>
<execution>
<id>compile-protoc</id>
<goals>
<goal>protoc</goal>
</goals>
<configuration>
<protocVersion>${protobuf.version}</protocVersion>
<protocCommand>${protoc.path}</protocCommand>
<imports>
<param>${basedir}/src/main/proto</param>
</imports>
<source>
<directory>${basedir}/src/main/proto</directory>
<includes>
<include>StorageContainerLocationProtocol.proto</include>
<include>hdds.proto</include>
<include>ScmBlockLocationProtocol.proto</include>
<include>SCMSecurityProtocol.proto</include>
</includes>
</source>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<configuration>
<excludeFilterFile>${basedir}/dev-support/findbugsExcludeFile.xml</excludeFilterFile>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,317 +0,0 @@
@echo off
@rem Licensed to the Apache Software Foundation (ASF) under one or more
@rem contributor license agreements. See the NOTICE file distributed with
@rem this work for additional information regarding copyright ownership.
@rem The ASF licenses this file to You under the Apache License, Version 2.0
@rem (the "License"); you may not use this file except in compliance with
@rem the License. You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem included in all the hadoop scripts with source command
@rem should not be executable directly
@rem also should not be passed any arguments, since we need original %*
if not defined HADOOP_COMMON_DIR (
set HADOOP_COMMON_DIR=share\hadoop\common
)
if not defined HADOOP_COMMON_LIB_JARS_DIR (
set HADOOP_COMMON_LIB_JARS_DIR=share\hadoop\common\lib
)
if not defined HADOOP_COMMON_LIB_NATIVE_DIR (
set HADOOP_COMMON_LIB_NATIVE_DIR=lib\native
)
if not defined HDFS_DIR (
set HDFS_DIR=share\hadoop\hdfs
)
if not defined HDFS_LIB_JARS_DIR (
set HDFS_LIB_JARS_DIR=share\hadoop\hdfs\lib
)
if not defined YARN_DIR (
set YARN_DIR=share\hadoop\yarn
)
if not defined YARN_LIB_JARS_DIR (
set YARN_LIB_JARS_DIR=share\hadoop\yarn\lib
)
if not defined MAPRED_DIR (
set MAPRED_DIR=share\hadoop\mapreduce
)
if not defined MAPRED_LIB_JARS_DIR (
set MAPRED_LIB_JARS_DIR=share\hadoop\mapreduce\lib
)
@rem the root of the Hadoop installation
set HADOOP_HOME=%~dp0
for %%i in (%HADOOP_HOME%.) do (
set HADOOP_HOME=%%~dpi
)
if "%HADOOP_HOME:~-1%" == "\" (
set HADOOP_HOME=%HADOOP_HOME:~0,-1%
)
if not exist %HADOOP_HOME%\share\hadoop\common\hadoop-common-*.jar (
@echo +================================================================+
@echo ^| Error: HADOOP_HOME is not set correctly ^|
@echo +----------------------------------------------------------------+
@echo ^| Please set your HADOOP_HOME variable to the absolute path of ^|
@echo ^| the directory that contains the hadoop distribution ^|
@echo +================================================================+
exit /b 1
)
if not defined HADOOP_CONF_DIR (
set HADOOP_CONF_DIR=%HADOOP_HOME%\etc\hadoop
)
@rem
@rem Allow alternate conf dir location.
@rem
if "%1" == "--config" (
set HADOOP_CONF_DIR=%2
shift
shift
)
@rem
@rem check to see it is specified whether to use the workers or the
@rem masters file
@rem
if "%1" == "--hosts" (
set HADOOP_WORKERS=%HADOOP_CONF_DIR%\%2
shift
shift
)
@rem
@rem Set log level. Default to INFO.
@rem
if "%1" == "--loglevel" (
set HADOOP_LOGLEVEL=%2
shift
shift
)
if exist %HADOOP_CONF_DIR%\hadoop-env.cmd (
call %HADOOP_CONF_DIR%\hadoop-env.cmd
)
@rem
@rem setup java environment variables
@rem
if not defined JAVA_HOME (
echo Error: JAVA_HOME is not set.
goto :eof
)
if not exist %JAVA_HOME%\bin\java.exe (
echo Error: JAVA_HOME is incorrectly set.
echo Please update %HADOOP_CONF_DIR%\hadoop-env.cmd
goto :eof
)
set JAVA=%JAVA_HOME%\bin\java
@rem some Java parameters
set JAVA_HEAP_MAX=-Xmx1000m
@rem
@rem check envvars which might override default args
@rem
if defined HADOOP_HEAPSIZE (
set JAVA_HEAP_MAX=-Xmx%HADOOP_HEAPSIZE%m
)
@rem
@rem CLASSPATH initially contains %HADOOP_CONF_DIR%
@rem
set CLASSPATH=%HADOOP_CONF_DIR%
if not defined HADOOP_COMMON_HOME (
if exist %HADOOP_HOME%\share\hadoop\common (
set HADOOP_COMMON_HOME=%HADOOP_HOME%
)
)
@rem
@rem for releases, add core hadoop jar & webapps to CLASSPATH
@rem
if exist %HADOOP_COMMON_HOME%\%HADOOP_COMMON_DIR%\webapps (
set CLASSPATH=!CLASSPATH!;%HADOOP_COMMON_HOME%\%HADOOP_COMMON_DIR%
)
if exist %HADOOP_COMMON_HOME%\%HADOOP_COMMON_LIB_JARS_DIR% (
set CLASSPATH=!CLASSPATH!;%HADOOP_COMMON_HOME%\%HADOOP_COMMON_LIB_JARS_DIR%\*
)
set CLASSPATH=!CLASSPATH!;%HADOOP_COMMON_HOME%\%HADOOP_COMMON_DIR%\*
@rem
@rem default log directory % file
@rem
if not defined HADOOP_LOG_DIR (
set HADOOP_LOG_DIR=%HADOOP_HOME%\logs
)
if not defined HADOOP_LOGFILE (
set HADOOP_LOGFILE=hadoop.log
)
if not defined HADOOP_LOGLEVEL (
set HADOOP_LOGLEVEL=INFO
)
if not defined HADOOP_ROOT_LOGGER (
set HADOOP_ROOT_LOGGER=%HADOOP_LOGLEVEL%,console
)
@rem
@rem default policy file for service-level authorization
@rem
if not defined HADOOP_POLICYFILE (
set HADOOP_POLICYFILE=hadoop-policy.xml
)
@rem
@rem Determine the JAVA_PLATFORM
@rem
for /f "delims=" %%A in ('%JAVA% -Xmx32m %HADOOP_JAVA_PLATFORM_OPTS% -classpath "%CLASSPATH%" org.apache.hadoop.util.PlatformName') do set JAVA_PLATFORM=%%A
@rem replace space with underscore
set JAVA_PLATFORM=%JAVA_PLATFORM: =_%
@rem
@rem setup 'java.library.path' for native hadoop code if necessary
@rem
@rem Check if we're running hadoop directly from the build
if exist %HADOOP_COMMON_HOME%\target\bin (
if defined JAVA_LIBRARY_PATH (
set JAVA_LIBRARY_PATH=%JAVA_LIBRARY_PATH%;%HADOOP_COMMON_HOME%\target\bin
) else (
set JAVA_LIBRARY_PATH=%HADOOP_COMMON_HOME%\target\bin
)
)
@rem For the distro case, check the bin folder
if exist %HADOOP_COMMON_HOME%\bin (
if defined JAVA_LIBRARY_PATH (
set JAVA_LIBRARY_PATH=%JAVA_LIBRARY_PATH%;%HADOOP_COMMON_HOME%\bin
) else (
set JAVA_LIBRARY_PATH=%HADOOP_COMMON_HOME%\bin
)
)
@rem
@rem setup a default TOOL_PATH
@rem
set TOOL_PATH=%HADOOP_HOME%\share\hadoop\tools\lib\*
set HADOOP_OPTS=%HADOOP_OPTS% -Dhadoop.log.dir=%HADOOP_LOG_DIR%
set HADOOP_OPTS=%HADOOP_OPTS% -Dhadoop.log.file=%HADOOP_LOGFILE%
set HADOOP_OPTS=%HADOOP_OPTS% -Dhadoop.home.dir=%HADOOP_HOME%
set HADOOP_OPTS=%HADOOP_OPTS% -Dhadoop.id.str=%HADOOP_IDENT_STRING%
set HADOOP_OPTS=%HADOOP_OPTS% -Dhadoop.root.logger=%HADOOP_ROOT_LOGGER%
if defined JAVA_LIBRARY_PATH (
set HADOOP_OPTS=%HADOOP_OPTS% -Djava.library.path=%JAVA_LIBRARY_PATH%
)
set HADOOP_OPTS=%HADOOP_OPTS% -Dhadoop.policy.file=%HADOOP_POLICYFILE%
@rem
@rem Disable ipv6 as it can cause issues
@rem
set HADOOP_OPTS=%HADOOP_OPTS% -Djava.net.preferIPv4Stack=true
@rem
@rem put hdfs in classpath if present
@rem
if not defined HADOOP_HDFS_HOME (
if exist %HADOOP_HOME%\%HDFS_DIR% (
set HADOOP_HDFS_HOME=%HADOOP_HOME%
)
)
if exist %HADOOP_HDFS_HOME%\%HDFS_DIR%\webapps (
set CLASSPATH=!CLASSPATH!;%HADOOP_HDFS_HOME%\%HDFS_DIR%
)
if exist %HADOOP_HDFS_HOME%\%HDFS_LIB_JARS_DIR% (
set CLASSPATH=!CLASSPATH!;%HADOOP_HDFS_HOME%\%HDFS_LIB_JARS_DIR%\*
)
set CLASSPATH=!CLASSPATH!;%HADOOP_HDFS_HOME%\%HDFS_DIR%\*
@rem
@rem put yarn in classpath if present
@rem
if not defined HADOOP_YARN_HOME (
if exist %HADOOP_HOME%\%YARN_DIR% (
set HADOOP_YARN_HOME=%HADOOP_HOME%
)
)
if exist %HADOOP_YARN_HOME%\%YARN_DIR%\webapps (
set CLASSPATH=!CLASSPATH!;%HADOOP_YARN_HOME%\%YARN_DIR%
)
if exist %HADOOP_YARN_HOME%\%YARN_LIB_JARS_DIR% (
set CLASSPATH=!CLASSPATH!;%HADOOP_YARN_HOME%\%YARN_LIB_JARS_DIR%\*
)
set CLASSPATH=!CLASSPATH!;%HADOOP_YARN_HOME%\%YARN_DIR%\*
@rem
@rem put mapred in classpath if present AND different from YARN
@rem
if not defined HADOOP_MAPRED_HOME (
if exist %HADOOP_HOME%\%MAPRED_DIR% (
set HADOOP_MAPRED_HOME=%HADOOP_HOME%
)
)
if not "%HADOOP_MAPRED_HOME%\%MAPRED_DIR%" == "%HADOOP_YARN_HOME%\%YARN_DIR%" (
if exist %HADOOP_MAPRED_HOME%\%MAPRED_DIR%\webapps (
set CLASSPATH=!CLASSPATH!;%HADOOP_MAPRED_HOME%\%MAPRED_DIR%
)
if exist %HADOOP_MAPRED_HOME%\%MAPRED_LIB_JARS_DIR% (
set CLASSPATH=!CLASSPATH!;%HADOOP_MAPRED_HOME%\%MAPRED_LIB_JARS_DIR%\*
)
set CLASSPATH=!CLASSPATH!;%HADOOP_MAPRED_HOME%\%MAPRED_DIR%\*
)
@rem
@rem add user-specified CLASSPATH last
@rem
if defined HADOOP_CLASSPATH (
if not defined HADOOP_USE_CLIENT_CLASSLOADER (
if defined HADOOP_USER_CLASSPATH_FIRST (
set CLASSPATH=%HADOOP_CLASSPATH%;%CLASSPATH%;
) else (
set CLASSPATH=%CLASSPATH%;%HADOOP_CLASSPATH%;
)
)
)
:eof

View File

@ -1,165 +0,0 @@
#!/usr/bin/env bash
#
# 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.
####
# IMPORTANT
####
## The hadoop-config.sh tends to get executed by non-Hadoop scripts.
## Those parts expect this script to parse/manipulate $@. In order
## to maintain backward compatibility, this means a surprising
## lack of functions for bits that would be much better off in
## a function.
##
## In other words, yes, there is some bad things happen here and
## unless we break the rest of the ecosystem, we can't change it. :(
# included in all the hadoop scripts with source command
# should not be executable directly
# also should not be passed any arguments, since we need original $*
#
# after doing more config, caller should also exec finalize
# function to finish last minute/default configs for
# settings that might be different between daemons & interactive
# you must be this high to ride the ride
if [[ -z "${BASH_VERSINFO[0]}" ]] \
|| [[ "${BASH_VERSINFO[0]}" -lt 3 ]] \
|| [[ "${BASH_VERSINFO[0]}" -eq 3 && "${BASH_VERSINFO[1]}" -lt 2 ]]; then
echo "bash v3.2+ is required. Sorry."
exit 1
fi
# In order to get partially bootstrapped, we need to figure out where
# we are located. Chances are good that our caller has already done
# this work for us, but just in case...
if [[ -z "${HADOOP_LIBEXEC_DIR}" ]]; then
_hadoop_common_this="${BASH_SOURCE-$0}"
HADOOP_LIBEXEC_DIR=$(cd -P -- "$(dirname -- "${_hadoop_common_this}")" >/dev/null && pwd -P)
fi
# get our functions defined for usage later
if [[ -n "${HADOOP_COMMON_HOME}" ]] &&
[[ -e "${HADOOP_COMMON_HOME}/libexec/hadoop-functions.sh" ]]; then
# shellcheck source=./hadoop-common-project/hadoop-common/src/main/bin/hadoop-functions.sh
. "${HADOOP_COMMON_HOME}/libexec/hadoop-functions.sh"
elif [[ -e "${HADOOP_LIBEXEC_DIR}/hadoop-functions.sh" ]]; then
# shellcheck source=./hadoop-common-project/hadoop-common/src/main/bin/hadoop-functions.sh
. "${HADOOP_LIBEXEC_DIR}/hadoop-functions.sh"
else
echo "ERROR: Unable to exec ${HADOOP_LIBEXEC_DIR}/hadoop-functions.sh." 1>&2
exit 1
fi
hadoop_deprecate_envvar HADOOP_PREFIX HADOOP_HOME
# allow overrides of the above and pre-defines of the below
if [[ -n "${HADOOP_COMMON_HOME}" ]] &&
[[ -e "${HADOOP_COMMON_HOME}/libexec/hadoop-layout.sh" ]]; then
# shellcheck source=./hadoop-common-project/hadoop-common/src/main/bin/hadoop-layout.sh.example
. "${HADOOP_COMMON_HOME}/libexec/hadoop-layout.sh"
elif [[ -e "${HADOOP_LIBEXEC_DIR}/hadoop-layout.sh" ]]; then
# shellcheck source=./hadoop-common-project/hadoop-common/src/main/bin/hadoop-layout.sh.example
. "${HADOOP_LIBEXEC_DIR}/hadoop-layout.sh"
fi
#
# IMPORTANT! We are not executing user provided code yet!
#
# Let's go! Base definitions so we can move forward
hadoop_bootstrap
# let's find our conf.
#
# first, check and process params passed to us
# we process this in-line so that we can directly modify $@
# if something downstream is processing that directly,
# we need to make sure our params have been ripped out
# note that we do many of them here for various utilities.
# this provides consistency and forces a more consistent
# user experience
# save these off in case our caller needs them
# shellcheck disable=SC2034
HADOOP_USER_PARAMS=("$@")
hadoop_parse_args "$@"
shift "${HADOOP_PARSE_COUNTER}"
#
# Setup the base-line environment
#
hadoop_find_confdir
hadoop_exec_hadoopenv
hadoop_import_shellprofiles
hadoop_exec_userfuncs
#
# IMPORTANT! User provided code is now available!
#
hadoop_exec_user_hadoopenv
hadoop_verify_confdir
hadoop_deprecate_envvar HADOOP_SLAVES HADOOP_WORKERS
hadoop_deprecate_envvar HADOOP_SLAVE_NAMES HADOOP_WORKER_NAMES
hadoop_deprecate_envvar HADOOP_SLAVE_SLEEP HADOOP_WORKER_SLEEP
# do all the OS-specific startup bits here
# this allows us to get a decent JAVA_HOME,
# call crle for LD_LIBRARY_PATH, etc.
hadoop_os_tricks
hadoop_java_setup
hadoop_basic_init
# inject any sub-project overrides, defaults, etc.
if declare -F hadoop_subproject_init >/dev/null ; then
hadoop_subproject_init
fi
hadoop_shellprofiles_init
# get the native libs in there pretty quick
hadoop_add_javalibpath "${HADOOP_HOME}/build/native"
hadoop_add_javalibpath "${HADOOP_HOME}/${HADOOP_COMMON_LIB_NATIVE_DIR}"
hadoop_shellprofiles_nativelib
# get the basic java class path for these subprojects
# in as quickly as possible since other stuff
# will definitely depend upon it.
hadoop_add_common_to_classpath
hadoop_shellprofiles_classpath
# user API commands can now be run since the runtime
# environment has been configured
hadoop_exec_hadooprc
#
# backwards compatibility. new stuff should
# call this when they are ready
#
if [[ -z "${HADOOP_NEW_CONFIG}" ]]; then
hadoop_finalize
fi

View File

@ -1,77 +0,0 @@
#!/usr/bin/env bash
# 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.
# Run a Hadoop command on all slave hosts.
function hadoop_usage
{
echo "Usage: hadoop-daemons.sh [--config confdir] [--hosts hostlistfile] (start|stop|status) <hadoop-command> <args...>"
}
this="${BASH_SOURCE-$0}"
bin=$(cd -P -- "$(dirname -- "${this}")" >/dev/null && pwd -P)
# let's locate libexec...
if [[ -n "${HADOOP_HOME}" ]]; then
HADOOP_DEFAULT_LIBEXEC_DIR="${HADOOP_HOME}/libexec"
else
HADOOP_DEFAULT_LIBEXEC_DIR="${bin}/../libexec"
fi
HADOOP_LIBEXEC_DIR="${HADOOP_LIBEXEC_DIR:-$HADOOP_DEFAULT_LIBEXEC_DIR}"
# shellcheck disable=SC2034
HADOOP_NEW_CONFIG=true
if [[ -f "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh" ]]; then
. "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh"
else
echo "ERROR: Cannot execute ${HADOOP_LIBEXEC_DIR}/hdfs-config.sh." 2>&1
exit 1
fi
if [[ $# = 0 ]]; then
hadoop_exit_with_usage 1
fi
daemonmode=$1
shift
if [[ -z "${HADOOP_HDFS_HOME}" ]]; then
hdfsscript="${HADOOP_HOME}/bin/hdfs"
else
hdfsscript="${HADOOP_HDFS_HOME}/bin/hdfs"
fi
hadoop_error "WARNING: Use of this script to ${daemonmode} HDFS daemons is deprecated."
hadoop_error "WARNING: Attempting to execute replacement \"hdfs --workers --daemon ${daemonmode}\" instead."
#
# Original input was usually:
# hadoop-daemons.sh (shell options) (start|stop) (datanode|...) (daemon options)
# we're going to turn this into
# hdfs --workers --daemon (start|stop) (rest of options)
#
for (( i = 0; i < ${#HADOOP_USER_PARAMS[@]}; i++ ))
do
if [[ "${HADOOP_USER_PARAMS[$i]}" =~ ^start$ ]] ||
[[ "${HADOOP_USER_PARAMS[$i]}" =~ ^stop$ ]] ||
[[ "${HADOOP_USER_PARAMS[$i]}" =~ ^status$ ]]; then
unset HADOOP_USER_PARAMS[$i]
fi
done
${hdfsscript} --workers --daemon "${daemonmode}" "${HADOOP_USER_PARAMS[@]}"

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +0,0 @@
#!/usr/bin/env bash
# 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.
# Run a shell command on all worker hosts.
#
# Environment Variables
#
# HADOOP_WORKERS File naming remote hosts.
# Default is ${HADOOP_CONF_DIR}/workers.
# HADOOP_CONF_DIR Alternate conf dir. Default is ${HADOOP_HOME}/conf.
# HADOOP_WORKER_SLEEP Seconds to sleep between spawning remote commands.
# HADOOP_SSH_OPTS Options passed to ssh when running remote commands.
##
function hadoop_usage
{
echo "Usage: workers.sh [--config confdir] command..."
}
# let's locate libexec...
if [[ -n "${HADOOP_HOME}" ]]; then
HADOOP_DEFAULT_LIBEXEC_DIR="${HADOOP_HOME}/libexec"
else
this="${BASH_SOURCE-$0}"
bin=$(cd -P -- "$(dirname -- "${this}")" >/dev/null && pwd -P)
HADOOP_DEFAULT_LIBEXEC_DIR="${bin}/../libexec"
fi
HADOOP_LIBEXEC_DIR="${HADOOP_LIBEXEC_DIR:-$HADOOP_DEFAULT_LIBEXEC_DIR}"
# shellcheck disable=SC2034
HADOOP_NEW_CONFIG=true
if [[ -f "${HADOOP_LIBEXEC_DIR}/hadoop-config.sh" ]]; then
. "${HADOOP_LIBEXEC_DIR}/hadoop-config.sh"
else
echo "ERROR: Cannot execute ${HADOOP_LIBEXEC_DIR}/hadoop-config.sh." 2>&1
exit 1
fi
# if no args specified, show usage
if [[ $# -le 0 ]]; then
hadoop_exit_with_usage 1
fi
hadoop_connect_to_hosts "$@"

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed 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. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
</configuration>

View File

@ -1,90 +0,0 @@
@echo off
@rem Licensed to the Apache Software Foundation (ASF) under one or more
@rem contributor license agreements. See the NOTICE file distributed with
@rem this work for additional information regarding copyright ownership.
@rem The ASF licenses this file to You under the Apache License, Version 2.0
@rem (the "License"); you may not use this file except in compliance with
@rem the License. You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem Set Hadoop-specific environment variables here.
@rem The only required environment variable is JAVA_HOME. All others are
@rem optional. When running a distributed configuration it is best to
@rem set JAVA_HOME in this file, so that it is correctly defined on
@rem remote nodes.
@rem The java implementation to use. Required.
set JAVA_HOME=%JAVA_HOME%
@rem The jsvc implementation to use. Jsvc is required to run secure datanodes.
@rem set JSVC_HOME=%JSVC_HOME%
@rem set HADOOP_CONF_DIR=
@rem Extra Java CLASSPATH elements. Automatically insert capacity-scheduler.
if exist %HADOOP_HOME%\contrib\capacity-scheduler (
if not defined HADOOP_CLASSPATH (
set HADOOP_CLASSPATH=%HADOOP_HOME%\contrib\capacity-scheduler\*.jar
) else (
set HADOOP_CLASSPATH=%HADOOP_CLASSPATH%;%HADOOP_HOME%\contrib\capacity-scheduler\*.jar
)
)
@rem The maximum amount of heap to use, in MB. Default is 1000.
@rem set HADOOP_HEAPSIZE=
@rem set HADOOP_NAMENODE_INIT_HEAPSIZE=""
@rem Extra Java runtime options. Empty by default.
@rem set HADOOP_OPTS=%HADOOP_OPTS% -Djava.net.preferIPv4Stack=true
@rem Command specific options appended to HADOOP_OPTS when specified
if not defined HADOOP_SECURITY_LOGGER (
set HADOOP_SECURITY_LOGGER=INFO,RFAS
)
if not defined HDFS_AUDIT_LOGGER (
set HDFS_AUDIT_LOGGER=INFO,NullAppender
)
set HADOOP_NAMENODE_OPTS=-Dhadoop.security.logger=%HADOOP_SECURITY_LOGGER% -Dhdfs.audit.logger=%HDFS_AUDIT_LOGGER% %HADOOP_NAMENODE_OPTS%
set HADOOP_DATANODE_OPTS=-Dhadoop.security.logger=ERROR,RFAS %HADOOP_DATANODE_OPTS%
set HADOOP_SECONDARYNAMENODE_OPTS=-Dhadoop.security.logger=%HADOOP_SECURITY_LOGGER% -Dhdfs.audit.logger=%HDFS_AUDIT_LOGGER% %HADOOP_SECONDARYNAMENODE_OPTS%
@rem The following applies to multiple commands (fs, dfs, fsck, distcp etc)
set HADOOP_CLIENT_OPTS=-Xmx512m %HADOOP_CLIENT_OPTS%
@rem set HADOOP_JAVA_PLATFORM_OPTS="-XX:-UsePerfData %HADOOP_JAVA_PLATFORM_OPTS%"
@rem On secure datanodes, user to run the datanode as after dropping privileges
set HADOOP_SECURE_DN_USER=%HADOOP_SECURE_DN_USER%
@rem Where log files are stored. %HADOOP_HOME%/logs by default.
@rem set HADOOP_LOG_DIR=%HADOOP_LOG_DIR%\%USERNAME%
@rem Where log files are stored in the secure data environment.
set HADOOP_SECURE_DN_LOG_DIR=%HADOOP_LOG_DIR%\%HADOOP_HDFS_USER%
@rem
@rem Router-based HDFS Federation specific parameters
@rem Specify the JVM options to be used when starting the RBF Routers.
@rem These options will be appended to the options specified as HADOOP_OPTS
@rem and therefore may override any similar flags set in HADOOP_OPTS
@rem
@rem set HADOOP_DFSROUTER_OPTS=""
@rem
@rem The directory where pid files are stored. /tmp by default.
@rem NOTE: this should be set to a directory that can only be written to by
@rem the user that will run the hadoop daemons. Otherwise there is the
@rem potential for a symlink attack.
set HADOOP_PID_DIR=%HADOOP_PID_DIR%
set HADOOP_SECURE_DN_PID_DIR=%HADOOP_PID_DIR%
@rem A string representing this instance of hadoop. %USERNAME% by default.
set HADOOP_IDENT_STRING=%USERNAME%

View File

@ -1,439 +0,0 @@
#
# 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.
# Set Hadoop-specific environment variables here.
##
## THIS FILE ACTS AS THE MASTER FILE FOR ALL HADOOP PROJECTS.
## SETTINGS HERE WILL BE READ BY ALL HADOOP COMMANDS. THEREFORE,
## ONE CAN USE THIS FILE TO SET YARN, HDFS, AND MAPREDUCE
## CONFIGURATION OPTIONS INSTEAD OF xxx-env.sh.
##
## Precedence rules:
##
## {yarn-env.sh|hdfs-env.sh} > hadoop-env.sh > hard-coded defaults
##
## {YARN_xyz|HDFS_xyz} > HADOOP_xyz > hard-coded defaults
##
# Many of the options here are built from the perspective that users
# may want to provide OVERWRITING values on the command line.
# For example:
#
# JAVA_HOME=/usr/java/testing hdfs dfs -ls
#
# Therefore, the vast majority (BUT NOT ALL!) of these defaults
# are configured for substitution and not append. If append
# is preferable, modify this file accordingly.
###
# Generic settings for HADOOP
###
# Technically, the only required environment variable is JAVA_HOME.
# All others are optional. However, the defaults are probably not
# preferred. Many sites configure these options outside of Hadoop,
# such as in /etc/profile.d
# The java implementation to use. By default, this environment
# variable is REQUIRED on ALL platforms except OS X!
# export JAVA_HOME=
# Location of Hadoop. By default, Hadoop will attempt to determine
# this location based upon its execution path.
# export HADOOP_HOME=
# Location of Hadoop's configuration information. i.e., where this
# file is living. If this is not defined, Hadoop will attempt to
# locate it based upon its execution path.
#
# NOTE: It is recommend that this variable not be set here but in
# /etc/profile.d or equivalent. Some options (such as
# --config) may react strangely otherwise.
#
# export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop
# The maximum amount of heap to use (Java -Xmx). If no unit
# is provided, it will be converted to MB. Daemons will
# prefer any Xmx setting in their respective _OPT variable.
# There is no default; the JVM will autoscale based upon machine
# memory size.
# export HADOOP_HEAPSIZE_MAX=
# The minimum amount of heap to use (Java -Xms). If no unit
# is provided, it will be converted to MB. Daemons will
# prefer any Xms setting in their respective _OPT variable.
# There is no default; the JVM will autoscale based upon machine
# memory size.
# export HADOOP_HEAPSIZE_MIN=
# Enable extra debugging of Hadoop's JAAS binding, used to set up
# Kerberos security.
# export HADOOP_JAAS_DEBUG=true
# Extra Java runtime options for all Hadoop commands. We don't support
# IPv6 yet/still, so by default the preference is set to IPv4.
# export HADOOP_OPTS="-Djava.net.preferIPv4Stack=true"
# For Kerberos debugging, an extended option set logs more information
# export HADOOP_OPTS="-Djava.net.preferIPv4Stack=true -Dsun.security.krb5.debug=true -Dsun.security.spnego.debug"
# Some parts of the shell code may do special things dependent upon
# the operating system. We have to set this here. See the next
# section as to why....
export HADOOP_OS_TYPE=${HADOOP_OS_TYPE:-$(uname -s)}
# Extra Java runtime options for some Hadoop commands
# and clients (i.e., hdfs dfs -blah). These get appended to HADOOP_OPTS for
# such commands. In most cases, # this should be left empty and
# let users supply it on the command line.
# export HADOOP_CLIENT_OPTS=""
#
# A note about classpaths.
#
# By default, Apache Hadoop overrides Java's CLASSPATH
# environment variable. It is configured such
# that it starts out blank with new entries added after passing
# a series of checks (file/dir exists, not already listed aka
# de-deduplication). During de-deduplication, wildcards and/or
# directories are *NOT* expanded to keep it simple. Therefore,
# if the computed classpath has two specific mentions of
# awesome-methods-1.0.jar, only the first one added will be seen.
# If two directories are in the classpath that both contain
# awesome-methods-1.0.jar, then Java will pick up both versions.
# An additional, custom CLASSPATH. Site-wide configs should be
# handled via the shellprofile functionality, utilizing the
# hadoop_add_classpath function for greater control and much
# harder for apps/end-users to accidentally override.
# Similarly, end users should utilize ${HOME}/.hadooprc .
# This variable should ideally only be used as a short-cut,
# interactive way for temporary additions on the command line.
# export HADOOP_CLASSPATH="/some/cool/path/on/your/machine"
# Should HADOOP_CLASSPATH be first in the official CLASSPATH?
# export HADOOP_USER_CLASSPATH_FIRST="yes"
# If HADOOP_USE_CLIENT_CLASSLOADER is set, the classpath along
# with the main jar are handled by a separate isolated
# client classloader when 'hadoop jar', 'yarn jar', or 'mapred job'
# is utilized. If it is set, HADOOP_CLASSPATH and
# HADOOP_USER_CLASSPATH_FIRST are ignored.
# export HADOOP_USE_CLIENT_CLASSLOADER=true
# HADOOP_CLIENT_CLASSLOADER_SYSTEM_CLASSES overrides the default definition of
# system classes for the client classloader when HADOOP_USE_CLIENT_CLASSLOADER
# is enabled. Names ending in '.' (period) are treated as package names, and
# names starting with a '-' are treated as negative matches. For example,
# export HADOOP_CLIENT_CLASSLOADER_SYSTEM_CLASSES="-org.apache.hadoop.UserClass,java.,javax.,org.apache.hadoop."
# Enable optional, bundled Hadoop features
# This is a comma delimited list. It may NOT be overridden via .hadooprc
# Entries may be added/removed as needed.
# export HADOOP_OPTIONAL_TOOLS="@@@HADOOP_OPTIONAL_TOOLS@@@"
###
# Options for remote shell connectivity
###
# There are some optional components of hadoop that allow for
# command and control of remote hosts. For example,
# start-dfs.sh will attempt to bring up all NNs, DNS, etc.
# Options to pass to SSH when one of the "log into a host and
# start/stop daemons" scripts is executed
# export HADOOP_SSH_OPTS="-o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=10s"
# The built-in ssh handler will limit itself to 10 simultaneous connections.
# For pdsh users, this sets the fanout size ( -f )
# Change this to increase/decrease as necessary.
# export HADOOP_SSH_PARALLEL=10
# Filename which contains all of the hosts for any remote execution
# helper scripts # such as workers.sh, start-dfs.sh, etc.
# export HADOOP_WORKERS="${HADOOP_CONF_DIR}/workers"
###
# Options for all daemons
###
#
#
# Many options may also be specified as Java properties. It is
# very common, and in many cases, desirable, to hard-set these
# in daemon _OPTS variables. Where applicable, the appropriate
# Java property is also identified. Note that many are re-used
# or set differently in certain contexts (e.g., secure vs
# non-secure)
#
# Where (primarily) daemon log files are stored.
# ${HADOOP_HOME}/logs by default.
# Java property: hadoop.log.dir
# export HADOOP_LOG_DIR=${HADOOP_HOME}/logs
# A string representing this instance of hadoop. $USER by default.
# This is used in writing log and pid files, so keep that in mind!
# Java property: hadoop.id.str
# export HADOOP_IDENT_STRING=$USER
# How many seconds to pause after stopping a daemon
# export HADOOP_STOP_TIMEOUT=5
# Where pid files are stored. /tmp by default.
# export HADOOP_PID_DIR=/tmp
# Default log4j setting for interactive commands
# Java property: hadoop.root.logger
# export HADOOP_ROOT_LOGGER=INFO,console
# Default log4j setting for daemons spawned explicitly by
# --daemon option of hadoop, hdfs, mapred and yarn command.
# Java property: hadoop.root.logger
# export HADOOP_DAEMON_ROOT_LOGGER=INFO,RFA
# Default log level and output location for security-related messages.
# You will almost certainly want to change this on a per-daemon basis via
# the Java property (i.e., -Dhadoop.security.logger=foo). (Note that the
# defaults for the NN and 2NN override this by default.)
# Java property: hadoop.security.logger
# export HADOOP_SECURITY_LOGGER=INFO,NullAppender
# Default process priority level
# Note that sub-processes will also run at this level!
# export HADOOP_NICENESS=0
# Default name for the service level authorization file
# Java property: hadoop.policy.file
# export HADOOP_POLICYFILE="hadoop-policy.xml"
#
# NOTE: this is not used by default! <-----
# You can define variables right here and then re-use them later on.
# For example, it is common to use the same garbage collection settings
# for all the daemons. So one could define:
#
# export HADOOP_GC_SETTINGS="-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps"
#
# .. and then use it as per the b option under the namenode.
###
# Secure/privileged execution
###
#
# Out of the box, Hadoop uses jsvc from Apache Commons to launch daemons
# on privileged ports. This functionality can be replaced by providing
# custom functions. See hadoop-functions.sh for more information.
#
# The jsvc implementation to use. Jsvc is required to run secure datanodes
# that bind to privileged ports to provide authentication of data transfer
# protocol. Jsvc is not required if SASL is configured for authentication of
# data transfer protocol using non-privileged ports.
# export JSVC_HOME=/usr/bin
#
# This directory contains pids for secure and privileged processes.
#export HADOOP_SECURE_PID_DIR=${HADOOP_PID_DIR}
#
# This directory contains the logs for secure and privileged processes.
# Java property: hadoop.log.dir
# export HADOOP_SECURE_LOG=${HADOOP_LOG_DIR}
#
# When running a secure daemon, the default value of HADOOP_IDENT_STRING
# ends up being a bit bogus. Therefore, by default, the code will
# replace HADOOP_IDENT_STRING with HADOOP_xx_SECURE_USER. If one wants
# to keep HADOOP_IDENT_STRING untouched, then uncomment this line.
# export HADOOP_SECURE_IDENT_PRESERVE="true"
###
# NameNode specific parameters
###
# Default log level and output location for file system related change
# messages. For non-namenode daemons, the Java property must be set in
# the appropriate _OPTS if one wants something other than INFO,NullAppender
# Java property: hdfs.audit.logger
# export HDFS_AUDIT_LOGGER=INFO,NullAppender
# Specify the JVM options to be used when starting the NameNode.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# a) Set JMX options
# export HDFS_NAMENODE_OPTS="-Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1026"
#
# b) Set garbage collection logs
# export HDFS_NAMENODE_OPTS="${HADOOP_GC_SETTINGS} -Xloggc:${HADOOP_LOG_DIR}/gc-rm.log-$(date +'%Y%m%d%H%M')"
#
# c) ... or set them directly
# export HDFS_NAMENODE_OPTS="-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:${HADOOP_LOG_DIR}/gc-rm.log-$(date +'%Y%m%d%H%M')"
# this is the default:
# export HDFS_NAMENODE_OPTS="-Dhadoop.security.logger=INFO,RFAS"
###
# SecondaryNameNode specific parameters
###
# Specify the JVM options to be used when starting the SecondaryNameNode.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# This is the default:
# export HDFS_SECONDARYNAMENODE_OPTS="-Dhadoop.security.logger=INFO,RFAS"
###
# DataNode specific parameters
###
# Specify the JVM options to be used when starting the DataNode.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# This is the default:
# export HDFS_DATANODE_OPTS="-Dhadoop.security.logger=ERROR,RFAS"
# On secure datanodes, user to run the datanode as after dropping privileges.
# This **MUST** be uncommented to enable secure HDFS if using privileged ports
# to provide authentication of data transfer protocol. This **MUST NOT** be
# defined if SASL is configured for authentication of data transfer protocol
# using non-privileged ports.
# This will replace the hadoop.id.str Java property in secure mode.
# export HDFS_DATANODE_SECURE_USER=hdfs
# Supplemental options for secure datanodes
# By default, Hadoop uses jsvc which needs to know to launch a
# server jvm.
# export HDFS_DATANODE_SECURE_EXTRA_OPTS="-jvm server"
###
# NFS3 Gateway specific parameters
###
# Specify the JVM options to be used when starting the NFS3 Gateway.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_NFS3_OPTS=""
# Specify the JVM options to be used when starting the Hadoop portmapper.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_PORTMAP_OPTS="-Xmx512m"
# Supplemental options for priviliged gateways
# By default, Hadoop uses jsvc which needs to know to launch a
# server jvm.
# export HDFS_NFS3_SECURE_EXTRA_OPTS="-jvm server"
# On privileged gateways, user to run the gateway as after dropping privileges
# This will replace the hadoop.id.str Java property in secure mode.
# export HDFS_NFS3_SECURE_USER=nfsserver
###
# ZKFailoverController specific parameters
###
# Specify the JVM options to be used when starting the ZKFailoverController.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_ZKFC_OPTS=""
###
# QuorumJournalNode specific parameters
###
# Specify the JVM options to be used when starting the QuorumJournalNode.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_JOURNALNODE_OPTS=""
###
# HDFS Balancer specific parameters
###
# Specify the JVM options to be used when starting the HDFS Balancer.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_BALANCER_OPTS=""
###
# HDFS Mover specific parameters
###
# Specify the JVM options to be used when starting the HDFS Mover.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_MOVER_OPTS=""
###
# Router-based HDFS Federation specific parameters
# Specify the JVM options to be used when starting the RBF Routers.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_DFSROUTER_OPTS=""
###
# Ozone Manager specific parameters
###
# Specify the JVM options to be used when starting the Ozone Manager.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_OM_OPTS=""
###
# HDFS StorageContainerManager specific parameters
###
# Specify the JVM options to be used when starting the HDFS Storage Container Manager.
# These options will be appended to the options specified as HADOOP_OPTS
# and therefore may override any similar flags set in HADOOP_OPTS
#
# export HDFS_STORAGECONTAINERMANAGER_OPTS=""
###
# Advanced Users Only!
###
#
# When building Hadoop, one can add the class paths to the commands
# via this special env var:
# export HADOOP_ENABLE_BUILD_PATHS="true"
#
# To prevent accidents, shell commands be (superficially) locked
# to only allow certain users to execute certain subcommands.
# It uses the format of (command)_(subcommand)_USER.
#
# For example, to limit who can execute the namenode command,
# export HDFS_NAMENODE_USER=hdfs
###
# Registry DNS specific parameters
###
# For privileged registry DNS, user to run as after dropping privileges
# This will replace the hadoop.id.str Java property in secure mode.
# export HADOOP_REGISTRYDNS_SECURE_USER=yarn
# Supplemental options for privileged registry DNS
# By default, Hadoop uses jsvc which needs to know to launch a
# server jvm.
# export HADOOP_REGISTRYDNS_SECURE_EXTRA_OPTS="-jvm server"

View File

@ -1,99 +0,0 @@
# 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.
# syntax: [prefix].[source|sink].[instance].[options]
# See javadoc of package-info.java for org.apache.hadoop.metrics2 for details
*.sink.file.class=org.apache.hadoop.metrics2.sink.FileSink
# default sampling period, in seconds
*.period=10
# The namenode-metrics.out will contain metrics from all context
#namenode.sink.file.filename=namenode-metrics.out
# Specifying a special sampling period for namenode:
#namenode.sink.*.period=8
#datanode.sink.file.filename=datanode-metrics.out
#resourcemanager.sink.file.filename=resourcemanager-metrics.out
#nodemanager.sink.file.filename=nodemanager-metrics.out
#mrappmaster.sink.file.filename=mrappmaster-metrics.out
#jobhistoryserver.sink.file.filename=jobhistoryserver-metrics.out
# the following example split metrics of different
# context to different sinks (in this case files)
#nodemanager.sink.file_jvm.class=org.apache.hadoop.metrics2.sink.FileSink
#nodemanager.sink.file_jvm.context=jvm
#nodemanager.sink.file_jvm.filename=nodemanager-jvm-metrics.out
#nodemanager.sink.file_mapred.class=org.apache.hadoop.metrics2.sink.FileSink
#nodemanager.sink.file_mapred.context=mapred
#nodemanager.sink.file_mapred.filename=nodemanager-mapred-metrics.out
#
# Below are for sending metrics to Ganglia
#
# for Ganglia 3.0 support
# *.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink30
#
# for Ganglia 3.1 support
# *.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31
# *.sink.ganglia.period=10
# default for supportsparse is false
# *.sink.ganglia.supportsparse=true
#*.sink.ganglia.slope=jvm.metrics.gcCount=zero,jvm.metrics.memHeapUsedM=both
#*.sink.ganglia.dmax=jvm.metrics.threadsBlocked=70,jvm.metrics.memHeapUsedM=40
# Tag values to use for the ganglia prefix. If not defined no tags are used.
# If '*' all tags are used. If specifying multiple tags separate them with
# commas. Note that the last segment of the property name is the context name.
#
# A typical use of tags is separating the metrics by the HDFS rpc port
# and HDFS service rpc port.
# For example:
# With following HDFS configuration:
# dfs.namenode.rpc-address is set as namenodeAddress:9110
# dfs.namenode.servicerpc-address is set as namenodeAddress:9111
# If no tags are used, following metric would be gathered:
# rpc.rpc.NumOpenConnections
# If using "*.sink.ganglia.tagsForPrefix.rpc=port",
# following metrics would be gathered:
# rpc.rpc.port=9110.NumOpenConnections
# rpc.rpc.port=9111.NumOpenConnections
#
#*.sink.ganglia.tagsForPrefix.jvm=ProcessName
#*.sink.ganglia.tagsForPrefix.dfs=HAState,IsOutOfSync
#*.sink.ganglia.tagsForPrefix.rpc=port
#*.sink.ganglia.tagsForPrefix.rpcdetailed=port
#*.sink.ganglia.tagsForPrefix.metricssystem=*
#*.sink.ganglia.tagsForPrefix.ugi=*
#*.sink.ganglia.tagsForPrefix.mapred=
#namenode.sink.ganglia.servers=yourgangliahost_1:8649,yourgangliahost_2:8649
#datanode.sink.ganglia.servers=yourgangliahost_1:8649,yourgangliahost_2:8649
#resourcemanager.sink.ganglia.servers=yourgangliahost_1:8649,yourgangliahost_2:8649
#nodemanager.sink.ganglia.servers=yourgangliahost_1:8649,yourgangliahost_2:8649
#mrappmaster.sink.ganglia.servers=yourgangliahost_1:8649,yourgangliahost_2:8649
#jobhistoryserver.sink.ganglia.servers=yourgangliahost_1:8649,yourgangliahost_2:8649

View File

@ -1,275 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
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.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>security.client.protocol.acl</name>
<value>*</value>
<description>ACL for ClientProtocol, which is used by user code
via the DistributedFileSystem.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.client.datanode.protocol.acl</name>
<value>*</value>
<description>ACL for ClientDatanodeProtocol, the client-to-datanode protocol
for block recovery.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.datanode.protocol.acl</name>
<value>*</value>
<description>ACL for DatanodeProtocol, which is used by datanodes to
communicate with the namenode.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.inter.datanode.protocol.acl</name>
<value>*</value>
<description>ACL for InterDatanodeProtocol, the inter-datanode protocol
for updating generation timestamp.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.namenode.protocol.acl</name>
<value>*</value>
<description>ACL for NamenodeProtocol, the protocol used by the secondary
namenode to communicate with the namenode.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.admin.operations.protocol.acl</name>
<value>*</value>
<description>ACL for AdminOperationsProtocol. Used for admin commands.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.refresh.user.mappings.protocol.acl</name>
<value>*</value>
<description>ACL for RefreshUserMappingsProtocol. Used to refresh
users mappings. The ACL is a comma-separated list of user and
group names. The user and group list is separated by a blank. For
e.g. "alice,bob users,wheel". A special value of "*" means all
users are allowed.</description>
</property>
<property>
<name>security.refresh.policy.protocol.acl</name>
<value>*</value>
<description>ACL for RefreshAuthorizationPolicyProtocol, used by the
dfsadmin and mradmin commands to refresh the security policy in-effect.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.ha.service.protocol.acl</name>
<value>*</value>
<description>ACL for HAService protocol used by HAAdmin to manage the
active and stand-by states of namenode.</description>
</property>
<property>
<name>security.router.admin.protocol.acl</name>
<value>*</value>
<description>ACL for RouterAdmin Protocol. The ACL is a comma-separated
list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.
</description>
</property>
<property>
<name>security.zkfc.protocol.acl</name>
<value>*</value>
<description>ACL for access to the ZK Failover Controller
</description>
</property>
<property>
<name>security.qjournal.service.protocol.acl</name>
<value>*</value>
<description>ACL for QJournalProtocol, used by the NN to communicate with
JNs when using the QuorumJournalManager for edit logs.</description>
</property>
<property>
<name>security.interqjournal.service.protocol.acl</name>
<value>*</value>
<description>ACL for InterQJournalProtocol, used by the JN to
communicate with other JN
</description>
</property>
<property>
<name>security.mrhs.client.protocol.acl</name>
<value>*</value>
<description>ACL for HSClientProtocol, used by job clients to
communciate with the MR History Server job status etc.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<!-- YARN Protocols -->
<property>
<name>security.resourcetracker.protocol.acl</name>
<value>*</value>
<description>ACL for ResourceTrackerProtocol, used by the
ResourceManager and NodeManager to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.resourcemanager-administration.protocol.acl</name>
<value>*</value>
<description>ACL for ResourceManagerAdministrationProtocol, for admin commands.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.applicationclient.protocol.acl</name>
<value>*</value>
<description>ACL for ApplicationClientProtocol, used by the ResourceManager
and applications submission clients to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.applicationmaster.protocol.acl</name>
<value>*</value>
<description>ACL for ApplicationMasterProtocol, used by the ResourceManager
and ApplicationMasters to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.containermanagement.protocol.acl</name>
<value>*</value>
<description>ACL for ContainerManagementProtocol protocol, used by the NodeManager
and ApplicationMasters to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.resourcelocalizer.protocol.acl</name>
<value>*</value>
<description>ACL for ResourceLocalizer protocol, used by the NodeManager
and ResourceLocalizer to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.job.task.protocol.acl</name>
<value>*</value>
<description>ACL for TaskUmbilicalProtocol, used by the map and reduce
tasks to communicate with the parent tasktracker.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.job.client.protocol.acl</name>
<value>*</value>
<description>ACL for MRClientProtocol, used by job clients to
communciate with the MR ApplicationMaster to query job status etc.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.applicationhistory.protocol.acl</name>
<value>*</value>
<description>ACL for ApplicationHistoryProtocol, used by the timeline
server and the generic history service client to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.collector-nodemanager.protocol.acl</name>
<value>*</value>
<description>ACL for CollectorNodemanagerProtocol, used by nodemanager
if timeline service v2 is enabled, for the timeline collector and nodemanager
to communicate with each other.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.applicationmaster-nodemanager.applicationmaster.protocol.acl</name>
<value>*</value>
<description>ACL for ApplicationMasterProtocol, used by the Nodemanager
and ApplicationMasters to communicate.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
<property>
<name>security.distributedscheduling.protocol.acl</name>
<value>*</value>
<description>ACL for DistributedSchedulingAMProtocol, used by the Nodemanager
and Resourcemanager to communicate.
The ACL is a comma-separated list of user and group names. The user and
group list is separated by a blank. For e.g. "alice,bob users,wheel".
A special value of "*" means all users are allowed.</description>
</property>
</configuration>

View File

@ -1,252 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds;
import org.apache.hadoop.hdds.utils.db.DBProfile;
/**
* This class contains constants for configuration keys and default values
* used in hdds.
*/
public final class HddsConfigKeys {
public static final String HDDS_HEARTBEAT_INTERVAL =
"hdds.heartbeat.interval";
public static final String HDDS_HEARTBEAT_INTERVAL_DEFAULT =
"30s";
public static final String HDDS_NODE_REPORT_INTERVAL =
"hdds.node.report.interval";
public static final String HDDS_NODE_REPORT_INTERVAL_DEFAULT =
"60s";
public static final String HDDS_CONTAINER_REPORT_INTERVAL =
"hdds.container.report.interval";
public static final String HDDS_CONTAINER_REPORT_INTERVAL_DEFAULT =
"60s";
public static final String HDDS_PIPELINE_REPORT_INTERVAL =
"hdds.pipeline.report.interval";
public static final String HDDS_PIPELINE_REPORT_INTERVAL_DEFAULT =
"60s";
public static final String HDDS_COMMAND_STATUS_REPORT_INTERVAL =
"hdds.command.status.report.interval";
public static final String HDDS_COMMAND_STATUS_REPORT_INTERVAL_DEFAULT =
"60s";
public static final String HDDS_CONTAINER_ACTION_MAX_LIMIT =
"hdds.container.action.max.limit";
public static final int HDDS_CONTAINER_ACTION_MAX_LIMIT_DEFAULT =
20;
public static final String HDDS_PIPELINE_ACTION_MAX_LIMIT =
"hdds.pipeline.action.max.limit";
public static final int HDDS_PIPELINE_ACTION_MAX_LIMIT_DEFAULT =
20;
// Configuration to allow volume choosing policy.
public static final String HDDS_DATANODE_VOLUME_CHOOSING_POLICY =
"hdds.datanode.volume.choosing.policy";
// DB PKIProfile used by ROCKDB instances.
public static final String HDDS_DB_PROFILE = "hdds.db.profile";
public static final DBProfile HDDS_DEFAULT_DB_PROFILE = DBProfile.DISK;
// Once a container usage crosses this threshold, it is eligible for
// closing.
public static final String HDDS_CONTAINER_CLOSE_THRESHOLD =
"hdds.container.close.threshold";
public static final float HDDS_CONTAINER_CLOSE_THRESHOLD_DEFAULT = 0.9f;
public static final String HDDS_SCM_SAFEMODE_ENABLED =
"hdds.scm.safemode.enabled";
public static final boolean HDDS_SCM_SAFEMODE_ENABLED_DEFAULT = true;
public static final String HDDS_SCM_SAFEMODE_MIN_DATANODE =
"hdds.scm.safemode.min.datanode";
public static final int HDDS_SCM_SAFEMODE_MIN_DATANODE_DEFAULT = 1;
public static final String
HDDS_SCM_WAIT_TIME_AFTER_SAFE_MODE_EXIT =
"hdds.scm.wait.time.after.safemode.exit";
public static final String
HDDS_SCM_WAIT_TIME_AFTER_SAFE_MODE_EXIT_DEFAULT = "5m";
public static final String HDDS_SCM_SAFEMODE_PIPELINE_AVAILABILITY_CHECK =
"hdds.scm.safemode.pipeline-availability.check";
public static final boolean
HDDS_SCM_SAFEMODE_PIPELINE_AVAILABILITY_CHECK_DEFAULT = false;
// % of containers which should have at least one reported replica
// before SCM comes out of safe mode.
public static final String HDDS_SCM_SAFEMODE_THRESHOLD_PCT =
"hdds.scm.safemode.threshold.pct";
public static final double HDDS_SCM_SAFEMODE_THRESHOLD_PCT_DEFAULT = 0.99;
// percentage of healthy pipelines, where all 3 datanodes are reported in the
// pipeline.
public static final String HDDS_SCM_SAFEMODE_HEALTHY_PIPELINE_THRESHOLD_PCT =
"hdds.scm.safemode.healthy.pipelie.pct";
public static final double
HDDS_SCM_SAFEMODE_HEALTHY_PIPELINE_THRESHOLD_PCT_DEFAULT = 0.10;
public static final String HDDS_SCM_SAFEMODE_ONE_NODE_REPORTED_PIPELINE_PCT =
"hdds.scm.safemode.atleast.one.node.reported.pipeline.pct";
public static final double
HDDS_SCM_SAFEMODE_ONE_NODE_REPORTED_PIPELINE_PCT_DEFAULT = 0.90;
public static final String HDDS_LOCK_MAX_CONCURRENCY =
"hdds.lock.max.concurrency";
public static final int HDDS_LOCK_MAX_CONCURRENCY_DEFAULT = 100;
// This configuration setting is used as a fallback location by all
// Ozone/HDDS services for their metadata. It is useful as a single
// config point for test/PoC clusters.
//
// In any real cluster where performance matters, the SCM, OM and DN
// metadata locations must be configured explicitly.
public static final String OZONE_METADATA_DIRS = "ozone.metadata.dirs";
public static final String HDDS_PROMETHEUS_ENABLED =
"hdds.prometheus.endpoint.enabled";
public static final String HDDS_PROFILER_ENABLED =
"hdds.profiler.endpoint.enabled";
public static final String HDDS_KEY_LEN = "hdds.key.len";
public static final int HDDS_DEFAULT_KEY_LEN = 2048;
public static final String HDDS_KEY_ALGORITHM = "hdds.key.algo";
public static final String HDDS_DEFAULT_KEY_ALGORITHM = "RSA";
public static final String HDDS_SECURITY_PROVIDER = "hdds.security.provider";
public static final String HDDS_DEFAULT_SECURITY_PROVIDER = "BC";
public static final String HDDS_KEY_DIR_NAME = "hdds.key.dir.name";
public static final String HDDS_KEY_DIR_NAME_DEFAULT = "keys";
// TODO : Talk to StorageIO classes and see if they can return a secure
// storage location for each node.
public static final String HDDS_METADATA_DIR_NAME = "hdds.metadata.dir";
public static final String HDDS_PRIVATE_KEY_FILE_NAME =
"hdds.priv.key.file.name";
public static final String HDDS_PRIVATE_KEY_FILE_NAME_DEFAULT = "private.pem";
public static final String HDDS_PUBLIC_KEY_FILE_NAME = "hdds.public.key.file"
+ ".name";
public static final String HDDS_PUBLIC_KEY_FILE_NAME_DEFAULT = "public.pem";
public static final String HDDS_BLOCK_TOKEN_EXPIRY_TIME =
"hdds.block.token.expiry.time";
public static final String HDDS_BLOCK_TOKEN_EXPIRY_TIME_DEFAULT = "1d";
/**
* Maximum duration of certificates issued by SCM including Self-Signed Roots.
* The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS
* Default value is 5 years and written as P1865D.
*/
public static final String HDDS_X509_MAX_DURATION = "hdds.x509.max.duration";
// Limit Certificate duration to a max value of 5 years.
public static final String HDDS_X509_MAX_DURATION_DEFAULT= "P1865D";
public static final String HDDS_X509_SIGNATURE_ALGO =
"hdds.x509.signature.algorithm";
public static final String HDDS_X509_SIGNATURE_ALGO_DEFAULT = "SHA256withRSA";
public static final String HDDS_BLOCK_TOKEN_ENABLED =
"hdds.block.token.enabled";
public static final boolean HDDS_BLOCK_TOKEN_ENABLED_DEFAULT = false;
public static final String HDDS_X509_DIR_NAME = "hdds.x509.dir.name";
public static final String HDDS_X509_DIR_NAME_DEFAULT = "certs";
public static final String HDDS_X509_FILE_NAME = "hdds.x509.file.name";
public static final String HDDS_X509_FILE_NAME_DEFAULT = "certificate.crt";
/**
* Default duration of certificates issued by SCM CA.
* The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS
* Default value is 5 years and written as P1865D.
*/
public static final String HDDS_X509_DEFAULT_DURATION = "hdds.x509.default" +
".duration";
// Default Certificate duration to one year.
public static final String HDDS_X509_DEFAULT_DURATION_DEFAULT = "P365D";
/**
* Do not instantiate.
*/
private HddsConfigKeys() {
}
// Enable TLS for GRPC clients/server in ozone.
public static final String HDDS_GRPC_TLS_ENABLED = "hdds.grpc.tls.enabled";
public static final boolean HDDS_GRPC_TLS_ENABLED_DEFAULT = false;
// Choose TLS provider the default is set to OPENSSL for better performance.
public static final String HDDS_GRPC_TLS_PROVIDER = "hdds.grpc.tls.provider";
public static final String HDDS_GRPC_TLS_PROVIDER_DEFAULT = "OPENSSL";
// Test only settings for using test signed certificate, authority assume to
// be localhost.
public static final String HDDS_GRPC_TLS_TEST_CERT = "hdds.grpc.tls" +
".test.cert";
public static final boolean HDDS_GRPC_TLS_TEST_CERT_DEFAULT = false;
// Comma separated acls (users, groups) allowing clients accessing
// datanode container protocol
// when hadoop.security.authorization is true, this needs to be set in
// hadoop-policy.xml, "*" allows all users/groups to access.
public static final String
HDDS_SECURITY_CLIENT_DATANODE_CONTAINER_PROTOCOL_ACL =
"hdds.security.client.datanode.container.protocol.acl";
// Comma separated acls (users, groups) allowing clients accessing
// scm container protocol
// when hadoop.security.authorization is true, this needs to be set in
// hadoop-policy.xml, "*" allows all users/groups to access.
public static final String HDDS_SECURITY_CLIENT_SCM_CONTAINER_PROTOCOL_ACL =
"hdds.security.client.scm.container.protocol.acl";
// Comma separated acls (users, groups) allowing clients accessing
// scm block protocol
// when hadoop.security.authorization is true, this needs to be set in
// hadoop-policy.xml, "*" allows all users/groups to access.
public static final String HDDS_SECURITY_CLIENT_SCM_BLOCK_PROTOCOL_ACL =
"hdds.security.client.scm.block.protocol.acl";
// Comma separated acls (users, groups) allowing clients accessing
// scm certificate protocol
// when hadoop.security.authorization is true, this needs to be set in
// hadoop-policy.xml, "*" allows all users/groups to access.
public static final String HDDS_SECURITY_CLIENT_SCM_CERTIFICATE_PROTOCOL_ACL =
"hdds.security.client.scm.certificate.protocol.acl";
// Determines if the Container Chunk Manager will write user data to disk
// Set to false only for specific performance tests
public static final String HDDS_CONTAINER_PERSISTDATA =
"hdds.container.chunk.persistdata";
public static final boolean HDDS_CONTAINER_PERSISTDATA_DEFAULT = true;
public static final String HDDS_CONTAINER_SCRUB_ENABLED =
"hdds.container.scrub.enabled";
public static final boolean HDDS_CONTAINER_SCRUB_ENABLED_DEFAULT = false;
public static final String HDDS_DATANODE_HTTP_ENABLED_KEY =
"hdds.datanode.http.enabled";
public static final String HDDS_DATANODE_HTTP_BIND_HOST_KEY =
"hdds.datanode.http-bind-host";
public static final String HDDS_DATANODE_HTTPS_BIND_HOST_KEY =
"hdds.datanode.https-bind-host";
public static final String HDDS_DATANODE_HTTP_ADDRESS_KEY =
"hdds.datanode.http-address";
public static final String HDDS_DATANODE_HTTPS_ADDRESS_KEY =
"hdds.datanode.https-address";
public static final String HDDS_DATANODE_HTTP_BIND_HOST_DEFAULT = "0.0.0.0";
public static final int HDDS_DATANODE_HTTP_BIND_PORT_DEFAULT = 9882;
public static final int HDDS_DATANODE_HTTPS_BIND_PORT_DEFAULT = 9883;
public static final String
HDDS_DATANODE_HTTP_KERBEROS_PRINCIPAL_KEY =
"hdds.datanode.http.kerberos.principal";
public static final String
HDDS_DATANODE_HTTP_KERBEROS_KEYTAB_FILE_KEY =
"hdds.datanode.http.kerberos.keytab";
}

View File

@ -1,53 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* HDDS Id generator.
*/
public final class HddsIdFactory {
private HddsIdFactory() {
}
private static final AtomicLong LONG_COUNTER = new AtomicLong(
System.currentTimeMillis());
/**
* Returns an incrementing long. This class doesn't
* persist initial value for long Id's, so incremental id's after restart
* may collide with previously generated Id's.
*
* @return long
*/
public static long getLongId() {
return LONG_COUNTER.incrementAndGet();
}
/**
* Returns a uuid.
*
* @return UUID.
*/
public static UUID getUUId() {
return UUID.randomUUID();
}
}

View File

@ -1,505 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds;
import javax.management.ObjectName;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolPB;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
import org.apache.hadoop.hdds.scm.protocolPB.ScmBlockLocationProtocolPB;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.io.retry.RetryPolicies;
import org.apache.hadoop.io.retry.RetryPolicy;
import org.apache.hadoop.ipc.Client;
import org.apache.hadoop.ipc.ProtobufRpcEngine;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.metrics2.MetricsSystem;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.source.JvmMetrics;
import org.apache.hadoop.metrics2.util.MBeans;
import org.apache.hadoop.net.DNS;
import org.apache.hadoop.net.NetUtils;
import com.google.common.net.HostAndPort;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_DNS_INTERFACE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_DNS_NAMESERVER_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HOST_NAME_KEY;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ENABLED;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ENABLED_DEFAULT;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* HDDS specific stateless utility functions.
*/
@InterfaceAudience.Private
@InterfaceStability.Stable
public final class HddsUtils {
private static final Logger LOG = LoggerFactory.getLogger(HddsUtils.class);
/**
* The service ID of the solitary Ozone SCM service.
*/
public static final String OZONE_SCM_SERVICE_ID = "OzoneScmService";
public static final String OZONE_SCM_SERVICE_INSTANCE_ID =
"OzoneScmServiceInstance";
private static final TimeZone UTC_ZONE = TimeZone.getTimeZone("UTC");
private static final int NO_PORT = -1;
private HddsUtils() {
}
/**
* Retrieve the socket address that should be used by clients to connect
* to the SCM.
*
* @param conf
* @return Target InetSocketAddress for the SCM client endpoint.
*/
public static InetSocketAddress getScmAddressForClients(Configuration conf) {
Optional<String> host = getHostNameFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY);
if (!host.isPresent()) {
// Fallback to Ozone SCM names.
Collection<InetSocketAddress> scmAddresses = getSCMAddresses(conf);
if (scmAddresses.size() > 1) {
throw new IllegalArgumentException(
ScmConfigKeys.OZONE_SCM_NAMES +
" must contain a single hostname. Multiple SCM hosts are " +
"currently unsupported");
}
host = Optional.of(scmAddresses.iterator().next().getHostName());
}
if (!host.isPresent()) {
throw new IllegalArgumentException(
ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY + " must be defined. See"
+ " https://wiki.apache.org/hadoop/Ozone#Configuration for "
+ "details"
+ " on configuring Ozone.");
}
final Optional<Integer> port = getPortNumberFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY);
return NetUtils.createSocketAddr(host.get() + ":" + port
.orElse(ScmConfigKeys.OZONE_SCM_CLIENT_PORT_DEFAULT));
}
/**
* Retrieve the socket address that should be used by clients to connect
* to the SCM for block service. If
* {@link ScmConfigKeys#OZONE_SCM_BLOCK_CLIENT_ADDRESS_KEY} is not defined
* then {@link ScmConfigKeys#OZONE_SCM_CLIENT_ADDRESS_KEY} is used. If neither
* is defined then {@link ScmConfigKeys#OZONE_SCM_NAMES} is used.
*
* @param conf
* @return Target InetSocketAddress for the SCM block client endpoint.
* @throws IllegalArgumentException if configuration is not defined.
*/
public static InetSocketAddress getScmAddressForBlockClients(
Configuration conf) {
Optional<String> host = getHostNameFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_ADDRESS_KEY);
if (!host.isPresent()) {
host = getHostNameFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY);
}
if (!host.isPresent()) {
// Fallback to Ozone SCM names.
Collection<InetSocketAddress> scmAddresses = getSCMAddresses(conf);
if (scmAddresses.size() > 1) {
throw new IllegalArgumentException(
ScmConfigKeys.OZONE_SCM_NAMES +
" must contain a single hostname. Multiple SCM hosts are " +
"currently unsupported");
}
host = Optional.of(scmAddresses.iterator().next().getHostName());
}
if (!host.isPresent()) {
throw new IllegalArgumentException(
ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_ADDRESS_KEY
+ " must be defined. See"
+ " https://wiki.apache.org/hadoop/Ozone#Configuration"
+ " for details on configuring Ozone.");
}
final Optional<Integer> port = getPortNumberFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_ADDRESS_KEY);
return NetUtils.createSocketAddr(host.get() + ":" + port
.orElse(ScmConfigKeys.OZONE_SCM_BLOCK_CLIENT_PORT_DEFAULT));
}
/**
* Create a scm security client.
* @param conf - Ozone configuration.
*
* @return {@link SCMSecurityProtocol}
* @throws IOException
*/
public static SCMSecurityProtocolClientSideTranslatorPB getScmSecurityClient(
OzoneConfiguration conf) throws IOException {
RPC.setProtocolEngine(conf, SCMSecurityProtocolPB.class,
ProtobufRpcEngine.class);
long scmVersion =
RPC.getProtocolVersion(ScmBlockLocationProtocolPB.class);
InetSocketAddress address =
getScmAddressForSecurityProtocol(conf);
RetryPolicy retryPolicy =
RetryPolicies.retryForeverWithFixedSleep(
1000, TimeUnit.MILLISECONDS);
SCMSecurityProtocolClientSideTranslatorPB scmSecurityClient =
new SCMSecurityProtocolClientSideTranslatorPB(
RPC.getProtocolProxy(SCMSecurityProtocolPB.class, scmVersion,
address, UserGroupInformation.getCurrentUser(),
conf, NetUtils.getDefaultSocketFactory(conf),
Client.getRpcTimeout(conf), retryPolicy).getProxy());
return scmSecurityClient;
}
/**
* Retrieve the hostname, trying the supplied config keys in order.
* Each config value may be absent, or if present in the format
* host:port (the :port part is optional).
*
* @param conf - Conf
* @param keys a list of configuration key names.
*
* @return first hostname component found from the given keys, or absent.
* @throws IllegalArgumentException if any values are not in the 'host'
* or host:port format.
*/
public static Optional<String> getHostNameFromConfigKeys(Configuration conf,
String... keys) {
for (final String key : keys) {
final String value = conf.getTrimmed(key);
final Optional<String> hostName = getHostName(value);
if (hostName.isPresent()) {
return hostName;
}
}
return Optional.empty();
}
/**
* Gets the hostname or Indicates that it is absent.
* @param value host or host:port
* @return hostname
*/
public static Optional<String> getHostName(String value) {
if ((value == null) || value.isEmpty()) {
return Optional.empty();
}
String hostname = value.replaceAll("\\:[0-9]+$", "");
if (hostname.length() == 0) {
return Optional.empty();
} else {
return Optional.of(hostname);
}
}
/**
* Gets the port if there is one, throws otherwise.
* @param value String in host:port format.
* @return Port
*/
public static Optional<Integer> getHostPort(String value) {
if ((value == null) || value.isEmpty()) {
return Optional.empty();
}
int port = HostAndPort.fromString(value).getPortOrDefault(NO_PORT);
if (port == NO_PORT) {
return Optional.empty();
} else {
return Optional.of(port);
}
}
/**
* Retrieve the port number, trying the supplied config keys in order.
* Each config value may be absent, or if present in the format
* host:port (the :port part is optional).
*
* @param conf Conf
* @param keys a list of configuration key names.
*
* @return first port number component found from the given keys, or absent.
* @throws IllegalArgumentException if any values are not in the 'host'
* or host:port format.
*/
public static Optional<Integer> getPortNumberFromConfigKeys(
Configuration conf, String... keys) {
for (final String key : keys) {
final String value = conf.getTrimmed(key);
final Optional<Integer> hostPort = getHostPort(value);
if (hostPort.isPresent()) {
return hostPort;
}
}
return Optional.empty();
}
/**
* Retrieve the socket addresses of all storage container managers.
*
* @param conf
* @return A collection of SCM addresses
* @throws IllegalArgumentException If the configuration is invalid
*/
public static Collection<InetSocketAddress> getSCMAddresses(
Configuration conf) throws IllegalArgumentException {
Collection<InetSocketAddress> addresses =
new HashSet<InetSocketAddress>();
Collection<String> names =
conf.getTrimmedStringCollection(ScmConfigKeys.OZONE_SCM_NAMES);
if (names == null || names.isEmpty()) {
throw new IllegalArgumentException(ScmConfigKeys.OZONE_SCM_NAMES
+ " need to be a set of valid DNS names or IP addresses."
+ " Null or empty address list found.");
}
final Optional<Integer> defaultPort = Optional
.of(ScmConfigKeys.OZONE_SCM_DEFAULT_PORT);
for (String address : names) {
Optional<String> hostname = getHostName(address);
if (!hostname.isPresent()) {
throw new IllegalArgumentException("Invalid hostname for SCM: "
+ hostname);
}
Optional<Integer> port = getHostPort(address);
InetSocketAddress addr = NetUtils.createSocketAddr(hostname.get(),
port.orElse(defaultPort.get()));
addresses.add(addr);
}
return addresses;
}
public static boolean isHddsEnabled(Configuration conf) {
return conf.getBoolean(OZONE_ENABLED, OZONE_ENABLED_DEFAULT);
}
/**
* Returns the hostname for this datanode. If the hostname is not
* explicitly configured in the given config, then it is determined
* via the DNS class.
*
* @param conf Configuration
*
* @return the hostname (NB: may not be a FQDN)
* @throws UnknownHostException if the dfs.datanode.dns.interface
* option is used and the hostname can not be determined
*/
public static String getHostName(Configuration conf)
throws UnknownHostException {
String name = conf.get(DFS_DATANODE_HOST_NAME_KEY);
if (name == null) {
String dnsInterface = conf.get(
CommonConfigurationKeys.HADOOP_SECURITY_DNS_INTERFACE_KEY);
String nameServer = conf.get(
CommonConfigurationKeys.HADOOP_SECURITY_DNS_NAMESERVER_KEY);
boolean fallbackToHosts = false;
if (dnsInterface == null) {
// Try the legacy configuration keys.
dnsInterface = conf.get(DFS_DATANODE_DNS_INTERFACE_KEY);
nameServer = conf.get(DFS_DATANODE_DNS_NAMESERVER_KEY);
} else {
// If HADOOP_SECURITY_DNS_* is set then also attempt hosts file
// resolution if DNS fails. We will not use hosts file resolution
// by default to avoid breaking existing clusters.
fallbackToHosts = true;
}
name = DNS.getDefaultHost(dnsInterface, nameServer, fallbackToHosts);
}
return name;
}
/**
* Checks if the container command is read only or not.
* @param proto ContainerCommand Request proto
* @return True if its readOnly , false otherwise.
*/
public static boolean isReadOnly(
ContainerProtos.ContainerCommandRequestProto proto) {
switch (proto.getCmdType()) {
case ReadContainer:
case ReadChunk:
case ListBlock:
case GetBlock:
case GetSmallFile:
case ListContainer:
case ListChunk:
case GetCommittedBlockLength:
return true;
case CloseContainer:
case WriteChunk:
case UpdateContainer:
case CompactChunk:
case CreateContainer:
case DeleteChunk:
case DeleteContainer:
case DeleteBlock:
case PutBlock:
case PutSmallFile:
default:
return false;
}
}
/**
* Register the provided MBean with additional JMX ObjectName properties.
* If additional properties are not supported then fallback to registering
* without properties.
*
* @param serviceName - see {@link MBeans#register}
* @param mBeanName - see {@link MBeans#register}
* @param jmxProperties - additional JMX ObjectName properties.
* @param mBean - the MBean to register.
* @return the named used to register the MBean.
*/
public static ObjectName registerWithJmxProperties(
String serviceName, String mBeanName, Map<String, String> jmxProperties,
Object mBean) {
try {
// Check support for registering with additional properties.
final Method registerMethod = MBeans.class.getMethod(
"register", String.class, String.class,
Map.class, Object.class);
return (ObjectName) registerMethod.invoke(
null, serviceName, mBeanName, jmxProperties, mBean);
} catch (NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
// Fallback
if (LOG.isTraceEnabled()) {
LOG.trace("Registering MBean {} without additional properties {}",
mBeanName, jmxProperties);
}
return MBeans.register(serviceName, mBeanName, mBean);
}
}
/**
* Get the current UTC time in milliseconds.
* @return the current UTC time in milliseconds.
*/
public static long getUtcTime() {
return Calendar.getInstance(UTC_ZONE).getTimeInMillis();
}
/**
* Retrieve the socket address that should be used by clients to connect
* to the SCM for
* {@link org.apache.hadoop.hdds.protocol.SCMSecurityProtocol}. If
* {@link ScmConfigKeys#OZONE_SCM_SECURITY_SERVICE_ADDRESS_KEY} is not defined
* then {@link ScmConfigKeys#OZONE_SCM_CLIENT_ADDRESS_KEY} is used. If neither
* is defined then {@link ScmConfigKeys#OZONE_SCM_NAMES} is used.
*
* @param conf
* @return Target InetSocketAddress for the SCM block client endpoint.
* @throws IllegalArgumentException if configuration is not defined.
*/
public static InetSocketAddress getScmAddressForSecurityProtocol(
Configuration conf) {
Optional<String> host = getHostNameFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_SECURITY_SERVICE_ADDRESS_KEY);
if (!host.isPresent()) {
host = getHostNameFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY);
}
if (!host.isPresent()) {
// Fallback to Ozone SCM names.
Collection<InetSocketAddress> scmAddresses = getSCMAddresses(conf);
if (scmAddresses.size() > 1) {
throw new IllegalArgumentException(
ScmConfigKeys.OZONE_SCM_NAMES +
" must contain a single hostname. Multiple SCM hosts are " +
"currently unsupported");
}
host = Optional.of(scmAddresses.iterator().next().getHostName());
}
if (!host.isPresent()) {
throw new IllegalArgumentException(
ScmConfigKeys.OZONE_SCM_SECURITY_SERVICE_ADDRESS_KEY
+ " must be defined. See"
+ " https://wiki.apache.org/hadoop/Ozone#Configuration"
+ " for details on configuring Ozone.");
}
final Optional<Integer> port = getPortNumberFromConfigKeys(conf,
ScmConfigKeys.OZONE_SCM_SECURITY_SERVICE_PORT_KEY);
return NetUtils.createSocketAddr(host.get() + ":" + port
.orElse(ScmConfigKeys.OZONE_SCM_SECURITY_SERVICE_PORT_DEFAULT));
}
/**
* Initialize hadoop metrics system for Ozone servers.
* @param configuration OzoneConfiguration to use.
* @param serverName The logical name of the server components.
* @return
*/
public static MetricsSystem initializeMetrics(
OzoneConfiguration configuration, String serverName) {
MetricsSystem metricsSystem = DefaultMetricsSystem.initialize(serverName);
JvmMetrics.create(serverName,
configuration.get(DFSConfigKeys.DFS_METRICS_SESSION_ID_KEY),
DefaultMetricsSystem.instance());
return metricsSystem;
}
}

View File

@ -1,107 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.cli;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.fs.Path;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import picocli.CommandLine.ExecutionException;
import picocli.CommandLine.Option;
import picocli.CommandLine.RunLast;
/**
* This is a generic parent class for all the ozone related cli tools.
*/
public class GenericCli implements Callable<Void>, GenericParentCommand {
@Option(names = {"--verbose"},
description = "More verbose output. Show the stack trace of the errors.")
private boolean verbose;
@Option(names = {"-D", "--set"})
private Map<String, String> configurationOverrides = new HashMap<>();
@Option(names = {"-conf"})
private String configurationPath;
private final CommandLine cmd;
public GenericCli() {
cmd = new CommandLine(this);
}
public void run(String[] argv) {
try {
execute(argv);
} catch (ExecutionException ex) {
printError(ex.getCause() == null ? ex : ex.getCause());
System.exit(-1);
}
}
@VisibleForTesting
public void execute(String[] argv) {
cmd.parseWithHandler(new RunLast(), argv);
}
protected void printError(Throwable error) {
//message could be null in case of NPE. This is unexpected so we can
//print out the stack trace.
if (verbose || error.getMessage() == null
|| error.getMessage().length() == 0) {
error.printStackTrace(System.err);
} else {
System.err.println(error.getMessage().split("\n")[0]);
}
}
@Override
public Void call() throws Exception {
throw new MissingSubcommandException(cmd);
}
@Override
public OzoneConfiguration createOzoneConfiguration() {
OzoneConfiguration ozoneConf = new OzoneConfiguration();
if (configurationPath != null) {
ozoneConf.addResource(new Path(configurationPath));
}
if (configurationOverrides != null) {
for (Entry<String, String> entry : configurationOverrides.entrySet()) {
ozoneConf.set(entry.getKey(), entry.getValue());
}
}
return ozoneConf;
}
@VisibleForTesting
public picocli.CommandLine getCmd() {
return cmd;
}
@Override
public boolean isVerbose() {
return verbose;
}
}

View File

@ -1,29 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.cli;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
/**
* Interface to access the higher level parameters.
*/
public interface GenericParentCommand {
boolean isVerbose();
OzoneConfiguration createOzoneConfiguration();
}

View File

@ -1,35 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.cli;
import org.apache.hadoop.hdds.utils.HddsVersionInfo;
import picocli.CommandLine.IVersionProvider;
/**
* Version provider for the CLI interface.
*/
public class HddsVersionProvider implements IVersionProvider {
@Override
public String[] getVersion() throws Exception {
String[] result = new String[] {
HddsVersionInfo.HDDS_VERSION_INFO.getBuildVersion()
};
return result;
}
}

View File

@ -1,31 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.cli;
import picocli.CommandLine;
/**
* Exception to throw if subcommand is not selected but required.
*/
public class MissingSubcommandException extends CommandLine.ParameterException {
public MissingSubcommandException(CommandLine cmd) {
super(cmd, "Incomplete command");
}
}

View File

@ -1,22 +0,0 @@
/**
* 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.
*/
/**
* Generic helper class to make instantiate picocli based cli tools.
*/
package org.apache.hadoop.hdds.cli;

View File

@ -1,127 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.client;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import java.util.Objects;
/**
* BlockID of Ozone (containerID + localID + blockCommitSequenceId).
*/
public class BlockID {
private ContainerBlockID containerBlockID;
private long blockCommitSequenceId;
public BlockID(long containerID, long localID) {
this(containerID, localID, 0);
}
private BlockID(long containerID, long localID, long bcsID) {
containerBlockID = new ContainerBlockID(containerID, localID);
blockCommitSequenceId = bcsID;
}
public BlockID(ContainerBlockID containerBlockID) {
this(containerBlockID, 0);
}
private BlockID(ContainerBlockID containerBlockID, long bcsId) {
this.containerBlockID = containerBlockID;
blockCommitSequenceId = bcsId;
}
public long getContainerID() {
return containerBlockID.getContainerID();
}
public long getLocalID() {
return containerBlockID.getLocalID();
}
public long getBlockCommitSequenceId() {
return blockCommitSequenceId;
}
public void setBlockCommitSequenceId(long blockCommitSequenceId) {
this.blockCommitSequenceId = blockCommitSequenceId;
}
public ContainerBlockID getContainerBlockID() {
return containerBlockID;
}
public void setContainerBlockID(ContainerBlockID containerBlockID) {
this.containerBlockID = containerBlockID;
}
@Override
public String toString() {
return new StringBuilder().append(getContainerBlockID().toString())
.append(" bcsId: ")
.append(blockCommitSequenceId)
.toString();
}
public ContainerProtos.DatanodeBlockID getDatanodeBlockIDProtobuf() {
return ContainerProtos.DatanodeBlockID.newBuilder().
setContainerID(containerBlockID.getContainerID())
.setLocalID(containerBlockID.getLocalID())
.setBlockCommitSequenceId(blockCommitSequenceId).build();
}
public static BlockID getFromProtobuf(
ContainerProtos.DatanodeBlockID blockID) {
return new BlockID(blockID.getContainerID(),
blockID.getLocalID(), blockID.getBlockCommitSequenceId());
}
public HddsProtos.BlockID getProtobuf() {
return HddsProtos.BlockID.newBuilder()
.setContainerBlockID(containerBlockID.getProtobuf())
.setBlockCommitSequenceId(blockCommitSequenceId).build();
}
public static BlockID getFromProtobuf(HddsProtos.BlockID blockID) {
return new BlockID(
ContainerBlockID.getFromProtobuf(blockID.getContainerBlockID()),
blockID.getBlockCommitSequenceId());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BlockID blockID = (BlockID) o;
return containerBlockID.equals(blockID.getContainerBlockID())
&& blockCommitSequenceId == blockID.getBlockCommitSequenceId();
}
@Override
public int hashCode() {
return Objects
.hash(containerBlockID.getContainerID(), containerBlockID.getLocalID(),
blockCommitSequenceId);
}
}

View File

@ -1,79 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.client;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import java.util.Objects;
/**
* BlockID returned by SCM during allocation of block (containerID + localID).
*/
public class ContainerBlockID {
private long containerID;
private long localID;
public ContainerBlockID(long containerID, long localID) {
this.containerID = containerID;
this.localID = localID;
}
public long getContainerID() {
return containerID;
}
public long getLocalID() {
return localID;
}
@Override
public String toString() {
return new StringBuffer()
.append("conID: ")
.append(containerID)
.append(" locID: ")
.append(localID).toString();
}
public HddsProtos.ContainerBlockID getProtobuf() {
return HddsProtos.ContainerBlockID.newBuilder().
setContainerID(containerID).setLocalID(localID).build();
}
public static ContainerBlockID getFromProtobuf(
HddsProtos.ContainerBlockID containerBlockID) {
return new ContainerBlockID(containerBlockID.getContainerID(),
containerBlockID.getLocalID());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ContainerBlockID blockID = (ContainerBlockID) o;
return containerID == blockID.containerID && localID == blockID.localID;
}
@Override
public int hashCode() {
return Objects.hash(containerID, localID);
}
}

View File

@ -1,203 +0,0 @@
/**
* 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.hdds.client;
import org.apache.hadoop.ozone.OzoneConsts;
/**
* represents an OzoneQuota Object that can be applied to
* a storage volume.
*/
public class OzoneQuota {
public static final String OZONE_QUOTA_BYTES = "BYTES";
public static final String OZONE_QUOTA_MB = "MB";
public static final String OZONE_QUOTA_GB = "GB";
public static final String OZONE_QUOTA_TB = "TB";
private Units unit;
private long size;
/** Quota Units.*/
public enum Units {UNDEFINED, BYTES, KB, MB, GB, TB}
/**
* Returns size.
*
* @return long
*/
public long getSize() {
return size;
}
/**
* Returns Units.
*
* @return Unit in MB, GB or TB
*/
public Units getUnit() {
return unit;
}
/**
* Constructs a default Quota object.
*/
public OzoneQuota() {
this.size = 0;
this.unit = Units.UNDEFINED;
}
/**
* Constructor for Ozone Quota.
*
* @param size Long Size
* @param unit MB, GB or TB
*/
public OzoneQuota(long size, Units unit) {
this.size = size;
this.unit = unit;
}
/**
* Formats a quota as a string.
*
* @param quota the quota to format
* @return string representation of quota
*/
public static String formatQuota(OzoneQuota quota) {
return String.valueOf(quota.size) + quota.unit;
}
/**
* Parses a user provided string and returns the
* Quota Object.
*
* @param quotaString Quota String
*
* @return OzoneQuota object
*
* @throws IllegalArgumentException
*/
public static OzoneQuota parseQuota(String quotaString)
throws IllegalArgumentException {
if ((quotaString == null) || (quotaString.isEmpty())) {
throw new IllegalArgumentException(
"Quota string cannot be null or empty.");
}
String uppercase = quotaString.toUpperCase().replaceAll("\\s+", "");
String size = "";
int nSize;
Units currUnit = Units.MB;
Boolean found = false;
if (uppercase.endsWith(OZONE_QUOTA_MB)) {
size = uppercase
.substring(0, uppercase.length() - OZONE_QUOTA_MB.length());
currUnit = Units.MB;
found = true;
}
if (uppercase.endsWith(OZONE_QUOTA_GB)) {
size = uppercase
.substring(0, uppercase.length() - OZONE_QUOTA_GB.length());
currUnit = Units.GB;
found = true;
}
if (uppercase.endsWith(OZONE_QUOTA_TB)) {
size = uppercase
.substring(0, uppercase.length() - OZONE_QUOTA_TB.length());
currUnit = Units.TB;
found = true;
}
if (uppercase.endsWith(OZONE_QUOTA_BYTES)) {
size = uppercase
.substring(0, uppercase.length() - OZONE_QUOTA_BYTES.length());
currUnit = Units.BYTES;
found = true;
}
if (!found) {
throw new IllegalArgumentException(
"Quota unit not recognized. Supported values are BYTES, MB, GB and " +
"TB.");
}
nSize = Integer.parseInt(size);
if (nSize < 0) {
throw new IllegalArgumentException("Quota cannot be negative.");
}
return new OzoneQuota(nSize, currUnit);
}
/**
* Returns size in Bytes or -1 if there is no Quota.
*/
public long sizeInBytes() {
switch (this.unit) {
case BYTES:
return this.getSize();
case MB:
return this.getSize() * OzoneConsts.MB;
case GB:
return this.getSize() * OzoneConsts.GB;
case TB:
return this.getSize() * OzoneConsts.TB;
case UNDEFINED:
default:
return -1;
}
}
/**
* Returns OzoneQuota corresponding to size in bytes.
*
* @param sizeInBytes size in bytes to be converted
*
* @return OzoneQuota object
*/
public static OzoneQuota getOzoneQuota(long sizeInBytes) {
long size;
Units unit;
if (sizeInBytes % OzoneConsts.TB == 0) {
size = sizeInBytes / OzoneConsts.TB;
unit = Units.TB;
} else if (sizeInBytes % OzoneConsts.GB == 0) {
size = sizeInBytes / OzoneConsts.GB;
unit = Units.GB;
} else if (sizeInBytes % OzoneConsts.MB == 0) {
size = sizeInBytes / OzoneConsts.MB;
unit = Units.MB;
} else {
size = sizeInBytes;
unit = Units.BYTES;
}
return new OzoneQuota((int)size, unit);
}
@Override
public String toString() {
return size + " " + unit;
}
}

View File

@ -1,81 +0,0 @@
/**
* 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.hdds.client;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
/**
* The replication factor to be used while writing key into ozone.
*/
public enum ReplicationFactor {
ONE(1),
THREE(3);
/**
* Integer representation of replication.
*/
private int value;
/**
* Initializes ReplicationFactor with value.
* @param value replication value
*/
ReplicationFactor(int value) {
this.value = value;
}
/**
* Returns enum value corresponding to the int value.
* @param value replication value
* @return ReplicationFactor
*/
public static ReplicationFactor valueOf(int value) {
if(value == 1) {
return ONE;
}
if (value == 3) {
return THREE;
}
throw new IllegalArgumentException("Unsupported value: " + value);
}
public static ReplicationFactor fromProto(
HddsProtos.ReplicationFactor replicationFactor) {
if (replicationFactor == null) {
return null;
}
switch (replicationFactor) {
case ONE:
return ReplicationFactor.ONE;
case THREE:
return ReplicationFactor.THREE;
default:
throw new IllegalArgumentException(
"Unsupported ProtoBuf replication factor: " + replicationFactor);
}
}
/**
* Returns integer representation of ReplicationFactor.
* @return replication value
*/
public int getValue() {
return value;
}
}

View File

@ -1,48 +0,0 @@
/**
* 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.hdds.client;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
/**
* The replication type to be used while writing key into ozone.
*/
public enum ReplicationType {
RATIS,
STAND_ALONE,
CHAINED;
public static ReplicationType fromProto(
HddsProtos.ReplicationType replicationType) {
if (replicationType == null) {
return null;
}
switch (replicationType) {
case RATIS:
return ReplicationType.RATIS;
case STAND_ALONE:
return ReplicationType.STAND_ALONE;
case CHAINED:
return ReplicationType.CHAINED;
default:
throw new IllegalArgumentException(
"Unsupported ProtoBuf replication type: " + replicationType);
}
}
}

View File

@ -1,23 +0,0 @@
/**
* 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.hdds.client;
/**
* Base property types for HDDS containers and replications.
*/

View File

@ -1,190 +0,0 @@
/**
* 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.hdds.conf;
import com.google.gson.Gson;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.HttpHeaders;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.http.HttpServer2;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_TAGS_SYSTEM_KEY;
/**
* A servlet to print out the running configuration data.
*/
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
@InterfaceStability.Unstable
public class HddsConfServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected static final String FORMAT_JSON = "json";
protected static final String FORMAT_XML = "xml";
private static final String COMMAND = "cmd";
private static final OzoneConfiguration OZONE_CONFIG =
new OzoneConfiguration();
private static final transient Logger LOG =
LoggerFactory.getLogger(HddsConfServlet.class);
/**
* Return the Configuration of the daemon hosting this servlet.
* This is populated when the HttpServer starts.
*/
private Configuration getConfFromContext() {
Configuration conf = (Configuration) getServletContext().getAttribute(
HttpServer2.CONF_CONTEXT_ATTRIBUTE);
assert conf != null;
return conf;
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (!HttpServer2.isInstrumentationAccessAllowed(getServletContext(),
request, response)) {
return;
}
String format = parseAcceptHeader(request);
if (FORMAT_XML.equals(format)) {
response.setContentType("text/xml; charset=utf-8");
} else if (FORMAT_JSON.equals(format)) {
response.setContentType("application/json; charset=utf-8");
}
String name = request.getParameter("name");
Writer out = response.getWriter();
String cmd = request.getParameter(COMMAND);
processCommand(cmd, format, request, response, out, name);
out.close();
}
private void processCommand(String cmd, String format,
HttpServletRequest request, HttpServletResponse response, Writer out,
String name)
throws IOException {
try {
if (cmd == null) {
if (FORMAT_XML.equals(format)) {
response.setContentType("text/xml; charset=utf-8");
} else if (FORMAT_JSON.equals(format)) {
response.setContentType("application/json; charset=utf-8");
}
writeResponse(getConfFromContext(), out, format, name);
} else {
processConfigTagRequest(request, out);
}
} catch (BadFormatException bfe) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, bfe.getMessage());
} catch (IllegalArgumentException iae) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, iae.getMessage());
}
}
@VisibleForTesting
static String parseAcceptHeader(HttpServletRequest request) {
String format = request.getHeader(HttpHeaders.ACCEPT);
return format != null && format.contains(FORMAT_JSON) ?
FORMAT_JSON : FORMAT_XML;
}
/**
* Guts of the servlet - extracted for easy testing.
*/
static void writeResponse(Configuration conf,
Writer out, String format, String propertyName)
throws IOException, IllegalArgumentException, BadFormatException {
if (FORMAT_JSON.equals(format)) {
Configuration.dumpConfiguration(conf, propertyName, out);
} else if (FORMAT_XML.equals(format)) {
conf.writeXml(propertyName, out);
} else {
throw new BadFormatException("Bad format: " + format);
}
}
/**
* Exception for signal bad content type.
*/
public static class BadFormatException extends Exception {
private static final long serialVersionUID = 1L;
public BadFormatException(String msg) {
super(msg);
}
}
private void processConfigTagRequest(HttpServletRequest request,
Writer out) throws IOException {
String cmd = request.getParameter(COMMAND);
Gson gson = new Gson();
Configuration config = getOzoneConfig();
switch (cmd) {
case "getOzoneTags":
out.write(gson.toJson(config.get(OZONE_TAGS_SYSTEM_KEY)
.split(",")));
break;
case "getPropertyByTag":
String tags = request.getParameter("tags");
Map<String, Properties> propMap = new HashMap<>();
for (String tag : tags.split(",")) {
if (config.isPropertyTag(tag)) {
Properties properties = config.getAllPropertiesByTag(tag);
propMap.put(tag, properties);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Not a valid tag" + tag);
}
}
}
out.write(gson.toJsonTree(propMap).toString());
break;
default:
throw new IllegalArgumentException(cmd + " is not a valid command.");
}
}
private static Configuration getOzoneConfig() {
return OZONE_CONFIG;
}
}

View File

@ -1,328 +0,0 @@
/**
* 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.hdds.conf;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import com.google.common.base.Preconditions;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
/**
* Configuration for ozone.
*/
@InterfaceAudience.Private
public class OzoneConfiguration extends Configuration {
static {
activate();
}
public static OzoneConfiguration of(Configuration conf) {
Preconditions.checkNotNull(conf);
return conf instanceof OzoneConfiguration
? (OzoneConfiguration) conf
: new OzoneConfiguration(conf);
}
public OzoneConfiguration() {
OzoneConfiguration.activate();
loadDefaults();
}
public OzoneConfiguration(Configuration conf) {
super(conf);
//load the configuration from the classloader of the original conf.
setClassLoader(conf.getClassLoader());
if (!(conf instanceof OzoneConfiguration)) {
loadDefaults();
}
}
private void loadDefaults() {
try {
//there could be multiple ozone-default-generated.xml files on the
// classpath, which are generated by the annotation processor.
// Here we add all of them to the list of the available configuration.
Enumeration<URL> generatedDefaults =
OzoneConfiguration.class.getClassLoader().getResources(
"ozone-default-generated.xml");
while (generatedDefaults.hasMoreElements()) {
addResource(generatedDefaults.nextElement());
}
} catch (IOException e) {
e.printStackTrace();
}
addResource("ozone-site.xml");
}
public List<Property> readPropertyFromXml(URL url) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(XMLConfiguration.class);
Unmarshaller um = context.createUnmarshaller();
XMLConfiguration config = (XMLConfiguration) um.unmarshal(url);
return config.getProperties();
}
/**
* Create a Configuration object and inject the required configuration values.
*
* @param configurationClass The class where the fields are annotated with
* the configuration.
* @return Initiated java object where the config fields are injected.
*/
public <T> T getObject(Class<T> configurationClass) {
T configuration;
try {
configuration = configurationClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new ConfigurationException(
"Configuration class can't be created: " + configurationClass, e);
}
ConfigGroup configGroup =
configurationClass.getAnnotation(ConfigGroup.class);
String prefix = configGroup.prefix();
for (Method setterMethod : configurationClass.getMethods()) {
if (setterMethod.isAnnotationPresent(Config.class)) {
String methodLocation =
configurationClass + "." + setterMethod.getName();
Config configAnnotation = setterMethod.getAnnotation(Config.class);
String key = prefix + "." + configAnnotation.key();
Class<?>[] parameterTypes = setterMethod.getParameterTypes();
if (parameterTypes.length != 1) {
throw new ConfigurationException(
"@Config annotation should be used on simple setter: "
+ methodLocation);
}
ConfigType type = configAnnotation.type();
if (type == ConfigType.AUTO) {
type = detectConfigType(parameterTypes[0], methodLocation);
}
//Note: default value is handled by ozone-default.xml. Here we can
//use any default.
try {
switch (type) {
case STRING:
setterMethod.invoke(configuration, get(key));
break;
case INT:
setterMethod.invoke(configuration,
getInt(key, 0));
break;
case BOOLEAN:
setterMethod.invoke(configuration,
getBoolean(key, false));
break;
case LONG:
setterMethod.invoke(configuration,
getLong(key, 0));
break;
case TIME:
setterMethod.invoke(configuration,
getTimeDuration(key, 0, configAnnotation.timeUnit()));
break;
default:
throw new ConfigurationException(
"Unsupported ConfigType " + type + " on " + methodLocation);
}
} catch (InvocationTargetException | IllegalAccessException e) {
throw new ConfigurationException(
"Can't inject configuration to " + methodLocation, e);
}
}
}
return configuration;
}
private ConfigType detectConfigType(Class<?> parameterType,
String methodLocation) {
ConfigType type;
if (parameterType == String.class) {
type = ConfigType.STRING;
} else if (parameterType == Integer.class || parameterType == int.class) {
type = ConfigType.INT;
} else if (parameterType == Long.class || parameterType == long.class) {
type = ConfigType.LONG;
} else if (parameterType == Boolean.class
|| parameterType == boolean.class) {
type = ConfigType.BOOLEAN;
} else {
throw new ConfigurationException(
"Unsupported configuration type " + parameterType + " in "
+ methodLocation);
}
return type;
}
/**
* Class to marshall/un-marshall configuration from xml files.
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "configuration")
public static class XMLConfiguration {
@XmlElement(name = "property", type = Property.class)
private List<Property> properties = new ArrayList<>();
public XMLConfiguration() {
}
public XMLConfiguration(List<Property> properties) {
this.properties = properties;
}
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
}
/**
* Class to marshall/un-marshall configuration properties from xml files.
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "property")
public static class Property implements Comparable<Property> {
private String name;
private String value;
private String tag;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int compareTo(Property o) {
if (this == o) {
return 0;
}
return this.getName().compareTo(o.getName());
}
@Override
public String toString() {
return this.getName() + " " + this.getValue() + " " + this.getTag();
}
@Override
public int hashCode() {
return this.getName().hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj instanceof Property) && (((Property) obj).getName())
.equals(this.getName());
}
}
public static void activate() {
// adds the default resources
Configuration.addDefaultResource("hdfs-default.xml");
Configuration.addDefaultResource("hdfs-site.xml");
Configuration.addDefaultResource("ozone-default.xml");
}
/**
* The super class method getAllPropertiesByTag
* does not override values of properties
* if there is no tag present in the configs of
* newly added resources.
*
* @param tag
* @return Properties that belong to the tag
*/
@Override
public Properties getAllPropertiesByTag(String tag) {
// Call getProps first to load the newly added resources
// before calling super.getAllPropertiesByTag
Properties updatedProps = getProps();
Properties propertiesByTag = super.getAllPropertiesByTag(tag);
Properties props = new Properties();
Enumeration properties = propertiesByTag.propertyNames();
while (properties.hasMoreElements()) {
Object propertyName = properties.nextElement();
// get the current value of the property
Object value = updatedProps.getProperty(propertyName.toString());
if (value != null) {
props.put(propertyName, value);
}
}
return props;
}
}

View File

@ -1,18 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.conf;

View File

@ -1,36 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.function;
import com.google.protobuf.ServiceException;
/**
* Functional interface like java.util.function.Function but with
* checked exception.
*/
@FunctionalInterface
public interface FunctionWithServiceException<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t) throws ServiceException;
}

View File

@ -1,22 +0,0 @@
/**
* 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.
*/
/**
* Functional interfaces for ozone, similar to java.util.function.
*/
package org.apache.hadoop.hdds.function;

View File

@ -1,23 +0,0 @@
/**
* 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.hdds;
/**
* Generic HDDS specific configurator and helper classes.
*/

View File

@ -1,493 +0,0 @@
/**
* 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.hdds.protocol;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.net.NetConstants;
import org.apache.hadoop.hdds.scm.net.NodeImpl;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* DatanodeDetails class contains details about DataNode like:
* - UUID of the DataNode.
* - IP and Hostname details.
* - Port details to which the DataNode will be listening.
*/
@InterfaceAudience.Private
@InterfaceStability.Evolving
public class DatanodeDetails extends NodeImpl implements
Comparable<DatanodeDetails> {
/**
* DataNode's unique identifier in the cluster.
*/
private final UUID uuid;
private String ipAddress;
private String hostName;
private List<Port> ports;
private String certSerialId;
/**
* Constructs DatanodeDetails instance. DatanodeDetails.Builder is used
* for instantiating DatanodeDetails.
* @param uuid DataNode's UUID
* @param ipAddress IP Address of this DataNode
* @param hostName DataNode's hostname
* @param networkLocation DataNode's network location path
* @param ports Ports used by the DataNode
* @param certSerialId serial id from SCM issued certificate.
*/
private DatanodeDetails(String uuid, String ipAddress, String hostName,
String networkLocation, List<Port> ports, String certSerialId) {
super(hostName, networkLocation, NetConstants.NODE_COST_DEFAULT);
this.uuid = UUID.fromString(uuid);
this.ipAddress = ipAddress;
this.hostName = hostName;
this.ports = ports;
this.certSerialId = certSerialId;
}
protected DatanodeDetails(DatanodeDetails datanodeDetails) {
super(datanodeDetails.getHostName(), datanodeDetails.getNetworkLocation(),
datanodeDetails.getCost());
this.uuid = datanodeDetails.uuid;
this.ipAddress = datanodeDetails.ipAddress;
this.hostName = datanodeDetails.hostName;
this.ports = datanodeDetails.ports;
this.setNetworkName(datanodeDetails.getNetworkName());
}
/**
* Returns the DataNode UUID.
*
* @return UUID of DataNode
*/
public UUID getUuid() {
return uuid;
}
/**
* Returns the string representation of DataNode UUID.
*
* @return UUID of DataNode
*/
public String getUuidString() {
return uuid.toString();
}
/**
* Sets the IP address of Datanode.
*
* @param ip IP Address
*/
public void setIpAddress(String ip) {
this.ipAddress = ip;
}
/**
* Returns IP address of DataNode.
*
* @return IP address
*/
public String getIpAddress() {
return ipAddress;
}
/**
* Sets the Datanode hostname.
*
* @param host hostname
*/
public void setHostName(String host) {
this.hostName = host;
}
/**
* Returns Hostname of DataNode.
*
* @return Hostname
*/
public String getHostName() {
return hostName;
}
/**
* Sets a DataNode Port.
*
* @param port DataNode port
*/
public void setPort(Port port) {
// If the port is already in the list remove it first and add the
// new/updated port value.
ports.remove(port);
ports.add(port);
}
/**
* Returns all the Ports used by DataNode.
*
* @return DataNode Ports
*/
public List<Port> getPorts() {
return ports;
}
/**
* Given the name returns port number, null if the asked port is not found.
*
* @param name Name of the port
*
* @return Port
*/
public Port getPort(Port.Name name) {
for (Port port : ports) {
if (port.getName().equals(name)) {
return port;
}
}
return null;
}
/**
* Returns a DatanodeDetails from the protocol buffers.
*
* @param datanodeDetailsProto - protoBuf Message
* @return DatanodeDetails
*/
public static DatanodeDetails getFromProtoBuf(
HddsProtos.DatanodeDetailsProto datanodeDetailsProto) {
DatanodeDetails.Builder builder = newBuilder();
builder.setUuid(datanodeDetailsProto.getUuid());
if (datanodeDetailsProto.hasIpAddress()) {
builder.setIpAddress(datanodeDetailsProto.getIpAddress());
}
if (datanodeDetailsProto.hasHostName()) {
builder.setHostName(datanodeDetailsProto.getHostName());
}
if (datanodeDetailsProto.hasCertSerialId()) {
builder.setCertSerialId(datanodeDetailsProto.getCertSerialId());
}
for (HddsProtos.Port port : datanodeDetailsProto.getPortsList()) {
builder.addPort(newPort(
Port.Name.valueOf(port.getName().toUpperCase()), port.getValue()));
}
if (datanodeDetailsProto.hasNetworkName()) {
builder.setNetworkName(datanodeDetailsProto.getNetworkName());
}
if (datanodeDetailsProto.hasNetworkLocation()) {
builder.setNetworkLocation(datanodeDetailsProto.getNetworkLocation());
}
return builder.build();
}
/**
* Returns a DatanodeDetails protobuf message from a datanode ID.
* @return HddsProtos.DatanodeDetailsProto
*/
public HddsProtos.DatanodeDetailsProto getProtoBufMessage() {
HddsProtos.DatanodeDetailsProto.Builder builder =
HddsProtos.DatanodeDetailsProto.newBuilder()
.setUuid(getUuidString());
if (ipAddress != null) {
builder.setIpAddress(ipAddress);
}
if (hostName != null) {
builder.setHostName(hostName);
}
if (certSerialId != null) {
builder.setCertSerialId(certSerialId);
}
if (!Strings.isNullOrEmpty(getNetworkName())) {
builder.setNetworkName(getNetworkName());
}
if (!Strings.isNullOrEmpty(getNetworkLocation())) {
builder.setNetworkLocation(getNetworkLocation());
}
for (Port port : ports) {
builder.addPorts(HddsProtos.Port.newBuilder()
.setName(port.getName().toString())
.setValue(port.getValue())
.build());
}
return builder.build();
}
@Override
public String toString() {
return uuid.toString() + "{" +
"ip: " +
ipAddress +
", host: " +
hostName +
", networkLocation: " +
getNetworkLocation() +
", certSerialId: " + certSerialId +
"}";
}
@Override
public int compareTo(DatanodeDetails that) {
return this.getUuid().compareTo(that.getUuid());
}
@Override
public boolean equals(Object obj) {
return obj instanceof DatanodeDetails &&
uuid.equals(((DatanodeDetails) obj).uuid);
}
@Override
public int hashCode() {
return uuid.hashCode();
}
/**
* Returns DatanodeDetails.Builder instance.
*
* @return DatanodeDetails.Builder
*/
public static Builder newBuilder() {
return new Builder();
}
/**
* Builder class for building DatanodeDetails.
*/
public static final class Builder {
private String id;
private String ipAddress;
private String hostName;
private String networkName;
private String networkLocation;
private List<Port> ports;
private String certSerialId;
/**
* Default private constructor. To create Builder instance use
* DatanodeDetails#newBuilder.
*/
private Builder() {
ports = new ArrayList<>();
}
/**
* Sets the DatanodeUuid.
*
* @param uuid DatanodeUuid
* @return DatanodeDetails.Builder
*/
public Builder setUuid(String uuid) {
this.id = uuid;
return this;
}
/**
* Sets the IP address of DataNode.
*
* @param ip address
* @return DatanodeDetails.Builder
*/
public Builder setIpAddress(String ip) {
this.ipAddress = ip;
return this;
}
/**
* Sets the hostname of DataNode.
*
* @param host hostname
* @return DatanodeDetails.Builder
*/
public Builder setHostName(String host) {
this.hostName = host;
return this;
}
/**
* Sets the network name of DataNode.
*
* @param name network name
* @return DatanodeDetails.Builder
*/
public Builder setNetworkName(String name) {
this.networkName = name;
return this;
}
/**
* Sets the network location of DataNode.
*
* @param loc location
* @return DatanodeDetails.Builder
*/
public Builder setNetworkLocation(String loc) {
this.networkLocation = loc;
return this;
}
/**
* Adds a DataNode Port.
*
* @param port DataNode port
*
* @return DatanodeDetails.Builder
*/
public Builder addPort(Port port) {
this.ports.add(port);
return this;
}
/**
* Adds certificate serial id.
*
* @param certId Serial id of SCM issued certificate.
*
* @return DatanodeDetails.Builder
*/
public Builder setCertSerialId(String certId) {
this.certSerialId = certId;
return this;
}
/**
* Builds and returns DatanodeDetails instance.
*
* @return DatanodeDetails
*/
public DatanodeDetails build() {
Preconditions.checkNotNull(id);
if (networkLocation == null) {
networkLocation = NetConstants.DEFAULT_RACK;
}
DatanodeDetails dn = new DatanodeDetails(id, ipAddress, hostName,
networkLocation, ports, certSerialId);
if (networkName != null) {
dn.setNetworkName(networkName);
}
return dn;
}
}
/**
* Constructs a new Port with name and value.
*
* @param name Name of the port
* @param value Port number
*
* @return {@code Port} instance
*/
public static Port newPort(Port.Name name, Integer value) {
return new Port(name, value);
}
/**
* Container to hold DataNode Port details.
*/
public static final class Port {
/**
* Ports that are supported in DataNode.
*/
public enum Name {
STANDALONE, RATIS, REST
}
private Name name;
private Integer value;
/**
* Private constructor for constructing Port object. Use
* DatanodeDetails#newPort to create a new Port object.
*
* @param name
* @param value
*/
private Port(Name name, Integer value) {
this.name = name;
this.value = value;
}
/**
* Returns the name of the port.
*
* @return Port name
*/
public Name getName() {
return name;
}
/**
* Returns the port number.
*
* @return Port number
*/
public Integer getValue() {
return value;
}
@Override
public int hashCode() {
return name.hashCode();
}
/**
* Ports are considered equal if they have the same name.
*
* @param anObject
* The object to compare this {@code Port} against
* @return {@code true} if the given object represents a {@code Port}
and has the same name, {@code false} otherwise
*/
@Override
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof Port) {
return name.equals(((Port) anObject).name);
}
return false;
}
}
/**
* Returns serial id of SCM issued certificate.
*
* @return certificate serial id
*/
public String getCertSerialId() {
return certSerialId;
}
/**
* Set certificate serial id of SCM issued certificate.
*
*/
public void setCertSerialId(String certSerialId) {
this.certSerialId = certSerialId;
}
}

View File

@ -1,80 +0,0 @@
/**
* 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.hdds.protocol;
import java.io.IOException;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.OzoneManagerDetailsProto;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.apache.hadoop.security.KerberosInfo;
/**
* The protocol used to perform security related operations with SCM.
*/
@KerberosInfo(
serverPrincipal = ScmConfigKeys.HDDS_SCM_KERBEROS_PRINCIPAL_KEY)
@InterfaceAudience.Private
public interface SCMSecurityProtocol {
@SuppressWarnings("checkstyle:ConstantName")
/**
* Version 1: Initial version.
*/
long versionID = 1L;
/**
* Get SCM signed certificate for DataNode.
*
* @param dataNodeDetails - DataNode Details.
* @param certSignReq - Certificate signing request.
* @return byte[] - SCM signed certificate.
*/
String getDataNodeCertificate(
DatanodeDetailsProto dataNodeDetails,
String certSignReq) throws IOException;
/**
* Get SCM signed certificate for OM.
*
* @param omDetails - DataNode Details.
* @param certSignReq - Certificate signing request.
* @return String - pem encoded SCM signed
* certificate.
*/
String getOMCertificate(OzoneManagerDetailsProto omDetails,
String certSignReq) throws IOException;
/**
* Get SCM signed certificate for given certificate serial id if it exists.
* Throws exception if it's not found.
*
* @param certSerialId - Certificate serial id.
* @return String - pem encoded SCM signed
* certificate with given cert id if it
* exists.
*/
String getCertificate(String certSerialId) throws IOException;
/**
* Get CA certificate.
*
* @return String - pem encoded CA certificate.
*/
String getCACertificate() throws IOException;
}

View File

@ -1,22 +0,0 @@
/**
* 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.
*/
/**
* This package contains HDDS protocol related classes.
*/
package org.apache.hadoop.hdds.protocol;

View File

@ -1,213 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.protocolPB;
import java.io.Closeable;
import java.io.IOException;
import java.util.function.Consumer;
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.OzoneManagerDetailsProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCACertificateRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertificateRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertRequestProto;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMSecurityRequest;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMSecurityRequest.Builder;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMSecurityResponse;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.Type;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.hadoop.ipc.ProtobufHelper;
import org.apache.hadoop.ipc.ProtocolTranslator;
import org.apache.hadoop.ipc.RPC;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import static org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetOMCertRequestProto;
/**
* This class is the client-side translator that forwards requests for
* {@link SCMSecurityProtocol} to the {@link SCMSecurityProtocolPB} proxy.
*/
public class SCMSecurityProtocolClientSideTranslatorPB implements
SCMSecurityProtocol, ProtocolTranslator, Closeable {
/**
* RpcController is not used and hence is set to null.
*/
private static final RpcController NULL_RPC_CONTROLLER = null;
private final SCMSecurityProtocolPB rpcProxy;
public SCMSecurityProtocolClientSideTranslatorPB(
SCMSecurityProtocolPB rpcProxy) {
this.rpcProxy = rpcProxy;
}
/**
* Helper method to wrap the request and send the message.
*/
private SCMSecurityResponse submitRequest(
SCMSecurityProtocolProtos.Type type,
Consumer<Builder> builderConsumer) throws IOException {
final SCMSecurityResponse response;
try {
Builder builder = SCMSecurityRequest.newBuilder()
.setCmdType(type)
.setTraceID(TracingUtil.exportCurrentSpan());
builderConsumer.accept(builder);
SCMSecurityRequest wrapper = builder.build();
response = rpcProxy.submitRequest(NULL_RPC_CONTROLLER, wrapper);
} catch (ServiceException ex) {
throw ProtobufHelper.getRemoteException(ex);
}
return response;
}
/**
* Closes this stream and releases any system resources associated
* with it. If the stream is already closed then invoking this
* method has no effect.
*
* <p> As noted in {@link AutoCloseable#close()}, cases where the
* close may fail require careful attention. It is strongly advised
* to relinquish the underlying resources and to internally
* <em>mark</em> the {@code Closeable} as closed, prior to throwing
* the {@code IOException}.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void close() throws IOException {
RPC.stopProxy(rpcProxy);
}
/**
* Get SCM signed certificate for DataNode.
*
* @param dataNodeDetails - DataNode Details.
* @param certSignReq - Certificate signing request.
* @return byte[] - SCM signed certificate.
*/
@Override
public String getDataNodeCertificate(DatanodeDetailsProto dataNodeDetails,
String certSignReq) throws IOException {
return getDataNodeCertificateChain(dataNodeDetails, certSignReq)
.getX509Certificate();
}
/**
* Get SCM signed certificate for OM.
*
* @param omDetails - OzoneManager Details.
* @param certSignReq - Certificate signing request.
* @return byte[] - SCM signed certificate.
*/
@Override
public String getOMCertificate(OzoneManagerDetailsProto omDetails,
String certSignReq) throws IOException {
return getOMCertChain(omDetails, certSignReq).getX509Certificate();
}
/**
* Get SCM signed certificate for OM.
*
* @param omDetails - OzoneManager Details.
* @param certSignReq - Certificate signing request.
* @return byte[] - SCM signed certificate.
*/
public SCMGetCertResponseProto getOMCertChain(
OzoneManagerDetailsProto omDetails, String certSignReq)
throws IOException {
SCMGetOMCertRequestProto request = SCMGetOMCertRequestProto
.newBuilder()
.setCSR(certSignReq)
.setOmDetails(omDetails)
.build();
return submitRequest(Type.GetOMCertificate,
builder -> builder.setGetOMCertRequest(request))
.getGetCertResponseProto();
}
/**
* Get SCM signed certificate with given serial id. Throws exception if
* certificate is not found.
*
* @param certSerialId - Certificate serial id.
* @return string - pem encoded certificate.
*/
@Override
public String getCertificate(String certSerialId) throws IOException {
SCMGetCertificateRequestProto request = SCMGetCertificateRequestProto
.newBuilder()
.setCertSerialId(certSerialId)
.build();
return submitRequest(Type.GetCertificate,
builder -> builder.setGetCertificateRequest(request))
.getGetCertResponseProto()
.getX509Certificate();
}
/**
* Get SCM signed certificate for Datanode.
*
* @param dnDetails - Datanode Details.
* @param certSignReq - Certificate signing request.
* @return byte[] - SCM signed certificate.
*/
public SCMGetCertResponseProto getDataNodeCertificateChain(
DatanodeDetailsProto dnDetails, String certSignReq)
throws IOException {
SCMGetDataNodeCertRequestProto request =
SCMGetDataNodeCertRequestProto.newBuilder()
.setCSR(certSignReq)
.setDatanodeDetails(dnDetails)
.build();
return submitRequest(Type.GetDataNodeCertificate,
builder -> builder.setGetDataNodeCertRequest(request))
.getGetCertResponseProto();
}
/**
* Get CA certificate.
*
* @return serial - Root certificate.
*/
@Override
public String getCACertificate() throws IOException {
SCMGetCACertificateRequestProto protoIns = SCMGetCACertificateRequestProto
.getDefaultInstance();
return submitRequest(Type.GetCACertificate,
builder -> builder.setGetCACertificateRequest(protoIns))
.getGetCertResponseProto().getX509Certificate();
}
/**
* Return the proxy object underlying this protocol translator.
*
* @return the proxy object underlying this protocol translator.
*/
@Override
public Object getUnderlyingProxyObject() {
return rpcProxy;
}
}

View File

@ -1,35 +0,0 @@
/**
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.hdds.protocolPB;
import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMSecurityProtocolService;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.apache.hadoop.ipc.ProtocolInfo;
import org.apache.hadoop.security.KerberosInfo;
/**
* Protocol for security related operations on SCM.
*/
@ProtocolInfo(protocolName =
"org.apache.hadoop.hdds.protocol.SCMSecurityProtocol",
protocolVersion = 1)
@KerberosInfo(serverPrincipal = ScmConfigKeys.HDDS_SCM_KERBEROS_PRINCIPAL_KEY)
public interface SCMSecurityProtocolPB extends
SCMSecurityProtocolService.BlockingInterface {
}

View File

@ -1,22 +0,0 @@
/**
* 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.hdds.protocolPB;
/**
* This package contains classes for wiring HDDS protobuf calls to rpc.
*/

View File

@ -1,107 +0,0 @@
/*
* 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.hdds.ratis;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandRequestProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.PutSmallFileRequestProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Type;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.WriteChunkRequestProto;
import org.apache.ratis.protocol.Message;
import org.apache.ratis.protocol.RaftGroupId;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.apache.ratis.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
import org.apache.ratis.util.JavaUtils;
import java.util.Objects;
import java.util.function.Supplier;
/**
* Implementing the {@link Message} interface
* for {@link ContainerCommandRequestProto}.
*/
public final class ContainerCommandRequestMessage implements Message {
public static ContainerCommandRequestMessage toMessage(
ContainerCommandRequestProto request, String traceId) {
final ContainerCommandRequestProto.Builder b
= ContainerCommandRequestProto.newBuilder(request);
if (traceId != null) {
b.setTraceID(traceId);
}
ByteString data = ByteString.EMPTY;
if (request.getCmdType() == Type.WriteChunk) {
final WriteChunkRequestProto w = request.getWriteChunk();
data = w.getData();
b.setWriteChunk(w.toBuilder().clearData());
} else if (request.getCmdType() == Type.PutSmallFile) {
final PutSmallFileRequestProto p = request.getPutSmallFile();
data = p.getData();
b.setPutSmallFile(p.toBuilder().setData(ByteString.EMPTY));
}
return new ContainerCommandRequestMessage(b.build(), data);
}
public static ContainerCommandRequestProto toProto(
ByteString bytes, RaftGroupId groupId)
throws InvalidProtocolBufferException {
final int i = 4 + bytes.asReadOnlyByteBuffer().getInt();
final ContainerCommandRequestProto header
= ContainerCommandRequestProto.parseFrom(bytes.substring(4, i));
// TODO: setting pipeline id can be avoided if the client is sending it.
// In such case, just have to validate the pipeline id.
final ContainerCommandRequestProto.Builder b = header.toBuilder();
if (groupId != null) {
b.setPipelineID(groupId.getUuid().toString());
}
final ByteString data = bytes.substring(i);
if (header.getCmdType() == Type.WriteChunk) {
b.setWriteChunk(b.getWriteChunkBuilder().setData(data));
} else if (header.getCmdType() == Type.PutSmallFile) {
b.setPutSmallFile(b.getPutSmallFileBuilder().setData(data));
}
return b.build();
}
private final ContainerCommandRequestProto header;
private final ByteString data;
private final Supplier<ByteString> contentSupplier
= JavaUtils.memoize(this::buildContent);
private ContainerCommandRequestMessage(
ContainerCommandRequestProto header, ByteString data) {
this.header = Objects.requireNonNull(header, "header == null");
this.data = Objects.requireNonNull(data, "data == null");
}
private ByteString buildContent() {
final ByteString headerBytes = header.toByteString();
return RatisHelper.int2ByteString(headerBytes.size())
.concat(headerBytes)
.concat(data);
}
@Override
public ByteString getContent() {
return contentSupplier.get();
}
@Override
public String toString() {
return header + ", data.size=" + data.size();
}
}

View File

@ -1,290 +0,0 @@
/*
* 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.hdds.ratis;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.security.exception.SCMSecurityException;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateServer;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.ratis.RaftConfigKeys;
import org.apache.ratis.client.RaftClient;
import org.apache.ratis.client.RaftClientConfigKeys;
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.grpc.GrpcConfigKeys;
import org.apache.ratis.grpc.GrpcFactory;
import org.apache.ratis.grpc.GrpcTlsConfig;
import org.apache.ratis.proto.RaftProtos;
import org.apache.ratis.protocol.RaftGroup;
import org.apache.ratis.protocol.RaftGroupId;
import org.apache.ratis.protocol.RaftPeer;
import org.apache.ratis.protocol.RaftPeerId;
import org.apache.ratis.retry.RetryPolicies;
import org.apache.ratis.retry.RetryPolicy;
import org.apache.ratis.rpc.RpcType;
import org.apache.ratis.rpc.SupportedRpcType;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.apache.ratis.util.SizeInBytes;
import org.apache.ratis.util.TimeDuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Ratis helper methods.
*/
public interface RatisHelper {
Logger LOG = LoggerFactory.getLogger(RatisHelper.class);
static String toRaftPeerIdString(DatanodeDetails id) {
return id.getUuidString();
}
static UUID toDatanodeId(String peerIdString) {
return UUID.fromString(peerIdString);
}
static UUID toDatanodeId(RaftPeerId peerId) {
return toDatanodeId(peerId.toString());
}
static UUID toDatanodeId(RaftProtos.RaftPeerProto peerId) {
return toDatanodeId(RaftPeerId.valueOf(peerId.getId()));
}
static String toRaftPeerAddressString(DatanodeDetails id) {
return id.getIpAddress() + ":" +
id.getPort(DatanodeDetails.Port.Name.RATIS).getValue();
}
static RaftPeerId toRaftPeerId(DatanodeDetails id) {
return RaftPeerId.valueOf(toRaftPeerIdString(id));
}
static RaftPeer toRaftPeer(DatanodeDetails id) {
return new RaftPeer(toRaftPeerId(id), toRaftPeerAddressString(id));
}
static List<RaftPeer> toRaftPeers(Pipeline pipeline) {
return toRaftPeers(pipeline.getNodes());
}
static <E extends DatanodeDetails> List<RaftPeer> toRaftPeers(
List<E> datanodes) {
return datanodes.stream().map(RatisHelper::toRaftPeer)
.collect(Collectors.toList());
}
/* TODO: use a dummy id for all groups for the moment.
* It should be changed to a unique id for each group.
*/
RaftGroupId DUMMY_GROUP_ID =
RaftGroupId.valueOf(ByteString.copyFromUtf8("AOzoneRatisGroup"));
RaftGroup EMPTY_GROUP = RaftGroup.valueOf(DUMMY_GROUP_ID,
Collections.emptyList());
static RaftGroup emptyRaftGroup() {
return EMPTY_GROUP;
}
static RaftGroup newRaftGroup(Collection<RaftPeer> peers) {
return peers.isEmpty()? emptyRaftGroup()
: RaftGroup.valueOf(DUMMY_GROUP_ID, peers);
}
static RaftGroup newRaftGroup(RaftGroupId groupId,
Collection<DatanodeDetails> peers) {
final List<RaftPeer> newPeers = peers.stream()
.map(RatisHelper::toRaftPeer)
.collect(Collectors.toList());
return peers.isEmpty() ? RaftGroup.valueOf(groupId, Collections.emptyList())
: RaftGroup.valueOf(groupId, newPeers);
}
static RaftGroup newRaftGroup(Pipeline pipeline) {
return RaftGroup.valueOf(RaftGroupId.valueOf(pipeline.getId().getId()),
toRaftPeers(pipeline));
}
static RaftClient newRaftClient(RpcType rpcType, Pipeline pipeline,
RetryPolicy retryPolicy, int maxOutStandingRequest,
GrpcTlsConfig tlsConfig, TimeDuration timeout) throws IOException {
return newRaftClient(rpcType, toRaftPeerId(pipeline.getFirstNode()),
newRaftGroup(RaftGroupId.valueOf(pipeline.getId().getId()),
pipeline.getNodes()), retryPolicy, maxOutStandingRequest, tlsConfig,
timeout);
}
static TimeDuration getClientRequestTimeout(Configuration conf) {
// Set the client requestTimeout
final TimeUnit timeUnit =
OzoneConfigKeys.DFS_RATIS_CLIENT_REQUEST_TIMEOUT_DURATION_DEFAULT
.getUnit();
final long duration = conf.getTimeDuration(
OzoneConfigKeys.DFS_RATIS_CLIENT_REQUEST_TIMEOUT_DURATION_KEY,
OzoneConfigKeys.DFS_RATIS_CLIENT_REQUEST_TIMEOUT_DURATION_DEFAULT
.getDuration(), timeUnit);
final TimeDuration clientRequestTimeout =
TimeDuration.valueOf(duration, timeUnit);
return clientRequestTimeout;
}
static RaftClient newRaftClient(RpcType rpcType, RaftPeer leader,
RetryPolicy retryPolicy, int maxOutstandingRequests,
GrpcTlsConfig tlsConfig, TimeDuration clientRequestTimeout) {
return newRaftClient(rpcType, leader.getId(),
newRaftGroup(new ArrayList<>(Arrays.asList(leader))), retryPolicy,
maxOutstandingRequests, tlsConfig, clientRequestTimeout);
}
static RaftClient newRaftClient(RpcType rpcType, RaftPeer leader,
RetryPolicy retryPolicy, int maxOutstandingRequests,
TimeDuration clientRequestTimeout) {
return newRaftClient(rpcType, leader.getId(),
newRaftGroup(new ArrayList<>(Arrays.asList(leader))), retryPolicy,
maxOutstandingRequests, null, clientRequestTimeout);
}
static RaftClient newRaftClient(RpcType rpcType, RaftPeerId leader,
RaftGroup group, RetryPolicy retryPolicy, int maxOutStandingRequest,
GrpcTlsConfig tlsConfig, TimeDuration clientRequestTimeout) {
if (LOG.isTraceEnabled()) {
LOG.trace("newRaftClient: {}, leader={}, group={}",
rpcType, leader, group);
}
final RaftProperties properties = new RaftProperties();
RaftConfigKeys.Rpc.setType(properties, rpcType);
RaftClientConfigKeys.Rpc
.setRequestTimeout(properties, clientRequestTimeout);
GrpcConfigKeys.setMessageSizeMax(properties,
SizeInBytes.valueOf(OzoneConsts.OZONE_SCM_CHUNK_MAX_SIZE));
GrpcConfigKeys.OutputStream.setOutstandingAppendsMax(properties,
maxOutStandingRequest);
RaftClient.Builder builder = RaftClient.newBuilder()
.setRaftGroup(group)
.setLeaderId(leader)
.setProperties(properties)
.setRetryPolicy(retryPolicy);
// TODO: GRPC TLS only for now, netty/hadoop RPC TLS support later.
if (tlsConfig != null && rpcType == SupportedRpcType.GRPC) {
builder.setParameters(GrpcFactory.newRaftParameters(tlsConfig));
}
return builder.build();
}
// For External gRPC client to server with gRPC TLS.
// No mTLS for external client as SCM CA does not issued certificates for them
static GrpcTlsConfig createTlsClientConfig(SecurityConfig conf,
X509Certificate caCert) {
GrpcTlsConfig tlsConfig = null;
if (conf.isSecurityEnabled() && conf.isGrpcTlsEnabled()) {
tlsConfig = new GrpcTlsConfig(null, null,
caCert, false);
}
return tlsConfig;
}
// For Internal gRPC client from SCM to DN with gRPC TLS
static GrpcTlsConfig createTlsClientConfigForSCM(SecurityConfig conf,
CertificateServer certificateServer) throws IOException {
if (conf.isSecurityEnabled() && conf.isGrpcTlsEnabled()) {
try {
X509Certificate caCert =
CertificateCodec.getX509Certificate(
certificateServer.getCACertificate());
return new GrpcTlsConfig(null, null,
caCert, false);
} catch (CertificateException ex) {
throw new SCMSecurityException("Fail to find SCM CA certificate.", ex);
}
}
return null;
}
// For gRPC server running DN container service with gPRC TLS
// No mTLS as the channel is shared for for external client, which
// does not have SCM CA issued certificates.
// In summary:
// authenticate from server to client is via TLS.
// authenticate from client to server is via block token (or container token).
static GrpcTlsConfig createTlsServerConfigForDN(SecurityConfig conf,
CertificateClient caClient) {
if (conf.isSecurityEnabled() && conf.isGrpcTlsEnabled()) {
return new GrpcTlsConfig(
caClient.getPrivateKey(), caClient.getCertificate(),
null, false);
}
return null;
}
static RetryPolicy createRetryPolicy(Configuration conf) {
int maxRetryCount =
conf.getInt(OzoneConfigKeys.DFS_RATIS_CLIENT_REQUEST_MAX_RETRIES_KEY,
OzoneConfigKeys.
DFS_RATIS_CLIENT_REQUEST_MAX_RETRIES_DEFAULT);
long retryInterval = conf.getTimeDuration(OzoneConfigKeys.
DFS_RATIS_CLIENT_REQUEST_RETRY_INTERVAL_KEY, OzoneConfigKeys.
DFS_RATIS_CLIENT_REQUEST_RETRY_INTERVAL_DEFAULT
.toIntExact(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
TimeDuration sleepDuration =
TimeDuration.valueOf(retryInterval, TimeUnit.MILLISECONDS);
RetryPolicy retryPolicy = RetryPolicies
.retryUpToMaximumCountWithFixedSleep(maxRetryCount, sleepDuration);
return retryPolicy;
}
static Long getMinReplicatedIndex(
Collection<RaftProtos.CommitInfoProto> commitInfos) {
return commitInfos.stream().map(RaftProtos.CommitInfoProto::getCommitIndex)
.min(Long::compareTo).orElse(null);
}
static ByteString int2ByteString(int n) {
final ByteString.Output out = ByteString.newOutput();
try(DataOutputStream dataOut = new DataOutputStream(out)) {
dataOut.writeInt(n);
} catch (IOException e) {
throw new IllegalStateException(
"Failed to write integer n = " + n + " to a ByteString.", e);
}
return out.toByteString();
}
}

View File

@ -1,22 +0,0 @@
/**
* 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.hdds.ratis;
/**
* This package contains classes related to Apache Ratis.
*/

View File

@ -1,62 +0,0 @@
/*
* 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.hdds.scm;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.apache.ratis.thirdparty.com.google.protobuf.UnsafeByteOperations;
import java.nio.ByteBuffer;
import java.util.function.Function;
/**
* Helper class to create a conversion function from ByteBuffer to ByteString
* based on the property
* {@link OzoneConfigKeys#OZONE_UNSAFEBYTEOPERATIONS_ENABLED} in the
* Ozone configuration.
*/
public final class ByteStringConversion {
private ByteStringConversion(){} // no instantiation.
/**
* Creates the conversion function to be used to convert ByteBuffers to
* ByteString instances to be used in protobuf messages.
*
* @param config the Ozone configuration
* @return the conversion function defined by
* {@link OzoneConfigKeys#OZONE_UNSAFEBYTEOPERATIONS_ENABLED}
* @see <pre>ByteBuffer</pre>
*/
public static Function<ByteBuffer, ByteString> createByteBufferConversion(
Configuration config){
boolean unsafeEnabled =
config!=null && config.getBoolean(
OzoneConfigKeys.OZONE_UNSAFEBYTEOPERATIONS_ENABLED,
OzoneConfigKeys.OZONE_UNSAFEBYTEOPERATIONS_ENABLED_DEFAULT);
if (unsafeEnabled) {
return buffer -> UnsafeByteOperations.unsafeWrap(buffer);
} else {
return buffer -> {
ByteString retval = ByteString.copyFrom(buffer);
buffer.flip();
return retval;
};
}
}
}

View File

@ -1,375 +0,0 @@
/*
* 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.hdds.scm;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.ratis.proto.RaftProtos.ReplicationLevel;
import org.apache.ratis.util.TimeDuration;
import java.util.concurrent.TimeUnit;
/**
* This class contains constants for configuration keys used in SCM.
*/
@InterfaceAudience.Public
@InterfaceStability.Unstable
public final class ScmConfigKeys {
// Location of SCM DB files. For now we just support a single
// metadata dir but in future we may support multiple for redundancy or
// performance.
public static final String OZONE_SCM_DB_DIRS = "ozone.scm.db.dirs";
public static final String DFS_CONTAINER_RATIS_ENABLED_KEY
= "dfs.container.ratis.enabled";
public static final boolean DFS_CONTAINER_RATIS_ENABLED_DEFAULT
= false;
public static final String DFS_CONTAINER_RATIS_RPC_TYPE_KEY
= "dfs.container.ratis.rpc.type";
public static final String DFS_CONTAINER_RATIS_RPC_TYPE_DEFAULT
= "GRPC";
public static final String DFS_CONTAINER_RATIS_NUM_WRITE_CHUNK_THREADS_KEY
= "dfs.container.ratis.num.write.chunk.threads";
public static final int DFS_CONTAINER_RATIS_NUM_WRITE_CHUNK_THREADS_DEFAULT
= 60;
public static final String DFS_CONTAINER_RATIS_REPLICATION_LEVEL_KEY
= "dfs.container.ratis.replication.level";
public static final ReplicationLevel
DFS_CONTAINER_RATIS_REPLICATION_LEVEL_DEFAULT = ReplicationLevel.MAJORITY;
public static final String DFS_CONTAINER_RATIS_NUM_CONTAINER_OP_EXECUTORS_KEY
= "dfs.container.ratis.num.container.op.executors";
public static final int DFS_CONTAINER_RATIS_NUM_CONTAINER_OP_EXECUTORS_DEFAULT
= 10;
public static final String DFS_CONTAINER_RATIS_SEGMENT_SIZE_KEY =
"dfs.container.ratis.segment.size";
public static final String DFS_CONTAINER_RATIS_SEGMENT_SIZE_DEFAULT =
"1MB";
public static final String DFS_CONTAINER_RATIS_SEGMENT_PREALLOCATED_SIZE_KEY =
"dfs.container.ratis.segment.preallocated.size";
public static final String
DFS_CONTAINER_RATIS_SEGMENT_PREALLOCATED_SIZE_DEFAULT = "16KB";
public static final String
DFS_CONTAINER_RATIS_STATEMACHINEDATA_SYNC_TIMEOUT =
"dfs.container.ratis.statemachinedata.sync.timeout";
public static final TimeDuration
DFS_CONTAINER_RATIS_STATEMACHINEDATA_SYNC_TIMEOUT_DEFAULT =
TimeDuration.valueOf(10, TimeUnit.SECONDS);
public static final String
DFS_CONTAINER_RATIS_STATEMACHINEDATA_SYNC_RETRIES =
"dfs.container.ratis.statemachinedata.sync.retries";
public static final int
DFS_CONTAINER_RATIS_STATEMACHINEDATA_SYNC_RETRIES_DEFAULT = -1;
public static final String
DFS_CONTAINER_RATIS_STATEMACHINE_MAX_PENDING_APPLY_TXNS =
"dfs.container.ratis.statemachine.max.pending.apply-transactions";
// The default value of maximum number of pending state machine apply
// transactions is kept same as default snapshot threshold.
public static final int
DFS_CONTAINER_RATIS_STATEMACHINE_MAX_PENDING_APPLY_TXNS_DEFAULT =
100000;
public static final String DFS_CONTAINER_RATIS_LOG_QUEUE_NUM_ELEMENTS =
"dfs.container.ratis.log.queue.num-elements";
public static final int DFS_CONTAINER_RATIS_LOG_QUEUE_NUM_ELEMENTS_DEFAULT =
1024;
public static final String DFS_CONTAINER_RATIS_LOG_QUEUE_BYTE_LIMIT =
"dfs.container.ratis.log.queue.byte-limit";
public static final String DFS_CONTAINER_RATIS_LOG_QUEUE_BYTE_LIMIT_DEFAULT =
"4GB";
public static final String
DFS_CONTAINER_RATIS_LOG_APPENDER_QUEUE_NUM_ELEMENTS =
"dfs.container.ratis.log.appender.queue.num-elements";
public static final int
DFS_CONTAINER_RATIS_LOG_APPENDER_QUEUE_NUM_ELEMENTS_DEFAULT = 1;
public static final String DFS_CONTAINER_RATIS_LOG_APPENDER_QUEUE_BYTE_LIMIT =
"dfs.container.ratis.log.appender.queue.byte-limit";
public static final String
DFS_CONTAINER_RATIS_LOG_APPENDER_QUEUE_BYTE_LIMIT_DEFAULT = "32MB";
public static final String DFS_CONTAINER_RATIS_LOG_PURGE_GAP =
"dfs.container.ratis.log.purge.gap";
// TODO: Set to 1024 once RATIS issue around purge is fixed.
public static final int DFS_CONTAINER_RATIS_LOG_PURGE_GAP_DEFAULT =
1000000;
public static final String DFS_CONTAINER_RATIS_LEADER_NUM_PENDING_REQUESTS =
"dfs.container.ratis.leader.num.pending.requests";
public static final int
DFS_CONTAINER_RATIS_LEADER_NUM_PENDING_REQUESTS_DEFAULT = 4096;
// expiry interval stateMachineData cache entry inside containerStateMachine
public static final String
DFS_CONTAINER_RATIS_STATEMACHINEDATA_CACHE_EXPIRY_INTERVAL =
"dfs.container.ratis.statemachine.cache.expiry.interval";
public static final String
DFS_CONTAINER_RATIS_STATEMACHINEDATA_CACHE_EXPIRY_INTERVAL_DEFAULT =
"10s";
public static final String DFS_RATIS_CLIENT_REQUEST_TIMEOUT_DURATION_KEY =
"dfs.ratis.client.request.timeout.duration";
public static final TimeDuration
DFS_RATIS_CLIENT_REQUEST_TIMEOUT_DURATION_DEFAULT =
TimeDuration.valueOf(3000, TimeUnit.MILLISECONDS);
public static final String DFS_RATIS_CLIENT_REQUEST_MAX_RETRIES_KEY =
"dfs.ratis.client.request.max.retries";
public static final int DFS_RATIS_CLIENT_REQUEST_MAX_RETRIES_DEFAULT = 180;
public static final String DFS_RATIS_CLIENT_REQUEST_RETRY_INTERVAL_KEY =
"dfs.ratis.client.request.retry.interval";
public static final TimeDuration
DFS_RATIS_CLIENT_REQUEST_RETRY_INTERVAL_DEFAULT =
TimeDuration.valueOf(1000, TimeUnit.MILLISECONDS);
public static final String DFS_RATIS_SERVER_RETRY_CACHE_TIMEOUT_DURATION_KEY =
"dfs.ratis.server.retry-cache.timeout.duration";
public static final TimeDuration
DFS_RATIS_SERVER_RETRY_CACHE_TIMEOUT_DURATION_DEFAULT =
TimeDuration.valueOf(600000, TimeUnit.MILLISECONDS);
public static final String DFS_RATIS_SERVER_REQUEST_TIMEOUT_DURATION_KEY =
"dfs.ratis.server.request.timeout.duration";
public static final TimeDuration
DFS_RATIS_SERVER_REQUEST_TIMEOUT_DURATION_DEFAULT =
TimeDuration.valueOf(3000, TimeUnit.MILLISECONDS);
public static final String
DFS_RATIS_LEADER_ELECTION_MINIMUM_TIMEOUT_DURATION_KEY =
"dfs.ratis.leader.election.minimum.timeout.duration";
public static final TimeDuration
DFS_RATIS_LEADER_ELECTION_MINIMUM_TIMEOUT_DURATION_DEFAULT =
TimeDuration.valueOf(5, TimeUnit.SECONDS);
public static final String DFS_RATIS_SNAPSHOT_THRESHOLD_KEY =
"dfs.ratis.snapshot.threshold";
public static final long DFS_RATIS_SNAPSHOT_THRESHOLD_DEFAULT = 100000;
public static final String DFS_RATIS_SERVER_FAILURE_DURATION_KEY =
"dfs.ratis.server.failure.duration";
public static final TimeDuration
DFS_RATIS_SERVER_FAILURE_DURATION_DEFAULT =
TimeDuration.valueOf(120, TimeUnit.SECONDS);
// TODO : this is copied from OzoneConsts, may need to move to a better place
public static final String OZONE_SCM_CHUNK_SIZE_KEY = "ozone.scm.chunk.size";
// 16 MB by default
public static final String OZONE_SCM_CHUNK_SIZE_DEFAULT = "16MB";
public static final String OZONE_SCM_CLIENT_PORT_KEY =
"ozone.scm.client.port";
public static final int OZONE_SCM_CLIENT_PORT_DEFAULT = 9860;
public static final String OZONE_SCM_DATANODE_PORT_KEY =
"ozone.scm.datanode.port";
public static final int OZONE_SCM_DATANODE_PORT_DEFAULT = 9861;
// OZONE_OM_PORT_DEFAULT = 9862
public static final String OZONE_SCM_BLOCK_CLIENT_PORT_KEY =
"ozone.scm.block.client.port";
public static final int OZONE_SCM_BLOCK_CLIENT_PORT_DEFAULT = 9863;
public static final String OZONE_SCM_SECURITY_SERVICE_PORT_KEY =
"ozone.scm.security.service.port";
public static final int OZONE_SCM_SECURITY_SERVICE_PORT_DEFAULT = 9961;
// Container service client
public static final String OZONE_SCM_CLIENT_ADDRESS_KEY =
"ozone.scm.client.address";
public static final String OZONE_SCM_CLIENT_BIND_HOST_KEY =
"ozone.scm.client.bind.host";
public static final String OZONE_SCM_CLIENT_BIND_HOST_DEFAULT =
"0.0.0.0";
// Block service client
public static final String OZONE_SCM_BLOCK_CLIENT_ADDRESS_KEY =
"ozone.scm.block.client.address";
public static final String OZONE_SCM_BLOCK_CLIENT_BIND_HOST_KEY =
"ozone.scm.block.client.bind.host";
public static final String OZONE_SCM_BLOCK_CLIENT_BIND_HOST_DEFAULT =
"0.0.0.0";
// SCM Security service address.
public static final String OZONE_SCM_SECURITY_SERVICE_ADDRESS_KEY =
"ozone.scm.security.service.address";
public static final String OZONE_SCM_SECURITY_SERVICE_BIND_HOST_KEY =
"ozone.scm.security.service.bind.host";
public static final String OZONE_SCM_SECURITY_SERVICE_BIND_HOST_DEFAULT =
"0.0.0.0";
public static final String OZONE_SCM_DATANODE_ADDRESS_KEY =
"ozone.scm.datanode.address";
public static final String OZONE_SCM_DATANODE_BIND_HOST_KEY =
"ozone.scm.datanode.bind.host";
public static final String OZONE_SCM_DATANODE_BIND_HOST_DEFAULT =
"0.0.0.0";
public static final String OZONE_SCM_HTTP_ENABLED_KEY =
"ozone.scm.http.enabled";
public static final String OZONE_SCM_HTTP_BIND_HOST_KEY =
"ozone.scm.http-bind-host";
public static final String OZONE_SCM_HTTPS_BIND_HOST_KEY =
"ozone.scm.https-bind-host";
public static final String OZONE_SCM_HTTP_ADDRESS_KEY =
"ozone.scm.http-address";
public static final String OZONE_SCM_HTTPS_ADDRESS_KEY =
"ozone.scm.https-address";
public static final String HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY =
"hdds.scm.kerberos.keytab.file";
public static final String HDDS_SCM_KERBEROS_PRINCIPAL_KEY =
"hdds.scm.kerberos.principal";
public static final String OZONE_SCM_HTTP_BIND_HOST_DEFAULT = "0.0.0.0";
public static final int OZONE_SCM_HTTP_BIND_PORT_DEFAULT = 9876;
public static final int OZONE_SCM_HTTPS_BIND_PORT_DEFAULT = 9877;
public static final String HDDS_REST_HTTP_ADDRESS_KEY =
"hdds.rest.http-address";
public static final String HDDS_REST_HTTP_ADDRESS_DEFAULT = "0.0.0.0:9880";
public static final String HDDS_DATANODE_DIR_KEY = "hdds.datanode.dir";
public static final String HDDS_REST_CSRF_ENABLED_KEY =
"hdds.rest.rest-csrf.enabled";
public static final boolean HDDS_REST_CSRF_ENABLED_DEFAULT = false;
public static final String HDDS_REST_NETTY_HIGH_WATERMARK =
"hdds.rest.netty.high.watermark";
public static final int HDDS_REST_NETTY_HIGH_WATERMARK_DEFAULT = 65536;
public static final int HDDS_REST_NETTY_LOW_WATERMARK_DEFAULT = 32768;
public static final String HDDS_REST_NETTY_LOW_WATERMARK =
"hdds.rest.netty.low.watermark";
public static final String OZONE_SCM_HANDLER_COUNT_KEY =
"ozone.scm.handler.count.key";
public static final int OZONE_SCM_HANDLER_COUNT_DEFAULT = 10;
public static final String OZONE_SCM_SECURITY_HANDLER_COUNT_KEY =
"ozone.scm.security.handler.count.key";
public static final int OZONE_SCM_SECURITY_HANDLER_COUNT_DEFAULT = 2;
public static final String OZONE_SCM_DEADNODE_INTERVAL =
"ozone.scm.dead.node.interval";
public static final String OZONE_SCM_DEADNODE_INTERVAL_DEFAULT =
"10m";
public static final String OZONE_SCM_HEARTBEAT_PROCESS_INTERVAL =
"ozone.scm.heartbeat.thread.interval";
public static final String OZONE_SCM_HEARTBEAT_PROCESS_INTERVAL_DEFAULT =
"3s";
public static final String OZONE_SCM_STALENODE_INTERVAL =
"ozone.scm.stale.node.interval";
public static final String OZONE_SCM_STALENODE_INTERVAL_DEFAULT =
"5m";
public static final String OZONE_SCM_HEARTBEAT_RPC_TIMEOUT =
"ozone.scm.heartbeat.rpc-timeout";
public static final String OZONE_SCM_HEARTBEAT_RPC_TIMEOUT_DEFAULT =
"1s";
/**
* Defines how frequently we will log the missing of heartbeat to a specific
* SCM. In the default case we will write a warning message for each 10
* sequential heart beats that we miss to a specific SCM. This is to avoid
* overrunning the log with lots of HB missed Log statements.
*/
public static final String OZONE_SCM_HEARTBEAT_LOG_WARN_INTERVAL_COUNT =
"ozone.scm.heartbeat.log.warn.interval.count";
public static final int OZONE_SCM_HEARTBEAT_LOG_WARN_DEFAULT =
10;
// ozone.scm.names key is a set of DNS | DNS:PORT | IP Address | IP:PORT.
// Written as a comma separated string. e.g. scm1, scm2:8020, 7.7.7.7:7777
//
// If this key is not specified datanodes will not be able to find
// SCM. The SCM membership can be dynamic, so this key should contain
// all possible SCM names. Once the SCM leader is discovered datanodes will
// get the right list of SCMs to heartbeat to from the leader.
// While it is good for the datanodes to know the names of all SCM nodes,
// it is sufficient to actually know the name of on working SCM. That SCM
// will be able to return the information about other SCMs that are part of
// the SCM replicated Log.
//
//In case of a membership change, any one of the SCM machines will be
// able to send back a new list to the datanodes.
public static final String OZONE_SCM_NAMES = "ozone.scm.names";
public static final int OZONE_SCM_DEFAULT_PORT =
OZONE_SCM_DATANODE_PORT_DEFAULT;
// The path where datanode ID is to be written to.
// if this value is not set then container startup will fail.
public static final String OZONE_SCM_DATANODE_ID_DIR =
"ozone.scm.datanode.id.dir";
public static final String OZONE_SCM_DB_CACHE_SIZE_MB =
"ozone.scm.db.cache.size.mb";
public static final int OZONE_SCM_DB_CACHE_SIZE_DEFAULT = 128;
public static final String OZONE_SCM_CONTAINER_SIZE =
"ozone.scm.container.size";
public static final String OZONE_SCM_CONTAINER_SIZE_DEFAULT = "5GB";
public static final String OZONE_SCM_CONTAINER_PLACEMENT_IMPL_KEY =
"ozone.scm.container.placement.impl";
public static final String OZONE_SCM_PIPELINE_OWNER_CONTAINER_COUNT =
"ozone.scm.pipeline.owner.container.count";
public static final int OZONE_SCM_PIPELINE_OWNER_CONTAINER_COUNT_DEFAULT = 3;
public static final String
OZONE_SCM_KEY_VALUE_CONTAINER_DELETION_CHOOSING_POLICY =
"ozone.scm.keyvalue.container.deletion-choosing.policy";
public static final String OZONE_SCM_CONTAINER_CREATION_LEASE_TIMEOUT =
"ozone.scm.container.creation.lease.timeout";
public static final String
OZONE_SCM_CONTAINER_CREATION_LEASE_TIMEOUT_DEFAULT = "60s";
public static final String OZONE_SCM_PIPELINE_DESTROY_TIMEOUT =
"ozone.scm.pipeline.destroy.timeout";
public static final String OZONE_SCM_PIPELINE_DESTROY_TIMEOUT_DEFAULT =
"66s";
public static final String OZONE_SCM_PIPELINE_CREATION_INTERVAL =
"ozone.scm.pipeline.creation.interval";
public static final String OZONE_SCM_PIPELINE_CREATION_INTERVAL_DEFAULT =
"120s";
public static final String OZONE_SCM_BLOCK_DELETION_MAX_RETRY =
"ozone.scm.block.deletion.max.retry";
public static final int OZONE_SCM_BLOCK_DELETION_MAX_RETRY_DEFAULT = 4096;
public static final String HDDS_SCM_WATCHER_TIMEOUT =
"hdds.scm.watcher.timeout";
public static final String HDDS_SCM_WATCHER_TIMEOUT_DEFAULT =
"10m";
public static final String
HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY =
"hdds.scm.http.kerberos.principal";
public static final String
HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY =
"hdds.scm.http.kerberos.keytab";
// Network topology
public static final String OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE =
"ozone.scm.network.topology.schema.file";
public static final String OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE_DEFAULT =
"network-topology-default.xml";
public static final String HDDS_TRACING_ENABLED = "hdds.tracing.enabled";
public static final boolean HDDS_TRACING_ENABLED_DEFAULT = true;
/**
* Never constructed.
*/
private ScmConfigKeys() {
}
}

View File

@ -1,81 +0,0 @@
/*
* 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.hdds.scm;
/**
* ScmInfo wraps the result returned from SCM#getScmInfo which
* contains clusterId and the SCM Id.
*/
public final class ScmInfo {
private String clusterId;
private String scmId;
/**
* Builder for ScmInfo.
*/
public static class Builder {
private String clusterId;
private String scmId;
/**
* sets the cluster id.
* @param cid clusterId to be set
* @return Builder for ScmInfo
*/
public Builder setClusterId(String cid) {
this.clusterId = cid;
return this;
}
/**
* sets the scmId.
* @param id scmId
* @return Builder for scmInfo
*/
public Builder setScmId(String id) {
this.scmId = id;
return this;
}
public ScmInfo build() {
return new ScmInfo(clusterId, scmId);
}
}
private ScmInfo(String clusterId, String scmId) {
this.clusterId = clusterId;
this.scmId = scmId;
}
/**
* Gets the clusterId from the Version file.
* @return ClusterId
*/
public String getClusterId() {
return clusterId;
}
/**
* Gets the SCM Id from the Version file.
* @return SCM Id
*/
public String getScmId() {
return scmId;
}
}

View File

@ -1,82 +0,0 @@
/*
* 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.hdds.scm;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos
.ContainerCommandResponseProto;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* This class represents the reply from XceiverClient.
*/
public class XceiverClientReply {
private CompletableFuture<ContainerCommandResponseProto> response;
private Long logIndex;
/**
* List of datanodes where the command got executed and reply is received.
* If there is an exception in the reply, these datanodes will inform
* about the servers where there is a failure.
*/
private List<DatanodeDetails> datanodes;
public XceiverClientReply(
CompletableFuture<ContainerCommandResponseProto> response) {
this(response, null);
}
public XceiverClientReply(
CompletableFuture<ContainerCommandResponseProto> response,
List<DatanodeDetails> datanodes) {
this.logIndex = (long) 0;
this.response = response;
this.datanodes = datanodes == null ? new ArrayList<>() : datanodes;
}
public CompletableFuture<ContainerCommandResponseProto> getResponse() {
return response;
}
public long getLogIndex() {
return logIndex;
}
public void setLogIndex(Long logIndex) {
this.logIndex = logIndex;
}
public List<DatanodeDetails> getDatanodes() {
return datanodes;
}
public void addDatanode(DatanodeDetails dn) {
datanodes.add(dn);
}
public void setResponse(
CompletableFuture<ContainerCommandResponseProto> response) {
this.response = response;
}
}

View File

@ -1,179 +0,0 @@
/*
* 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.hdds.scm;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandRequestProto;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerCommandResponseProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hdds.scm.storage.CheckedBiFunction;
/**
* A Client for the storageContainer protocol.
*/
public abstract class XceiverClientSpi implements Closeable {
final private AtomicInteger referenceCount;
private boolean isEvicted;
XceiverClientSpi() {
this.referenceCount = new AtomicInteger(0);
this.isEvicted = false;
}
void incrementReference() {
this.referenceCount.incrementAndGet();
}
void decrementReference() {
this.referenceCount.decrementAndGet();
cleanup();
}
void setEvicted() {
isEvicted = true;
cleanup();
}
// close the xceiverClient only if,
// 1) there is no refcount on the client
// 2) it has been evicted from the cache.
private void cleanup() {
if (referenceCount.get() == 0 && isEvicted) {
close();
}
}
@VisibleForTesting
public int getRefcount() {
return referenceCount.get();
}
/**
* Connects to the leader in the pipeline.
*/
public abstract void connect() throws Exception;
/**
* Connects to the leader in the pipeline using encoded token. To be used
* in a secure cluster.
*/
public abstract void connect(String encodedToken) throws Exception;
@Override
public abstract void close();
/**
* Returns the pipeline of machines that host the container used by this
* client.
*
* @return pipeline of machines that host the container
*/
public abstract Pipeline getPipeline();
/**
* Sends a given command to server and gets the reply back.
* @param request Request
* @return Response to the command
* @throws IOException
*/
public ContainerCommandResponseProto sendCommand(
ContainerCommandRequestProto request) throws IOException {
try {
XceiverClientReply reply;
reply = sendCommandAsync(request);
ContainerCommandResponseProto responseProto = reply.getResponse().get();
return responseProto;
} catch (ExecutionException | InterruptedException e) {
throw new IOException("Failed to command " + request, e);
}
}
/**
* Sends a given command to server and gets the reply back along with
* the server associated info.
* @param request Request
* @param validators functions to validate the response
* @return Response to the command
* @throws IOException
*/
public ContainerCommandResponseProto sendCommand(
ContainerCommandRequestProto request, List<CheckedBiFunction> validators)
throws IOException {
try {
XceiverClientReply reply;
reply = sendCommandAsync(request);
ContainerCommandResponseProto responseProto = reply.getResponse().get();
for (CheckedBiFunction function : validators) {
function.apply(request, responseProto);
}
return responseProto;
} catch (ExecutionException | InterruptedException e) {
throw new IOException("Failed to command " + request, e);
}
}
/**
* Sends a given command to server gets a waitable future back.
*
* @param request Request
* @return Response to the command
* @throws IOException
*/
public abstract XceiverClientReply
sendCommandAsync(ContainerCommandRequestProto request)
throws IOException, ExecutionException, InterruptedException;
/**
* Returns pipeline Type.
*
* @return - {Stand_Alone, Ratis or Chained}
*/
public abstract HddsProtos.ReplicationType getPipelineType();
/**
* Check if an specfic commitIndex is replicated to majority/all servers.
* @param index index to watch for
* @param timeout timeout provided for the watch operation to complete
* @return reply containing the min commit index replicated to all or majority
* servers in case of a failure
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
* @throws IOException
*/
public abstract XceiverClientReply watchForCommit(long index, long timeout)
throws InterruptedException, ExecutionException, TimeoutException,
IOException;
/**
* returns the min commit index replicated to all servers.
* @return min commit index replicated to all servers.
*/
public abstract long getReplicatedMinCommitIndex();
}

View File

@ -1,241 +0,0 @@
/*
* 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.hdds.scm.client;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hdds.scm.container.common.helpers.ContainerWithPipeline;
import org.apache.hadoop.hdds.scm.container.ContainerInfo;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos
.ContainerDataProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
/**
* The interface to call into underlying container layer.
*
* Written as interface to allow easy testing: implement a mock container layer
* for standalone testing of CBlock API without actually calling into remote
* containers. Actual container layer can simply re-implement this.
*
* NOTE this is temporarily needed class. When SCM containers are full-fledged,
* this interface will likely be removed.
*/
@InterfaceStability.Unstable
public interface ScmClient extends Closeable {
/**
* Creates a Container on SCM and returns the pipeline.
* @return ContainerInfo
* @throws IOException
*/
ContainerWithPipeline createContainer(String owner) throws IOException;
/**
* Gets a container by Name -- Throws if the container does not exist.
* @param containerId - Container ID
* @return Pipeline
* @throws IOException
*/
ContainerInfo getContainer(long containerId) throws IOException;
/**
* Gets a container by Name -- Throws if the container does not exist.
* @param containerId - Container ID
* @return ContainerWithPipeline
* @throws IOException
*/
ContainerWithPipeline getContainerWithPipeline(long containerId)
throws IOException;
/**
* Close a container.
*
* @param containerId - ID of the container.
* @param pipeline - Pipeline where the container is located.
* @throws IOException
*/
void closeContainer(long containerId, Pipeline pipeline) throws IOException;
/**
* Close a container.
*
* @param containerId - ID of the container.
* @throws IOException
*/
void closeContainer(long containerId) throws IOException;
/**
* Deletes an existing container.
* @param containerId - ID of the container.
* @param pipeline - Pipeline that represents the container.
* @param force - true to forcibly delete the container.
* @throws IOException
*/
void deleteContainer(long containerId, Pipeline pipeline, boolean force)
throws IOException;
/**
* Deletes an existing container.
* @param containerId - ID of the container.
* @param force - true to forcibly delete the container.
* @throws IOException
*/
void deleteContainer(long containerId, boolean force) throws IOException;
/**
* Lists a range of containers and get their info.
*
* @param startContainerID start containerID.
* @param count count must be {@literal >} 0.
*
* @return a list of pipeline.
* @throws IOException
*/
List<ContainerInfo> listContainer(long startContainerID,
int count) throws IOException;
/**
* Read meta data from an existing container.
* @param containerID - ID of the container.
* @param pipeline - Pipeline where the container is located.
* @return ContainerInfo
* @throws IOException
*/
ContainerDataProto readContainer(long containerID, Pipeline pipeline)
throws IOException;
/**
* Read meta data from an existing container.
* @param containerID - ID of the container.
* @return ContainerInfo
* @throws IOException
*/
ContainerDataProto readContainer(long containerID)
throws IOException;
/**
* Gets the container size -- Computed by SCM from Container Reports.
* @param containerID - ID of the container.
* @return number of bytes used by this container.
* @throws IOException
*/
long getContainerSize(long containerID) throws IOException;
/**
* Creates a Container on SCM and returns the pipeline.
* @param type - Replication Type.
* @param replicationFactor - Replication Factor
* @return ContainerInfo
* @throws IOException - in case of error.
*/
ContainerWithPipeline createContainer(HddsProtos.ReplicationType type,
HddsProtos.ReplicationFactor replicationFactor,
String owner) throws IOException;
/**
* Returns a set of Nodes that meet a query criteria.
* @param nodeStatuses - Criteria that we want the node to have.
* @param queryScope - Query scope - Cluster or pool.
* @param poolName - if it is pool, a pool name is required.
* @return A set of nodes that meet the requested criteria.
* @throws IOException
*/
List<HddsProtos.Node> queryNode(HddsProtos.NodeState nodeStatuses,
HddsProtos.QueryScope queryScope, String poolName) throws IOException;
/**
* Creates a specified replication pipeline.
* @param type - Type
* @param factor - Replication factor
* @param nodePool - Set of machines.
* @throws IOException
*/
Pipeline createReplicationPipeline(HddsProtos.ReplicationType type,
HddsProtos.ReplicationFactor factor, HddsProtos.NodePool nodePool)
throws IOException;
/**
* Returns the list of active Pipelines.
*
* @return list of Pipeline
* @throws IOException in case of any exception
*/
List<Pipeline> listPipelines() throws IOException;
/**
* Activates the pipeline given a pipeline ID.
*
* @param pipelineID PipelineID to activate.
* @throws IOException In case of exception while activating the pipeline
*/
void activatePipeline(HddsProtos.PipelineID pipelineID) throws IOException;
/**
* Deactivates the pipeline given a pipeline ID.
*
* @param pipelineID PipelineID to deactivate.
* @throws IOException In case of exception while deactivating the pipeline
*/
void deactivatePipeline(HddsProtos.PipelineID pipelineID) throws IOException;
/**
* Closes the pipeline given a pipeline ID.
*
* @param pipelineID PipelineID to close.
* @throws IOException In case of exception while closing the pipeline
*/
void closePipeline(HddsProtos.PipelineID pipelineID) throws IOException;
/**
* Check if SCM is in safe mode.
*
* @return Returns true if SCM is in safe mode else returns false.
* @throws IOException
*/
boolean inSafeMode() throws IOException;
/**
* Force SCM out of safe mode.
*
* @return returns true if operation is successful.
* @throws IOException
*/
boolean forceExitSafeMode() throws IOException;
/**
* Start ReplicationManager.
*/
void startReplicationManager() throws IOException;
/**
* Stop ReplicationManager.
*/
void stopReplicationManager() throws IOException;
/**
* Returns ReplicationManager status.
*
* @return True if ReplicationManager is running, false otherwise.
*/
boolean getReplicationManagerStatus() throws IOException;
}

View File

@ -1,24 +0,0 @@
/**
* 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.hdds.scm.client;
/**
* This package contains classes for the client of the storage container
* protocol.
*/

View File

@ -1,46 +0,0 @@
/**
* 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.hdds.scm.container;
import java.io.IOException;
/**
* Signals that ContainerException of some sort has occurred. This is parent
* of all the exceptions thrown by ContainerManager.
*/
public class ContainerException extends IOException {
/**
* Constructs an {@code ContainerException} with {@code null}
* as its error detail message.
*/
public ContainerException() {
super();
}
/**
* Constructs an {@code ContainerException} with the specified detail message.
*
* @param message
* The detail message (which is saved for later retrieval
* by the {@link #getMessage()} method)
*/
public ContainerException(String message) {
super(message);
}
}

View File

@ -1,107 +0,0 @@
/*
* 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.hdds.scm.container;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Longs;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* Container ID is an integer that is a value between 1..MAX_CONTAINER ID.
* <p>
* We are creating a specific type for this to avoid mixing this with
* normal integers in code.
*/
public final class ContainerID implements Comparable<ContainerID> {
private final long id;
// TODO: make this private.
/**
* Constructs ContainerID.
*
* @param id int
*/
public ContainerID(long id) {
this.id = id;
}
/**
* Factory method for creation of ContainerID.
* @param containerID long
* @return ContainerID.
*/
public static ContainerID valueof(final long containerID) {
Preconditions.checkState(containerID > 0,
"Container ID should be a positive long. "+ containerID);
return new ContainerID(containerID);
}
/**
* Returns int representation of ID.
*
* @return int
*/
public long getId() {
return id;
}
public byte[] getBytes() {
return Longs.toByteArray(id);
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final ContainerID that = (ContainerID) o;
return new EqualsBuilder()
.append(getId(), that.getId())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(61, 71)
.append(getId())
.toHashCode();
}
@Override
public int compareTo(final ContainerID that) {
Preconditions.checkNotNull(that);
return new CompareToBuilder()
.append(this.getId(), that.getId())
.build();
}
@Override
public String toString() {
return "#" + id;
}
}

View File

@ -1,471 +0,0 @@
/*
* 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.hdds.scm.container;
import static java.lang.Math.max;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.base.Preconditions;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.Comparator;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationType;
import org.apache.hadoop.hdds.scm.pipeline.PipelineID;
import org.apache.hadoop.util.Time;
/**
* Class wraps ozone container info.
*/
public class ContainerInfo implements Comparator<ContainerInfo>,
Comparable<ContainerInfo>, Externalizable {
private static final ObjectWriter WRITER;
private static final String SERIALIZATION_ERROR_MSG = "Java serialization not"
+ " supported. Use protobuf instead.";
static {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper
.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
WRITER = mapper.writerWithDefaultPrettyPrinter();
}
private HddsProtos.LifeCycleState state;
@JsonIgnore
private PipelineID pipelineID;
private ReplicationFactor replicationFactor;
private ReplicationType replicationType;
private long usedBytes;
private long numberOfKeys;
private long lastUsed;
// The wall-clock ms since the epoch at which the current state enters.
private long stateEnterTime;
private String owner;
private long containerID;
private long deleteTransactionId;
// The sequenceId of a close container cannot change, and all the
// container replica should have the same sequenceId.
private long sequenceId;
/**
* Allows you to maintain private data on ContainerInfo. This is not
* serialized via protobuf, just allows us to maintain some private data.
*/
@JsonIgnore
private byte[] data;
@SuppressWarnings("parameternumber")
ContainerInfo(
long containerID,
HddsProtos.LifeCycleState state,
PipelineID pipelineID,
long usedBytes,
long numberOfKeys,
long stateEnterTime,
String owner,
long deleteTransactionId,
long sequenceId,
ReplicationFactor replicationFactor,
ReplicationType repType) {
this.containerID = containerID;
this.pipelineID = pipelineID;
this.usedBytes = usedBytes;
this.numberOfKeys = numberOfKeys;
this.lastUsed = Time.monotonicNow();
this.state = state;
this.stateEnterTime = stateEnterTime;
this.owner = owner;
this.deleteTransactionId = deleteTransactionId;
this.sequenceId = sequenceId;
this.replicationFactor = replicationFactor;
this.replicationType = repType;
}
/**
* Needed for serialization findbugs.
*/
public ContainerInfo() {
}
public static ContainerInfo fromProtobuf(HddsProtos.ContainerInfoProto info) {
ContainerInfo.Builder builder = new ContainerInfo.Builder();
return builder.setPipelineID(
PipelineID.getFromProtobuf(info.getPipelineID()))
.setUsedBytes(info.getUsedBytes())
.setNumberOfKeys(info.getNumberOfKeys())
.setState(info.getState())
.setStateEnterTime(info.getStateEnterTime())
.setOwner(info.getOwner())
.setContainerID(info.getContainerID())
.setDeleteTransactionId(info.getDeleteTransactionId())
.setReplicationFactor(info.getReplicationFactor())
.setReplicationType(info.getReplicationType())
.build();
}
public long getContainerID() {
return containerID;
}
public HddsProtos.LifeCycleState getState() {
return state;
}
public void setState(HddsProtos.LifeCycleState state) {
this.state = state;
}
public long getStateEnterTime() {
return stateEnterTime;
}
public ReplicationFactor getReplicationFactor() {
return replicationFactor;
}
public PipelineID getPipelineID() {
return pipelineID;
}
public long getUsedBytes() {
return usedBytes;
}
public void setUsedBytes(long value) {
usedBytes = value;
}
public long getNumberOfKeys() {
return numberOfKeys;
}
public void setNumberOfKeys(long value) {
numberOfKeys = value;
}
public long getDeleteTransactionId() {
return deleteTransactionId;
}
public long getSequenceId() {
return sequenceId;
}
public void updateDeleteTransactionId(long transactionId) {
deleteTransactionId = max(transactionId, deleteTransactionId);
}
public void updateSequenceId(long sequenceID) {
assert (isOpen() || state == HddsProtos.LifeCycleState.QUASI_CLOSED);
sequenceId = max(sequenceID, sequenceId);
}
public ContainerID containerID() {
return new ContainerID(getContainerID());
}
/**
* Gets the last used time from SCM's perspective.
*
* @return time in milliseconds.
*/
public long getLastUsed() {
return lastUsed;
}
public ReplicationType getReplicationType() {
return replicationType;
}
public void updateLastUsedTime() {
lastUsed = Time.monotonicNow();
}
public HddsProtos.ContainerInfoProto getProtobuf() {
HddsProtos.ContainerInfoProto.Builder builder =
HddsProtos.ContainerInfoProto.newBuilder();
Preconditions.checkState(containerID > 0);
return builder.setContainerID(getContainerID())
.setUsedBytes(getUsedBytes())
.setNumberOfKeys(getNumberOfKeys()).setState(getState())
.setStateEnterTime(getStateEnterTime()).setContainerID(getContainerID())
.setDeleteTransactionId(getDeleteTransactionId())
.setPipelineID(getPipelineID().getProtobuf())
.setReplicationFactor(getReplicationFactor())
.setReplicationType(getReplicationType())
.setOwner(getOwner())
.build();
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
@Override
public String toString() {
return "ContainerInfo{"
+ "id=" + containerID
+ ", state=" + state
+ ", pipelineID=" + pipelineID
+ ", stateEnterTime=" + stateEnterTime
+ ", owner=" + owner
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ContainerInfo that = (ContainerInfo) o;
return new EqualsBuilder()
.append(getContainerID(), that.getContainerID())
// TODO : Fix this later. If we add these factors some tests fail.
// So Commenting this to continue and will enforce this with
// Changes in pipeline where we remove Container Name to
// SCMContainerinfo from Pipeline.
// .append(pipeline.getFactor(), that.pipeline.getFactor())
// .append(pipeline.getType(), that.pipeline.getType())
.append(owner, that.owner)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(11, 811)
.append(getContainerID())
.append(getOwner())
.toHashCode();
}
/**
* Compares its two arguments for order. Returns a negative integer, zero, or
* a positive integer as the first argument is less than, equal to, or greater
* than the second.<p>
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the first
* argument is less than, equal to, or greater than the second.
* @throws NullPointerException if an argument is null and this comparator
* does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
@Override
public int compare(ContainerInfo o1, ContainerInfo o2) {
return Long.compare(o1.getLastUsed(), o2.getLastUsed());
}
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less than,
* equal to, or greater than the specified object.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object is
* less than, equal to, or greater than the specified object.
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
@Override
public int compareTo(ContainerInfo o) {
return this.compare(this, o);
}
/**
* Returns a JSON string of this object.
*
* @return String - json string
* @throws IOException
*/
public String toJsonString() throws IOException {
return WRITER.writeValueAsString(this);
}
/**
* Returns private data that is set on this containerInfo.
*
* @return blob, the user can interpret it any way they like.
*/
public byte[] getData() {
if (this.data != null) {
return Arrays.copyOf(this.data, this.data.length);
} else {
return null;
}
}
/**
* Set private data on ContainerInfo object.
*
* @param data -- private data.
*/
public void setData(byte[] data) {
if (data != null) {
this.data = Arrays.copyOf(data, data.length);
}
}
/**
* Throws IOException as default java serialization is not supported. Use
* serialization via protobuf instead.
*
* @param out the stream to write the object to
* @throws IOException Includes any I/O exceptions that may occur
* @serialData Overriding methods should use this tag to describe
* the data layout of this Externalizable object.
* List the sequence of element types and, if possible,
* relate the element to a public/protected field and/or
* method of this Externalizable class.
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
throw new IOException(SERIALIZATION_ERROR_MSG);
}
/**
* Throws IOException as default java serialization is not supported. Use
* serialization via protobuf instead.
*
* @param in the stream to read data from in order to restore the object
* @throws IOException if I/O errors occur
* @throws ClassNotFoundException If the class for an object being
* restored cannot be found.
*/
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
throw new IOException(SERIALIZATION_ERROR_MSG);
}
/**
* Builder class for ContainerInfo.
*/
public static class Builder {
private HddsProtos.LifeCycleState state;
private long used;
private long keys;
private long stateEnterTime;
private String owner;
private long containerID;
private long deleteTransactionId;
private long sequenceId;
private PipelineID pipelineID;
private ReplicationFactor replicationFactor;
private ReplicationType replicationType;
public Builder setReplicationType(
ReplicationType repType) {
this.replicationType = repType;
return this;
}
public Builder setPipelineID(PipelineID pipelineId) {
this.pipelineID = pipelineId;
return this;
}
public Builder setReplicationFactor(ReplicationFactor repFactor) {
this.replicationFactor = repFactor;
return this;
}
public Builder setContainerID(long id) {
Preconditions.checkState(id >= 0);
this.containerID = id;
return this;
}
public Builder setState(HddsProtos.LifeCycleState lifeCycleState) {
this.state = lifeCycleState;
return this;
}
public Builder setUsedBytes(long bytesUsed) {
this.used = bytesUsed;
return this;
}
public Builder setNumberOfKeys(long keyCount) {
this.keys = keyCount;
return this;
}
public Builder setStateEnterTime(long time) {
this.stateEnterTime = time;
return this;
}
public Builder setOwner(String containerOwner) {
this.owner = containerOwner;
return this;
}
public Builder setDeleteTransactionId(long deleteTransactionID) {
this.deleteTransactionId = deleteTransactionID;
return this;
}
public Builder setSequenceId(long sequenceID) {
this.sequenceId = sequenceID;
return this;
}
public ContainerInfo build() {
return new ContainerInfo(containerID, state, pipelineID,
used, keys, stateEnterTime, owner, deleteTransactionId,
sequenceId, replicationFactor, replicationType);
}
}
/**
* Check if a container is in open state, this will check if the
* container is either open or closing state. Any containers in these states
* is managed as an open container by SCM.
*/
public boolean isOpen() {
return state == HddsProtos.LifeCycleState.OPEN
|| state == HddsProtos.LifeCycleState.CLOSING;
}
}

View File

@ -1,44 +0,0 @@
/**
* 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.hdds.scm.container;
/**
* Signals that a container is missing from ContainerManager.
*/
public class ContainerNotFoundException extends ContainerException {
/**
* Constructs an {@code ContainerNotFoundException} with {@code null}
* as its error detail message.
*/
public ContainerNotFoundException() {
super();
}
/**
* Constructs an {@code ContainerNotFoundException} with the specified
* detail message.
*
* @param message
* The detail message (which is saved for later retrieval
* by the {@link #getMessage()} method)
*/
public ContainerNotFoundException(String message) {
super(message);
}
}

View File

@ -1,45 +0,0 @@
/**
* 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.hdds.scm.container;
/**
* Signals that a ContainerReplica is missing from the Container in
* ContainerManager.
*/
public class ContainerReplicaNotFoundException extends ContainerException {
/**
* Constructs an {@code ContainerReplicaNotFoundException} with {@code null}
* as its error detail message.
*/
public ContainerReplicaNotFoundException() {
super();
}
/**
* Constructs an {@code ContainerReplicaNotFoundException} with the
* specified detail message.
*
* @param message
* The detail message (which is saved for later retrieval
* by the {@link #getMessage()} method)
*/
public ContainerReplicaNotFoundException(String message) {
super(message);
}
}

View File

@ -1,66 +0,0 @@
/*
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.client.ContainerBlockID;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
/**
* Allocated block wraps the result returned from SCM#allocateBlock which
* contains a Pipeline and the key.
*/
public final class AllocatedBlock {
private Pipeline pipeline;
private ContainerBlockID containerBlockID;
/**
* Builder for AllocatedBlock.
*/
public static class Builder {
private Pipeline pipeline;
private ContainerBlockID containerBlockID;
public Builder setPipeline(Pipeline p) {
this.pipeline = p;
return this;
}
public Builder setContainerBlockID(ContainerBlockID blockId) {
this.containerBlockID = blockId;
return this;
}
public AllocatedBlock build() {
return new AllocatedBlock(pipeline, containerBlockID);
}
}
private AllocatedBlock(Pipeline pipeline, ContainerBlockID containerBlockID) {
this.pipeline = pipeline;
this.containerBlockID = containerBlockID;
}
public Pipeline getPipeline() {
return pipeline;
}
public ContainerBlockID getBlockID() {
return containerBlockID;
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
/**
* Exceptions thrown when a block is yet to be committed on the datanode.
*/
public class BlockNotCommittedException extends StorageContainerException {
/**
* Constructs an {@code IOException} with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by
* the {@link #getMessage()} method)
*/
public BlockNotCommittedException(String message) {
super(message, ContainerProtos.Result.BLOCK_NOT_COMMITTED);
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
/**
* Exceptions thrown when a write/update opearation is done on non-open
* container.
*/
public class ContainerNotOpenException extends StorageContainerException {
/**
* Constructs an {@code IOException} with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by
* the {@link #getMessage()} method)
*/
public ContainerNotOpenException(String message) {
super(message, ContainerProtos.Result.CONTAINER_NOT_OPEN);
}
}

View File

@ -1,137 +0,0 @@
/*
* 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.hdds.scm.container.common.helpers;
import java.util.Comparator;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.container.ContainerInfo;
import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.scm.pipeline.UnknownPipelineStateException;
/**
* Class wraps ozone container info.
*/
public class ContainerWithPipeline implements Comparator<ContainerWithPipeline>,
Comparable<ContainerWithPipeline> {
private final ContainerInfo containerInfo;
private final Pipeline pipeline;
public ContainerWithPipeline(ContainerInfo containerInfo, Pipeline pipeline) {
this.containerInfo = containerInfo;
this.pipeline = pipeline;
}
public ContainerInfo getContainerInfo() {
return containerInfo;
}
public Pipeline getPipeline() {
return pipeline;
}
public static ContainerWithPipeline fromProtobuf(
HddsProtos.ContainerWithPipeline allocatedContainer)
throws UnknownPipelineStateException {
return new ContainerWithPipeline(
ContainerInfo.fromProtobuf(allocatedContainer.getContainerInfo()),
Pipeline.getFromProtobuf(allocatedContainer.getPipeline()));
}
public HddsProtos.ContainerWithPipeline getProtobuf()
throws UnknownPipelineStateException {
HddsProtos.ContainerWithPipeline.Builder builder =
HddsProtos.ContainerWithPipeline.newBuilder();
builder.setContainerInfo(getContainerInfo().getProtobuf())
.setPipeline(getPipeline().getProtobufMessage());
return builder.build();
}
@Override
public String toString() {
return containerInfo.toString() + " | " + pipeline.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ContainerWithPipeline that = (ContainerWithPipeline) o;
return new EqualsBuilder()
.append(getContainerInfo(), that.getContainerInfo())
.append(getPipeline(), that.getPipeline())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(11, 811)
.append(getContainerInfo())
.append(getPipeline())
.toHashCode();
}
/**
* Compares its two arguments for order. Returns a negative integer, zero, or
* a positive integer as the first argument is less than, equal to, or greater
* than the second.<p>
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the first
* argument is less than, equal to, or greater than the second.
* @throws NullPointerException if an argument is null and this comparator
* does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
@Override
public int compare(ContainerWithPipeline o1, ContainerWithPipeline o2) {
return o1.getContainerInfo().compareTo(o2.getContainerInfo());
}
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less than,
* equal to, or greater than the specified object.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object is
* less than, equal to, or greater than the specified object.
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
@Override
public int compareTo(ContainerWithPipeline o) {
return this.compare(this, o);
}
}

View File

@ -1,53 +0,0 @@
/**
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.client.BlockID;
import static org.apache.hadoop.hdds.protocol.proto
.ScmBlockLocationProtocolProtos.DeleteScmBlockResult;
/**
* Class wraps storage container manager block deletion results.
*/
public class DeleteBlockResult {
private BlockID blockID;
private DeleteScmBlockResult.Result result;
public DeleteBlockResult(final BlockID blockID,
final DeleteScmBlockResult.Result result) {
this.blockID = blockID;
this.result = result;
}
/**
* Get block id deleted.
* @return block id.
*/
public BlockID getBlockID() {
return blockID;
}
/**
* Get key deletion result.
* @return key deletion result.
*/
public DeleteScmBlockResult.Result getResult() {
return result;
}
}

View File

@ -1,111 +0,0 @@
/**
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.scm.container.ContainerID;
import org.apache.hadoop.hdds.scm.pipeline.PipelineID;
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
/**
* This class contains set of dns and containers which ozone client provides
* to be handed over to SCM when block allocation request comes.
*/
public class ExcludeList {
private final List<DatanodeDetails> datanodes;
private final List<ContainerID> containerIds;
private final List<PipelineID> pipelineIds;
public ExcludeList() {
datanodes = new ArrayList<>();
containerIds = new ArrayList<>();
pipelineIds = new ArrayList<>();
}
public List<ContainerID> getContainerIds() {
return containerIds;
}
public List<DatanodeDetails> getDatanodes() {
return datanodes;
}
public void addDatanodes(Collection<DatanodeDetails> dns) {
datanodes.addAll(dns);
}
public void addDatanode(DatanodeDetails dn) {
datanodes.add(dn);
}
public void addConatinerId(ContainerID containerId) {
containerIds.add(containerId);
}
public void addPipeline(PipelineID pipelineId) {
pipelineIds.add(pipelineId);
}
public List<PipelineID> getPipelineIds() {
return pipelineIds;
}
public HddsProtos.ExcludeListProto getProtoBuf() {
HddsProtos.ExcludeListProto.Builder builder =
HddsProtos.ExcludeListProto.newBuilder();
containerIds
.forEach(id -> builder.addContainerIds(id.getId()));
datanodes.forEach(dn -> {
builder.addDatanodes(dn.getUuidString());
});
pipelineIds.forEach(pipelineID -> {
builder.addPipelineIds(pipelineID.getProtobuf());
});
return builder.build();
}
public static ExcludeList getFromProtoBuf(
HddsProtos.ExcludeListProto excludeListProto) {
ExcludeList excludeList = new ExcludeList();
excludeListProto.getContainerIdsList().forEach(id -> {
excludeList.addConatinerId(ContainerID.valueof(id));
});
DatanodeDetails.Builder builder = DatanodeDetails.newBuilder();
excludeListProto.getDatanodesList().forEach(dn -> {
builder.setUuid(dn);
excludeList.addDatanode(builder.build());
});
excludeListProto.getPipelineIdsList().forEach(pipelineID -> {
excludeList.addPipeline(PipelineID.getFromProtobuf(pipelineID));
});
return excludeList;
}
public void clear() {
datanodes.clear();
containerIds.clear();
pipelineIds.clear();
}
}

View File

@ -1,35 +0,0 @@
/*
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
/**
* Exceptions thrown when a container is in invalid state while doing a I/O.
*/
public class InvalidContainerStateException extends StorageContainerException {
/**
* Constructs an {@code IOException} with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by
* the {@link #getMessage()} method)
*/
public InvalidContainerStateException(String message) {
super(message, ContainerProtos.Result.INVALID_CONTAINER_STATE);
}
}

View File

@ -1,104 +0,0 @@
/*
* 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.hdds.scm.container.common.helpers;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import java.io.IOException;
/**
* Exceptions thrown from the Storage Container.
*/
public class StorageContainerException extends IOException {
private ContainerProtos.Result result;
/**
* Constructs an {@code IOException} with {@code null}
* as its error detail message.
*/
public StorageContainerException(ContainerProtos.Result result) {
this.result = result;
}
/**
* Constructs an {@code IOException} with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by
* the {@link #getMessage()} method)
* @param result - The result code
*/
public StorageContainerException(String message,
ContainerProtos.Result result) {
super(message);
this.result = result;
}
/**
* Constructs an {@code IOException} with the specified detail message
* and cause.
* <p>
* <p> Note that the detail message associated with {@code cause} is
* <i>not</i> automatically incorporated into this exception's detail
* message.
*
* @param message The detail message (which is saved for later retrieval by
* the {@link #getMessage()} method)
*
* @param cause The cause (which is saved for later retrieval by the {@link
* #getCause()} method). (A null value is permitted, and indicates that the
* cause is nonexistent or unknown.)
*
* @param result - The result code
* @since 1.6
*/
public StorageContainerException(String message, Throwable cause,
ContainerProtos.Result result) {
super(message, cause);
this.result = result;
}
/**
* Constructs an {@code IOException} with the specified cause and a
* detail message of {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of {@code cause}).
* This constructor is useful for IO exceptions that are little more
* than wrappers for other throwables.
*
* @param cause The cause (which is saved for later retrieval by the {@link
* #getCause()} method). (A null value is permitted, and indicates that the
* cause is nonexistent or unknown.)
* @param result - The result code
* @since 1.6
*/
public StorageContainerException(Throwable cause, ContainerProtos.Result
result) {
super(cause);
this.result = result;
}
/**
* Returns Result.
*
* @return Result.
*/
public ContainerProtos.Result getResult() {
return result;
}
}

View File

@ -1,22 +0,0 @@
/**
* 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.hdds.scm.container.common.helpers;
/**
Contains protocol buffer helper classes and utilites used in
impl.
**/

View File

@ -1,18 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.scm.container;

View File

@ -1,45 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.scm.container.placement.algorithms;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import java.io.IOException;
import java.util.List;
/**
* A ContainerPlacementPolicy support choosing datanodes to build replication
* pipeline with specified constraints.
*/
public interface ContainerPlacementPolicy {
/**
* Given the replication factor and size required, return set of datanodes
* that satisfy the nodes and size requirement.
*
* @param excludedNodes - list of nodes to be excluded.
* @param favoredNodes - list of nodes preferred.
* @param nodesRequired - number of datanodes required.
* @param sizeRequired - size required for the container or block.
* @return list of datanodes chosen.
* @throws IOException
*/
List<DatanodeDetails> chooseDatanodes(List<DatanodeDetails> excludedNodes,
List<DatanodeDetails> favoredNodes, int nodesRequired, long sizeRequired)
throws IOException;
}

View File

@ -1,21 +0,0 @@
/**
* 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.hdds.scm.container.placement.algorithms;
/**
Contains container placement policy interface definition.
**/

View File

@ -1,127 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.scm.exceptions;
import java.io.IOException;
/**
* Exception thrown by SCM.
*/
public class SCMException extends IOException {
private final ResultCodes result;
/**
* Constructs an {@code IOException} with {@code null}
* as its error detail message.
*/
public SCMException(ResultCodes result) {
this.result = result;
}
/**
* Constructs an {@code IOException} with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by
* the
* {@link #getMessage()} method)
*/
public SCMException(String message, ResultCodes result) {
super(message);
this.result = result;
}
/**
* Constructs an {@code IOException} with the specified detail message
* and cause.
* <p>
* <p> Note that the detail message associated with {@code cause} is
* <i>not</i> automatically incorporated into this exception's detail
* message.
*
* @param message The detail message (which is saved for later retrieval by
* the
* {@link #getMessage()} method)
* @param cause The cause (which is saved for later retrieval by the {@link
* #getCause()} method). (A null value is permitted, and indicates that the
* cause is nonexistent or unknown.)
* @since 1.6
*/
public SCMException(String message, Throwable cause, ResultCodes result) {
super(message, cause);
this.result = result;
}
/**
* Constructs an {@code IOException} with the specified cause and a
* detail message of {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of {@code cause}).
* This constructor is useful for IO exceptions that are little more
* than wrappers for other throwables.
*
* @param cause The cause (which is saved for later retrieval by the {@link
* #getCause()} method). (A null value is permitted, and indicates that the
* cause is nonexistent or unknown.)
* @since 1.6
*/
public SCMException(Throwable cause, ResultCodes result) {
super(cause);
this.result = result;
}
/**
* Returns resultCode.
* @return ResultCode
*/
public ResultCodes getResult() {
return result;
}
/**
* Error codes to make it easy to decode these exceptions.
*/
public enum ResultCodes {
OK,
FAILED_TO_LOAD_NODEPOOL,
FAILED_TO_FIND_NODE_IN_POOL,
FAILED_TO_FIND_HEALTHY_NODES,
FAILED_TO_FIND_NODES_WITH_SPACE,
FAILED_TO_FIND_SUITABLE_NODE,
INVALID_CAPACITY,
INVALID_BLOCK_SIZE,
SAFE_MODE_EXCEPTION,
FAILED_TO_LOAD_OPEN_CONTAINER,
FAILED_TO_ALLOCATE_CONTAINER,
FAILED_TO_CHANGE_CONTAINER_STATE,
FAILED_TO_CHANGE_PIPELINE_STATE,
CONTAINER_EXISTS,
FAILED_TO_FIND_CONTAINER,
FAILED_TO_FIND_CONTAINER_WITH_SPACE,
BLOCK_EXISTS,
FAILED_TO_FIND_BLOCK,
IO_EXCEPTION,
UNEXPECTED_CONTAINER_STATE,
SCM_NOT_INITIALIZED,
DUPLICATE_DATANODE,
NO_SUCH_DATANODE,
NO_REPLICA_FOUND,
FAILED_TO_FIND_ACTIVE_PIPELINE,
FAILED_TO_INIT_CONTAINER_PLACEMENT_POLICY,
FAILED_TO_ALLOCATE_ENOUGH_BLOCKS,
INTERNAL_ERROR
}
}

View File

@ -1,21 +0,0 @@
/**
* 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.hdds.scm.exceptions;
/**
Exception objects for the SCM Server.
*/

View File

@ -1,85 +0,0 @@
/**
* 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.hdds.scm.net;
import java.util.Collection;
import java.util.List;
/**
* The interface defines an inner node in a network topology.
* An inner node represents network topology entities, such as data center,
* rack, switch or logical group.
*/
public interface InnerNode extends Node {
/** A factory interface to get new InnerNode instance. */
interface Factory<N extends InnerNode> {
/** Construct an InnerNode from name, location, parent, level and cost. */
N newInnerNode(String name, String location, InnerNode parent, int level,
int cost);
}
/**
* Add node <i>n</i> to the subtree of this node.
* @param n node to be added
* @return true if the node is added; false otherwise
*/
boolean add(Node n);
/**
* Remove node <i>n</i> from the subtree of this node.
* @param n node to be deleted
*/
void remove(Node n);
/**
* Given a node's string representation, return a reference to the node.
* @param loc string location of the format /dc/rack/nodegroup/node
* @return null if the node is not found
*/
Node getNode(String loc);
/**
* @return number of its all nodes at level <i>level</i>. Here level is a
* relative level. If level is 1, means node itself. If level is 2, means its
* direct children, and so on.
**/
int getNumOfNodes(int level);
/**
* Get <i>leafIndex</i> leaf of this subtree.
*
* @param leafIndex an indexed leaf of the node
* @return the leaf node corresponding to the given index.
*/
Node getLeaf(int leafIndex);
/**
* Get <i>leafIndex</i> leaf of this subtree.
*
* @param leafIndex ode's index, start from 0, skip the nodes in
* excludedScope and excludedNodes with ancestorGen
* @param excludedScopes the excluded scopes
* @param excludedNodes nodes to be excluded. If ancestorGen is not 0,
* the chosen node will not share same ancestor with
* those in excluded nodes at the specified generation
* @param ancestorGen ignored with value is 0
* @return the leaf node corresponding to the given index
*/
Node getLeaf(int leafIndex, List<String> excludedScopes,
Collection<Node> excludedNodes, int ancestorGen);
}

View File

@ -1,509 +0,0 @@
/**
* 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.hdds.scm.net;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.hadoop.hdds.scm.net.NetConstants.PATH_SEPARATOR_STR;
import static org.apache.hadoop.hdds.scm.net.NetConstants.PATH_SEPARATOR;
/**
* A thread safe class that implements InnerNode interface.
*/
public class InnerNodeImpl extends NodeImpl implements InnerNode {
protected static class Factory implements InnerNode.Factory<InnerNodeImpl> {
protected Factory() {}
public InnerNodeImpl newInnerNode(String name, String location,
InnerNode parent, int level, int cost) {
return new InnerNodeImpl(name, location, parent, level, cost);
}
}
static final Factory FACTORY = new Factory();
// a map of node's network name to Node for quick search and keep
// the insert order
private final HashMap<String, Node> childrenMap =
new LinkedHashMap<String, Node>();
// number of descendant leaves under this node
private int numOfLeaves;
// LOGGER
public static final Logger LOG = LoggerFactory.getLogger(InnerNodeImpl.class);
/**
* Construct an InnerNode from its name, network location, parent, level and
* its cost.
**/
protected InnerNodeImpl(String name, String location, InnerNode parent,
int level, int cost) {
super(name, location, parent, level, cost);
}
/** @return the number of children this node has */
private int getNumOfChildren() {
return childrenMap.size();
}
/** @return its leaf nodes number */
@Override
public int getNumOfLeaves() {
return numOfLeaves;
}
/**
* @return number of its all nodes at level <i>level</i>. Here level is a
* relative level. If level is 1, means node itself. If level is 2, means its
* direct children, and so on.
**/
public int getNumOfNodes(int level) {
Preconditions.checkArgument(level > 0);
int count = 0;
if (level == 1) {
count += 1;
} else if (level == 2) {
count += getNumOfChildren();
} else {
for (Node node: childrenMap.values()) {
if (node instanceof InnerNode) {
count += ((InnerNode)node).getNumOfNodes(level -1);
} else {
throw new RuntimeException("Cannot support Level:" + level +
" on this node " + this.toString());
}
}
}
return count;
}
/**
* Judge if this node is the parent of a leave node <i>n</i>.
* @return true if this node is the parent of <i>n</i>
*/
private boolean isLeafParent() {
if (childrenMap.isEmpty()) {
return true;
}
Node child = childrenMap.values().iterator().next();
return child instanceof InnerNode ? false : true;
}
/**
* Judge if this node is the parent of node <i>node</i>.
* @param node a node
* @return true if this node is the parent of <i>n</i>
*/
private boolean isParent(Node node) {
return node.getNetworkLocation().equals(this.getNetworkFullPath());
}
/**
* Add node <i>node</i> to the subtree of this node.
* @param node node to be added
* @return true if the node is added, false if is only updated
*/
public boolean add(Node node) {
if (!isAncestor(node)) {
throw new IllegalArgumentException(node.getNetworkName()
+ ", which is located at " + node.getNetworkLocation()
+ ", is not a descendant of " + this.getNetworkFullPath());
}
if (isParent(node)) {
// this node is the parent, then add it directly
node.setParent(this);
node.setLevel(this.getLevel() + 1);
Node current = childrenMap.put(node.getNetworkName(), node);
if (current != null) {
return false;
}
} else {
// find the next level ancestor node
String ancestorName = getNextLevelAncestorName(node);
InnerNode childNode = (InnerNode)childrenMap.get(ancestorName);
if (childNode == null) {
// create a new InnerNode for this ancestor node
childNode = createChildNode(ancestorName);
childrenMap.put(childNode.getNetworkName(), childNode);
}
// add node to the subtree of the next ancestor node
if (!childNode.add(node)) {
return false;
}
}
numOfLeaves++;
return true;
}
/**
* Remove node <i>node</i> from the subtree of this node.
* @param node node to be deleted
*/
public void remove(Node node) {
if (!isAncestor(node)) {
throw new IllegalArgumentException(node.getNetworkName()
+ ", which is located at " + node.getNetworkLocation()
+ ", is not a descendant of " + this.getNetworkFullPath());
}
if (isParent(node)) {
// this node is the parent, remove it directly
if (childrenMap.containsKey(node.getNetworkName())) {
childrenMap.remove(node.getNetworkName());
node.setParent(null);
} else {
throw new RuntimeException("Should not come to here. Node:" +
node.getNetworkFullPath() + ", Parent:" +
this.getNetworkFullPath());
}
} else {
// find the next ancestor node
String ancestorName = getNextLevelAncestorName(node);
InnerNodeImpl childNode = (InnerNodeImpl)childrenMap.get(ancestorName);
Preconditions.checkNotNull(childNode, "InnerNode is deleted before leaf");
// remove node from the parent node
childNode.remove(node);
// if the parent node has no children, remove the parent node too
if (childNode.getNumOfChildren() == 0) {
childrenMap.remove(ancestorName);
}
}
numOfLeaves--;
}
/**
* Given a node's string representation, return a reference to the node.
* Node can be leaf node or inner node.
*
* @param loc string location of a node. If loc starts with "/", it's a
* absolute path, otherwise a relative path. Following examples
* are all accepted,
* 1. /dc1/rm1/rack1 -> an inner node
* 2. /dc1/rm1/rack1/node1 -> a leaf node
* 3. rack1/node1 -> a relative path to this node
*
* @return null if the node is not found
*/
public Node getNode(String loc) {
if (loc == null) {
return null;
}
String fullPath = this.getNetworkFullPath();
if (loc.equalsIgnoreCase(fullPath)) {
return this;
}
// remove current node's location from loc when it's a absolute path
if (fullPath.equals(NetConstants.PATH_SEPARATOR_STR)) {
// current node is ROOT
if (loc.startsWith(PATH_SEPARATOR_STR)) {
loc = loc.substring(1);
}
} else if (loc.startsWith(fullPath)) {
loc = loc.substring(fullPath.length());
// skip the separator "/"
loc = loc.substring(1);
}
String[] path = loc.split(PATH_SEPARATOR_STR, 2);
Node child = childrenMap.get(path[0]);
if (child == null) {
return null;
}
if (path.length == 1){
return child;
}
if (child instanceof InnerNode) {
return ((InnerNode)child).getNode(path[1]);
} else {
return null;
}
}
/**
* get <i>leafIndex</i> leaf of this subtree.
*
* @param leafIndex an indexed leaf of the node
* @return the leaf node corresponding to the given index.
*/
public Node getLeaf(int leafIndex) {
Preconditions.checkArgument(leafIndex >= 0);
// children are leaves
if (isLeafParent()) {
// range check
if (leafIndex >= getNumOfChildren()) {
return null;
}
return getChildNode(leafIndex);
} else {
for(Node node : childrenMap.values()) {
InnerNodeImpl child = (InnerNodeImpl)node;
int leafCount = child.getNumOfLeaves();
if (leafIndex < leafCount) {
return child.getLeaf(leafIndex);
} else {
leafIndex -= leafCount;
}
}
return null;
}
}
/**
* Get <i>leafIndex</i> leaf of this subtree.
*
* @param leafIndex node's index, start from 0, skip the nodes in
* excludedScope and excludedNodes with ancestorGen
* @param excludedScopes the exclude scopes
* @param excludedNodes nodes to be excluded from. If ancestorGen is not 0,
* the chosen node will not share same ancestor with
* those in excluded nodes at the specified generation
* @param ancestorGen apply to excludeNodes, when value is 0, then no same
* ancestor enforcement on excludedNodes
* @return the leaf node corresponding to the given index.
* Example:
*
* / --- root
* / \
* / \
* / \
* / \
* dc1 dc2
* / \ / \
* / \ / \
* / \ / \
* rack1 rack2 rack1 rack2
* / \ / \ / \ / \
* n1 n2 n3 n4 n5 n6 n7 n8
*
* Input:
* leafIndex = 2
* excludedScope = /dc2/rack2
* excludedNodes = {/dc1/rack1/n1}
* ancestorGen = 1
*
* Output:
* node /dc1/rack2/n5
*
* Explanation:
* Since excludedNodes is n1 and ancestorGen is 1, it means nodes under
* /root/dc1/rack1 are excluded. Given leafIndex start from 0, LeafIndex 2
* means picking the 3th available node, which is n5.
*
*/
public Node getLeaf(int leafIndex, List<String> excludedScopes,
Collection<Node> excludedNodes, int ancestorGen) {
Preconditions.checkArgument(leafIndex >= 0 && ancestorGen >= 0);
// come to leaf parent layer
if (isLeafParent()) {
return getLeafOnLeafParent(leafIndex, excludedScopes, excludedNodes);
}
int maxLevel = NodeSchemaManager.getInstance().getMaxLevel();
// this node's children, it's generation as the ancestor of the leaf node
int currentGen = maxLevel - this.getLevel() - 1;
// build an ancestor(children) to exclude node count map
Map<Node, Integer> countMap =
getAncestorCountMap(excludedNodes, ancestorGen, currentGen);
// nodes covered by excluded scope
Map<String, Integer> excludedNodeCount =
getExcludedScopeNodeCount(excludedScopes);
for (Node child : childrenMap.values()) {
int leafCount = child.getNumOfLeaves();
// skip nodes covered by excluded scopes
for (Map.Entry<String, Integer> entry: excludedNodeCount.entrySet()) {
if (entry.getKey().startsWith(child.getNetworkFullPath())) {
leafCount -= entry.getValue();
}
}
// skip nodes covered by excluded nodes and ancestorGen
Integer count = countMap.get(child);
if (count != null) {
leafCount -= count;
}
if (leafIndex < leafCount) {
return ((InnerNode)child).getLeaf(leafIndex, excludedScopes,
excludedNodes, ancestorGen);
} else {
leafIndex -= leafCount;
}
}
return null;
}
@Override
public boolean equals(Object to) {
if (to == null) {
return false;
}
if (this == to) {
return true;
}
return this.toString().equals(to.toString());
}
@Override
public int hashCode() {
return super.hashCode();
}
/**
* Get a ancestor to its excluded node count map.
*
* @param nodes a collection of leaf nodes to exclude
* @param genToExclude the ancestor generation to exclude
* @param genToReturn the ancestor generation to return the count map
* @return the map.
* example:
*
* * --- root
* / \
* * * -- genToReturn =2
* / \ / \
* * * * * -- genToExclude = 1
* /\ /\ /\ /\
* * * * * * * * * -- nodes
*/
private Map<Node, Integer> getAncestorCountMap(Collection<Node> nodes,
int genToExclude, int genToReturn) {
Preconditions.checkState(genToExclude >= 0);
Preconditions.checkState(genToReturn >= 0);
if (nodes == null || nodes.size() == 0) {
return Collections.emptyMap();
}
// with the recursive call, genToReturn can be smaller than genToExclude
if (genToReturn < genToExclude) {
genToExclude = genToReturn;
}
// ancestorToExclude to ancestorToReturn map
HashMap<Node, Node> ancestorMap = new HashMap<>();
for (Node node: nodes) {
Node ancestorToExclude = node.getAncestor(genToExclude);
Node ancestorToReturn = node.getAncestor(genToReturn);
if (ancestorToExclude == null || ancestorToReturn == null) {
LOG.warn("Ancestor not found, node: " + node.getNetworkFullPath() +
", generation to exclude: " + genToExclude +
", generation to return:" + genToReturn);
continue;
}
ancestorMap.put(ancestorToExclude, ancestorToReturn);
}
// ancestorToReturn to exclude node count map
HashMap<Node, Integer> countMap = new HashMap<>();
for (Map.Entry<Node, Node> entry : ancestorMap.entrySet()) {
countMap.compute(entry.getValue(),
(key, n) -> (n == null ? 0 : n) + entry.getKey().getNumOfLeaves());
}
return countMap;
}
/**
* Get the node with leafIndex, considering skip nodes in excludedScope
* and in excludeNodes list.
*/
private Node getLeafOnLeafParent(int leafIndex, List<String> excludedScopes,
Collection<Node> excludedNodes) {
Preconditions.checkArgument(isLeafParent() && leafIndex >= 0);
if (leafIndex >= getNumOfChildren()) {
return null;
}
for(Node node : childrenMap.values()) {
if (excludedNodes != null && excludedNodes.contains(node)) {
continue;
}
if (excludedScopes != null && excludedScopes.size() > 0) {
if (excludedScopes.stream().anyMatch(scope ->
node.getNetworkFullPath().startsWith(scope))) {
continue;
}
}
if (leafIndex == 0) {
return node;
}
leafIndex--;
}
return null;
}
/**
* Return child's name of this node which is an ancestor of node <i>n</i>.
*/
private String getNextLevelAncestorName(Node n) {
int parentPathLen = this.getNetworkFullPath().length();
String name = n.getNetworkLocation().substring(parentPathLen);
if (name.charAt(0) == PATH_SEPARATOR) {
name = name.substring(1);
}
int index = name.indexOf(PATH_SEPARATOR);
if (index != -1) {
name = name.substring(0, index);
}
return name;
}
/**
* Creates a child node to be added to the list of children.
* @param name The name of the child node
* @return A new inner node
* @see InnerNodeImpl(String, String, InnerNode, int)
*/
private InnerNodeImpl createChildNode(String name) {
int childLevel = this.getLevel() + 1;
int cost = NodeSchemaManager.getInstance().getCost(childLevel);
return new InnerNodeImpl(name, this.getNetworkFullPath(), this, childLevel,
cost);
}
/** Get node with index <i>index</i>. */
private Node getChildNode(int index) {
Iterator iterator = childrenMap.values().iterator();
Node node = null;
while(index >= 0 && iterator.hasNext()) {
node = (Node)iterator.next();
index--;
}
return node;
}
/** Get how many leaf nodes are covered by the excludedScopes(no overlap). */
private Map<String, Integer> getExcludedScopeNodeCount(
List<String> excludedScopes) {
HashMap<String, Integer> nodeCounts = new HashMap<>();
if (excludedScopes == null || excludedScopes.isEmpty()) {
return nodeCounts;
}
for (String scope: excludedScopes) {
Node excludedScopeNode = getNode(scope);
nodeCounts.put(scope, excludedScopeNode == null ? 0 :
excludedScopeNode.getNumOfLeaves());
}
return nodeCounts;
}
}

View File

@ -1,67 +0,0 @@
/**
* 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.hdds.scm.net;
import org.apache.hadoop.hdds.scm.net.NodeSchema.LayerType;
/**
* Class to hold network topology related constants and configurations.
*/
public final class NetConstants {
private NetConstants() {
// Prevent instantiation
}
public final static char PATH_SEPARATOR = '/';
/** Path separator as a string. */
public final static String PATH_SEPARATOR_STR = "/";
public final static String SCOPE_REVERSE_STR = "~";
/** string representation of root. */
public final static String ROOT = "";
public final static int INNER_NODE_COST_DEFAULT = 1;
public final static int NODE_COST_DEFAULT = 0;
public final static int ANCESTOR_GENERATION_DEFAULT = 0;
public final static int ROOT_LEVEL = 1;
public final static String NODE_COST_PREFIX = "$";
public final static String DEFAULT_RACK = "/default-rack";
public final static String DEFAULT_NODEGROUP = "/default-nodegroup";
public final static String DEFAULT_DATACENTER = "/default-datacenter";
public final static String DEFAULT_REGION = "/default-dataregion";
// Build-in network topology node schema
public static final NodeSchema ROOT_SCHEMA =
new NodeSchema.Builder().setType(LayerType.ROOT).build();
public static final NodeSchema REGION_SCHEMA =
new NodeSchema.Builder().setType(LayerType.INNER_NODE)
.setDefaultName(DEFAULT_REGION).build();
public static final NodeSchema DATACENTER_SCHEMA =
new NodeSchema.Builder().setType(LayerType.INNER_NODE)
.setDefaultName(DEFAULT_DATACENTER).build();
public static final NodeSchema RACK_SCHEMA =
new NodeSchema.Builder().setType(LayerType.INNER_NODE)
.setDefaultName(DEFAULT_RACK).build();
public static final NodeSchema NODEGROUP_SCHEMA =
new NodeSchema.Builder().setType(LayerType.INNER_NODE)
.setDefaultName(DEFAULT_NODEGROUP).build();
public static final NodeSchema LEAF_SCHEMA =
new NodeSchema.Builder().setType(LayerType.LEAF_NODE).build();
}

View File

@ -1,161 +0,0 @@
/**
* 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.hdds.scm.net;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
/**
* Utility class to facilitate network topology functions.
*/
public final class NetUtils {
public static final Logger LOG = LoggerFactory.getLogger(NetUtils.class);
private NetUtils() {
// Prevent instantiation
}
/**
* Normalize a path by stripping off any trailing.
* {@link NetConstants#PATH_SEPARATOR}
* @param path path to normalize.
* @return the normalised path
* If <i>path</i>is empty or null, then {@link NetConstants#ROOT} is returned
*/
public static String normalize(String path) {
if (path == null || path.length() == 0) {
return NetConstants.ROOT;
}
if (path.charAt(0) != NetConstants.PATH_SEPARATOR) {
throw new IllegalArgumentException(
"Network Location path does not start with "
+ NetConstants.PATH_SEPARATOR_STR + ": " + path);
}
// Remove any trailing NetConstants.PATH_SEPARATOR
return path.length() == 1 ? path :
path.replaceAll(NetConstants.PATH_SEPARATOR_STR + "+$", "");
}
/**
* Given a network topology location string, return its network topology
* depth, E.g. the depth of /dc1/rack1/ng1/node1 is 5.
*/
public static int locationToDepth(String location) {
String newLocation = normalize(location);
return newLocation.equals(NetConstants.PATH_SEPARATOR_STR) ? 1 :
newLocation.split(NetConstants.PATH_SEPARATOR_STR).length;
}
/**
* Remove node from mutableExcludedNodes if it's covered by excludedScope.
* Please noted that mutableExcludedNodes content might be changed after the
* function call.
*/
public static void removeDuplicate(NetworkTopology topology,
Collection<Node> mutableExcludedNodes, List<String> mutableExcludedScopes,
int ancestorGen) {
if (CollectionUtils.isEmpty(mutableExcludedNodes) ||
CollectionUtils.isEmpty(mutableExcludedScopes) || topology == null) {
return;
}
Iterator<Node> iterator = mutableExcludedNodes.iterator();
while (iterator.hasNext() && (!mutableExcludedScopes.isEmpty())) {
Node node = iterator.next();
Node ancestor = topology.getAncestor(node, ancestorGen);
if (ancestor == null) {
LOG.warn("Fail to get ancestor generation " + ancestorGen +
" of node :" + node);
continue;
}
// excludedScope is child of ancestor
List<String> duplicateList = mutableExcludedScopes.stream()
.filter(scope -> scope.startsWith(ancestor.getNetworkFullPath()))
.collect(Collectors.toList());
mutableExcludedScopes.removeAll(duplicateList);
// ancestor is covered by excludedScope
mutableExcludedScopes.stream().forEach(scope -> {
if (ancestor.getNetworkFullPath().startsWith(scope)) {
// remove exclude node if it's covered by excludedScope
iterator.remove();
}
});
}
}
/**
* Remove node from mutableExcludedNodes if it's not part of scope
* Please noted that mutableExcludedNodes content might be changed after the
* function call.
*/
public static void removeOutscope(Collection<Node> mutableExcludedNodes,
String scope) {
if (CollectionUtils.isEmpty(mutableExcludedNodes) || scope == null) {
return;
}
synchronized (mutableExcludedNodes) {
Iterator<Node> iterator = mutableExcludedNodes.iterator();
while (iterator.hasNext()) {
Node next = iterator.next();
if (!next.getNetworkFullPath().startsWith(scope)) {
iterator.remove();
}
}
}
}
/**
* Get a ancestor list for nodes on generation <i>generation</i>.
*
* @param nodes a collection of leaf nodes
* @param generation the ancestor generation
* @return the ancestor list. If no ancestor is found, then a empty list is
* returned.
*/
public static List<Node> getAncestorList(NetworkTopology topology,
Collection<Node> nodes, int generation) {
List<Node> ancestorList = new ArrayList<>();
if (topology == null || CollectionUtils.isEmpty(nodes) ||
generation == 0) {
return ancestorList;
}
Iterator<Node> iterator = nodes.iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
Node ancestor = topology.getAncestor(node, generation);
if (ancestor == null) {
LOG.warn("Fail to get ancestor generation " + generation +
" of node :" + node);
continue;
}
if (!ancestorList.contains(ancestor)) {
ancestorList.add(ancestor);
}
}
return ancestorList;
}
}

View File

@ -1,229 +0,0 @@
/**
* 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.hdds.scm.net;
import java.util.Collection;
import java.util.List;
/**
* The interface defines a network topology.
*/
public interface NetworkTopology {
/** Exception for invalid network topology detection. */
class InvalidTopologyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public InvalidTopologyException(String msg) {
super(msg);
}
}
/**
* Add a leaf node. This will be called when a new datanode is added.
* @param node node to be added; can be null
* @exception IllegalArgumentException if add a node to a leave or node to be
* added is not a leaf
*/
void add(Node node);
/**
* Remove a node from the network topology. This will be called when a
* existing datanode is removed from the system.
* @param node node to be removed; cannot be null
*/
void remove(Node node);
/**
* Check if the tree already contains node <i>node</i>.
* @param node a node
* @return true if <i>node</i> is already in the tree; false otherwise
*/
boolean contains(Node node);
/**
* Compare the direct parent of each node for equality.
* @return true if their parent are the same
*/
boolean isSameParent(Node node1, Node node2);
/**
* Compare the specified ancestor generation of each node for equality.
* ancestorGen 1 means parent.
* @return true if their specified generation ancestor are equal
*/
boolean isSameAncestor(Node node1, Node node2, int ancestorGen);
/**
* Get the ancestor for node on generation <i>ancestorGen</i>.
*
* @param node the node to get ancestor
* @param ancestorGen the ancestor generation
* @return the ancestor. If no ancestor is found, then null is returned.
*/
Node getAncestor(Node node, int ancestorGen);
/**
* Return the max level of this topology, start from 1 for ROOT. For example,
* topology like "/rack/node" has the max level '3'.
*/
int getMaxLevel();
/**
* Given a string representation of a node, return its reference.
* @param loc a path string representing a node, can be leaf or inner node
* @return a reference to the node; null if the node is not in the tree
*/
Node getNode(String loc);
/**
* Given a string representation of a InnerNode, return its leaf nodes count.
* @param loc a path-like string representation of a InnerNode
* @return the number of leaf nodes, 0 if it's not an InnerNode or the node
* doesn't exist
*/
int getNumOfLeafNode(String loc);
/**
* Return the node numbers at level <i>level</i>.
* @param level topology level, start from 1, which means ROOT
* @return the number of nodes on the level
*/
int getNumOfNodes(int level);
/**
* Randomly choose a node in the scope.
* @param scope range of nodes from which a node will be chosen. If scope
* starts with ~, choose one from the all nodes except for the
* ones in <i>scope</i>; otherwise, choose one from <i>scope</i>.
* @return the chosen node
*/
Node chooseRandom(String scope);
/**
* Randomly choose a node in the scope, ano not in the exclude scope.
* @param scope range of nodes from which a node will be chosen. cannot start
* with ~
* @param excludedScopes the chosen nodes cannot be in these ranges. cannot
* starts with ~
* @return the chosen node
*/
Node chooseRandom(String scope, List<String> excludedScopes);
/**
* Randomly choose a leaf node from <i>scope</i>.
*
* If scope starts with ~, choose one from the all nodes except for the
* ones in <i>scope</i>; otherwise, choose nodes from <i>scope</i>.
* If excludedNodes is given, choose a node that's not in excludedNodes.
*
* @param scope range of nodes from which a node will be chosen
* @param excludedNodes nodes to be excluded
*
* @return the chosen node
*/
Node chooseRandom(String scope, Collection<Node> excludedNodes);
/**
* Randomly choose a leaf node from <i>scope</i>.
*
* If scope starts with ~, choose one from the all nodes except for the
* ones in <i>scope</i>; otherwise, choose nodes from <i>scope</i>.
* If excludedNodes is given, choose a node that's not in excludedNodes.
*
* @param scope range of nodes from which a node will be chosen
* @param excludedNodes nodes to be excluded from.
* @param ancestorGen matters when excludeNodes is not null. It means the
* ancestor generation that's not allowed to share between chosen node and the
* excludedNodes. For example, if ancestorGen is 1, means chosen node
* cannot share the same parent with excludeNodes. If value is 2, cannot
* share the same grand parent, and so on. If ancestorGen is 0, then no
* effect.
*
* @return the chosen node
*/
Node chooseRandom(String scope, Collection<Node> excludedNodes,
int ancestorGen);
/**
* Randomly choose one node from <i>scope</i>, share the same generation
* ancestor with <i>affinityNode</i>, and exclude nodes in
* <i>excludeScope</i> and <i>excludeNodes</i>.
*
* @param scope range of nodes from which a node will be chosen, cannot start
* with ~
* @param excludedScopes ranges of nodes to be excluded, cannot start with ~
* @param excludedNodes nodes to be excluded
* @param affinityNode when not null, the chosen node should share the same
* ancestor with this node at generation ancestorGen.
* Ignored when value is null
* @param ancestorGen If 0, then no same generation ancestor enforcement on
* both excludedNodes and affinityNode. If greater than 0,
* then apply to affinityNode(if not null), or apply to
* excludedNodes if affinityNode is null
* @return the chosen node
*/
Node chooseRandom(String scope, List<String> excludedScopes,
Collection<Node> excludedNodes, Node affinityNode, int ancestorGen);
/**
* Choose the node at index <i>index</i> from <i>scope</i>, share the same
* generation ancestor with <i>affinityNode</i>, and exclude nodes in
* <i>excludeScope</i> and <i>excludeNodes</i>.
*
* @param leafIndex node index, exclude nodes in excludedScope and
* excludedNodes
* @param scope range of nodes from which a node will be chosen, cannot start
* with ~
* @param excludedScopes ranges of nodes to be excluded, cannot start with ~
* @param excludedNodes nodes to be excluded
* @param affinityNode when not null, the chosen node should share the same
* ancestor with this node at generation ancestorGen.
* Ignored when value is null
* @param ancestorGen If 0, then no same generation ancestor enforcement on
* both excludedNodes and affinityNode. If greater than 0,
* then apply to affinityNode(if not null), or apply to
* excludedNodes if affinityNode is null
* @return the chosen node
*/
Node getNode(int leafIndex, String scope, List<String> excludedScopes,
Collection<Node> excludedNodes, Node affinityNode, int ancestorGen);
/** Return the distance cost between two nodes
* The distance cost from one node to its parent is it's parent's cost
* The distance cost between two nodes is calculated by summing up their
* distances cost to their closest common ancestor.
* @param node1 one node
* @param node2 another node
* @return the distance cost between node1 and node2 which is zero if they
* are the same or {@link Integer#MAX_VALUE} if node1 or node2 do not belong
* to the cluster
*/
int getDistanceCost(Node node1, Node node2);
/**
* Sort nodes array by network distance to <i>reader</i> to reduces network
* traffic and improves performance.
*
* As an additional twist, we also randomize the nodes at each network
* distance. This helps with load balancing when there is data skew.
*
* @param reader Node where need the data
* @param nodes Available replicas with the requested data
* @param activeLen Number of active nodes at the front of the array
*/
List<? extends Node> sortByDistanceCost(Node reader,
List<? extends Node> nodes, int activeLen);
}

View File

@ -1,798 +0,0 @@
/**
* 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.hdds.scm.net;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import static org.apache.hadoop.hdds.scm.net.NetConstants.ROOT;
import static org.apache.hadoop.hdds.scm.net.NetConstants.SCOPE_REVERSE_STR;
import static org.apache.hadoop.hdds.scm.net.NetConstants.ANCESTOR_GENERATION_DEFAULT;
/**
* The class represents a cluster of computers with a tree hierarchical
* network topology. In the network topology, leaves represent data nodes
* (computers) and inner nodes represent datacenter/core-switches/routers that
* manages traffic in/out of data centers or racks.
*/
public class NetworkTopologyImpl implements NetworkTopology{
public static final Logger LOG =
LoggerFactory.getLogger(NetworkTopology.class);
/** The Inner node crate factory. */
private final InnerNode.Factory factory;
/** The root cluster tree. */
private final InnerNode clusterTree;
/** Depth of all leaf nodes. */
private final int maxLevel;
/** Schema manager. */
private final NodeSchemaManager schemaManager;
/** Lock to coordinate cluster tree access. */
private ReadWriteLock netlock = new ReentrantReadWriteLock(true);
public NetworkTopologyImpl(Configuration conf) {
schemaManager = NodeSchemaManager.getInstance();
schemaManager.init(conf);
maxLevel = schemaManager.getMaxLevel();
factory = InnerNodeImpl.FACTORY;
clusterTree = factory.newInnerNode(ROOT, null, null,
NetConstants.ROOT_LEVEL,
schemaManager.getCost(NetConstants.ROOT_LEVEL));
}
@VisibleForTesting
public NetworkTopologyImpl(NodeSchemaManager manager) {
schemaManager = manager;
maxLevel = schemaManager.getMaxLevel();
factory = InnerNodeImpl.FACTORY;
clusterTree = factory.newInnerNode(ROOT, null, null,
NetConstants.ROOT_LEVEL,
schemaManager.getCost(NetConstants.ROOT_LEVEL));
}
/**
* Add a leaf node. This will be called when a new datanode is added.
* @param node node to be added; can be null
* @exception IllegalArgumentException if add a node to a leave or node to be
* added is not a leaf
*/
public void add(Node node) {
Preconditions.checkArgument(node != null, "node cannot be null");
if (node instanceof InnerNode) {
throw new IllegalArgumentException(
"Not allowed to add an inner node: "+ node.getNetworkFullPath());
}
int newDepth = NetUtils.locationToDepth(node.getNetworkLocation()) + 1;
// Check depth
if (maxLevel != newDepth) {
throw new InvalidTopologyException("Failed to add " +
node.getNetworkFullPath() + ": Its path depth is not " + maxLevel);
}
netlock.writeLock().lock();
boolean add;
try {
add = clusterTree.add(node);
}finally {
netlock.writeLock().unlock();
}
if (add) {
LOG.info("Added a new node: " + node.getNetworkFullPath());
if (LOG.isDebugEnabled()) {
LOG.debug("NetworkTopology became:\n{}", this);
}
}
}
/**
* Remove a node from the network topology. This will be called when a
* existing datanode is removed from the system.
* @param node node to be removed; cannot be null
*/
public void remove(Node node) {
Preconditions.checkArgument(node != null, "node cannot be null");
if (node instanceof InnerNode) {
throw new IllegalArgumentException(
"Not allowed to remove an inner node: "+ node.getNetworkFullPath());
}
netlock.writeLock().lock();
try {
clusterTree.remove(node);
}finally {
netlock.writeLock().unlock();
}
LOG.info("Removed a node: " + node.getNetworkFullPath());
if (LOG.isDebugEnabled()) {
LOG.debug("NetworkTopology became:\n{}", this);
}
}
/**
* Check if the tree already contains node <i>node</i>.
* @param node a node
* @return true if <i>node</i> is already in the tree; false otherwise
*/
public boolean contains(Node node) {
Preconditions.checkArgument(node != null, "node cannot be null");
netlock.readLock().lock();
try {
Node parent = node.getParent();
while (parent != null && parent != clusterTree) {
parent = parent.getParent();
}
if (parent == clusterTree) {
return true;
}
} finally {
netlock.readLock().unlock();
}
return false;
}
/**
* Compare the specified ancestor generation of each node for equality.
* @return true if their specified generation ancestor are equal
*/
public boolean isSameAncestor(Node node1, Node node2, int ancestorGen) {
if (node1 == null || node2 == null || ancestorGen <= 0) {
return false;
}
netlock.readLock().lock();
try {
return node1.getAncestor(ancestorGen) == node2.getAncestor(ancestorGen);
} finally {
netlock.readLock().unlock();
}
}
/**
* Compare the direct parent of each node for equality.
* @return true if their parent are the same
*/
public boolean isSameParent(Node node1, Node node2) {
if (node1 == null || node2 == null) {
return false;
}
netlock.readLock().lock();
try {
node1 = node1.getParent();
node2 = node2.getParent();
return node1 == node2;
} finally {
netlock.readLock().unlock();
}
}
/**
* Get the ancestor for node on generation <i>ancestorGen</i>.
*
* @param node the node to get ancestor
* @param ancestorGen the ancestor generation
* @return the ancestor. If no ancestor is found, then null is returned.
*/
public Node getAncestor(Node node, int ancestorGen) {
if (node == null) {
return null;
}
netlock.readLock().lock();
try {
return node.getAncestor(ancestorGen);
} finally {
netlock.readLock().unlock();
}
}
/**
* Given a string representation of a node(leaf or inner), return its
* reference.
* @param loc a path string representing a node, can be leaf or inner node
* @return a reference to the node, null if the node is not in the tree
*/
public Node getNode(String loc) {
loc = NetUtils.normalize(loc);
netlock.readLock().lock();
try {
if (!ROOT.equals(loc)) {
return clusterTree.getNode(loc);
} else {
return clusterTree;
}
} finally {
netlock.readLock().unlock();
}
}
/**
* Given a string representation of Node, return its leaf nodes count.
* @param loc a path-like string representation of Node
* @return the number of leaf nodes for InnerNode, 1 for leaf node, 0 if node
* doesn't exist
*/
public int getNumOfLeafNode(String loc) {
netlock.readLock().lock();
try {
Node node = getNode(loc);
if (node != null) {
return node.getNumOfLeaves();
}
} finally {
netlock.readLock().unlock();
}
return 0;
}
/**
* Return the max level of this tree, start from 1 for ROOT. For example,
* topology like "/rack/node" has the max level '3'.
*/
public int getMaxLevel() {
return maxLevel;
}
/**
* Return the node numbers at level <i>level</i>.
* @param level topology level, start from 1, which means ROOT
* @return the number of nodes on the level
*/
public int getNumOfNodes(int level) {
Preconditions.checkArgument(level > 0 && level <= maxLevel,
"Invalid level");
netlock.readLock().lock();
try {
return clusterTree.getNumOfNodes(level);
} finally {
netlock.readLock().unlock();
}
}
/**
* Randomly choose a node in the scope.
* @param scope range of nodes from which a node will be chosen. If scope
* starts with ~, choose one from the all nodes except for the
* ones in <i>scope</i>; otherwise, choose one from <i>scope</i>.
* @return the chosen node
*/
public Node chooseRandom(String scope) {
if (scope == null) {
scope = ROOT;
}
if (scope.startsWith(SCOPE_REVERSE_STR)) {
ArrayList<String> excludedScopes = new ArrayList();
excludedScopes.add(scope.substring(1));
return chooseRandom(ROOT, excludedScopes, null, null,
ANCESTOR_GENERATION_DEFAULT);
} else {
return chooseRandom(scope, null, null, null, ANCESTOR_GENERATION_DEFAULT);
}
}
/**
* Randomly choose a node in the scope, ano not in the exclude scope.
* @param scope range of nodes from which a node will be chosen. cannot start
* with ~
* @param excludedScopes the chosen node cannot be in these ranges. cannot
* starts with ~
* @return the chosen node
*/
public Node chooseRandom(String scope, List<String> excludedScopes) {
return chooseRandom(scope, excludedScopes, null, null,
ANCESTOR_GENERATION_DEFAULT);
}
/**
* Randomly choose a leaf node from <i>scope</i>.
*
* If scope starts with ~, choose one from the all nodes except for the
* ones in <i>scope</i>; otherwise, choose nodes from <i>scope</i>.
* If excludedNodes is given, choose a node that's not in excludedNodes.
*
* @param scope range of nodes from which a node will be chosen
* @param excludedNodes nodes to be excluded
*
* @return the chosen node
*/
public Node chooseRandom(String scope, Collection<Node> excludedNodes) {
if (scope == null) {
scope = ROOT;
}
if (scope.startsWith(SCOPE_REVERSE_STR)) {
ArrayList<String> excludedScopes = new ArrayList();
excludedScopes.add(scope.substring(1));
return chooseRandom(ROOT, excludedScopes, excludedNodes, null,
ANCESTOR_GENERATION_DEFAULT);
} else {
return chooseRandom(scope, null, excludedNodes, null,
ANCESTOR_GENERATION_DEFAULT);
}
}
/**
* Randomly choose a leaf node from <i>scope</i>.
*
* If scope starts with ~, choose one from the all nodes except for the
* ones in <i>scope</i>; otherwise, choose nodes from <i>scope</i>.
* If excludedNodes is given, choose a node that's not in excludedNodes.
*
* @param scope range of nodes from which a node will be chosen
* @param excludedNodes nodes to be excluded from.
* @param ancestorGen matters when excludeNodes is not null. It means the
* ancestor generation that's not allowed to share between chosen node and the
* excludedNodes. For example, if ancestorGen is 1, means chosen node
* cannot share the same parent with excludeNodes. If value is 2, cannot
* share the same grand parent, and so on. If ancestorGen is 0, then no
* effect.
*
* @return the chosen node
*/
public Node chooseRandom(String scope, Collection<Node> excludedNodes,
int ancestorGen) {
if (scope == null) {
scope = ROOT;
}
if (scope.startsWith(SCOPE_REVERSE_STR)) {
ArrayList<String> excludedScopes = new ArrayList();
excludedScopes.add(scope.substring(1));
return chooseRandom(ROOT, excludedScopes, excludedNodes, null,
ancestorGen);
} else {
return chooseRandom(scope, null, excludedNodes, null, ancestorGen);
}
}
/**
* Randomly choose one leaf node from <i>scope</i>, share the same generation
* ancestor with <i>affinityNode</i>, and exclude nodes in
* <i>excludeScope</i> and <i>excludeNodes</i>.
*
* @param scope range of nodes from which a node will be chosen, cannot start
* with ~
* @param excludedScopes ranges of nodes to be excluded, cannot start with ~
* @param excludedNodes nodes to be excluded
* @param affinityNode when not null, the chosen node should share the same
* ancestor with this node at generation ancestorGen.
* Ignored when value is null
* @param ancestorGen If 0, then no same generation ancestor enforcement on
* both excludedNodes and affinityNode. If greater than 0,
* then apply to affinityNode(if not null), or apply to
* excludedNodes if affinityNode is null
* @return the chosen node
*/
public Node chooseRandom(String scope, List<String> excludedScopes,
Collection<Node> excludedNodes, Node affinityNode, int ancestorGen) {
if (scope == null) {
scope = ROOT;
}
checkScope(scope);
checkExcludedScopes(excludedScopes);
checkAffinityNode(affinityNode);
checkAncestorGen(ancestorGen);
netlock.readLock().lock();
try {
return chooseNodeInternal(scope, -1, excludedScopes,
excludedNodes, affinityNode, ancestorGen);
} finally {
netlock.readLock().unlock();
}
}
/**
* Choose the leaf node at index <i>index</i> from <i>scope</i>, share the
* same generation ancestor with <i>affinityNode</i>, and exclude nodes in
* <i>excludeScope</i> and <i>excludeNodes</i>.
*
* @param leafIndex node index, exclude nodes in excludedScope and
* excludedNodes
* @param scope range of nodes from which a node will be chosen, cannot start
* with ~
* @param excludedScopes ranges of nodes to be excluded, cannot start with ~
* @param excludedNodes nodes to be excluded
* @param affinityNode when not null, the chosen node should share the same
* ancestor with this node at generation ancestorGen.
* Ignored when value is null
* @param ancestorGen If 0, then no same generation ancestor enforcement on
* both excludedNodes and affinityNode. If greater than 0,
* then apply to affinityNode(if not null), or apply to
* excludedNodes if affinityNode is null
* @return the chosen node
* Example:
*
* / --- root
* / \
* / \
* / \
* / \
* dc1 dc2
* / \ / \
* / \ / \
* / \ / \
* rack1 rack2 rack1 rack2
* / \ / \ / \ / \
* n1 n2 n3 n4 n5 n6 n7 n8
*
* Input:
* leafIndex = 1
* excludedScope = /dc2
* excludedNodes = {/dc1/rack1/n1}
* affinityNode = /dc1/rack2/n2
* ancestorGen = 2
*
* Output:
* node /dc1/rack2/n4
*
* Explanation:
* With affinityNode n2 and ancestorGen 2, it means we can only pick node
* from subtree /dc1. LeafIndex 1, so we pick the 2nd available node n4.
*
*/
public Node getNode(int leafIndex, String scope, List<String> excludedScopes,
Collection<Node> excludedNodes, Node affinityNode, int ancestorGen) {
Preconditions.checkArgument(leafIndex >= 0);
if (scope == null) {
scope = ROOT;
}
checkScope(scope);
checkExcludedScopes(excludedScopes);
checkAffinityNode(affinityNode);
checkAncestorGen(ancestorGen);
netlock.readLock().lock();
try {
return chooseNodeInternal(scope, leafIndex, excludedScopes,
excludedNodes, affinityNode, ancestorGen);
} finally {
netlock.readLock().unlock();
}
}
private Node chooseNodeInternal(String scope, int leafIndex,
List<String> excludedScopes, Collection<Node> excludedNodes,
Node affinityNode, int ancestorGen) {
Preconditions.checkArgument(scope != null);
String finalScope = scope;
if (affinityNode != null && ancestorGen > 0) {
Node affinityAncestor = affinityNode.getAncestor(ancestorGen);
if (affinityAncestor == null) {
throw new IllegalArgumentException("affinityNode " +
affinityNode.getNetworkFullPath() + " doesn't have ancestor on" +
" generation " + ancestorGen);
}
// affinity ancestor should has overlap with scope
if (affinityAncestor.getNetworkFullPath().startsWith(scope)){
finalScope = affinityAncestor.getNetworkFullPath();
} else if (!scope.startsWith(affinityAncestor.getNetworkFullPath())) {
return null;
}
// reset ancestor generation since the new scope is identified now
ancestorGen = 0;
}
// check overlap of excludedScopes and finalScope
List<String> mutableExcludedScopes = null;
if (excludedScopes != null && !excludedScopes.isEmpty()) {
mutableExcludedScopes = new ArrayList<>();
for (String s: excludedScopes) {
// excludeScope covers finalScope
if (finalScope.startsWith(s)) {
return null;
}
// excludeScope and finalScope share nothing case
if (s.startsWith(finalScope)) {
if (!mutableExcludedScopes.stream().anyMatch(
e -> s.startsWith(e))) {
mutableExcludedScopes.add(s);
}
}
}
}
// clone excludedNodes before remove duplicate in it
Collection<Node> mutableExNodes = null;
// Remove duplicate in excludedNodes
if (excludedNodes != null) {
mutableExNodes =
excludedNodes.stream().distinct().collect(Collectors.toList());
}
// remove duplicate in mutableExNodes and mutableExcludedScopes
NetUtils.removeDuplicate(this, mutableExNodes, mutableExcludedScopes,
ancestorGen);
// calculate available node count
Node scopeNode = getNode(finalScope);
int availableNodes = getAvailableNodesCount(
scopeNode.getNetworkFullPath(), mutableExcludedScopes, mutableExNodes,
ancestorGen);
if (availableNodes <= 0) {
LOG.warn("No available node in (scope=\"{}\" excludedScope=\"{}\" " +
"excludedNodes=\"{}\" ancestorGen=\"{}\").",
scopeNode.getNetworkFullPath(), excludedScopes, excludedNodes,
ancestorGen);
return null;
}
// scope is a Leaf node
if (!(scopeNode instanceof InnerNode)) {
return scopeNode;
}
Node ret;
int nodeIndex;
if (leafIndex >= 0) {
nodeIndex = leafIndex % availableNodes;
ret = ((InnerNode)scopeNode).getLeaf(nodeIndex, mutableExcludedScopes,
mutableExNodes, ancestorGen);
} else {
nodeIndex = ThreadLocalRandom.current().nextInt(availableNodes);
ret = ((InnerNode)scopeNode).getLeaf(nodeIndex, mutableExcludedScopes,
mutableExNodes, ancestorGen);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Choosing node[index={},random={}] from \"{}\" available " +
"nodes, scope=\"{}\", excludedScope=\"{}\", excludeNodes=\"{}\".",
nodeIndex, (leafIndex == -1 ? "true" : "false"), availableNodes,
scopeNode.getNetworkFullPath(), excludedScopes, excludedNodes);
LOG.debug("Chosen node = {}", (ret == null ? "not found" :
ret.toString()));
}
return ret;
}
/** Return the distance cost between two nodes
* The distance cost from one node to its parent is it's parent's cost
* The distance cost between two nodes is calculated by summing up their
* distances cost to their closest common ancestor.
* @param node1 one node
* @param node2 another node
* @return the distance cost between node1 and node2 which is zero if they
* are the same or {@link Integer#MAX_VALUE} if node1 or node2 do not belong
* to the cluster
*/
public int getDistanceCost(Node node1, Node node2) {
if ((node1 != null && node2 != null && node1.equals(node2)) ||
(node1 == null && node2 == null)) {
return 0;
}
if (node1 == null || node2 == null) {
LOG.warn("One of the nodes is a null pointer");
return Integer.MAX_VALUE;
}
int cost = 0;
netlock.readLock().lock();
try {
if ((node1.getAncestor(maxLevel - 1) != clusterTree) ||
(node2.getAncestor(maxLevel - 1) != clusterTree)) {
LOG.debug("One of the nodes is outside of network topology");
return Integer.MAX_VALUE;
}
int level1 = node1.getLevel();
int level2 = node2.getLevel();
if (level1 > maxLevel || level2 > maxLevel) {
return Integer.MAX_VALUE;
}
while(level1 > level2 && node1 != null) {
node1 = node1.getParent();
level1--;
cost += node1 == null? 0 : node1.getCost();
}
while(level2 > level1 && node2 != null) {
node2 = node2.getParent();
level2--;
cost += node2 == null? 0 : node2.getCost();
}
while(node1 != null && node2 != null && node1 != node2) {
node1 = node1.getParent();
node2 = node2.getParent();
cost += node1 == null? 0 : node1.getCost();
cost += node2 == null? 0 : node2.getCost();
}
return cost;
} finally {
netlock.readLock().unlock();
}
}
/**
* Sort nodes array by network distance to <i>reader</i> to reduces network
* traffic and improves performance.
*
* As an additional twist, we also randomize the nodes at each network
* distance. This helps with load balancing when there is data skew.
*
* @param reader Node where need the data
* @param nodes Available replicas with the requested data
* @param activeLen Number of active nodes at the front of the array
*/
public List<? extends Node> sortByDistanceCost(Node reader,
List<? extends Node> nodes, int activeLen) {
/** Sort weights for the nodes array */
if (reader == null) {
return nodes;
}
int[] costs = new int[activeLen];
for (int i = 0; i < activeLen; i++) {
costs[i] = getDistanceCost(reader, nodes.get(i));
}
// Add cost/node pairs to a TreeMap to sort
TreeMap<Integer, List<Node>> tree = new TreeMap<Integer, List<Node>>();
for (int i = 0; i < activeLen; i++) {
int cost = costs[i];
Node node = nodes.get(i);
List<Node> list = tree.get(cost);
if (list == null) {
list = Lists.newArrayListWithExpectedSize(1);
tree.put(cost, list);
}
list.add(node);
}
List<Node> ret = new ArrayList<>();
for (List<Node> list: tree.values()) {
if (list != null) {
Collections.shuffle(list);
for (Node n: list) {
ret.add(n);
}
}
}
Preconditions.checkState(ret.size() == activeLen,
"Wrong number of nodes sorted!");
return ret;
}
/**
* Return the number of leaves in <i>scope</i> but not in
* <i>excludedNodes</i> and <i>excludeScope</i>.
* @param scope the scope
* @param excludedScopes excluded scopes
* @param mutableExcludedNodes a list of excluded nodes, content might be
* changed after the call
* @param ancestorGen same generation ancestor prohibit on excludedNodes
* @return number of available nodes
*/
private int getAvailableNodesCount(String scope, List<String> excludedScopes,
Collection<Node> mutableExcludedNodes, int ancestorGen) {
Preconditions.checkArgument(scope != null);
Node scopeNode = getNode(scope);
if (scopeNode == null) {
return 0;
}
NetUtils.removeOutscope(mutableExcludedNodes, scope);
List<Node> excludedAncestorList =
NetUtils.getAncestorList(this, mutableExcludedNodes, ancestorGen);
for (Node ancestor : excludedAncestorList) {
if (scope.startsWith(ancestor.getNetworkFullPath())){
return 0;
}
}
// number of nodes to exclude
int excludedCount = 0;
if (excludedScopes != null) {
for (String excludedScope: excludedScopes) {
Node excludedScopeNode = getNode(excludedScope);
if (excludedScopeNode != null) {
if (excludedScope.startsWith(scope)) {
excludedCount += excludedScopeNode.getNumOfLeaves();
} else if (scope.startsWith(excludedScope)) {
return 0;
}
}
}
}
// excludedNodes is not null case
if (mutableExcludedNodes != null && (!mutableExcludedNodes.isEmpty())) {
if (ancestorGen == 0) {
for (Node node: mutableExcludedNodes) {
if (contains(node)) {
excludedCount++;
}
}
} else {
for (Node ancestor : excludedAncestorList) {
if (ancestor.getNetworkFullPath().startsWith(scope)) {
excludedCount += ancestor.getNumOfLeaves();
}
}
}
}
int availableCount = scopeNode.getNumOfLeaves() - excludedCount;
Preconditions.checkState(availableCount >= 0);
return availableCount;
}
@Override
public String toString() {
// print max level
StringBuilder tree = new StringBuilder();
tree.append("Level: ");
tree.append(maxLevel);
tree.append("\n");
netlock.readLock().lock();
try {
// print the number of leaves
int numOfLeaves = clusterTree.getNumOfLeaves();
tree.append("Number of leaves:");
tree.append(numOfLeaves);
tree.append("\n");
// print all nodes
for (int i = 0; i < numOfLeaves; i++) {
tree.append(clusterTree.getLeaf(i).getNetworkFullPath());
tree.append("\n");
}
} finally {
netlock.readLock().unlock();
}
return tree.toString();
}
private void checkScope(String scope) {
if (scope != null && scope.startsWith(SCOPE_REVERSE_STR)) {
throw new IllegalArgumentException("scope " + scope +
" should not start with " + SCOPE_REVERSE_STR);
}
}
private void checkExcludedScopes(List<String> excludedScopes) {
if (!CollectionUtils.isEmpty(excludedScopes)) {
excludedScopes.stream().forEach(scope -> {
if (scope.startsWith(SCOPE_REVERSE_STR)) {
throw new IllegalArgumentException("excludedScope " + scope +
" cannot start with " + SCOPE_REVERSE_STR);
}
});
}
}
private void checkAffinityNode(Node affinityNode) {
if (affinityNode != null && (!contains(affinityNode))) {
throw new IllegalArgumentException("Affinity node " +
affinityNode.getNetworkFullPath() + " is not a member of topology");
}
}
private void checkAncestorGen(int ancestorGen) {
if (ancestorGen > (maxLevel - 1) || ancestorGen < 0) {
throw new IllegalArgumentException("ancestorGen " + ancestorGen +
" exceeds this network topology acceptable level [0, " +
(maxLevel - 1) + "]");
}
}
}

View File

@ -1,101 +0,0 @@
/**
* 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.hdds.scm.net;
/**
* The interface defines a node in a network topology.
* A node may be a leave representing a data node or an inner
* node representing a data center or rack.
* Each node has a name and its location in the network is
* decided by a string with syntax similar to a file name.
* For example, a data node's name is hostname:port# and if it's located at
* rack "orange" in data center "dog", the string representation of its
* network location will be /dog/orange.
*/
public interface Node {
/** @return the string representation of this node's network location path,
* exclude itself. In another words, its parent's full network location */
String getNetworkLocation();
/**
* Set this node's network location.
* @param location it's network location
*/
void setNetworkLocation(String location);
/** @return this node's self name in network topology. This should be node's
* IP or hostname.
* */
String getNetworkName();
/**
* Set this node's name, can be hostname or Ipaddress.
* @param name it's network name
*/
void setNetworkName(String name);
/** @return this node's full path in network topology. It's the concatenation
* of location and name.
* */
String getNetworkFullPath();
/** @return this node's parent */
InnerNode getParent();
/**
* Set this node's parent.
* @param parent the parent
*/
void setParent(InnerNode parent);
/** @return this node's ancestor, generation 0 is itself, generation 1 is
* node's parent, and so on.*/
Node getAncestor(int generation);
/**
* @return this node's level in the tree.
* E.g. the root of a tree returns 1 and root's children return 2
*/
int getLevel();
/**
* Set this node's level in the tree.
* @param i the level
*/
void setLevel(int i);
/**
* @return this node's cost when network traffic go through it.
* E.g. the cost of going cross a switch is 1, and cost of going through a
* datacenter can be 5.
* Be default the cost of leaf datanode is 0, all other node is 1.
*/
int getCost();
/** @return the leaf nodes number under this node. */
int getNumOfLeaves();
/**
* Judge if this node is an ancestor of node <i>n</i>.
* Ancestor includes itself and parents case.
*
* @param n a node
* @return true if this node is an ancestor of <i>n</i>
*/
boolean isAncestor(Node n);
}

View File

@ -1,222 +0,0 @@
/**
* 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.hdds.scm.net;
import com.google.common.base.Preconditions;
import static org.apache.hadoop.hdds.scm.net.NetConstants.ROOT;
import static org.apache.hadoop.hdds.scm.net.NetConstants.PATH_SEPARATOR_STR;
/**
* A thread safe class that implements interface Node.
*/
public class NodeImpl implements Node {
// host:port#
private String name;
// string representation of this node's location, such as /dc1/rack1
private String location;
// location + "/" + name
private String path;
// which level of the tree the node resides, start from 1 for root
private int level;
// node's parent
private InnerNode parent;
// the cost to go through this node
private final int cost;
/**
* Construct a node from its name and its location.
* @param name this node's name (can be null, must not contain
* {@link NetConstants#PATH_SEPARATOR})
* @param location this node's location
*/
public NodeImpl(String name, String location, int cost) {
if (name != null && name.contains(PATH_SEPARATOR_STR)) {
throw new IllegalArgumentException(
"Network location name:" + name + " should not contain " +
PATH_SEPARATOR_STR);
}
this.name = (name == null) ? ROOT : name;
this.location = NetUtils.normalize(location);
this.path = getPath();
this.cost = cost;
}
/**
* Construct a node from its name and its location.
*
* @param name this node's name (can be null, must not contain
* {@link NetConstants#PATH_SEPARATOR})
* @param location this node's location
* @param parent this node's parent node
* @param level this node's level in the tree
* @param cost this node's cost if traffic goes through it
*/
public NodeImpl(String name, String location, InnerNode parent, int level,
int cost) {
this(name, location, cost);
this.parent = parent;
this.level = level;
}
/**
* @return this node's name
*/
public String getNetworkName() {
return name;
}
/**
* Set this node's name, can be hostname or Ipaddress.
* @param networkName it's network name
*/
public void setNetworkName(String networkName) {
this.name = networkName;
this.path = getPath();
}
/**
* @return this node's network location
*/
public String getNetworkLocation() {
return location;
}
/**
* Set this node's network location.
* @param networkLocation it's network location
*/
@Override
public void setNetworkLocation(String networkLocation) {
this.location = networkLocation;
this.path = getPath();
}
/**
* @return this node's full path in network topology. It's the concatenation
* of location and name.
*/
public String getNetworkFullPath() {
return path;
}
/**
* @return this node's parent
*/
public InnerNode getParent() {
return parent;
}
/**
* @return this node's ancestor, generation 0 is itself, generation 1 is
* node's parent, and so on.
*/
public Node getAncestor(int generation) {
Preconditions.checkArgument(generation >= 0);
Node current = this;
while (generation > 0 && current != null) {
current = current.getParent();
generation--;
}
return current;
}
/**
* Set this node's parent.
*
* @param parent the parent
*/
public void setParent(InnerNode parent) {
this.parent = parent;
}
/**
* @return this node's level in the tree.
* E.g. the root of a tree returns 0 and its children return 1
*/
public int getLevel() {
return this.level;
}
/**
* Set this node's level in the tree.
*
* @param level the level
*/
public void setLevel(int level) {
this.level = level;
}
/**
* @return this node's cost when network traffic go through it.
* E.g. the cost of going cross a switch is 1, and cost of going through a
* datacenter is 5.
* Be default the cost of leaf datanode is 0, all other inner node is 1.
*/
public int getCost() {
return this.cost;
}
/** @return the leaf nodes number under this node. */
public int getNumOfLeaves() {
return 1;
}
/**
* Check if this node is an ancestor of node <i>node</i>. Ancestor includes
* itself and parents case;
* @param node a node
* @return true if this node is an ancestor of <i>node</i>
*/
public boolean isAncestor(Node node) {
return this.getNetworkFullPath().equals(PATH_SEPARATOR_STR) ||
node.getNetworkLocation().startsWith(this.getNetworkFullPath()) ||
node.getNetworkFullPath().equalsIgnoreCase(
this.getNetworkFullPath());
}
@Override
public boolean equals(Object to) {
if (to == null) {
return false;
}
if (this == to) {
return true;
}
return this.toString().equals(to.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* @return this node's path as its string representation
*/
@Override
public String toString() {
return getNetworkFullPath();
}
private String getPath() {
return this.location.equals(PATH_SEPARATOR_STR) ?
this.location + this.name :
this.location + PATH_SEPARATOR_STR + this.name;
}
}

View File

@ -1,183 +0,0 @@
/**
* 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.hdds.scm.net;
import org.apache.hadoop.HadoopIllegalArgumentException;
import java.util.List;
/**
* Network topology schema to housekeeper relevant information.
*/
public final class NodeSchema {
/**
* Network topology layer type enum definition.
*/
public enum LayerType{
ROOT("Root", NetConstants.INNER_NODE_COST_DEFAULT),
INNER_NODE("InnerNode", NetConstants.INNER_NODE_COST_DEFAULT),
LEAF_NODE("Leaf", NetConstants.NODE_COST_DEFAULT);
private final String description;
// default cost
private final int cost;
LayerType(String description, int cost) {
this.description = description;
this.cost = cost;
}
@Override
public String toString() {
return description;
}
public int getCost(){
return cost;
}
public static LayerType getType(String typeStr) {
for (LayerType type: LayerType.values()) {
if (typeStr.equalsIgnoreCase(type.toString())) {
return type;
}
}
return null;
}
}
// default cost
private int cost;
// layer Type, mandatory property
private LayerType type;
// default name, can be null or ""
private String defaultName;
// layer prefix, can be null or ""
private String prefix;
// sublayer
private List<NodeSchema> sublayer;
/**
* Builder for NodeSchema.
*/
public static class Builder {
private int cost = -1;
private LayerType type;
private String defaultName;
private String prefix;
public Builder setCost(int nodeCost) {
this.cost = nodeCost;
return this;
}
public Builder setPrefix(String nodePrefix) {
this.prefix = nodePrefix;
return this;
}
public Builder setType(LayerType nodeType) {
this.type = nodeType;
return this;
}
public Builder setDefaultName(String nodeDefaultName) {
this.defaultName = nodeDefaultName;
return this;
}
public NodeSchema build() {
if (type == null) {
throw new HadoopIllegalArgumentException("Type is mandatory for a " +
"network topology node layer definition");
}
if (cost == -1) {
cost = type.getCost();
}
return new NodeSchema(type, cost, prefix, defaultName);
}
}
/**
* Constructor.
* @param type layer type
* @param cost layer's default cost
* @param prefix layer's prefix
* @param defaultName layer's default name is if specified
*/
public NodeSchema(LayerType type, int cost, String prefix,
String defaultName) {
this.type = type;
this.cost = cost;
this.prefix = prefix;
this.defaultName = defaultName;
}
/**
* Constructor. This constructor is only used when build NodeSchema from
* YAML file.
*/
public NodeSchema() {
this.type = LayerType.INNER_NODE;
}
public boolean matchPrefix(String name) {
if (name == null || name.isEmpty() || prefix == null || prefix.isEmpty()) {
return false;
}
return name.trim().toLowerCase().startsWith(prefix.toLowerCase());
}
public LayerType getType() {
return this.type;
}
public void setType(LayerType type) {
this.type = type;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getDefaultName() {
return this.defaultName;
}
public void setDefaultName(String name) {
this.defaultName = name;
}
public int getCost() {
return this.cost;
}
public void setCost(int cost) {
this.cost = cost;
}
public void setSublayer(List<NodeSchema> sublayer) {
this.sublayer = sublayer;
}
public List<NodeSchema> getSublayer() {
return sublayer;
}
}

View File

@ -1,489 +0,0 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hdds.scm.net;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.hdds.scm.net.NodeSchema.LayerType;
import org.yaml.snakeyaml.Yaml;
/**
* A Network topology layer schema loading tool that loads user defined network
* layer schema data from a XML configuration file.
*/
public final class NodeSchemaLoader {
private static final Logger LOG
= LoggerFactory.getLogger(NodeSchemaLoader.class);
private static final String CONFIGURATION_TAG = "configuration";
private static final String LAYOUT_VERSION_TAG = "layoutversion";
private static final String TOPOLOGY_TAG = "topology";
private static final String TOPOLOGY_PATH = "path";
private static final String TOPOLOGY_ENFORCE_PREFIX = "enforceprefix";
private static final String LAYERS_TAG = "layers";
private static final String LAYER_TAG = "layer";
private static final String LAYER_ID = "id";
private static final String LAYER_TYPE = "type";
private static final String LAYER_COST = "cost";
private static final String LAYER_PREFIX = "prefix";
private static final String LAYER_DEFAULT_NAME = "default";
private static final int LAYOUT_VERSION = 1;
private volatile static NodeSchemaLoader instance = null;
private NodeSchemaLoader() {}
public static NodeSchemaLoader getInstance() {
if (instance == null) {
instance = new NodeSchemaLoader();
}
return instance;
}
/**
* Class to house keep the result of parsing a network topology schema file.
*/
public static class NodeSchemaLoadResult {
private List<NodeSchema> schemaList;
private boolean enforcePrefix;
NodeSchemaLoadResult(List<NodeSchema> schemaList, boolean enforcePrefix) {
this.schemaList = schemaList;
this.enforcePrefix = enforcePrefix;
}
public boolean isEnforePrefix() {
return enforcePrefix;
}
public List<NodeSchema> getSchemaList() {
return schemaList;
}
}
/**
* Load user defined network layer schemas from a XML/YAML configuration file.
* @param schemaFilePath path of schema file
* @return all valid node schemas defined in schema file
*/
public NodeSchemaLoadResult loadSchemaFromFile(String schemaFilePath)
throws IllegalArgumentException, FileNotFoundException {
try {
File schemaFile = new File(schemaFilePath);
if (schemaFile.exists()) {
LOG.info("Load network topology schema file " +
schemaFile.getAbsolutePath());
try (FileInputStream inputStream = new FileInputStream(schemaFile)) {
return loadSchemaFromStream(schemaFilePath, inputStream);
}
} else {
// try to load with classloader
ClassLoader classloader =
Thread.currentThread().getContextClassLoader();
if (classloader == null) {
classloader = NodeSchemaLoader.class.getClassLoader();
}
if (classloader != null) {
try (InputStream stream = classloader
.getResourceAsStream(schemaFilePath)) {
if (stream != null) {
LOG.info("Loading file from " + classloader
.getResources(schemaFilePath));
return loadSchemaFromStream(schemaFilePath, stream);
}
}
}
}
String msg = "Network topology layer schema file " +
schemaFilePath + "[" + schemaFile.getAbsolutePath() +
"] is not found.";
LOG.warn(msg);
throw new FileNotFoundException(msg);
} catch (FileNotFoundException e) {
throw e;
} catch (ParserConfigurationException | IOException | SAXException e) {
throw new IllegalArgumentException("Failed to load network topology node"
+ " schema file: " + schemaFilePath + " , error:" + e.getMessage(),
e);
}
}
private NodeSchemaLoadResult loadSchemaFromStream(String schemaFilePath,
InputStream stream)
throws ParserConfigurationException, SAXException, IOException {
if (FilenameUtils.getExtension(schemaFilePath).toLowerCase()
.compareTo("yaml") == 0) {
return loadSchemaFromYaml(stream);
} else {
return loadSchema(stream);
}
}
/**
* Load network topology layer schemas from a XML configuration file.
* @param inputStream schema file as an inputStream
* @return all valid node schemas defined in schema file
* @throws ParserConfigurationException ParserConfigurationException happen
* @throws IOException no such schema file
* @throws SAXException xml file has some invalid elements
* @throws IllegalArgumentException xml file content is logically invalid
*/
private NodeSchemaLoadResult loadSchema(InputStream inputStream) throws
ParserConfigurationException, SAXException, IOException {
LOG.info("Loading network topology layer schema file");
// Read and parse the schema file.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setIgnoringComments(true);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(inputStream);
Element root = doc.getDocumentElement();
if (!CONFIGURATION_TAG.equals(root.getTagName())) {
throw new IllegalArgumentException("Bad network topology layer schema " +
"configuration file: top-level element not <" + CONFIGURATION_TAG +
">");
}
NodeSchemaLoadResult schemaList;
if (root.getElementsByTagName(LAYOUT_VERSION_TAG).getLength() == 1) {
if (loadLayoutVersion(root) == LAYOUT_VERSION) {
if (root.getElementsByTagName(LAYERS_TAG).getLength() == 1) {
Map<String, NodeSchema> schemas = loadLayersSection(root);
if (root.getElementsByTagName(TOPOLOGY_TAG).getLength() == 1) {
schemaList = loadTopologySection(root, schemas);
} else {
throw new IllegalArgumentException("Bad network topology layer " +
"schema configuration file: no or multiple <" + TOPOLOGY_TAG +
"> element");
}
} else {
throw new IllegalArgumentException("Bad network topology layer schema"
+ " configuration file: no or multiple <" + LAYERS_TAG +
">element");
}
} else {
throw new IllegalArgumentException("The parse failed because of bad "
+ LAYOUT_VERSION_TAG + " value, expected:" + LAYOUT_VERSION);
}
} else {
throw new IllegalArgumentException("Bad network topology layer schema " +
"configuration file: no or multiple <" + LAYOUT_VERSION_TAG +
"> elements");
}
return schemaList;
}
/**
* Load network topology layer schemas from a YAML configuration file.
* @param schemaFile as inputStream
* @return all valid node schemas defined in schema file
* @throws ParserConfigurationException ParserConfigurationException happen
* @throws IOException no such schema file
* @throws SAXException xml file has some invalid elements
* @throws IllegalArgumentException xml file content is logically invalid
*/
private NodeSchemaLoadResult loadSchemaFromYaml(InputStream schemaFile) {
LOG.info("Loading network topology layer schema file {}", schemaFile);
NodeSchemaLoadResult finalSchema;
try {
Yaml yaml = new Yaml();
NodeSchema nodeTree;
nodeTree = yaml.loadAs(schemaFile, NodeSchema.class);
List<NodeSchema> schemaList = new ArrayList<>();
if (nodeTree.getType() != LayerType.ROOT) {
throw new IllegalArgumentException("First layer is not a ROOT node."
+ " schema file.");
}
schemaList.add(nodeTree);
if (nodeTree.getSublayer() != null) {
nodeTree = nodeTree.getSublayer().get(0);
}
while (nodeTree != null) {
if (nodeTree.getType() == LayerType.LEAF_NODE
&& nodeTree.getSublayer() != null) {
throw new IllegalArgumentException("Leaf node in the middle of path."
+ " schema file.");
}
if (nodeTree.getType() == LayerType.ROOT) {
throw new IllegalArgumentException("Multiple root nodes are defined."
+ " schema file.");
}
schemaList.add(nodeTree);
if (nodeTree.getSublayer() != null) {
nodeTree = nodeTree.getSublayer().get(0);
} else {
break;
}
}
finalSchema = new NodeSchemaLoadResult(schemaList, true);
} catch (Exception e) {
throw new IllegalArgumentException("Fail to load network topology node"
+ " schema file: " + schemaFile + " , error:"
+ e.getMessage(), e);
}
return finalSchema;
}
/**
* Load layoutVersion from root element in the XML configuration file.
* @param root root element
* @return layout version
*/
private int loadLayoutVersion(Element root) {
int layoutVersion;
Text text = (Text) root.getElementsByTagName(LAYOUT_VERSION_TAG)
.item(0).getFirstChild();
if (text != null) {
String value = text.getData().trim();
try {
layoutVersion = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Bad " + LAYOUT_VERSION_TAG +
" value " + value + " is found. It should be an integer.");
}
} else {
throw new IllegalArgumentException("Value of <" + LAYOUT_VERSION_TAG +
"> is null");
}
return layoutVersion;
}
/**
* Load layers from root element in the XML configuration file.
* @param root root element
* @return A map of node schemas with layer ID and layer schema
*/
private Map<String, NodeSchema> loadLayersSection(Element root) {
NodeList elements = root.getElementsByTagName(LAYER_TAG);
Map<String, NodeSchema> schemas = new HashMap<String, NodeSchema>();
for (int i = 0; i < elements.getLength(); i++) {
Node node = elements.item(i);
if (node instanceof Element) {
Element element = (Element) node;
if (LAYER_TAG.equals(element.getTagName())) {
String layerId = element.getAttribute(LAYER_ID);
NodeSchema schema = parseLayerElement(element);
if (!schemas.containsValue(schema)) {
schemas.put(layerId, schema);
} else {
throw new IllegalArgumentException("Repetitive layer in network " +
"topology node schema configuration file: " + layerId);
}
} else {
throw new IllegalArgumentException("Bad element in network topology "
+ "node schema configuration file: " + element.getTagName());
}
}
}
// Integrity check, only one ROOT and one LEAF is allowed
boolean foundRoot = false;
boolean foundLeaf = false;
for(NodeSchema schema: schemas.values()) {
if (schema.getType() == LayerType.ROOT) {
if (foundRoot) {
throw new IllegalArgumentException("Multiple ROOT layers are found" +
" in network topology schema configuration file");
} else {
foundRoot = true;
}
}
if (schema.getType() == LayerType.LEAF_NODE) {
if (foundLeaf) {
throw new IllegalArgumentException("Multiple LEAF layers are found" +
" in network topology schema configuration file");
} else {
foundLeaf = true;
}
}
}
if (!foundRoot) {
throw new IllegalArgumentException("No ROOT layer is found" +
" in network topology schema configuration file");
}
if (!foundLeaf) {
throw new IllegalArgumentException("No LEAF layer is found" +
" in network topology schema configuration file");
}
return schemas;
}
/**
* Load network topology from root element in the XML configuration file and
* sort node schemas according to the topology path.
* @param root root element
* @param schemas schema map
* @return all valid node schemas defined in schema file
*/
private NodeSchemaLoadResult loadTopologySection(Element root,
Map<String, NodeSchema> schemas) {
NodeList elements = root.getElementsByTagName(TOPOLOGY_TAG)
.item(0).getChildNodes();
List<NodeSchema> schemaList = new ArrayList<NodeSchema>();
boolean enforecePrefix = false;
for (int i = 0; i < elements.getLength(); i++) {
Node node = elements.item(i);
if (node instanceof Element) {
Element element = (Element) node;
String tagName = element.getTagName();
// Get the nonnull text value.
Text text = (Text) element.getFirstChild();
String value;
if (text != null) {
value = text.getData().trim();
if (value.isEmpty()) {
// Element with empty value is ignored
continue;
}
} else {
throw new IllegalArgumentException("Value of <" + tagName
+ "> is null");
}
if (TOPOLOGY_PATH.equals(tagName)) {
if(value.startsWith(NetConstants.PATH_SEPARATOR_STR)) {
value = value.substring(1, value.length());
}
String[] layerIDs = value.split(NetConstants.PATH_SEPARATOR_STR);
if (layerIDs == null || layerIDs.length != schemas.size()) {
throw new IllegalArgumentException("Topology path depth doesn't "
+ "match layer element numbers");
}
for (int j = 0; j < layerIDs.length; j++) {
if (schemas.get(layerIDs[j]) == null) {
throw new IllegalArgumentException("No layer found for id " +
layerIDs[j]);
}
}
if (schemas.get(layerIDs[0]).getType() != LayerType.ROOT) {
throw new IllegalArgumentException("Topology path doesn't start "
+ "with ROOT layer");
}
if (schemas.get(layerIDs[layerIDs.length -1]).getType() !=
LayerType.LEAF_NODE) {
throw new IllegalArgumentException("Topology path doesn't end "
+ "with LEAF layer");
}
for (int j = 0; j < layerIDs.length; j++) {
schemaList.add(schemas.get(layerIDs[j]));
}
} else if (TOPOLOGY_ENFORCE_PREFIX.equalsIgnoreCase(tagName)) {
enforecePrefix = Boolean.parseBoolean(value);
} else {
throw new IllegalArgumentException("Unsupported Element <" +
tagName + ">");
}
}
}
// Integrity check
if (enforecePrefix) {
// Every InnerNode should have prefix defined
for (NodeSchema schema: schemas.values()) {
if (schema.getType() == LayerType.INNER_NODE &&
schema.getPrefix() == null) {
throw new IllegalArgumentException("There is layer without prefix " +
"defined while prefix is enforced.");
}
}
}
return new NodeSchemaLoadResult(schemaList, enforecePrefix);
}
/**
* Load a layer from a layer element in the XML configuration file.
* @param element network topology node layer element
* @return ECSchema
*/
private NodeSchema parseLayerElement(Element element) {
NodeList fields = element.getChildNodes();
LayerType type = null;
int cost = 0;
String prefix = null;
String defaultName = null;
for (int i = 0; i < fields.getLength(); i++) {
Node fieldNode = fields.item(i);
if (fieldNode instanceof Element) {
Element field = (Element) fieldNode;
String tagName = field.getTagName();
// Get the nonnull text value.
Text text = (Text) field.getFirstChild();
String value;
if (text != null) {
value = text.getData().trim();
if (value.isEmpty()) {
// Element with empty value is ignored
continue;
}
} else {
continue;
}
if (LAYER_COST.equalsIgnoreCase(tagName)) {
cost = Integer.parseInt(value);
if (cost < 0) {
throw new IllegalArgumentException(
"Cost should be positive number or 0");
}
} else if (LAYER_TYPE.equalsIgnoreCase(tagName)) {
type = NodeSchema.LayerType.getType(value);
if (type == null) {
throw new IllegalArgumentException(
"Unsupported layer type:" + value);
}
} else if (LAYER_PREFIX.equalsIgnoreCase(tagName)) {
prefix = value;
} else if (LAYER_DEFAULT_NAME.equalsIgnoreCase(tagName)) {
defaultName = value;
} else {
throw new IllegalArgumentException("Unsupported Element <" + tagName
+ ">");
}
}
}
// type is a mandatory property
if (type == null) {
throw new IllegalArgumentException("Missing type Element");
}
return new NodeSchema(type, cost, prefix, defaultName);
}
}

View File

@ -1,135 +0,0 @@
/**
* 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.hdds.scm.net;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.apache.hadoop.hdds.scm.net.NodeSchemaLoader.NodeSchemaLoadResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.conf.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** The class manages all network topology schemas. */
public final class NodeSchemaManager {
private static final Logger LOG = LoggerFactory.getLogger(
NodeSchemaManager.class);
// All schema saved and sorted from ROOT to LEAF node
private List<NodeSchema> allSchema;
// enforcePrefix only applies to INNER_NODE
private boolean enforcePrefix;
// max level, includes ROOT level
private int maxLevel = -1;
private volatile static NodeSchemaManager instance = null;
private NodeSchemaManager() {
}
public static NodeSchemaManager getInstance() {
if (instance == null) {
instance = new NodeSchemaManager();
}
return instance;
}
public void init(Configuration conf) {
/**
* Load schemas from network topology schema configuration file
*/
String schemaFile = conf.get(
ScmConfigKeys.OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE,
ScmConfigKeys.OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE_DEFAULT);
NodeSchemaLoadResult result;
try {
result = NodeSchemaLoader.getInstance().loadSchemaFromFile(schemaFile);
allSchema = result.getSchemaList();
enforcePrefix = result.isEnforePrefix();
maxLevel = allSchema.size();
} catch (Throwable e) {
String msg = "Failed to load schema file:" + schemaFile
+ ", error: " + e.getMessage();
LOG.error(msg, e);
throw new RuntimeException(msg, e);
}
}
@VisibleForTesting
public void init(NodeSchema[] schemas, boolean enforce) {
allSchema = new ArrayList<>();
allSchema.addAll(Arrays.asList(schemas));
enforcePrefix = enforce;
maxLevel = schemas.length;
}
public int getMaxLevel() {
return maxLevel;
}
public int getCost(int level) {
Preconditions.checkArgument(level <= maxLevel &&
level >= (NetConstants.ROOT_LEVEL));
return allSchema.get(level - NetConstants.ROOT_LEVEL).getCost();
}
/**
* Given a incomplete network path, return its complete network path if
* possible. E.g. input is 'node1', output is '/rack-default/node1' if this
* schema manages ROOT, RACK and LEAF, with prefix defined and enforce prefix
* enabled.
*
* @param path the incomplete input path
* @return complete path, null if cannot carry out complete action or action
* failed
*/
public String complete(String path) {
if (!enforcePrefix) {
return null;
}
String normalizedPath = NetUtils.normalize(path);
String[] subPath = normalizedPath.split(NetConstants.PATH_SEPARATOR_STR);
if ((subPath.length) == maxLevel) {
return path;
}
StringBuffer newPath = new StringBuffer(NetConstants.ROOT);
// skip the ROOT and LEAF layer
int i, j;
for (i = 1, j = 1; i < subPath.length && j < (allSchema.size() - 1);) {
if (allSchema.get(j).matchPrefix(subPath[i])) {
newPath.append(NetConstants.PATH_SEPARATOR_STR + subPath[i]);
i++;
j++;
} else {
newPath.append(allSchema.get(j).getDefaultName());
j++;
}
}
if (i == (subPath.length - 1)) {
newPath.append(NetConstants.PATH_SEPARATOR_STR + subPath[i]);
return newPath.toString();
}
return null;
}
}

View File

@ -1,21 +0,0 @@
/**
* 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.hdds.scm.net;
/**
The network topology supported by Ozone.
*/

Some files were not shown because too many files have changed in this diff Show More