This is an automated email from the ASF dual-hosted git repository.

smengcl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new afad5a13de2 HDDS-14560. Show open containers per disk in Recon 
datanode view (#10820)
afad5a13de2 is described below

commit afad5a13de270a9a50798f45ba3091003e705b17
Author: Chi-Hsuan Huang <[email protected]>
AuthorDate: Sat Oct 10 12:44:34 2026 +0800

    HDDS-14560. Show open containers per disk in Recon datanode view (#10820)
---
 .../ozone/container/common/impl/ContainerSet.java  |  24 ++++
 .../ozone/container/ozoneimpl/OzoneContainer.java  |   6 +-
 .../container/common/impl/TestContainerSet.java    |  29 +++++
 .../proto/ScmServerDatanodeHeartbeatProtocol.proto |   2 +
 .../hadoop/ozone/recon/api/NodeEndpoint.java       |  21 ++++
 .../ozone/recon/api/types/DatanodeDiskInfo.java    | 128 +++++++++++++++++++++
 .../ozone/recon/api/types/DatanodeMetadata.java    |  16 +++
 .../__tests__/datanodes/DatanodesTable.test.tsx    |  37 +++++-
 .../src/v2/components/tables/datanodesTable.tsx    |  41 +++++++
 .../src/v2/pages/datanodes/datanodes.tsx           |   3 +-
 .../ozone-recon-web/src/v2/types/datanode.types.ts |  12 ++
 .../hadoop/ozone/recon/api/TestEndpoints.java      |  13 +++
 12 files changed, 329 insertions(+), 3 deletions(-)

diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
index 752fdaf9fd1..99d7358ff2a 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
@@ -29,6 +29,7 @@
 import java.time.Clock;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -520,6 +521,29 @@ public long containerCount(HddsVolume volume) {
     return volume.getContainerCount();
   }
 
+  /**
+   * Count the OPEN containers on each storage volume, keyed by volume 
storageID.
+   * Used by the node report so Recon can show open-container distribution per 
disk.
+   *
+   * @return map of volume storageID to its OPEN container count
+   */
+  public Map<String, Long> getOpenContainerCountsByVolume() {
+    Map<String, Long> counts = new HashMap<>();
+    Iterator<Map.Entry<Long, Container<?>>> iterator = 
getContainerMapIterator();
+    while (iterator.hasNext()) {
+      Container<?> container = iterator.next().getValue();
+      if (container.getContainerState() != State.OPEN) {
+        continue;
+      }
+      HddsVolume volume = container.getContainerData().getVolume();
+      if (volume == null) {
+        continue;
+      }
+      counts.merge(volume.getStorageID(), 1L, Long::sum);
+    }
+    return counts;
+  }
+
   /**
    * Return an containerMap iterator over {@link ContainerSet#containerMap}.
    * @return containerMap Iterator
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
index 93aa878759b..67b7d0b6213 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
@@ -800,8 +800,12 @@ public 
StorageContainerDatanodeProtocolProtos.NodeReportProto getNodeReport()
             = StorageContainerDatanodeProtocolProtos.
             NodeReportProto.newBuilder();
 
+    Map<String, Long> openCounts = 
getContainerSet().getOpenContainerCountsByVolume();
     for (StorageLocationReport report : reports) {
-      nrb.addStorageReport(report.getProtoBufMessage());
+      StorageContainerDatanodeProtocolProtos.StorageReportProto proto = 
report.getProtoBufMessage();
+      nrb.addStorageReport(proto.toBuilder()
+          
.setOpenContainerCount(openCounts.getOrDefault(proto.getStorageUuid(), 0L))
+          .build());
     }
 
     StorageLocationReport[] metaReports = metaVolumeSet.getStorageReport();
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
index 33b87f220ed..5d8a8854473 100644
--- 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
@@ -52,6 +52,7 @@
 import org.apache.hadoop.conf.StorageUnit;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
+import 
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State;
 import 
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto;
 import 
org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException;
 import org.apache.hadoop.ozone.container.common.interfaces.Container;
@@ -546,6 +547,34 @@ public void 
testContainerCountPerVolume(ContainerLayoutVersion layout)
     assertEquals(49, containerSet.containerCount(vol2));
   }
 
+  private static KeyValueContainer newContainerOnVolume(long id, HddsVolume 
volume, State state) {
+    KeyValueContainerData kvData = new KeyValueContainerData(id,
+        ContainerLayoutVersion.FILE_PER_BLOCK,
+        (long) StorageUnit.GB.toBytes(5), UUID.randomUUID().toString(),
+        UUID.randomUUID().toString());
+    kvData.setVolume(volume);
+    kvData.setState(state);
+    return new KeyValueContainer(kvData, new OzoneConfiguration());
+  }
+
+  @Test
+  public void testGetOpenContainerCountsByVolume() throws 
StorageContainerException {
+    ContainerSet containerSet = newContainerSet();
+    HddsVolume volume1 = mockHddsVolume("vol-1");
+    HddsVolume volume2 = mockHddsVolume("vol-2");
+
+    containerSet.addContainer(newContainerOnVolume(1L, volume1, State.OPEN));
+    containerSet.addContainer(newContainerOnVolume(2L, volume1, State.OPEN));
+    containerSet.addContainer(newContainerOnVolume(3L, volume2, State.OPEN));
+    containerSet.addContainer(newContainerOnVolume(4L, volume2, State.CLOSED));
+
+    Map<String, Long> counts = containerSet.getOpenContainerCountsByVolume();
+
+    assertEquals(2, counts.size());
+    assertEquals(2L, counts.get("vol-1"));
+    assertEquals(1L, counts.get("vol-2"));
+  }
+
   /**
    * Test that per-volume iterator only returns containers from that volume.
    */
diff --git 
a/hadoop-hdds/interface-server/src/main/proto/ScmServerDatanodeHeartbeatProtocol.proto
 
b/hadoop-hdds/interface-server/src/main/proto/ScmServerDatanodeHeartbeatProtocol.proto
index dc06699f4c0..50cf899e823 100644
--- 
a/hadoop-hdds/interface-server/src/main/proto/ScmServerDatanodeHeartbeatProtocol.proto
+++ 
b/hadoop-hdds/interface-server/src/main/proto/ScmServerDatanodeHeartbeatProtocol.proto
@@ -203,6 +203,8 @@ message StorageReportProto {
    */
   optional uint64 fsCapacity = 11 [default = 0];
   optional uint64 fsAvailable = 12 [default = 0];
+  // Number of OPEN containers currently on this storage volume.
+  optional uint64 openContainerCount = 13 [default = 0];
 }
 
 message MetadataStorageReportProto {
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 35397723d25..40ece8b9151 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
@@ -51,6 +51,7 @@
 import org.apache.hadoop.hdds.protocol.DatanodeID;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState;
+import 
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.StorageReportProto;
 import org.apache.hadoop.hdds.scm.container.ContainerID;
 import org.apache.hadoop.hdds.scm.container.ContainerInfo;
 import org.apache.hadoop.hdds.scm.container.ContainerNotFoundException;
@@ -64,6 +65,7 @@
 import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol;
 import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager;
 import org.apache.hadoop.ozone.ClientVersion;
+import org.apache.hadoop.ozone.recon.api.types.DatanodeDiskInfo;
 import org.apache.hadoop.ozone.recon.api.types.DatanodeMetadata;
 import org.apache.hadoop.ozone.recon.api.types.DatanodePipeline;
 import org.apache.hadoop.ozone.recon.api.types.DatanodeStorageReport;
@@ -161,6 +163,7 @@ public Response getDatanodes() {
           .setState(nodeState)
           .setPipelines(pipelines)
           .setLeaderCount(leaderCount.get())
+          .setDisks(getDisks(datanode))
           .build());
     });
 
@@ -192,6 +195,24 @@ private DatanodeStorageReport 
getStorageReport(DatanodeDetails datanode) {
     return builder.build();
   }
 
+  private List<DatanodeDiskInfo> getDisks(DatanodeInfo datanode) {
+    List<DatanodeDiskInfo> disks = new ArrayList<>();
+    for (StorageReportProto report : datanode.getStorageReports()) {
+      DatanodeDiskInfo.Builder builder = DatanodeDiskInfo.newBuilder()
+          .setStorageUuid(report.getStorageUuid())
+          .setStorageLocation(report.getStorageLocation())
+          .setCapacity(report.getCapacity())
+          .setUsed(report.getScmUsed())
+          .setRemaining(report.getRemaining())
+          .setCommitted(report.getCommitted());
+      if (report.hasOpenContainerCount()) {
+        builder.setOpenContainerCount(report.getOpenContainerCount());
+      }
+      disks.add(builder.build());
+    }
+    return disks;
+  }
+
   /**
    * Removes datanodes from Recon's memory and nodes table in Recon DB.
    * @param uuids the list of datanode uuid's
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeDiskInfo.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeDiskInfo.java
new file mode 100644
index 00000000000..6522570c4ac
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/types/DatanodeDiskInfo.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ozone.recon.api.types;
+
+/**
+ * Metadata object that contains per-disk storage information for a Datanode.
+ */
+public final class DatanodeDiskInfo {
+  private String storageUuid;
+  private String storageLocation;
+  private long capacity;
+  private long used;
+  private long remaining;
+  private long committed;
+  private Long openContainerCount;
+
+  private DatanodeDiskInfo(Builder builder) {
+    this.storageUuid = builder.storageUuid;
+    this.storageLocation = builder.storageLocation;
+    this.capacity = builder.capacity;
+    this.used = builder.used;
+    this.remaining = builder.remaining;
+    this.committed = builder.committed;
+    this.openContainerCount = builder.openContainerCount;
+  }
+
+  public String getStorageUuid() {
+    return storageUuid;
+  }
+
+  public String getStorageLocation() {
+    return storageLocation;
+  }
+
+  public long getCapacity() {
+    return capacity;
+  }
+
+  public long getUsed() {
+    return used;
+  }
+
+  public long getRemaining() {
+    return remaining;
+  }
+
+  public long getCommitted() {
+    return committed;
+  }
+
+  public Long getOpenContainerCount() {
+    return openContainerCount;
+  }
+
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  /**
+   * Builder class for DatanodeDiskInfo.
+   */
+  public static final class Builder {
+    private String storageUuid = "";
+    private String storageLocation = "";
+    private long capacity = 0;
+    private long used = 0;
+    private long remaining = 0;
+    private long committed = 0;
+    private Long openContainerCount;
+
+    private Builder() {
+    }
+
+    public Builder setStorageUuid(String storageUuid) {
+      this.storageUuid = storageUuid;
+      return this;
+    }
+
+    public Builder setStorageLocation(String storageLocation) {
+      this.storageLocation = storageLocation;
+      return this;
+    }
+
+    public Builder setCapacity(long capacity) {
+      this.capacity = capacity;
+      return this;
+    }
+
+    public Builder setUsed(long used) {
+      this.used = used;
+      return this;
+    }
+
+    public Builder setRemaining(long remaining) {
+      this.remaining = remaining;
+      return this;
+    }
+
+    public Builder setCommitted(long committed) {
+      this.committed = committed;
+      return this;
+    }
+
+    public Builder setOpenContainerCount(long openContainerCount) {
+      this.openContainerCount = openContainerCount;
+      return this;
+    }
+
+    public DatanodeDiskInfo build() {
+      return new DatanodeDiskInfo(this);
+    }
+  }
+}
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 b8fb46d294d..485239a7984 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.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import javax.xml.bind.annotation.XmlAccessType;
@@ -92,6 +93,9 @@ public final class DatanodeMetadata {
   @JsonInclude(JsonInclude.Include.NON_NULL)
   private String networkLocation;
 
+  @XmlElement(name = "disks")
+  private List<DatanodeDiskInfo> disks;
+
   private DatanodeMetadata(Builder builder) {
     this.hostname = builder.hostname;
     this.uuid = builder.uuid;
@@ -108,6 +112,7 @@ private DatanodeMetadata(Builder builder) {
     this.revision = builder.revision;
     this.apparentVersion = builder.apparentVersion;
     this.networkLocation = builder.networkLocation;
+    this.disks = builder.disks;
   }
 
   public String getHostname() {
@@ -170,6 +175,10 @@ public String getNetworkLocation() {
     return networkLocation;
   }
 
+  public List<DatanodeDiskInfo> getDisks() {
+    return disks;
+  }
+
   /**
    * Returns new builder class that builds a DatanodeMetadata.
    *
@@ -198,11 +207,13 @@ public static final class Builder {
     private String revision;
     private int apparentVersion;
     private String networkLocation;
+    private List<DatanodeDiskInfo> disks;
 
     public Builder() {
       this.containers = 0;
       this.openContainers = 0;
       this.leaderCount = 0;
+      this.disks = Collections.emptyList();
     }
 
     public Builder setHostname(String hostname) {
@@ -266,6 +277,11 @@ public Builder setDatanode(DatanodeInfo datanode) {
       return this;
     }
 
+    public Builder setDisks(List<DatanodeDiskInfo> disks) {
+      this.disks = disks;
+      return this;
+    }
+
     /**
      * Constructs DatanodeMetadata.
      *
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/__tests__/datanodes/DatanodesTable.test.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/__tests__/datanodes/DatanodesTable.test.tsx
index 5a5addd38f5..22e8f935383 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/__tests__/datanodes/DatanodesTable.test.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/__tests__/datanodes/DatanodesTable.test.tsx
@@ -18,7 +18,7 @@
 
 import React from 'react';
 import {vi} from 'vitest';
-import {fireEvent, render, screen} from '@testing-library/react';
+import {fireEvent, render, screen, within} from '@testing-library/react';
 
 import {DatanodeTableProps} from '@/v2/types/datanode.types';
 import DatanodesTable from '@/v2/components/tables/datanodesTable';
@@ -155,6 +155,41 @@ describe('DatanodesTable Component', () => {
     expect(checkboxes[2]).not.toBeDisabled(); // DEAD node
   });
 
+  test('distinguishes unreported and zero open container counts', () => {
+    const datanode = {
+      ...getDataWith('ozone-datanode-1', 'HEALTHY', 1),
+      disks: [
+        {
+          storageUuid: 'disk-1',
+          storageLocation: '/disk1',
+          capacity: 1000,
+          used: 100,
+          remaining: 900,
+          committed: 0,
+          openContainerCount: null
+        },
+        {
+          storageUuid: 'disk-2',
+          storageLocation: '/disk2',
+          capacity: 1000,
+          used: 100,
+          remaining: 900,
+          committed: 0,
+          openContainerCount: 0
+        }
+      ]
+    };
+    render(<DatanodesTable {...defaultProps} data={[datanode]} />);
+
+    const expandButton = document.querySelector('.ant-table-row-expand-icon');
+    expect(expandButton).not.toBeNull();
+    fireEvent.click(expandButton as HTMLElement);
+
+    const diskTable = screen.getByTestId('dn-disks-1');
+    expect(within(diskTable).getByText('N/A')).toBeInTheDocument();
+    expect(within(diskTable).getByText('0')).toBeInTheDocument();
+  });
+
   test('shows filesystem rows in storage tooltip when provided', async () => {
     render(
       <DatanodesTable
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/datanodesTable.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/datanodesTable.tsx
index e5ba3950bf1..1a56ec4f061 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/datanodesTable.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/tables/datanodesTable.tsx
@@ -40,6 +40,7 @@ import { getTimeDiffFromTimestamp } from 
'@/v2/utils/momentUtils';
 
 import {
   Datanode,
+  DatanodeDiskInfo,
   DatanodeOpState,
   DatanodeOpStateList,
   DatanodeState,
@@ -294,6 +295,41 @@ const DatanodesTable: React.FC<DatanodeTableProps> = ({
     decommissioningUuids = decommissionUuids;
   }, [decommissionUuids])
 
+  function expandedDiskRowRender(record: Datanode) {
+    const diskColumns: ColumnsType<DatanodeDiskInfo> = [
+      { title: 'Disk Location', dataIndex: 'storageLocation', key: 
'storageLocation' },
+      {
+        title: 'Open Containers',
+        dataIndex: 'openContainerCount',
+        key: 'openContainerCount',
+        sorter: (a: DatanodeDiskInfo, b: DatanodeDiskInfo) =>
+          (a.openContainerCount ?? Number.MAX_SAFE_INTEGER) -
+          (b.openContainerCount ?? Number.MAX_SAFE_INTEGER),
+        render: (openContainerCount: number | null) => openContainerCount ?? 
'N/A'
+      },
+      {
+        title: 'Capacity',
+        key: 'capacity',
+        render: (_: unknown, disk: DatanodeDiskInfo) => (
+          <StorageBar
+            capacity={disk.capacity}
+            used={disk.used}
+            remaining={disk.remaining}
+            committed={disk.committed} />
+        )
+      }
+    ];
+    return (
+      <Table
+        rowKey={(disk: DatanodeDiskInfo) => disk.storageUuid}
+        dataSource={record.disks ?? []}
+        columns={diskColumns}
+        pagination={false}
+        size='small'
+        data-testid={`dn-disks-${record.uuid}`} />
+    );
+  }
+
   return (
     <div>
       <Table
@@ -305,6 +341,11 @@ const DatanodesTable: React.FC<DatanodeTableProps> = ({
         pagination={paginationConfig}
         scroll={{ x: 'max-content', scrollToFirstRowOnChange: true }}
         locale={{ filterTitle: '' }}
+        expandable={{
+          expandRowByClick: false,
+          rowExpandable: (record: Datanode) => (record.disks?.length ?? 0) > 0,
+          expandedRowRender: expandedDiskRowRender
+        }}
         onRow={(record: Datanode) => ({
           'data-testid': `dntable-${record.uuid}`
         } as HTMLAttributes<HTMLElement>)}
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
index 0598bee8dfb..8920988fc4f 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/datanodes/datanodes.tsx
@@ -178,7 +178,8 @@ const Datanodes: React.FC<{}> = () => {
           setupTime: datanode.setupTime,
           revision: datanode.revision,
           buildDate: datanode.buildDate,
-          networkLocation: datanode.networkLocation
+          networkLocation: datanode.networkLocation,
+          disks: datanode.disks
         })
       );
 
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/datanode.types.ts
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/datanode.types.ts
index 809c0424962..22499d4d6e3 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/datanode.types.ts
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/datanode.types.ts
@@ -35,6 +35,16 @@ export const DatanodeOpStateList = [
 ] as const;
 export type DatanodeOpState = typeof DatanodeOpStateList[number];
 
+export type DatanodeDiskInfo = {
+  storageUuid: string;
+  storageLocation: string;
+  capacity: number;
+  used: number;
+  remaining: number;
+  committed: number;
+  openContainerCount: number | null;
+}
+
 export type DatanodeResponse = {
   hostname: string;
   state: DatanodeState;
@@ -51,6 +61,7 @@ export type DatanodeResponse = {
   revision: string;
   buildDate: string;
   networkLocation: string;
+  disks?: DatanodeDiskInfo[];
 }
 
 export type DatanodesResponse = {
@@ -78,6 +89,7 @@ export type Datanode = {
   revision: string;
   buildDate: string;
   networkLocation: string;
+  disks?: DatanodeDiskInfo[];
 }
 
 export type DatanodeDetails = {
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 d58d63c983f..a8a8cab6403 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
@@ -116,6 +116,7 @@
 import org.apache.hadoop.ozone.recon.api.types.BucketObjectDBInfo;
 import org.apache.hadoop.ozone.recon.api.types.BucketsResponse;
 import org.apache.hadoop.ozone.recon.api.types.ClusterStateResponse;
+import org.apache.hadoop.ozone.recon.api.types.DatanodeDiskInfo;
 import org.apache.hadoop.ozone.recon.api.types.DatanodeMetadata;
 import org.apache.hadoop.ozone.recon.api.types.DatanodesResponse;
 import org.apache.hadoop.ozone.recon.api.types.PipelineMetadata;
@@ -382,6 +383,7 @@ public void setUp() throws Exception {
             .setFsCapacity(40000)
             .setFsAvailable(15000)
             .setStorageUuid(UUID.randomUUID().toString())
+            .setOpenContainerCount(3)
             .setFailed(false).build();
     StorageReportProto storageReportProto2 =
         StorageReportProto.newBuilder().setStorageType(StorageTypeProto.DISK)
@@ -617,6 +619,17 @@ private void testDatanodeResponse(DatanodeMetadata 
datanodeMetadata)
       assertEquals(pipeline.getLeaderNode().getHostName(),
           datanodeMetadata.getPipelines().get(0).getLeaderNode());
       assertEquals(1, datanodeMetadata.getLeaderCount());
+      assertEquals(2, datanodeMetadata.getDisks().size());
+      DatanodeDiskInfo disk1 = datanodeMetadata.getDisks().stream()
+          .filter(d -> "/disk1".equals(d.getStorageLocation()))
+          .findFirst().orElse(null);
+      assertNotNull(disk1);
+      assertEquals(3L, disk1.getOpenContainerCount());
+      DatanodeDiskInfo disk2 = datanodeMetadata.getDisks().stream()
+          .filter(d -> "/disk2".equals(d.getStorageLocation()))
+          .findFirst().orElse(null);
+      assertNotNull(disk2);
+      assertNull(disk2.getOpenContainerCount());
       break;
     case HOST2:
       assertEquals(130000,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to