diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/NodeEndpoint.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/NodeEndpoint.java index 2f8849715ed4..49e3fd311b2d 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/NodeEndpoint.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/NodeEndpoint.java @@ -19,6 +19,7 @@ package org.apache.hadoop.ozone.recon.api; 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.scm.container.placement.metrics.SCMNodeStat; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; @@ -85,6 +86,7 @@ public Response getDatanodes() { } catch (NodeNotFoundException e) { LOG.warn("Cannot get nodeState for datanode {}", datanode, e); } + final NodeOperationalState nodeOpState = datanode.getPersistedOpState(); String hostname = datanode.getHostName(); Set pipelineIDs = nodeManager.getPipelines(datanode); List pipelines = new ArrayList<>(); @@ -123,6 +125,7 @@ public Response getDatanodes() { .withDatanodeStorageReport(storageReport) .withLastHeartbeat(nodeManager.getLastHeartbeat(datanode)) .withState(nodeState) + .withOperationalState(nodeOpState) .withPipelines(pipelines) .withLeaderCount(leaderCount.get()) .withUUid(datanode.getUuidString()) diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeMetadata.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeMetadata.java index f75ea3233f37..bb8bf29efd55 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeMetadata.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeMetadata.java @@ -18,6 +18,7 @@ package org.apache.hadoop.ozone.recon.api.types; import com.google.common.base.Preconditions; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState; import javax.xml.bind.annotation.XmlAccessType; @@ -40,6 +41,9 @@ public final class DatanodeMetadata { @XmlElement(name = "state") private NodeState state; + @XmlElement(name = "opState") + private NodeOperationalState opState; + @XmlElement(name = "lastHeartbeat") private long lastHeartbeat; @@ -71,6 +75,7 @@ private DatanodeMetadata(Builder builder) { this.hostname = builder.hostname; this.uuid = builder.uuid; this.state = builder.state; + this.opState = builder.opState; this.lastHeartbeat = builder.lastHeartbeat; this.datanodeStorageReport = builder.datanodeStorageReport; this.pipelines = builder.pipelines; @@ -90,6 +95,10 @@ public NodeState getState() { return state; } + public NodeOperationalState getOperationalState() { + return opState; + } + public long getLastHeartbeat() { return lastHeartbeat; } @@ -147,6 +156,7 @@ public static final class Builder { private String hostname; private String uuid; private NodeState state; + private NodeOperationalState opState; private long lastHeartbeat; private DatanodeStorageReport datanodeStorageReport; private List pipelines; @@ -172,6 +182,11 @@ public Builder withState(NodeState state) { return this; } + public Builder withOperationalState(NodeOperationalState opState) { + this.opState = opState; + return this; + } + public Builder withLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; return this; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json index 8d61333b087b..eb17c9cc8754 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json @@ -20,6 +20,7 @@ "hostname": "localhost1.storage.enterprise.com", "uuid": "b590734e-a5f2-11ea-bb37-0242ac130002", "state": "HEALTHY", + "opState": "IN_SERVICE", "lastHeartbeat": 1574728876059, "storageReport": { "capacity": 62725623808, @@ -51,6 +52,7 @@ "hostname": "localhost2.storage.enterprise.com", "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002", "state": "HEALTHY", + "opState": "DECOMMISSIONING", "lastHeartbeat": 1574724876059, "storageReport": { "capacity": 549755813888, @@ -80,8 +82,105 @@ }, { "hostname": "localhost3.storage.enterprise.com", + "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002", + "state": "HEALTHY", + "opState": "DECOMMISSIONED", + "lastHeartbeat": 1574724876059, + "storageReport": { + "capacity": 549755813888, + "used": 450971566080, + "remaining": 95784247808 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost2.storage.enterprise.com" + } + ], + "containers": 8192, + "leaderCount": 1, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1574724805059, + "revision": "caf471111cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-20T15:45Z" + }, + { + "hostname": "localhost4.storage.enterprise.com", + "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002", + "state": "HEALTHY", + "opState": "ENTERING_MAINTENANCE", + "lastHeartbeat": 1574724876059, + "storageReport": { + "capacity": 549755813888, + "used": 450971566080, + "remaining": 95784247808 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost2.storage.enterprise.com" + } + ], + "containers": 8192, + "leaderCount": 1, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1574724805059, + "revision": "caf471111cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-20T15:45Z" + }, + { + "hostname": "localhost5.storage.enterprise.com", + "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002", + "state": "HEALTHY", + "opState": "IN_MAINTENANCE", + "lastHeartbeat": 1574724876059, + "storageReport": { + "capacity": 549755813888, + "used": 450971566080, + "remaining": 95784247808 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost2.storage.enterprise.com" + } + ], + "containers": 8192, + "leaderCount": 1, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1574724805059, + "revision": "caf471111cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-20T15:45Z" + }, + { + "hostname": "localhost6.storage.enterprise.com", "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002", "state": "STALE", + "opState": "IN_SERVICE", "lastHeartbeat": 1343544879843, "storageReport": { "capacity": 140737488355328, @@ -116,9 +215,162 @@ "buildDate": "2020-07-19T13:45Z" }, { - "hostname": "localhost4.storage.enterprise.com", + "hostname": "localhost7.storage.enterprise.com", + "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002", + "state": "STALE", + "opState": "DECOMMISSIONING", + "lastHeartbeat": 1343544879843, + "storageReport": { + "capacity": 140737488355328, + "used": 43980465111040, + "remaining": 86757023244288 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + }, + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "STAND_ALONE", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + } + ], + "containers": 43, + "leaderCount": 2, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1343544679543, + "revision": "aaf470000cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-19T13:45Z" + }, + { + "hostname": "localhost8.storage.enterprise.com", + "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002", + "state": "STALE", + "opState": "DECOMMISSIONED", + "lastHeartbeat": 1343544879843, + "storageReport": { + "capacity": 140737488355328, + "used": 43980465111040, + "remaining": 86757023244288 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + }, + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "STAND_ALONE", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + } + ], + "containers": 43, + "leaderCount": 2, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1343544679543, + "revision": "aaf470000cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-19T13:45Z" + }, + { + "hostname": "localhost9.storage.enterprise.com", + "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002", + "state": "STALE", + "opState": "ENTERING_MAINTENANCE", + "lastHeartbeat": 1343544879843, + "storageReport": { + "capacity": 140737488355328, + "used": 43980465111040, + "remaining": 86757023244288 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + }, + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "STAND_ALONE", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + } + ], + "containers": 43, + "leaderCount": 2, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1343544679543, + "revision": "aaf470000cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-19T13:45Z" + }, + { + "hostname": "localhost10.storage.enterprise.com", + "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002", + "state": "STALE", + "opState": "IN_MAINTENANCE", + "lastHeartbeat": 1343544879843, + "storageReport": { + "capacity": 140737488355328, + "used": 43980465111040, + "remaining": 86757023244288 + }, + "pipelines": [ + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "RATIS", + "replicationFactor": 3, + "leaderNode": "localhost1.storage.enterprise.com" + }, + { + "pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982", + "replicationType": "RATIS", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + }, + { + "pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a", + "replicationType": "STAND_ALONE", + "replicationFactor": 1, + "leaderNode": "localhost3.storage.enterprise.com" + } + ], + "containers": 43, + "leaderCount": 2, + "version": "0.6.0-SNAPSHOT", + "setupTime": 1343544679543, + "revision": "aaf470000cdb9168ec013f4526bb997aa513e079", + "buildDate": "2020-07-19T13:45Z" + }, + { + "hostname": "localhost11.storage.enterprise.com", "uuid": "b5907a06-a5f2-11ea-bb37-0242ac130002", "state": "DEAD", + "opState": "IN_SERVICE", "lastHeartbeat": 1074724876059, "storageReport": { "capacity": 140737488355328, @@ -134,9 +386,10 @@ "buildDate": "2020-07-19T13:45Z" }, { - "hostname": "localhost5.storage.enterprise.com", + "hostname": "localhost12.storage.enterprise.com", "uuid": "b5907ac4-a5f2-11ea-bb37-0242ac130002", - "state": "DECOMMISSIONING", + "state": "DEAD", + "opState": "DECOMMISSIONING", "lastHeartbeat": 1574724876059, "storageReport": { "capacity": 805306368000, @@ -165,9 +418,10 @@ "buildDate": "2020-07-19T13:45Z" }, { - "hostname": "localhost6.storage.enterprise.com", + "hostname": "localhost13.storage.enterprise.com", "uuid": "b5907b82-a5f2-11ea-bb37-0242ac130002", - "state": "HEALTHY", + "state": "DEAD", + "opState": "DECOMMISSIONED", "lastHeartbeat": 1574724876059, "storageReport": { "capacity": 140737488355328, @@ -196,9 +450,10 @@ "buildDate": "2020-07-19T13:45Z" }, { - "hostname": "localhost7.storage.enterprise.com", + "hostname": "localhost14.storage.enterprise.com", "uuid": "b5907c40-a5f2-11ea-bb37-0242ac130002", - "state": "HEALTHY", + "state": "DEAD", + "opState": "ENTERING_MAINTENANCE", "lastHeartbeat": 1574724876059, "storageReport": { "capacity": 549755813888, @@ -233,9 +488,10 @@ "buildDate": "2020-07-19T13:45Z" }, { - "hostname": "localhost8.storage.enterprise.com", + "hostname": "localhost15.storage.enterprise.com", "uuid": "b5907cf4-a5f2-11ea-bb37-0242ac130002", - "state": "DECOMMISSIONED", + "state": "DEAD", + "opState": "IN_MAINTENANCE", "lastHeartbeat": 1574724876059, "storageReport": { "capacity": 140737488355328, @@ -264,9 +520,10 @@ "buildDate": "2020-07-20T15:45Z" }, { - "hostname": "localhost9.storage.enterprise.com", + "hostname": "localhost16.storage.enterprise.com", "uuid": "b5907f4c-a5f2-11ea-bb37-0242ac130002", "state": "HEALTHY", + "opState": "IN_SERVICE", "lastHeartbeat": 1574724874011, "storageReport": { "capacity": 140737488355328, @@ -295,9 +552,10 @@ "buildDate": "2020-07-20T15:45Z" }, { - "hostname": "localhost10.storage.enterprise.com", + "hostname": "localhost17.storage.enterprise.com", "uuid": "b590801e-a5f2-11ea-bb37-0242ac130002", "state": "HEALTHY", + "opState": "IN_SERVICE", "lastHeartbeat": 1574723876959, "storageReport": { "capacity": 140737488355328, @@ -332,9 +590,10 @@ "buildDate": "2020-07-20T15:45Z" }, { - "hostname": "localhost11.storage.enterprise.com", + "hostname": "localhost18.storage.enterprise.com", "uuid": "b59080e6-a5f2-11ea-bb37-0242ac130002", "state": "STALE", + "opState": "IN_SERVICE", "lastHeartbeat": 1474724876783, "storageReport": { "capacity": 140737488355328, @@ -363,9 +622,10 @@ "buildDate": "2020-07-20T10:45Z" }, { - "hostname": "localhost12.storage.enterprise.com", + "hostname": "localhost19.storage.enterprise.com", "uuid": "b59081a4-a5f2-11ea-bb37-0242ac130002", "state": "HEALTHY", + "opState": "IN_SERVICE", "lastHeartbeat": 1574724796532, "storageReport": { "capacity": 140737488355328, diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/types/datanode.types.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/types/datanode.types.tsx index e9cb16820814..8f92742916f3 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/types/datanode.types.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/types/datanode.types.tsx @@ -16,9 +16,15 @@ * limitations under the License. */ -export const DatanodeStatusList = ['HEALTHY', 'STALE', 'DEAD', 'DECOMMISSIONING', 'DECOMMISSIONED'] as const; -type DatanodeStatusTuple = typeof DatanodeStatusList; -export type DatanodeStatus = DatanodeStatusTuple[number]; // 'HEALTHY' | 'STALE' | 'DEAD' | 'DECOMMISSIONING' | 'DECOMMISSIONED'; +// Corresponds to HddsProtos.NodeState +export const DatanodeStateList = ['HEALTHY', 'STALE', 'DEAD'] as const; +type DatanodeStateType = typeof DatanodeStateList; +export type DatanodeState = DatanodeStateType[number]; + +// Corresponds to HddsProtos.NodeOperationalState +export const DatanodeOpStateList = ['IN_SERVICE', 'DECOMMISSIONING', 'DECOMMISSIONED', 'ENTERING_MAINTENANCE', 'IN_MAINTENANCE'] as const; +type DatanodeOpStateType = typeof DatanodeOpStateList; +export type DatanodeOpState = DatanodeOpStateType[number]; export interface IStorageReport { capacity: number; diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx index 91b6a45aa99d..a9631c66e62e 100644 --- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx +++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx @@ -23,7 +23,13 @@ import {PaginationConfig} from 'antd/lib/pagination'; import moment from 'moment'; import {ReplicationIcon} from 'utils/themeIcons'; import StorageBar from 'components/storageBar/storageBar'; -import {DatanodeStatus, DatanodeStatusList, IStorageReport} from 'types/datanode.types'; +import { + DatanodeState, + DatanodeStateList, + DatanodeOpState, + DatanodeOpStateList, + IStorageReport +} from 'types/datanode.types'; import './datanodes.less'; import {AutoReloadHelper} from 'utils/autoReloadHelper'; import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel'; @@ -34,7 +40,8 @@ import {ColumnSearch} from 'utils/columnSearch'; interface IDatanodeResponse { hostname: string; - state: DatanodeStatus; + state: DatanodeState; + opState: DatanodeOpState; lastHeartbeat: number; storageReport: IStorageReport; pipelines: IPipeline[]; @@ -54,7 +61,8 @@ interface IDatanodesResponse { interface IDatanode { hostname: string; - state: DatanodeStatus; + state: DatanodeState; + opState: DatanodeOpState; lastHeartbeat: number; storageUsed: number; storageTotal: number; @@ -85,28 +93,50 @@ interface IDatanodesState { columnOptions: IOption[]; } -const renderDatanodeStatus = (status: DatanodeStatus) => { - const statusIconMap = { +const renderDatanodeState = (state: DatanodeState) => { + const stateIconMap = { HEALTHY: , STALE: , - DEAD: , - DECOMMISSIONING: , - DECOMMISSIONED: + DEAD: }; - const icon = status in statusIconMap ? statusIconMap[status] : ''; - return {icon} {status}; + const icon = state in stateIconMap ? stateIconMap[state] : ''; + return {icon} {state}; +}; + +const renderDatanodeOpState = (opState: DatanodeOpState) => { + const opStateIconMap = { + IN_SERVICE: , + DECOMMISSIONING: , + DECOMMISSIONED: , + ENTERING_MAINTENANCE: , + IN_MAINTENANCE: + }; + const icon = opState in opStateIconMap ? opStateIconMap[opState] : ''; + return {icon} {opState}; }; const COLUMNS = [ { - title: 'Status', + title: 'State', dataIndex: 'state', key: 'state', isVisible: true, filterMultiple: true, - filters: DatanodeStatusList.map(status => ({text: status, value: status})), - onFilter: (value: DatanodeStatus, record: IDatanode) => record.state === value, - render: (text: DatanodeStatus) => renderDatanodeStatus(text), + filters: DatanodeStateList.map(state => ({text: state, value: state})), + onFilter: (value: DatanodeState, record: IDatanode) => record.state === value, + render: (text: DatanodeState) => renderDatanodeState(text), + sorter: (a: IDatanode, b: IDatanode) => a.state.localeCompare(b.state), + fixed: 'left' + }, + { + title: 'Operational State', + dataIndex: 'opState', + key: 'opState', + isVisible: true, + filterMultiple: true, + filters: DatanodeOpStateList.map(state => ({text: state, value: state})), + onFilter: (value: DatanodeOpState, record: IDatanode) => record.opState === value, + render: (text: DatanodeOpState) => renderDatanodeOpState(text), sorter: (a: IDatanode, b: IDatanode) => a.state.localeCompare(b.state), fixed: 'left' }, @@ -290,6 +320,7 @@ export class Datanodes extends React.Component, IDatanode hostname: datanode.hostname, uuid: datanode.uuid, state: datanode.state, + opState: datanode.opState, lastHeartbeat: datanode.lastHeartbeat, storageUsed: datanode.storageReport.used, storageTotal: datanode.storageReport.capacity, diff --git a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java index f5cdbdf17456..4a453b041eb6 100644 --- a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java +++ b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java @@ -24,6 +24,8 @@ import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.protocol.proto.HddsProtos .ExtendedDatanodeDetailsProto; +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.HddsProtos.ReplicationType; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.PipelineID; @@ -39,6 +41,8 @@ import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineReportsProto; import org.apache.hadoop.hdds.scm.container.ContainerInfo; import org.apache.hadoop.hdds.scm.container.common.helpers.ContainerWithPipeline; +import org.apache.hadoop.hdds.scm.node.NodeManager; +import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.pipeline.Pipeline; import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol; import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager; @@ -107,6 +111,7 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; /** * Test for Recon API endpoints. @@ -376,6 +381,11 @@ public void setUp() throws Exception { private void testDatanodeResponse(DatanodeMetadata datanodeMetadata) throws IOException { + // Check NodeState and NodeOperationalState field existence + Assert.assertEquals(NodeState.HEALTHY, datanodeMetadata.getState()); + Assert.assertEquals(NodeOperationalState.IN_SERVICE, + datanodeMetadata.getOperationalState()); + String hostname = datanodeMetadata.getHostname(); switch (hostname) { case HOST1: @@ -443,6 +453,43 @@ public void testGetDatanodes() throws Exception { reconScm.getPipelineManager() .getContainersInPipeline(pipeline.getId()).size() == 1); }); + + // Change Node OperationalState with NodeManager + final NodeManager nodeManager = reconScm.getScmNodeManager(); + final DatanodeDetails dnDetailsInternal = + nodeManager.getNodeByUuid(datanodeDetails.getUuidString()); + // Backup existing state and sanity check + final NodeStatus nStatus = nodeManager.getNodeStatus(dnDetailsInternal); + final NodeOperationalState backupOpState = + dnDetailsInternal.getPersistedOpState(); + final long backupOpStateExpiry = + dnDetailsInternal.getPersistedOpStateExpiryEpochSec(); + assertEquals(backupOpState, nStatus.getOperationalState()); + assertEquals(backupOpStateExpiry, nStatus.getOpStateExpiryEpochSeconds()); + + dnDetailsInternal.setPersistedOpState(NodeOperationalState.DECOMMISSIONING); + dnDetailsInternal.setPersistedOpStateExpiryEpochSec(666L); + nodeManager.setNodeOperationalState(dnDetailsInternal, + NodeOperationalState.DECOMMISSIONING, 666L); + // Check if the endpoint response reflects the change + response = nodeEndpoint.getDatanodes(); + datanodesResponse = (DatanodesResponse) response.getEntity(); + // Order of datanodes in the response is random + AtomicInteger count = new AtomicInteger(); + datanodesResponse.getDatanodes().forEach(metadata -> { + if (metadata.getUuid().equals(dnDetailsInternal.getUuidString())) { + count.incrementAndGet(); + assertEquals(NodeOperationalState.DECOMMISSIONING, + metadata.getOperationalState()); + } + }); + assertEquals(1, count.get()); + + // Restore state + dnDetailsInternal.setPersistedOpState(backupOpState); + dnDetailsInternal.setPersistedOpStateExpiryEpochSec(backupOpStateExpiry); + nodeManager.setNodeOperationalState(dnDetailsInternal, + backupOpState, backupOpStateExpiry); } @Test