You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by vi...@apache.org on 2020/07/14 18:50:34 UTC

[hadoop-ozone] branch master updated: HDDS-3798. Display version and setupTime of DN in recon web (#1136)

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 4d964f3  HDDS-3798. Display version and setupTime of DN in recon web (#1136)
4d964f3 is described below

commit 4d964f3f3d4e97e5e0131cd6edd048f5fffc72fa
Author: HuangTao <th...@163.com>
AuthorDate: Wed Jul 15 02:50:20 2020 +0800

    HDDS-3798. Display version and setupTime of DN in recon web (#1136)
---
 .gitignore                                         |   2 +
 .../hadoop/hdds/protocol/DatanodeDetails.java      |  89 +++++++++++++++++-
 .../apache/hadoop/ozone/HddsDatanodeService.java   |   5 ++
 .../interface-client/src/main/proto/hdds.proto     |   2 +
 .../interface-client/src/main/proto/proto.lock     |  12 ++-
 .../hadoop/ozone/recon/api/NodeEndpoint.java       |   2 +
 .../ozone/recon/api/types/DatanodeMetadata.java    |  28 ++++++
 .../webapps/recon/ozone-recon-web/api/db.json      |  48 +++++++---
 .../src/components/multiSelect/multiSelect.tsx     |   5 +-
 .../src/views/datanodes/datanodes.less             |  14 +++
 .../src/views/datanodes/datanodes.tsx              | 100 +++++++++++++++++++--
 11 files changed, 285 insertions(+), 22 deletions(-)

diff --git a/.gitignore b/.gitignore
index 551b1b5..e09c2eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@
 .classpath
 .project
 .settings
+*.factorypath
 target
 build
 dependency-reduced-pom.xml
@@ -61,5 +62,6 @@ output.xml
 report.html
 
 hadoop-hdds/docs/public
+hadoop-ozone/recon/node_modules
 
 .mvn
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/DatanodeDetails.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/DatanodeDetails.java
index 1b6a214..96f19a6 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/DatanodeDetails.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/protocol/DatanodeDetails.java
@@ -49,6 +49,8 @@ public class DatanodeDetails extends NodeImpl implements
   private String hostName;
   private List<Port> ports;
   private String certSerialId;
+  private String version;
+  private long setupTime;
 
   /**
    * Constructs DatanodeDetails instance. DatanodeDetails.Builder is used
@@ -59,15 +61,21 @@ public class DatanodeDetails extends NodeImpl implements
    * @param networkLocation DataNode's network location path
    * @param ports Ports used by the DataNode
    * @param certSerialId serial id from SCM issued certificate.
+   * @param version DataNode's version
+   * @param setupTime the setup time of DataNode
    */
+  @SuppressWarnings("parameternumber")
   private DatanodeDetails(UUID uuid, String ipAddress, String hostName,
-      String networkLocation, List<Port> ports, String certSerialId) {
+      String networkLocation, List<Port> ports, String certSerialId,
+      String version, long setupTime) {
     super(hostName, networkLocation, NetConstants.NODE_COST_DEFAULT);
     this.uuid = uuid;
     this.ipAddress = ipAddress;
     this.hostName = hostName;
     this.ports = ports;
     this.certSerialId = certSerialId;
+    this.version = version;
+    this.setupTime = setupTime;
   }
 
   public DatanodeDetails(DatanodeDetails datanodeDetails) {
@@ -79,6 +87,8 @@ public class DatanodeDetails extends NodeImpl implements
     this.ports = datanodeDetails.ports;
     this.setNetworkName(datanodeDetails.getNetworkName());
     this.setParent(datanodeDetails.getParent());
+    this.version = datanodeDetails.version;
+    this.setupTime = datanodeDetails.setupTime;
   }
 
   /**
@@ -207,6 +217,12 @@ public class DatanodeDetails extends NodeImpl implements
     if (datanodeDetailsProto.hasNetworkLocation()) {
       builder.setNetworkLocation(datanodeDetailsProto.getNetworkLocation());
     }
+    if (datanodeDetailsProto.hasVersion()) {
+      builder.setVersion(datanodeDetailsProto.getVersion());
+    }
+    if (datanodeDetailsProto.hasSetupTime()) {
+      builder.setSetupTime(datanodeDetailsProto.getSetupTime());
+    }
     return builder.build();
   }
 
@@ -248,6 +264,13 @@ public class DatanodeDetails extends NodeImpl implements
           .setValue(port.getValue())
           .build());
     }
+
+    if (!Strings.isNullOrEmpty(getVersion())) {
+      builder.setVersion(getVersion());
+    }
+
+    builder.setSetupTime(getSetupTime());
+
     return builder.build();
   }
 
@@ -300,6 +323,8 @@ public class DatanodeDetails extends NodeImpl implements
     private String networkLocation;
     private List<Port> ports;
     private String certSerialId;
+    private String version;
+    private long setupTime;
 
     /**
      * Default private constructor. To create Builder instance use
@@ -389,6 +414,30 @@ public class DatanodeDetails extends NodeImpl implements
     }
 
     /**
+     * Sets the DataNode version.
+     *
+     * @param ver the version of DataNode.
+     *
+     * @return DatanodeDetails.Builder
+     */
+    public Builder setVersion(String ver) {
+      this.version = ver;
+      return this;
+    }
+
+    /**
+     * Sets the DataNode setup time.
+     *
+     * @param time the setup time of DataNode.
+     *
+     * @return DatanodeDetails.Builder
+     */
+    public Builder setSetupTime(long time) {
+      this.setupTime = time;
+      return this;
+    }
+
+    /**
      * Builds and returns DatanodeDetails instance.
      *
      * @return DatanodeDetails
@@ -399,7 +448,7 @@ public class DatanodeDetails extends NodeImpl implements
         networkLocation = NetConstants.DEFAULT_RACK;
       }
       DatanodeDetails dn = new DatanodeDetails(id, ipAddress, hostName,
-          networkLocation, ports, certSerialId);
+          networkLocation, ports, certSerialId, version, setupTime);
       if (networkName != null) {
         dn.setNetworkName(networkName);
       }
@@ -505,4 +554,40 @@ public class DatanodeDetails extends NodeImpl implements
   public void setCertSerialId(String certSerialId) {
     this.certSerialId = certSerialId;
   }
+
+  /**
+   * Returns the DataNode version.
+   *
+   * @return DataNode version
+   */
+  public String getVersion() {
+    return version;
+  }
+
+  /**
+   * Set DataNode version.
+   *
+   * @param version DataNode version
+   */
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  /**
+   * Returns the DataNode setup time.
+   *
+   * @return DataNode setup time
+   */
+  public long getSetupTime() {
+    return setupTime;
+  }
+
+  /**
+   * Set DataNode setup time.
+   *
+   * @param setupTime DataNode setup time
+   */
+  public void setSetupTime(long setupTime) {
+    this.setupTime = setupTime;
+  }
 }
diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
index 7e896e7..08eef6f 100644
--- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
+++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/HddsDatanodeService.java
@@ -65,6 +65,8 @@ import static org.apache.hadoop.hdds.security.x509.certificate.utils.Certificate
 import static org.apache.hadoop.hdds.security.x509.certificates.utils.CertificateSignRequest.getEncodedString;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.HDDS_DATANODE_PLUGINS_KEY;
 import static org.apache.hadoop.util.ExitUtil.terminate;
+
+import org.apache.hadoop.util.Time;
 import org.bouncycastle.pkcs.PKCS10CertificationRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -191,6 +193,9 @@ public class HddsDatanodeService extends GenericCli implements ServicePlugin {
       datanodeDetails = initializeDatanodeDetails();
       datanodeDetails.setHostName(hostname);
       datanodeDetails.setIpAddress(ip);
+      datanodeDetails.setVersion(
+          HddsVersionInfo.HDDS_VERSION_INFO.getVersion());
+      datanodeDetails.setSetupTime(Time.now());
       TracingUtil.initTracing(
           "HddsDatanodeService." + datanodeDetails.getUuidString()
               .substring(0, 8), conf);
diff --git a/hadoop-hdds/interface-client/src/main/proto/hdds.proto b/hadoop-hdds/interface-client/src/main/proto/hdds.proto
index 23cc9cb..243e8ec 100644
--- a/hadoop-hdds/interface-client/src/main/proto/hdds.proto
+++ b/hadoop-hdds/interface-client/src/main/proto/hdds.proto
@@ -43,6 +43,8 @@ message DatanodeDetailsProto {
     // network name, can be Ip address or host name, depends
     optional string networkName = 6;
     optional string networkLocation = 7; // Network topology location
+    optional string version = 8;         // Datanode version
+    optional int64 setupTime = 9;
     // TODO(runzhiwang): when uuid is gone, specify 1 as the index of uuid128 and mark as required
     optional UUID uuid128 = 100; // UUID with 128 bits assigned to the Datanode.
 }
diff --git a/hadoop-hdds/interface-client/src/main/proto/proto.lock b/hadoop-hdds/interface-client/src/main/proto/proto.lock
index afdaf96..b27896c 100644
--- a/hadoop-hdds/interface-client/src/main/proto/proto.lock
+++ b/hadoop-hdds/interface-client/src/main/proto/proto.lock
@@ -1531,6 +1531,16 @@
                 "type": "string"
               },
               {
+                "id": 8,
+                "name": "version",
+                "type": "string"
+              },
+              {
+                "id": 9,
+                "name": "setupTime",
+                "type": "int64"
+              },
+              {
                 "id": 100,
                 "name": "uuid128",
                 "type": "UUID"
@@ -1925,4 +1935,4 @@
       }
     }
   ]
-}
\ No newline at end of file
+}
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 2c01749..42832de 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
@@ -121,6 +121,8 @@ public class NodeEndpoint {
           .withPipelines(pipelines)
           .withLeaderCount(leaderCount.get())
           .withUUid(datanode.getUuidString())
+          .withVersion(datanode.getVersion())
+          .withSetupTime(datanode.getSetupTime())
           .build());
     });
 
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 02d9ae8..542654e 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
@@ -55,6 +55,12 @@ public final class DatanodeMetadata {
   @XmlElement(name = "leaderCount")
   private int leaderCount;
 
+  @XmlElement(name = "version")
+  private String version;
+
+  @XmlElement(name = "setupTime")
+  private long setupTime;
+
   private DatanodeMetadata(Builder builder) {
     this.hostname = builder.hostname;
     this.uuid = builder.uuid;
@@ -64,6 +70,8 @@ public final class DatanodeMetadata {
     this.pipelines = builder.pipelines;
     this.containers = builder.containers;
     this.leaderCount = builder.leaderCount;
+    this.version = builder.version;
+    this.setupTime = builder.setupTime;
   }
 
   public String getHostname() {
@@ -98,6 +106,14 @@ public final class DatanodeMetadata {
     return uuid;
   }
 
+  public String getVersion() {
+    return version;
+  }
+
+  public long getSetupTime() {
+    return  setupTime;
+  }
+
   /**
    * Returns new builder class that builds a DatanodeMetadata.
    *
@@ -120,6 +136,8 @@ public final class DatanodeMetadata {
     private List<DatanodePipeline> pipelines;
     private int containers;
     private int leaderCount;
+    private String version;
+    private long setupTime;
 
     public Builder() {
       this.containers = 0;
@@ -167,6 +185,16 @@ public final class DatanodeMetadata {
       return this;
     }
 
+    public Builder withVersion(String version) {
+      this.version = version;
+      return this;
+    }
+
+    public Builder withSetupTime(long setupTime) {
+      this.setupTime = setupTime;
+      return this;
+    }
+
     /**
      * Constructs DatanodeMetadata.
      *
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 82fae37..d8d6eac 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
@@ -41,7 +41,9 @@
           }
         ],
         "containers": 80,
-        "leaderCount": 2
+        "leaderCount": 2,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574728775759
       },
       {
         "hostname": "localhost2.storage.enterprise.com",
@@ -68,7 +70,9 @@
           }
         ],
         "containers": 8192,
-        "leaderCount": 1
+        "leaderCount": 1,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724805059
       },
       {
         "hostname": "localhost3.storage.enterprise.com",
@@ -101,7 +105,9 @@
           }
         ],
         "containers": 43,
-        "leaderCount": 2
+        "leaderCount": 2,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1343544679543
       },
       {
         "hostname": "localhost4.storage.enterprise.com",
@@ -115,7 +121,9 @@
         },
         "pipelines": [],
         "containers": 0,
-        "leaderCount": 0
+        "leaderCount": 0,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1074724802059
       },
       {
         "hostname": "localhost5.storage.enterprise.com",
@@ -142,7 +150,9 @@
           }
         ],
         "containers": 643,
-        "leaderCount": 2
+        "leaderCount": 2,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724816029
       },
       {
         "hostname": "localhost6.storage.enterprise.com",
@@ -169,7 +179,9 @@
           }
         ],
         "containers": 5,
-        "leaderCount": 1
+        "leaderCount": 1,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724802059
       },
       {
         "hostname": "localhost7.storage.enterprise.com",
@@ -202,7 +214,9 @@
           }
         ],
         "containers": 64,
-        "leaderCount": 2
+        "leaderCount": 2,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724676009
       },
       {
         "hostname": "localhost8.storage.enterprise.com",
@@ -229,7 +243,9 @@
           }
         ],
         "containers": 21,
-        "leaderCount": 1
+        "leaderCount": 1,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724276050
       },
       {
         "hostname": "localhost9.storage.enterprise.com",
@@ -256,7 +272,9 @@
           }
         ],
         "containers": 897,
-        "leaderCount": 1
+        "leaderCount": 1,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724573011
       },
       {
         "hostname": "localhost10.storage.enterprise.com",
@@ -289,7 +307,9 @@
           }
         ],
         "containers": 6754,
-        "leaderCount": 2
+        "leaderCount": 2,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574723756059
       },
       {
         "hostname": "localhost11.storage.enterprise.com",
@@ -316,7 +336,9 @@
           }
         ],
         "containers": 78,
-        "leaderCount": 2
+        "leaderCount": 2,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1474724705783
       },
       {
         "hostname": "localhost12.storage.enterprise.com",
@@ -343,7 +365,9 @@
           }
         ],
         "containers": 543,
-        "leaderCount": 1
+        "leaderCount": 1,
+        "version": "0.6.0-SNAPSHOT",
+        "setupTime": 1574724706232
       }
     ]
   },
diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/multiSelect/multiSelect.tsx b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/multiSelect/multiSelect.tsx
index 19005dd..417c2ef 100644
--- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/multiSelect/multiSelect.tsx
+++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/multiSelect/multiSelect.tsx
@@ -36,6 +36,7 @@ interface IMultiSelectProps extends ReactSelectProps<IOption> {
   options: IOption[];
   allowSelectAll: boolean;
   allOption?: IOption;
+  maxShowValues?: number;
 }
 
 const defaultProps = {
@@ -48,7 +49,7 @@ const defaultProps = {
 export class MultiSelect extends PureComponent<IMultiSelectProps> {
   static defaultProps = defaultProps;
   render() {
-    const {allowSelectAll, allOption, options, onChange} = this.props;
+    const {allowSelectAll, allOption, options, maxShowValues = 5, onChange} = this.props;
     if (allowSelectAll) {
       const Option = (props: OptionProps<IOption>) => {
         return (
@@ -70,7 +71,7 @@ export class MultiSelect extends PureComponent<IMultiSelectProps> {
         let toBeRendered = children;
         if (currentValues.some(val => val.value === allOption!.value) && children) {
           toBeRendered = allOption!.label;
-        } else if (currentValues.length >= 5) {
+        } else if (currentValues.length > maxShowValues) {
           toBeRendered = `${currentValues.length} selected`;
         }
 
diff --git a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
index 4a3cdf5..10ec907 100644
--- a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
+++ b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
@@ -22,4 +22,18 @@
       margin-bottom: 5px;
     }
   }
+
+  .filter-block { 
+    font-size: 14px;
+    font-weight: normal;
+    display: inline-block;
+    margin-left: 20px;
+  }
+  
+  .multi-select-container {
+    padding-left: 5px;
+    margin-right: 5px;
+    display: inline-block;
+    min-width: 200px;
+  }
 }
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 bfba82a..877ebf9 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
@@ -27,6 +27,8 @@ import {DatanodeStatus, IStorageReport} from 'types/datanode.types';
 import './datanodes.less';
 import {AutoReloadHelper} from 'utils/autoReloadHelper';
 import AutoReloadPanel from 'components/autoReloadPanel/autoReloadPanel';
+import {MultiSelect, IOption} from 'components/multiSelect/multiSelect';
+import {ActionMeta, ValueType} from 'react-select';
 import {showDataFetchError} from 'utils/common';
 
 interface IDatanodeResponse {
@@ -38,6 +40,8 @@ interface IDatanodeResponse {
   containers: number;
   leaderCount: number;
   uuid: string;
+  version: string;
+  setupTime: number;
 }
 
 interface IDatanodesResponse {
@@ -56,6 +60,8 @@ interface IDatanode {
   containers: number;
   leaderCount: number;
   uuid: string;
+  version: string;
+  setupTime: number;
 }
 
 interface IPipeline {
@@ -70,6 +76,8 @@ interface IDatanodesState {
   dataSource: IDatanode[];
   totalCount: number;
   lastUpdated: number;
+  selectedColumns: IOption[];
+  columnOptions: IOption[];
 }
 
 const renderDatanodeStatus = (status: DatanodeStatus) => {
@@ -89,6 +97,7 @@ const COLUMNS = [
     title: 'Status',
     dataIndex: 'state',
     key: 'state',
+    isVisible: true,
     render: (text: DatanodeStatus) => renderDatanodeStatus(text),
     sorter: (a: IDatanode, b: IDatanode) => a.state.localeCompare(b.state)
   },
@@ -96,6 +105,7 @@ const COLUMNS = [
     title: 'Uuid',
     dataIndex: 'uuid',
     key: 'uuid',
+    isVisible: true,
     sorter: (a: IDatanode, b: IDatanode) => a.uuid.localeCompare(b.uuid),
     defaultSortOrder: 'ascend' as const
   },
@@ -103,6 +113,7 @@ const COLUMNS = [
     title: 'Hostname',
     dataIndex: 'hostname',
     key: 'hostname',
+    isVisible: true,
     sorter: (a: IDatanode, b: IDatanode) => a.hostname.localeCompare(b.hostname),
     defaultSortOrder: 'ascend' as const
   },
@@ -110,6 +121,7 @@ const COLUMNS = [
     title: 'Storage Capacity',
     dataIndex: 'storageUsed',
     key: 'storageUsed',
+    isVisible: true,
     sorter: (a: IDatanode, b: IDatanode) => a.storageRemaining - b.storageRemaining,
     render: (text: string, record: IDatanode) => (
       <StorageBar
@@ -120,6 +132,7 @@ const COLUMNS = [
     title: 'Last Heartbeat',
     dataIndex: 'lastHeartbeat',
     key: 'lastHeartbeat',
+    isVisible: true,
     sorter: (a: IDatanode, b: IDatanode) => a.lastHeartbeat - b.lastHeartbeat,
     render: (heartbeat: number) => {
       return heartbeat > 0 ? moment(heartbeat).format('lll') : 'NA';
@@ -129,6 +142,7 @@ const COLUMNS = [
     title: 'Pipeline ID(s)',
     dataIndex: 'pipelines',
     key: 'pipelines',
+    isVisible: true,
     render: (pipelines: IPipeline[], record: IDatanode) => {
       return (
         <div>
@@ -158,16 +172,46 @@ const COLUMNS = [
   </span>,
     dataIndex: 'leaderCount',
     key: 'leaderCount',
+    isVisible: true,
     sorter: (a: IDatanode, b: IDatanode) => a.leaderCount - b.leaderCount
   },
   {
     title: 'Containers',
     dataIndex: 'containers',
     key: 'containers',
+    isVisible: true,
     sorter: (a: IDatanode, b: IDatanode) => a.containers - b.containers
+  },
+  {
+    title: 'Version',
+    dataIndex: 'version',
+    key: 'version',
+    isVisible: false,
+    sorter: (a: IDatanode, b: IDatanode) => a.version.localeCompare(b.version),
+    defaultSortOrder: 'ascend' as const
+  },
+  {
+    title: 'SetupTime',
+    dataIndex: 'setupTime',
+    key: 'setupTime',
+    isVisible: false,
+    sorter: (a: IDatanode, b: IDatanode) => a.setupTime - b.setupTime,
+    render: (uptime: number) => {
+      return uptime > 0 ? moment(uptime).format('lll') : 'NA';
+    }
   }
 ];
 
+const allColumnsOption: IOption = {
+  label: 'Select all',
+  value: '*'
+};
+
+const defaultColumns: IOption[] = COLUMNS.map(column => ({
+  label: column.key,
+  value: column.key
+}));
+
 export class Datanodes extends React.Component<Record<string, object>, IDatanodesState> {
   autoReload: AutoReloadHelper;
 
@@ -177,11 +221,20 @@ export class Datanodes extends React.Component<Record<string, object>, IDatanode
       loading: false,
       dataSource: [],
       totalCount: 0,
-      lastUpdated: 0
+      lastUpdated: 0,
+      selectedColumns: [],
+      columnOptions: defaultColumns
     };
     this.autoReload = new AutoReloadHelper(this._loadData);
   }
 
+  _handleColumnChange = (selected: ValueType<IOption>, _action: ActionMeta<IOption>) => {
+    const selectedColumns = (selected as IOption[]);
+    this.setState({
+      selectedColumns
+    });
+  };
+
   _loadData = () => {
     this.setState({
       loading: true
@@ -201,14 +254,23 @@ export class Datanodes extends React.Component<Record<string, object>, IDatanode
           storageRemaining: datanode.storageReport.remaining,
           pipelines: datanode.pipelines,
           containers: datanode.containers,
-          leaderCount: datanode.leaderCount
+          leaderCount: datanode.leaderCount,
+          version: datanode.version,
+          setupTime: datanode.setupTime
         };
       });
+      const selectedColumns: IOption[] = COLUMNS.filter(column => column.isVisible).map(column => ({
+        label: column.key,
+        value: column.key
+      }));
+
       this.setState({
         loading: false,
         dataSource,
         totalCount,
         lastUpdated: Number(moment())
+      }, () => {
+        this._handleColumnChange(selectedColumns, {action: 'select-option'});
       });
     }).catch(error => {
       this.setState({
@@ -233,7 +295,7 @@ export class Datanodes extends React.Component<Record<string, object>, IDatanode
   };
 
   render() {
-    const {dataSource, loading, totalCount, lastUpdated} = this.state;
+    const {dataSource, loading, totalCount, lastUpdated, selectedColumns, columnOptions} = this.state;
     const paginationConfig: PaginationConfig = {
       showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total} datanodes`,
       showSizeChanger: true,
@@ -243,10 +305,38 @@ export class Datanodes extends React.Component<Record<string, object>, IDatanode
       <div className='datanodes-container'>
         <div className='page-header'>
           Datanodes ({totalCount})
-          <AutoReloadPanel isLoading={loading} lastUpdated={lastUpdated} togglePolling={this.autoReload.handleAutoReloadToggle} onReload={this._loadData}/>
+          <div className='filter-block'>
+            <MultiSelect
+              allowSelectAll
+              isMulti
+              maxShowValues={3}
+              className='multi-select-container'
+              options={columnOptions}
+              closeMenuOnSelect={false}
+              hideSelectedOptions={false}
+              value={selectedColumns}
+              allOption={allColumnsOption}
+              onChange={this._handleColumnChange}
+            /> Columns
+          </div>
+          <AutoReloadPanel
+            isLoading={loading}
+            lastUpdated={lastUpdated}
+            togglePolling={this.autoReload.handleAutoReloadToggle}
+            onReload={this._loadData}
+          />
         </div>
+
         <div className='content-div'>
-          <Table dataSource={dataSource} columns={COLUMNS} loading={loading} pagination={paginationConfig} rowKey='hostname'/>
+          <Table
+            dataSource={dataSource}
+            columns={COLUMNS.filter(column =>
+              selectedColumns.some(e => e.value === column.key)
+            )}
+            loading={loading}
+            pagination={paginationConfig}
+            rowKey='hostname'
+          />
         </div>
       </div>
     );


---------------------------------------------------------------------
To unsubscribe, e-mail: ozone-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: ozone-commits-help@hadoop.apache.org