This is an automated email from the ASF dual-hosted git repository. msingh pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ozone.git
commit b3aa289511a9003973e8ee883c7ff6cb8f5338b3 Author: Siddhant Sangwan <[email protected]> AuthorDate: Wed Mar 24 14:18:48 2021 +0530 HDDS-4872. Upgrade UsageInfoSubcommand with options to show most and least used datanodes. (#1982) --- .../apache/hadoop/hdds/scm/client/ScmClient.java | 19 ++- .../protocol/StorageContainerLocationProtocol.java | 23 ++- ...inerLocationProtocolClientSideTranslatorPB.java | 34 ++++- .../src/main/proto/ScmAdminProtocol.proto | 6 +- .../interface-client/src/main/proto/hdds.proto | 3 +- .../container/placement/metrics/SCMNodeStat.java | 23 +++ .../hadoop/hdds/scm/node/DatanodeUsageInfo.java | 166 +++++++++++++++++++++ .../apache/hadoop/hdds/scm/node/NodeManager.java | 23 ++- .../hadoop/hdds/scm/node/SCMNodeManager.java | 76 +++++++--- ...inerLocationProtocolServerSideTranslatorPB.java | 23 ++- .../hdds/scm/server/SCMClientProtocolServer.java | 76 ++++++++-- .../hadoop/hdds/scm/container/MockNodeManager.java | 16 ++ .../hdds/scm/container/SimpleMockNodeManager.java | 15 ++ .../testutils/ReplicationNodeManagerMock.java | 15 ++ .../hdds/scm/cli/ContainerOperationClient.java | 23 ++- .../hdds/scm/cli/datanode/UsageInfoSubcommand.java | 81 +++++----- 16 files changed, 511 insertions(+), 111 deletions(-) diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java index abb0be2..8ab2ba4 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java @@ -301,11 +301,24 @@ public interface ScmClient extends Closeable { * * @param ipaddress datanode ipaddress String * @param uuid datanode uuid String - * @return List of DatanodeUsageInfo. Each element contains info such as + * @return List of DatanodeUsageInfoProto. Each element contains info such as * capacity, SCMused, and remaining space. * @throws IOException */ - List<HddsProtos.DatanodeUsageInfo> getDatanodeUsageInfo(String ipaddress, - String uuid) + List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo(String ipaddress, + String uuid) throws IOException; + + /** + * Get usage information of most or least used datanodes. + * + * @param mostUsed true if most used, false if least used + * @param count Integer number of nodes to get info for + * @return List of DatanodeUsageInfoProto. Each element contains info such as + * capacity, SCMUsed, and remaining space. + * @throws IOException + */ + List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( + boolean mostUsed, int count) throws IOException; + } diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java index aa14a27..3666e22 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java @@ -262,13 +262,24 @@ public interface StorageContainerLocationProtocol extends Closeable { /** * Get Datanode usage information by ip or uuid. * - * @param ipaddress - datanode IP address String - * @param uuid - datanode UUID String - * @return List of DatanodeUsageInfo. Each element contains info such as + * @param ipaddress datanode IP address String + * @param uuid datanode UUID String + * @return List of DatanodeUsageInfoProto. Each element contains info such as * capacity, SCMused, and remaining space. * @throws IOException */ - List<HddsProtos.DatanodeUsageInfo> getDatanodeUsageInfo(String ipaddress, - String uuid) - throws IOException; + List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( + String ipaddress, String uuid) throws IOException; + + /** + * Get usage information of most or least used datanodes. + * + * @param mostUsed true if most used, false if least used + * @param count Integer number of nodes to get info for + * @return List of DatanodeUsageInfoProto. Each element contains info such as + * capacity, SCMUsed, and remaining space. + * @throws IOException + */ + List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( + boolean mostUsed, int count) throws IOException; } diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java index 24fdf0d..63a88bd 100644 --- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java +++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java @@ -623,14 +623,14 @@ public final class StorageContainerLocationProtocolClientSideTranslatorPB /** * Builds request for datanode usage information and receives response. * - * @param ipaddress - Address String - * @param uuid - UUID String - * @return List of DatanodeUsageInfo. Each element contains info such as + * @param ipaddress Address String + * @param uuid UUID String + * @return List of DatanodeUsageInfoProto. Each element contains info such as * capacity, SCMUsed, and remaining space. * @throws IOException */ @Override - public List<HddsProtos.DatanodeUsageInfo> getDatanodeUsageInfo( + public List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( String ipaddress, String uuid) throws IOException { DatanodeUsageInfoRequestProto request = @@ -646,6 +646,32 @@ public final class StorageContainerLocationProtocolClientSideTranslatorPB return response.getInfoList(); } + /** + * Get usage information of most or least used datanodes. + * + * @param mostUsed true if most used, false if least used + * @param count Integer number of nodes to get info for + * @return List of DatanodeUsageInfoProto. Each element contains info such as + * capacity, SCMUsed, and remaining space. + * @throws IOException + */ + @Override + public List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( + boolean mostUsed, int count) throws IOException { + DatanodeUsageInfoRequestProto request = + DatanodeUsageInfoRequestProto.newBuilder() + .setMostUsed(mostUsed) + .setCount(count) + .build(); + + DatanodeUsageInfoResponseProto response = + submitRequest(Type.DatanodeUsageInfo, + builder -> builder.setDatanodeUsageInfoRequest(request)) + .getDatanodeUsageInfoResponse(); + + return response.getInfoList(); + } + @Override public Object getUnderlyingProxyObject() { return rpcProxy; diff --git a/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto b/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto index 7330f2e..66b6d6f 100644 --- a/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto +++ b/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto @@ -255,15 +255,17 @@ message NodeQueryResponseProto { } /* - Usage info request message containing ip and uuid. + Datanode usage info request message. */ message DatanodeUsageInfoRequestProto { optional string ipaddress = 1; optional string uuid = 2; + optional bool mostUsed = 3; + optional uint32 count = 4; } message DatanodeUsageInfoResponseProto { - repeated DatanodeUsageInfo info = 1; + repeated DatanodeUsageInfoProto info = 1; } /* diff --git a/hadoop-hdds/interface-client/src/main/proto/hdds.proto b/hadoop-hdds/interface-client/src/main/proto/hdds.proto index 8250449..b43600c 100644 --- a/hadoop-hdds/interface-client/src/main/proto/hdds.proto +++ b/hadoop-hdds/interface-client/src/main/proto/hdds.proto @@ -156,10 +156,11 @@ message NodePool { repeated Node nodes = 1; } -message DatanodeUsageInfo { +message DatanodeUsageInfoProto { optional int64 capacity = 1; optional int64 used = 2; optional int64 remaining = 3; + optional DatanodeDetailsProto node = 4; } /** diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/placement/metrics/SCMNodeStat.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/placement/metrics/SCMNodeStat.java index 962bbb4..270df05 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/placement/metrics/SCMNodeStat.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/placement/metrics/SCMNodeStat.java @@ -138,4 +138,27 @@ public class SCMNodeStat implements NodeStat { public int hashCode() { return Long.hashCode(capacity.get() ^ scmUsed.get() ^ remaining.get()); } + + /** + * Compares this SCMNodeStat with other on the basis of remaining to + * capacity ratio. + * + * @param other The SCMNodeStat object to compare with this object. + * @return A value greater than 0 if this has lesser remaining ratio than the + * specified other, a value lesser than 0 if this has greater remaining ratio + * than the specified other, and 0 if remaining ratios are equal. + */ + public int compareByRemainingRatio(SCMNodeStat other) { + Preconditions.checkNotNull(other, "Argument cannot be null"); + + // if capacity is zero, replace with 1 for division to work + double thisCapacity = Math.max(this.getCapacity().get().doubleValue(), 1d); + double otherCapacity = Math.max( + other.getCapacity().get().doubleValue(), 1d); + + double thisRemainingRatio = this.getRemaining().get() / thisCapacity; + double otherRemainingRatio = other.getRemaining().get() / otherCapacity; + + return Double.compare(otherRemainingRatio, thisRemainingRatio); + } } diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/DatanodeUsageInfo.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/DatanodeUsageInfo.java new file mode 100644 index 0000000..a88b2b9 --- /dev/null +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/DatanodeUsageInfo.java @@ -0,0 +1,166 @@ +/** + * 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.node; + +import org.apache.hadoop.hdds.protocol.DatanodeDetails; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos; +import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat; + +import java.util.Comparator; + +public class DatanodeUsageInfo { + + private DatanodeDetails datanodeDetails; + private SCMNodeStat scmNodeStat; + + /** + * Constructs a DatanodeUsageInfo with DatanodeDetails and SCMNodeStat. + * + * @param datanodeDetails DatanodeDetails + * @param scmNodeStat SCMNodeStat + */ + public DatanodeUsageInfo( + DatanodeDetails datanodeDetails, + SCMNodeStat scmNodeStat) { + this.datanodeDetails = datanodeDetails; + this.scmNodeStat = scmNodeStat; + } + + /** + * Compares two DatanodeUsageInfo on the basis of remaining space to capacity + * ratio. + * + * @param first DatanodeUsageInfo + * @param second DatanodeUsageInfo + * @return a value greater than 0 if second has higher remaining to + * capacity ratio, a value lesser than 0 if first has higher remaining to + * capacity ratio, and 0 if both have equal ratios or first.equals(second) + * is true + */ + private static int compareByRemainingRatio(DatanodeUsageInfo first, + DatanodeUsageInfo second) { + if (first.equals(second)) { + return 0; + } + return first.getScmNodeStat() + .compareByRemainingRatio(second.getScmNodeStat()); + } + + /** + * Sets DatanodeDetails of this DatanodeUsageInfo. + * + * @param datanodeDetails the DatanodeDetails to use + */ + public void setDatanodeDetails( + DatanodeDetails datanodeDetails) { + this.datanodeDetails = datanodeDetails; + } + + /** + * Sets SCMNodeStat of this DatanodeUsageInfo. + * + * @param scmNodeStat the SCMNodeStat to use. + */ + public void setScmNodeStat( + SCMNodeStat scmNodeStat) { + this.scmNodeStat = scmNodeStat; + } + + /** + * Gets DatanodeDetails of this DatanodeUsageInfo. + * + * @return DatanodeDetails + */ + public DatanodeDetails getDatanodeDetails() { + return datanodeDetails; + } + + /** + * Gets SCMNodeStat of this DatanodeUsageInfo. + * + * @return SCMNodeStat + */ + public SCMNodeStat getScmNodeStat() { + return scmNodeStat; + } + + /** + * Gets Comparator that compares two DatanodeUsageInfo on the basis of + * remaining space to capacity ratio. + * + * @return Comparator to compare two DatanodeUsageInfo. The comparison + * function returns a value greater than 0 if second DatanodeUsageInfo has + * greater remaining space to capacity ratio, a value lesser than 0 if + * first DatanodeUsageInfo has greater remaining space to capacity ratio, + * and 0 if both have equal ratios or first.equals(second) is true + */ + public static Comparator<DatanodeUsageInfo> getMostUsedByRemainingRatio() { + return DatanodeUsageInfo::compareByRemainingRatio; + } + + /** + * Checks if the specified Object o is equal to this DatanodeUsageInfo. + * + * @param o Object to check + * @return true if both refer to the same object or if both have the same + * DatanodeDetails, false otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DatanodeUsageInfo that = (DatanodeUsageInfo) o; + return datanodeDetails.equals(that.datanodeDetails); + } + + @Override + public int hashCode() { + return datanodeDetails.hashCode(); + } + + /** + * Converts an object of type DatanodeUsageInfo to Protobuf type + * HddsProtos.DatanodeUsageInfoProto. + * + * @return Protobuf HddsProtos.DatanodeUsageInfo + */ + public HddsProtos.DatanodeUsageInfoProto toProto() { + return toProtoBuilder().build(); + } + + private HddsProtos.DatanodeUsageInfoProto.Builder toProtoBuilder() { + HddsProtos.DatanodeUsageInfoProto.Builder builder = + HddsProtos.DatanodeUsageInfoProto.newBuilder(); + + if (datanodeDetails != null) { + builder.setNode( + datanodeDetails.toProto(datanodeDetails.getCurrentVersion())); + } + if (scmNodeStat != null) { + builder.setCapacity(scmNodeStat.getCapacity().get()); + builder.setUsed(scmNodeStat.getScmUsed().get()); + builder.setRemaining(scmNodeStat.getRemaining().get()); + } + return builder; + } +} diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeManager.java index 22a1e81..9f4ac2e 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeManager.java @@ -17,17 +17,17 @@ */ package org.apache.hadoop.hdds.scm.node; +import org.apache.hadoop.hdds.protocol.DatanodeDetails; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.NodeReportProto; import org.apache.hadoop.hdds.scm.container.ContainerID; -import org.apache.hadoop.hdds.scm.net.NetworkTopology; -import org.apache.hadoop.hdds.scm.pipeline.Pipeline; -import org.apache.hadoop.hdds.scm.pipeline.PipelineID; import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeMetric; import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat; +import org.apache.hadoop.hdds.scm.net.NetworkTopology; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; -import org.apache.hadoop.hdds.protocol.DatanodeDetails; -import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState; -import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState; +import org.apache.hadoop.hdds.scm.pipeline.Pipeline; +import org.apache.hadoop.hdds.scm.pipeline.PipelineID; import org.apache.hadoop.hdds.server.events.EventHandler; import org.apache.hadoop.ozone.protocol.StorageContainerNodeProtocol; import org.apache.hadoop.ozone.protocol.commands.CommandForDatanode; @@ -118,6 +118,17 @@ public interface NodeManager extends StorageContainerNodeProtocol, Map<DatanodeDetails, SCMNodeStat> getNodeStats(); /** + * Gets a sorted list of most or least used DatanodeUsageInfo containing + * healthy, in-service nodes. If the specified mostUsed is true, the returned + * list is in descending order of usage. Otherwise, the returned list is in + * ascending order of usage. + * + * @param mostUsed true if most used, false if least used + * @return List of DatanodeUsageInfo + */ + List<DatanodeUsageInfo> getMostOrLeastUsedDatanodes(boolean mostUsed); + + /** * Return the node stat of the specified datanode. * @param datanodeDetails DatanodeDetails. * @return node stat if it is live/stale, null if it is decommissioned or diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java index c03990f..b5ecaac 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java @@ -17,26 +17,15 @@ */ package org.apache.hadoop.hdds.scm.node; -import javax.management.ObjectName; -import java.io.IOException; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.Collections; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; -import java.util.stream.Collectors; - +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.hadoop.hdds.DFSConfigKeysLegacy; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.DatanodeDetails; -import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.NodeReportProto; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineReportsProto; @@ -67,15 +56,25 @@ import org.apache.hadoop.ozone.protocol.commands.RegisteredCommand; import org.apache.hadoop.ozone.protocol.commands.SCMCommand; import org.apache.hadoop.ozone.protocol.commands.SetNodeOperationalStateCommand; import org.apache.hadoop.util.ReflectionUtils; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import org.apache.hadoop.util.Time; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.management.ObjectName; +import java.io.IOException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.stream.Collectors; + /** * Maintains information about the Datanodes on SCM side. * <p> @@ -532,6 +531,41 @@ public class SCMNodeManager implements NodeManager { } /** + * Gets a sorted list of most or least used DatanodeUsageInfo containing + * healthy, in-service nodes. If the specified mostUsed is true, the returned + * list is in descending order of usage. Otherwise, the returned list is in + * ascending order of usage. + * + * @param mostUsed true if most used, false if least used + * @return List of DatanodeUsageInfo + */ + public List<DatanodeUsageInfo> getMostOrLeastUsedDatanodes( + boolean mostUsed) { + List<DatanodeDetails> healthyNodes = + getNodes(NodeOperationalState.IN_SERVICE, NodeState.HEALTHY); + + List<DatanodeUsageInfo> datanodeUsageInfoList = + new ArrayList<>(healthyNodes.size()); + + // create a DatanodeUsageInfo from each DatanodeDetails and add it to the + // list + for (DatanodeDetails node : healthyNodes) { + SCMNodeStat stat = getNodeStatInternal(node); + datanodeUsageInfoList.add(new DatanodeUsageInfo(node, stat)); + } + + // sort the list according to appropriate comparator + if (mostUsed) { + datanodeUsageInfoList.sort( + DatanodeUsageInfo.getMostUsedByRemainingRatio().reversed()); + } else { + datanodeUsageInfoList.sort( + DatanodeUsageInfo.getMostUsedByRemainingRatio()); + } + return datanodeUsageInfoList; + } + + /** * Return the node stat of the specified datanode. * * @param datanodeDetails - datanode ID. diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java index af8db21..ab110c3 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java @@ -17,7 +17,6 @@ */ package org.apache.hadoop.hdds.scm.protocol; -import com.google.common.base.Strings; import com.google.protobuf.ProtocolMessageEnum; import com.google.protobuf.RpcController; import com.google.protobuf.ServiceException; @@ -572,21 +571,17 @@ public final class StorageContainerLocationProtocolServerSideTranslatorPB public DatanodeUsageInfoResponseProto getDatanodeUsageInfo( StorageContainerLocationProtocolProtos.DatanodeUsageInfoRequestProto request) throws IOException { - String ipaddress = null; - String uuid = null; - if (request.hasIpaddress()) { - ipaddress = request.getIpaddress(); - } - if (request.hasUuid()) { - uuid = request.getUuid(); - } - if (Strings.isNullOrEmpty(ipaddress) && Strings.isNullOrEmpty(uuid)) { - throw new IOException("No ip or uuid specified"); + List<HddsProtos.DatanodeUsageInfoProto> infoList; + + // get info by ip or uuid + if (request.hasUuid() || request.hasIpaddress()) { + infoList = impl.getDatanodeUsageInfo(request.getIpaddress(), + request.getUuid()); + } else { // get most or least used nodes + infoList = impl.getDatanodeUsageInfo(request.getMostUsed(), + request.getCount()); } - List<HddsProtos.DatanodeUsageInfo> infoList; - infoList = impl.getDatanodeUsageInfo(ipaddress, - uuid); return DatanodeUsageInfoResponseProto.newBuilder() .addAllInfo(infoList) .build(); diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java index b8f7fbc..315826c 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java @@ -43,6 +43,7 @@ import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat; import org.apache.hadoop.hdds.scm.events.SCMEvents; import org.apache.hadoop.hdds.scm.exceptions.SCMException; import org.apache.hadoop.hdds.scm.exceptions.SCMException.ResultCodes; +import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo; import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; import org.apache.hadoop.hdds.scm.pipeline.Pipeline; @@ -648,16 +649,17 @@ public class SCMClientProtocolServer implements } /** - * Get Datanode usage info (such as capacity, used) by ip or uuid. + * Get Datanode usage info such as capacity, SCMUsed, and remaining by ip + * or uuid. * - * @param ipaddress - Datanode Address String - * @param uuid - Datanode UUID String - * @return List of DatanodeUsageInfo. Each element contains usage info such - * as capacity, SCMUsed, and remaining space. - * @throws IOException + * @param ipaddress Datanode Address String + * @param uuid Datanode UUID String + * @return List of DatanodeUsageInfoProto. Each element contains usage info + * such as capacity, SCMUsed, and remaining space. + * @throws IOException if admin authentication fails */ @Override - public List<HddsProtos.DatanodeUsageInfo> getDatanodeUsageInfo( + public List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( String ipaddress, String uuid) throws IOException { // check admin authorisation @@ -682,7 +684,7 @@ public class SCMClientProtocolServer implements } // get datanode usage info - List<HddsProtos.DatanodeUsageInfo> infoList = new ArrayList<>(); + List<HddsProtos.DatanodeUsageInfoProto> infoList = new ArrayList<>(); for (DatanodeDetails node : nodes) { infoList.add(getUsageInfoFromDatanodeDetails(node)); } @@ -693,25 +695,69 @@ public class SCMClientProtocolServer implements /** * Get usage details for a specific DatanodeDetails node. * - * @param node - DatanodeDetails + * @param node DatanodeDetails * @return Usage info such as capacity, SCMUsed, and remaining space. - * @throws IOException */ - private HddsProtos.DatanodeUsageInfo getUsageInfoFromDatanodeDetails( - DatanodeDetails node) throws IOException { + private HddsProtos.DatanodeUsageInfoProto getUsageInfoFromDatanodeDetails( + DatanodeDetails node) { SCMNodeStat stat = scm.getScmNodeManager().getNodeStat(node).get(); long capacity = stat.getCapacity().get(); long used = stat.getScmUsed().get(); long remaining = stat.getRemaining().get(); - HddsProtos.DatanodeUsageInfo info = HddsProtos.DatanodeUsageInfo - .newBuilder() + return HddsProtos.DatanodeUsageInfoProto.newBuilder() .setCapacity(capacity) .setUsed(used) .setRemaining(remaining) + .setNode(node.toProto(node.getCurrentVersion())) .build(); - return info; + } + + /** + * Get a sorted list of most or least used DatanodeUsageInfo containing + * healthy, in-service nodes. + * + * @param mostUsed true if most used, false if least used + * @param count number of nodes to get; must be an integer greater than zero + * @return List of DatanodeUsageInfoProto. Each element contains usage info + * such as capacity, SCMUsed, and remaining space. + * @throws IOException if admin authentication fails + * @throws IllegalArgumentException if count is not an integer greater than + * zero + */ + @Override + public List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( + boolean mostUsed, int count) throws IOException, IllegalArgumentException{ + + // check admin authorisation + String remoteUser = getRpcRemoteUsername(); + try { + getScm().checkAdminAccess(remoteUser); + } catch (IOException e) { + LOG.error("Authorisation failed", e); + throw e; + } + + if (count < 1) { + throw new IllegalArgumentException("The specified parameter count must " + + "be an integer greater than zero."); + } + + List<DatanodeUsageInfo> datanodeUsageInfoList = + scm.getScmNodeManager().getMostOrLeastUsedDatanodes(mostUsed); + + // if count is greater than the size of list containing healthy, + // in-service nodes, just set count to that size + if (count > datanodeUsageInfoList.size()) { + count = datanodeUsageInfoList.size(); + } + + // return count number of DatanodeUsageInfoProto + return datanodeUsageInfoList.stream() + .map(DatanodeUsageInfo::toProto) + .limit(count) + .collect(Collectors.toList()); } /** diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/MockNodeManager.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/MockNodeManager.java index ddd4aef..bb49016 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/MockNodeManager.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/MockNodeManager.java @@ -24,6 +24,7 @@ import org.apache.hadoop.hdds.scm.net.NetConstants; import org.apache.hadoop.hdds.scm.net.NetworkTopology; import org.apache.hadoop.hdds.scm.net.NetworkTopologyImpl; import org.apache.hadoop.hdds.scm.net.Node; +import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo; import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.pipeline.Pipeline; import org.apache.hadoop.hdds.scm.pipeline.PipelineID; @@ -268,6 +269,21 @@ public class MockNodeManager implements NodeManager { } /** + * Gets a sorted list of most or least used DatanodeUsageInfo containing + * healthy, in-service nodes. If the specified mostUsed is true, the returned + * list is in descending order of usage. Otherwise, the returned list is in + * ascending order of usage. + * + * @param mostUsed true if most used, false if least used + * @return List of DatanodeUsageInfo + */ + @Override + public List<DatanodeUsageInfo> getMostOrLeastUsedDatanodes( + boolean mostUsed) { + return null; + } + + /** * Return the node stat of the specified datanode. * @param datanodeDetails - datanode details. * @return node stat if it is live/stale, null if it is decommissioned or diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/SimpleMockNodeManager.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/SimpleMockNodeManager.java index be4ab57..774d706 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/SimpleMockNodeManager.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/SimpleMockNodeManager.java @@ -24,6 +24,7 @@ import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeMetric; import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat; import org.apache.hadoop.hdds.scm.net.NetworkTopology; import org.apache.hadoop.hdds.scm.node.DatanodeInfo; +import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo; import org.apache.hadoop.hdds.scm.node.NodeManager; import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; @@ -211,6 +212,20 @@ public class SimpleMockNodeManager implements NodeManager { return null; } + /** + * Gets a sorted list of most or least used DatanodeUsageInfo containing + * healthy, in-service nodes. If the specified mostUsed is true, the returned + * list is in descending order of usage. Otherwise, the returned list is in + * ascending order of usage. + * + * @param mostUsed true if most used, false if least used + * @return List of DatanodeUsageInfo + */ + @Override + public List<DatanodeUsageInfo> getMostOrLeastUsedDatanodes(boolean mostUsed) { + return null; + } + @Override public SCMNodeMetric getNodeStat(DatanodeDetails datanodeDetails) { return null; diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/ozone/container/testutils/ReplicationNodeManagerMock.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/ozone/container/testutils/ReplicationNodeManagerMock.java index 048b953..3aba29f 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/ozone/container/testutils/ReplicationNodeManagerMock.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/ozone/container/testutils/ReplicationNodeManagerMock.java @@ -21,6 +21,7 @@ import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.protocol.proto .StorageContainerDatanodeProtocolProtos.PipelineReportsProto; import org.apache.hadoop.hdds.scm.container.ContainerID; +import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo; import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.net.NetworkTopology; import org.apache.hadoop.hdds.scm.pipeline.Pipeline; @@ -161,6 +162,20 @@ public class ReplicationNodeManagerMock implements NodeManager { } /** + * Gets a sorted list of most or least used DatanodeUsageInfo containing + * healthy, in-service nodes. If the specified mostUsed is true, the returned + * list is in descending order of usage. Otherwise, the returned list is in + * ascending order of usage. + * + * @param mostUsed true if most used, false if least used + * @return List of DatanodeUsageInfo + */ + @Override + public List<DatanodeUsageInfo> getMostOrLeastUsedDatanodes(boolean mostUsed) { + return null; + } + + /** * Return the node stat of the specified datanode. * * @param dd - datanode details. diff --git a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java index 8657667..3f4c55c 100644 --- a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java +++ b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java @@ -549,17 +549,32 @@ public class ContainerOperationClient implements ScmClient { /** * Get Datanode Usage information by ipaddress or uuid. * - * @param ipaddress - datanode ipaddress String - * @param uuid - datanode uuid String - * @return List of DatanodeUsageInfo. Each element contains info such as + * @param ipaddress datanode ipaddress String + * @param uuid datanode uuid String + * @return List of DatanodeUsageInfoProto. Each element contains info such as * capacity, SCMused, and remaining space. * @throws IOException */ @Override - public List<HddsProtos.DatanodeUsageInfo> getDatanodeUsageInfo( + public List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( String ipaddress, String uuid) throws IOException { return storageContainerLocationClient.getDatanodeUsageInfo(ipaddress, uuid); } + /** + * Get usage information of most or least used datanodes. + * + * @param mostUsed true if most used, false if least used + * @param count Integer number of nodes to get info for + * @return List of DatanodeUsageInfoProto. Each element contains info such as + * capacity, SCMUsed, and remaining space. + * @throws IOException + */ + @Override + public List<HddsProtos.DatanodeUsageInfoProto> getDatanodeUsageInfo( + boolean mostUsed, int count) throws IOException { + return storageContainerLocationClient.getDatanodeUsageInfo(mostUsed, count); + } + } diff --git a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/UsageInfoSubcommand.java b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/UsageInfoSubcommand.java index ff8d624..113ff98 100644 --- a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/UsageInfoSubcommand.java +++ b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/UsageInfoSubcommand.java @@ -41,58 +41,69 @@ import java.util.List; versionProvider = HddsVersionProvider.class) public class UsageInfoSubcommand extends ScmSubcommand { - @CommandLine.Option(names = {"--ip"}, paramLabel = "IP", description = - "Show info by datanode ip address") - private String ipaddress; + @CommandLine.ArgGroup(multiplicity = "1") + private ExclusiveArguments exclusiveArguments; - @CommandLine.Option(names = {"--uuid"}, paramLabel = "UUID", description = - "Show info by datanode UUID") - private String uuid; + private static class ExclusiveArguments { + @CommandLine.Option(names = {"--ip"}, paramLabel = "IP", description = + "Show info by datanode ip address.", defaultValue = "") + private String ipaddress; - public String getIpaddress() { - return ipaddress; - } + @CommandLine.Option(names = {"--uuid"}, paramLabel = "UUID", description = + "Show info by datanode UUID.", defaultValue = "") + private String uuid; - public void setIpaddress(String ipaddress) { - this.ipaddress = ipaddress; - } + @CommandLine.Option(names = {"-m", "--most-used"}, + description = "Show the most used datanodes.", + defaultValue = "false") + private boolean mostUsed; - public String getUuid() { - return uuid; + @CommandLine.Option(names = {"-l", "--least-used"}, + description = "Show the least used datanodes.", + defaultValue = "false") + private boolean leastUsed; } - public void setUuid(String uuid) { - this.uuid = uuid; - } + @CommandLine.Option(names = {"-c", "--count"}, description = "Number of " + + "datanodes to display (Default: ${DEFAULT-VALUE}).", + paramLabel = "NUMBER OF NODES", defaultValue = "3") + private int count; + @Override public void execute(ScmClient scmClient) throws IOException { - if (Strings.isNullOrEmpty(ipaddress)) { - ipaddress = ""; - } - if (Strings.isNullOrEmpty(uuid)) { - uuid = ""; - } - if (Strings.isNullOrEmpty(ipaddress) && Strings.isNullOrEmpty(uuid)) { - throw new IOException("ipaddress or uuid of the datanode must be " + - "specified."); + List<HddsProtos.DatanodeUsageInfoProto> infoList; + if (count < 1) { + throw new IOException("Count must be an integer greater than 0."); } - List<HddsProtos.DatanodeUsageInfo> infoList = - scmClient.getDatanodeUsageInfo(ipaddress, uuid); - - for (HddsProtos.DatanodeUsageInfo info : infoList) { - printInfo(info); + // fetch info by ip or uuid + if (!Strings.isNullOrEmpty(exclusiveArguments.ipaddress) || + !Strings.isNullOrEmpty(exclusiveArguments.uuid)) { + infoList = scmClient.getDatanodeUsageInfo(exclusiveArguments.ipaddress, + exclusiveArguments.uuid); + } else { // get info of most used or least used nodes + infoList = scmClient.getDatanodeUsageInfo(exclusiveArguments.mostUsed, + count); } + + infoList.forEach(this::printInfo); } - public void printInfo(HddsProtos.DatanodeUsageInfo info) { - Double capacity = (double)info.getCapacity(); - Double usedRatio = info.getUsed() / capacity; - Double remainingRatio = info.getRemaining() / capacity; + /** + * Print datanode usage information. + * + * @param info Information such as Capacity, SCMUsed etc. + */ + public void printInfo(HddsProtos.DatanodeUsageInfoProto info) { + double capacity = (double) info.getCapacity(); + double usedRatio = info.getUsed() / capacity; + double remainingRatio = info.getRemaining() / capacity; NumberFormat percentFormat = NumberFormat.getPercentInstance(); percentFormat.setMinimumFractionDigits(5); + System.out.printf("Usage info for datanode with UUID %s:%n", + info.getNode().getUuid()); System.out.printf("%-10s: %20sB %n", "Capacity", info.getCapacity()); System.out.printf("%-10s: %20sB (%s) %n", "SCMUsed", info.getUsed(), percentFormat.format(usedRatio)); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
