This is an automated email from the ASF dual-hosted git repository.
vivekratnavel pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hadoop-ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 34debd3 HDDS-3571. Recon: Display leader count in Datanodes page
(#919)
34debd3 is described below
commit 34debd34e6ed5eda1c4e337b9b7c04716c2ddabe
Author: Vivek Ratnavel Subramanian <[email protected]>
AuthorDate: Thu May 14 14:17:21 2020 -0700
HDDS-3571. Recon: Display leader count in Datanodes page (#919)
---
.../hadoop/ozone/recon/api/NodeEndpoint.java | 29 ++++--
.../ozone/recon/api/types/DatanodeMetadata.java | 106 +++++++++++++++++---
.../ozone/recon/api/types/DatanodePipeline.java | 8 +-
.../webapps/recon/ozone-recon-web/api/db.json | 111 ++++++++++++++-------
.../webapps/recon/ozone-recon-web/src/app.less | 19 +++-
.../recon/ozone-recon-web/src/utils/themeIcons.tsx | 15 ++-
.../src/views/datanodes/datanodes.tsx | 26 ++++-
.../src/views/pipelines/pipelines.tsx | 5 +-
.../hadoop/ozone/recon/api/TestEndpoints.java | 35 +++++--
9 files changed, 273 insertions(+), 81 deletions(-)
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 d59bee6..2924435 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
@@ -38,9 +38,11 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.hadoop.ozone.recon.scm.ReconPipelineManager;
import org.slf4j.Logger;
@@ -81,31 +83,44 @@ public class NodeEndpoint {
String hostname = datanode.getHostName();
Set<PipelineID> pipelineIDs = nodeManager.getPipelines(datanode);
List<DatanodePipeline> pipelines = new ArrayList<>();
+ AtomicInteger leaderCount = new AtomicInteger();
+ DatanodeMetadata.Builder builder = DatanodeMetadata.newBuilder();
pipelineIDs.forEach(pipelineID -> {
try {
Pipeline pipeline = pipelineManager.getPipeline(pipelineID);
+ String leaderNode = pipeline.getLeaderNode().getHostName();
DatanodePipeline datanodePipeline = new DatanodePipeline(
pipelineID.getId(),
pipeline.getType().toString(),
- pipeline.getFactor().getNumber()
+ pipeline.getFactor().getNumber(),
+ leaderNode
);
pipelines.add(datanodePipeline);
+ if (pipeline.getLeaderId().equals(datanode.getUuid())) {
+ leaderCount.getAndIncrement();
+ }
} catch (PipelineNotFoundException ex) {
LOG.warn("Cannot get pipeline {} for datanode {}, pipeline not
found",
pipelineID.getId(), hostname, ex);
+ } catch (IOException ioEx) {
+ LOG.warn("Cannot get leader node of pipeline with id {}.",
+ pipelineID.getId(), ioEx);
}
});
- int containers;
try {
- containers = nodeManager.getContainers(datanode).size();
+ int containers = nodeManager.getContainers(datanode).size();
+ builder.withContainers(containers);
} catch (NodeNotFoundException ex) {
- containers = 0;
LOG.warn("Cannot get containers, datanode {} not found.",
datanode.getUuid(), ex);
}
- long heartbeat = nodeManager.getLastHeartbeat(datanode);
- datanodes.add(new DatanodeMetadata(hostname, nodeState, heartbeat,
- storageReport, pipelines, containers));
+ datanodes.add(builder.withHostname(hostname)
+ .withDatanodeStorageReport(storageReport)
+ .withLastHeartbeat(nodeManager.getLastHeartbeat(datanode))
+ .withState(nodeState)
+ .withPipelines(pipelines)
+ .withLeaderCount(leaderCount.get())
+ .build());
});
DatanodesResponse datanodesResponse =
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 2378e8b..44490f1 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
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.ozone.recon.api.types;
+import com.google.common.base.Preconditions;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState;
import javax.xml.bind.annotation.XmlAccessType;
@@ -28,7 +29,7 @@ import java.util.List;
* Metadata object that represents a Datanode.
*/
@XmlAccessorType(XmlAccessType.FIELD)
-public class DatanodeMetadata {
+public final class DatanodeMetadata {
@XmlElement(name = "hostname")
private String hostname;
@@ -48,18 +49,17 @@ public class DatanodeMetadata {
@XmlElement(name = "containers")
private int containers;
- public DatanodeMetadata(String hostname,
- NodeState state,
- long lastHeartbeat,
- DatanodeStorageReport storageReport,
- List<DatanodePipeline> pipelines,
- int containers) {
- this.hostname = hostname;
- this.state = state;
- this.lastHeartbeat = lastHeartbeat;
- this.datanodeStorageReport = storageReport;
- this.pipelines = pipelines;
- this.containers = containers;
+ @XmlElement(name = "leaderCount")
+ private int leaderCount;
+
+ private DatanodeMetadata(Builder builder) {
+ this.hostname = builder.hostname;
+ this.state = builder.state;
+ this.lastHeartbeat = builder.lastHeartbeat;
+ this.datanodeStorageReport = builder.datanodeStorageReport;
+ this.pipelines = builder.pipelines;
+ this.containers = builder.containers;
+ this.leaderCount = builder.leaderCount;
}
public String getHostname() {
@@ -85,4 +85,84 @@ public class DatanodeMetadata {
public int getContainers() {
return containers;
}
+
+ public int getLeaderCount() {
+ return leaderCount;
+ }
+
+ /**
+ * Returns new builder class that builds a DatanodeMetadata.
+ *
+ * @return Builder
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for DatanodeMetadata.
+ */
+ @SuppressWarnings("checkstyle:hiddenfield")
+ public static final class Builder {
+ private String hostname;
+ private NodeState state;
+ private long lastHeartbeat;
+ private DatanodeStorageReport datanodeStorageReport;
+ private List<DatanodePipeline> pipelines;
+ private int containers;
+ private int leaderCount;
+
+ public Builder() {
+ this.containers = 0;
+ this.leaderCount = 0;
+ }
+
+ public Builder withHostname(String hostname) {
+ this.hostname = hostname;
+ return this;
+ }
+
+ public Builder withState(NodeState state) {
+ this.state = state;
+ return this;
+ }
+
+ public Builder withLastHeartbeat(long lastHeartbeat) {
+ this.lastHeartbeat = lastHeartbeat;
+ return this;
+ }
+
+ public Builder withDatanodeStorageReport(DatanodeStorageReport
+ datanodeStorageReport) {
+ this.datanodeStorageReport = datanodeStorageReport;
+ return this;
+ }
+
+ public Builder withPipelines(List<DatanodePipeline> pipelines) {
+ this.pipelines = pipelines;
+ return this;
+ }
+
+ public Builder withContainers(int containers) {
+ this.containers = containers;
+ return this;
+ }
+
+ public Builder withLeaderCount(int leaderCount) {
+ this.leaderCount = leaderCount;
+ return this;
+ }
+
+ /**
+ * Constructs DatanodeMetadata.
+ *
+ * @return instance of DatanodeMetadata.
+ */
+ public DatanodeMetadata build() {
+ Preconditions.checkNotNull(hostname);
+ Preconditions.checkNotNull(state);
+
+ return new DatanodeMetadata(this);
+ }
+ }
}
diff --git
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodePipeline.java
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodePipeline.java
index 6cc663b..cc38cd5 100644
---
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodePipeline.java
+++
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodePipeline.java
@@ -26,12 +26,14 @@ public class DatanodePipeline {
private UUID pipelineID;
private String replicationType;
private int replicationFactor;
+ private String leaderNode;
public DatanodePipeline(UUID pipelineID, String replicationType,
- int replicationFactor) {
+ int replicationFactor, String leaderNode) {
this.pipelineID = pipelineID;
this.replicationType = replicationType;
this.replicationFactor = replicationFactor;
+ this.leaderNode = leaderNode;
}
public UUID getPipelineID() {
@@ -45,4 +47,8 @@ public class DatanodePipeline {
public int getReplicationFactor() {
return replicationFactor;
}
+
+ public String getLeaderNode() {
+ return leaderNode;
+ }
}
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 a198bed..77d3a69 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
@@ -29,15 +29,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost1.storage.enterprise.com"
},
{
"pipelineID": "09d3a478-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost1.storage.enterprise.com"
}
],
- "containers": 80
+ "containers": 80,
+ "leaderCount": 2
},
{
"hostname": "localhost2.storage.enterprise.com",
@@ -52,15 +55,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost1.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost2.storage.enterprise.com"
}
],
- "containers": 8192
+ "containers": 8192,
+ "leaderCount": 1
},
{
"hostname": "localhost3.storage.enterprise.com",
@@ -75,20 +81,24 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost1.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost3.storage.enterprise.com"
},
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "STAND_ALONE",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost3.storage.enterprise.com"
}
],
- "containers": 43
+ "containers": 43,
+ "leaderCount": 2
},
{
"hostname": "localhost4.storage.enterprise.com",
@@ -100,7 +110,8 @@
"remaining": 110737488355328
},
"pipelines": [],
- "containers": 0
+ "containers": 0,
+ "leaderCount": 0
},
{
"hostname": "localhost5.storage.enterprise.com",
@@ -115,15 +126,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost5.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost5.storage.enterprise.com"
}
],
- "containers": 643
+ "containers": 643,
+ "leaderCount": 2
},
{
"hostname": "localhost6.storage.enterprise.com",
@@ -138,15 +152,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost5.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost6.storage.enterprise.com"
}
],
- "containers": 5
+ "containers": 5,
+ "leaderCount": 1
},
{
"hostname": "localhost7.storage.enterprise.com",
@@ -161,20 +178,24 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost5.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost7.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "STAND_ALONE",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost7.storage.enterprise.com"
}
],
- "containers": 64
+ "containers": 64,
+ "leaderCount": 2
},
{
"hostname": "localhost8.storage.enterprise.com",
@@ -189,15 +210,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost5.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost8.storage.enterprise.com"
}
],
- "containers": 21
+ "containers": 21,
+ "leaderCount": 1
},
{
"hostname": "localhost9.storage.enterprise.com",
@@ -212,15 +236,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost11.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost9.storage.enterprise.com"
}
],
- "containers": 897
+ "containers": 897,
+ "leaderCount": 1
},
{
"hostname": "localhost10.storage.enterprise.com",
@@ -235,20 +262,24 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost11.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost10.storage.enterprise.com"
},
{
"pipelineID": "01f2e105-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "STAND_ALONE",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost10.storage.enterprise.com"
}
],
- "containers": 6754
+ "containers": 6754,
+ "leaderCount": 2
},
{
"hostname": "localhost11.storage.enterprise.com",
@@ -263,15 +294,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost11.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost11.storage.enterprise.com"
}
],
- "containers": 78
+ "containers": 78,
+ "leaderCount": 2
},
{
"hostname": "localhost12.storage.enterprise.com",
@@ -286,15 +320,18 @@
{
"pipelineID": "02e3d908-ff01-4ce6-ad75-f3ec79bcc71a",
"replicationType": "RATIS",
- "replicationFactor": 3
+ "replicationFactor": 3,
+ "leaderNode": "localhost11.storage.enterprise.com"
},
{
"pipelineID": "05e3d908-ff01-4ce6-ad75-f3ec79bcc7982",
"replicationType": "RATIS",
- "replicationFactor": 1
+ "replicationFactor": 1,
+ "leaderNode": "localhost12.storage.enterprise.com"
}
],
- "containers": 543
+ "containers": 543,
+ "leaderCount": 1
}
]
},
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
index 3aae869..43fc19d 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.less
@@ -126,13 +126,13 @@ body {
.hexagon-shape(20, 12, @orange-7);
}
-.icon-text(@content) {
+.icon-text(@content, @fontcolor) {
text-align: center;
font-size: 12px;
font-weight: 700;
position: relative;
top: -3px;
- color: #fff;
+ color: @fontcolor;
&:after {
content: @content;
position: absolute;
@@ -140,7 +140,7 @@ body {
top: 4px;
width: 10px;
height: 0;
- color: white;
+ color: @fontcolor;
font-size: 12px;
z-index: 2;
}
@@ -148,14 +148,23 @@ body {
.icon-text-three-dots {
// In Unicode, \2026 is the horizontal ellipsis (...)
- .icon-text("\2026");
+ .icon-text("\2026", #fff);
+}
+
+.icon-text-three-dots-leader {
+ // In Unicode, \2026 is the horizontal ellipsis (...)
+ .icon-text("\2026", #ffde36);
}
.icon-text-one-dot {
- .icon-text(".");
+ .icon-text(".", #ffde36);
}
.replication-icon {
display: inline-block;
margin-right: 5px;
}
+
+.pointer {
+ cursor: pointer;
+}
\ No newline at end of file
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/themeIcons.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/themeIcons.tsx
index 7acb026..9868638 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/themeIcons.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/themeIcons.tsx
@@ -35,17 +35,21 @@ export class FilledIcon extends React.Component {
interface IRatisIconProps {
replicationFactor: number;
+ isLeader: boolean;
}
interface IReplicationIconProps {
replicationFactor: number;
replicationType: string;
+ leaderNode: string;
+ isLeader: boolean;
}
export class RatisIcon extends React.PureComponent<IRatisIconProps> {
render() {
- const {replicationFactor} = this.props;
- const textClass = replicationFactor >= 3 ? 'icon-text-three-dots' :
'icon-text-one-dot';
+ const {replicationFactor, isLeader} = this.props;
+ const threeFactorClass = isLeader ? 'icon-text-three-dots-leader' :
'icon-text-three-dots';
+ const textClass = replicationFactor >= 3 ? threeFactorClass :
'icon-text-one-dot';
return (
<div className='ratis-icon'>
<div className={textClass}>R</div>
@@ -66,11 +70,11 @@ export class StandaloneIcon extends React.PureComponent {
export class ReplicationIcon extends
React.PureComponent<IReplicationIconProps> {
render() {
- const {replicationType, replicationFactor} = this.props;
+ const {replicationType, replicationFactor, isLeader, leaderNode} =
this.props;
// Assign icons only for RATIS and STAND_ALONE types
let icon = null;
if (replicationType === 'RATIS') {
- icon = <RatisIcon replicationFactor={replicationFactor}/>;
+ icon = <RatisIcon replicationFactor={replicationFactor}
isLeader={isLeader}/>;
} else if (replicationType === 'STAND_ALONE') {
icon = <StandaloneIcon/>;
}
@@ -81,10 +85,11 @@ export class ReplicationIcon extends
React.PureComponent<IReplicationIconProps>
<div>
<div>Replication Type: {replicationType}</div>
<div>Replication Factor: {replicationFactor}</div>
+ <div>Leader Node: {leaderNode}</div>
</div>
);
icon = (
- <Tooltip title={tooltip} placement='right'>
+ <Tooltip title={tooltip} placement='right' className='pointer'>
<div className='replication-icon'>{icon}</div>
</Tooltip>
);
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 f3e510a..feb5b6f 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
@@ -18,7 +18,7 @@
import React from 'react';
import axios from 'axios';
-import {Table, Icon} from 'antd';
+import {Table, Icon, Tooltip} from 'antd';
import {PaginationConfig} from 'antd/lib/pagination';
import moment from 'moment';
import {ReplicationIcon} from 'utils/themeIcons';
@@ -36,6 +36,7 @@ interface IDatanodeResponse {
storageReport: IStorageReport;
pipelines: IPipeline[];
containers: number;
+ leaderCount: number;
}
interface IDatanodesResponse {
@@ -52,12 +53,14 @@ interface IDatanode {
storageRemaining: number;
pipelines: IPipeline[];
containers: number;
+ leaderCount: number;
}
interface IPipeline {
pipelineID: string;
replicationType: string;
replicationFactor: number;
+ leaderNode: string;
}
interface IDatanodesState {
@@ -117,13 +120,16 @@ const COLUMNS = [
title: 'Pipeline ID(s)',
dataIndex: 'pipelines',
key: 'pipelines',
- render: (pipelines: IPipeline[]) => {
+ render: (pipelines: IPipeline[], record: IDatanode) => {
return (
<div>
{
pipelines.map((pipeline, index) => (
<div key={index} className='pipeline-container'>
- <ReplicationIcon
replicationFactor={pipeline.replicationFactor}
replicationType={pipeline.replicationType}/>
+ <ReplicationIcon replicationFactor={pipeline.replicationFactor}
+ replicationType={pipeline.replicationType}
+ leaderNode={pipeline.leaderNode}
+ isLeader={pipeline.leaderNode ===
record.hostname}/>
{pipeline.pipelineID}
</div>
))
@@ -133,6 +139,17 @@ const COLUMNS = [
}
},
{
+ title: <span>
+ Leader Count
+ <Tooltip title='The number of Ratis Pipelines in which the given
datanode is elected as a leader.'>
+ <Icon type='info-circle'/>
+ </Tooltip>
+ </span>,
+ dataIndex: 'leaderCount',
+ key: 'leaderCount',
+ sorter: (a: IDatanode, b: IDatanode) => a.leaderCount - b.leaderCount
+ },
+ {
title: 'Containers',
dataIndex: 'containers',
key: 'containers',
@@ -171,7 +188,8 @@ export class Datanodes extends
React.Component<Record<string, object>, IDatanode
storageTotal: datanode.storageReport.capacity,
storageRemaining: datanode.storageReport.remaining,
pipelines: datanode.pipelines,
- containers: datanode.containers
+ containers: datanode.containers,
+ leaderCount: datanode.leaderCount
};
});
this.setState({
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
index 60b09b6..f339060 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/pipelines/pipelines.tsx
@@ -72,7 +72,10 @@ const COLUMNS = [
const replicationFactor = record.replicationFactor;
return (
<span>
- <ReplicationIcon replicationFactor={replicationFactor}
replicationType={replicationType}/>
+ <ReplicationIcon replicationFactor={replicationFactor}
+ replicationType={replicationType}
+ leaderNode={record.leaderNode}
+ isLeader={false}/>
{replicationType} ({replicationFactor})
</span>
);
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 66c015c..9234131 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
@@ -90,6 +90,10 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
private ContainerReportsProto containerReportsProto;
private DatanodeDetailsProto datanodeDetailsProto;
private Pipeline pipeline;
+ private final String host1 = "host1.datanode";
+ private final String host2 = "host2.datanode";
+ private final String ip1 = "1.1.1.1";
+ private final String ip2 = "2.2.2.2";
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -100,6 +104,10 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
temporaryFolder.newFolder());
datanodeDetails = randomDatanodeDetails();
datanodeDetails2 = randomDatanodeDetails();
+ datanodeDetails.setHostName(host1);
+ datanodeDetails.setIpAddress(ip1);
+ datanodeDetails2.setHostName(host2);
+ datanodeDetails2.setIpAddress(ip2);
pipeline = getRandomPipeline(datanodeDetails);
pipelineId = pipeline.getId().getId().toString();
@@ -175,9 +183,9 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
.addPipelineReport(pipelineReport).build();
datanodeDetailsProto =
DatanodeDetailsProto.newBuilder()
- .setHostName("host1.datanode")
+ .setHostName(host1)
.setUuid(datanodeId)
- .setIpAddress("1.1.1.1")
+ .setIpAddress(ip1)
.build();
StorageReportProto storageReportProto1 =
StorageReportProto.newBuilder().setStorageType(StorageTypeProto.DISK)
@@ -198,9 +206,9 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
DatanodeDetailsProto datanodeDetailsProto2 =
DatanodeDetailsProto.newBuilder()
- .setHostName("host2.datanode")
+ .setHostName(host2)
.setUuid(datanodeId2)
- .setIpAddress("2.2.2.2")
+ .setIpAddress(ip2)
.build();
StorageReportProto storageReportProto3 =
StorageReportProto.newBuilder().setStorageType(StorageTypeProto.DISK)
@@ -267,10 +275,11 @@ public class TestEndpoints extends AbstractReconSqlDBTest
{
writeDataToOm(reconOMMetadataManager, "key_three");
}
- private void testDatanodeResponse(DatanodeMetadata datanodeMetadata) {
+ private void testDatanodeResponse(DatanodeMetadata datanodeMetadata)
+ throws IOException {
String hostname = datanodeMetadata.getHostname();
switch (hostname) {
- case "host1.datanode":
+ case host1:
Assert.assertEquals(75000,
datanodeMetadata.getDatanodeStorageReport().getCapacity());
Assert.assertEquals(15400,
@@ -285,8 +294,11 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
datanodeMetadata.getPipelines().get(0).getReplicationFactor());
Assert.assertEquals(pipeline.getType().toString(),
datanodeMetadata.getPipelines().get(0).getReplicationType());
+ Assert.assertEquals(pipeline.getLeaderNode().getHostName(),
+ datanodeMetadata.getPipelines().get(0).getLeaderNode());
+ Assert.assertEquals(1, datanodeMetadata.getLeaderCount());
break;
- case "host2.datanode":
+ case host2:
Assert.assertEquals(130000,
datanodeMetadata.getDatanodeStorageReport().getCapacity());
Assert.assertEquals(17800,
@@ -295,6 +307,7 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
datanodeMetadata.getDatanodeStorageReport().getUsed());
Assert.assertEquals(0, datanodeMetadata.getPipelines().size());
+ Assert.assertEquals(0, datanodeMetadata.getLeaderCount());
break;
default:
Assert.fail(String.format("Datanode %s not registered",
@@ -310,7 +323,13 @@ public class TestEndpoints extends AbstractReconSqlDBTest {
Assert.assertEquals(2, datanodesResponse.getTotalCount());
Assert.assertEquals(2, datanodesResponse.getDatanodes().size());
- datanodesResponse.getDatanodes().forEach(this::testDatanodeResponse);
+ datanodesResponse.getDatanodes().forEach(datanodeMetadata -> {
+ try {
+ testDatanodeResponse(datanodeMetadata);
+ } catch (IOException e) {
+ Assert.fail(e.getMessage());
+ }
+ });
waitAndCheckConditionAfterHeartbeat(() -> {
Response response1 = nodeEndpoint.getDatanodes();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]