Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.KEY_TABLE;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
Expand All @@ -34,11 +36,13 @@
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hdds.fs.SpaceUsageSource;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeMetric;
import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat;
import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager;
import org.apache.hadoop.ozone.recon.ReconContext;
import org.apache.hadoop.ozone.recon.ReconUtils;
import org.apache.hadoop.ozone.recon.api.types.DUResponse;
import org.apache.hadoop.ozone.recon.api.types.DataNodeMetricsServiceResponse;
Expand Down Expand Up @@ -79,18 +83,23 @@ public class StorageDistributionEndpoint {
private final ReconGlobalStatsManager reconGlobalStatsManager;
private final ReconGlobalMetricsService reconGlobalMetricsService;
private final DataNodeMetricsService dataNodeMetricsService;
private final ReconContext reconContext;
private static final DateTimeFormatter TIMESTAMP_FORMATTER =
DateTimeFormatter.ofPattern("yyyyMMdd_HHmm");

@Inject
public StorageDistributionEndpoint(OzoneStorageContainerManager reconSCM,
NSSummaryEndpoint nsSummaryEndpoint,
ReconGlobalStatsManager reconGlobalStatsManager,
ReconGlobalMetricsService reconGlobalMetricsService,
DataNodeMetricsService dataNodeMetricsService) {
DataNodeMetricsService dataNodeMetricsService,
ReconContext reconContext) {
this.nodeManager = (ReconNodeManager) reconSCM.getScmNodeManager();
this.nsSummaryEndpoint = nsSummaryEndpoint;
this.reconGlobalStatsManager = reconGlobalStatsManager;
this.reconGlobalMetricsService = reconGlobalMetricsService;
this.dataNodeMetricsService = dataNodeMetricsService;
this.reconContext = reconContext;
}

@GET
Expand Down Expand Up @@ -190,16 +199,16 @@ public Response downloadDataNodeStorageDistribution() {
List<String> headers = Arrays.asList(
"HostName",
"Datanode UUID",
"Filesystem Capacity",
"Filesystem Used Space",
"Filesystem Remaining Space",
"Ozone Capacity",
"Ozone Used Space",
"Ozone Remaining Space",
"PreAllocated Container Space",
"Reserved Space",
"Minimum Free Space",
"Pending Block Size"
"Filesystem Capacity (Bytes)",
"Filesystem Used Space (Bytes)",
"Filesystem Remaining Space (Bytes)",
"Ozone Capacity (Bytes)",
"Ozone Used Space (Bytes)",
"Ozone Remaining Space (Bytes)",
"PreAllocated Container Space (Bytes)",
"Reserved Space (Bytes)",
"Minimum Free Space (Bytes)",
"Pending Block Size (Bytes)"
);

List<Function<DataNodeStoragePendingDeletionView, Object>> columns =
Expand All @@ -215,10 +224,22 @@ public Response downloadDataNodeStorageDistribution() {
v -> v.getReport() != null ? v.getReport().getCommitted() : -1,
v -> v.getReport() != null ? v.getReport().getReserved() : -1,
v -> v.getReport() != null ? v.getReport().getMinimumFreeSpace() : -1,
v -> v.getReport() != null ? v.getMetric().getPendingBlockSize() : -1
v -> v.getMetric() != null ? v.getMetric().getPendingBlockSize() : -1
);

return ReconUtils.downloadCsv("datanode_storage_and_pending_deletion_stats.csv", headers, data, columns);
String timestamp = LocalDateTime.now().format(TIMESTAMP_FORMATTER);
// Retrieve clusterId from ReconContext
String clusterID = "UnknownCluster";
try {
if (reconContext != null && StringUtils.isNotBlank(reconContext.getClusterId())) {
clusterID = reconContext.getClusterId();
}
} catch (Exception e) {
LOG.warn("Could not retrieve cluster ID for export filename", e);
}
String fileName = String.format("Datanode_Insights_%s_%s.csv", clusterID, timestamp);

return ReconUtils.downloadCsv(fileName, headers, data, columns);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.apache.hadoop.hdds.scm.node.DatanodeInfo;
import org.apache.hadoop.hdds.scm.node.NodeStatus;
import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager;
import org.apache.hadoop.ozone.recon.ReconContext;
import org.apache.hadoop.ozone.recon.api.types.DUResponse;
import org.apache.hadoop.ozone.recon.api.types.DataNodeMetricsServiceResponse;
import org.apache.hadoop.ozone.recon.api.types.DatanodePendingDeletionMetrics;
Expand Down Expand Up @@ -82,15 +83,14 @@ public class TestStorageDistributionEndpoint {
private static final String TEXT_PLAIN = "text/plain";
private static final String TEXT_CSV = "text/csv";
private static final String CONTENT_DISPOSITION = "Content-Disposition";
private static final String DOWNLOAD_CONTENT_DISPOSITION =
"attachment; filename=\"datanode_storage_and_pending_deletion_stats.csv\"";
private static final String METRICS_MISSING_ERROR =
"Metrics data is missing despite FINISHED status.";
private static final String ROOT_PATH = "/";
private static final String HOSTNAME_PREFIX = "datanode-";
private static final String PENDING_DIRECTORY_SIZE_KEY = "pendingDirectorySize";
private static final String PENDING_KEY_SIZE_KEY = "pendingKeySize";
private static final String TOTAL_REPLICATED_DATA_SIZE_KEY = "totalReplicatedDataSize";
private static final String CLUSTER_ID = "TestClusterID";

private DataNodeMetricsService dataNodeMetricsService;
private StorageDistributionEndpoint storageDistributionEndpoint;
Expand All @@ -108,11 +108,14 @@ public void setup() {
OzoneStorageContainerManager reconSCM = mock(OzoneStorageContainerManager.class);
when(reconSCM.getScmNodeManager()).thenReturn(nodeManager);
reconGlobalStatsManager = mock(ReconGlobalStatsManager.class);
ReconContext reconContext = mock(ReconContext.class);
when(reconContext.getClusterId()).thenReturn(CLUSTER_ID);
storageDistributionEndpoint = new StorageDistributionEndpoint(reconSCM,
nssummaryEndpoint,
reconGlobalStatsManager,
reconGlobalMetricsService,
dataNodeMetricsService);
dataNodeMetricsService,
reconContext);
}

@Test
Expand Down Expand Up @@ -189,7 +192,10 @@ public void testDownloadReturnsCsvWithMetrics() throws Exception {
// then
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
assertEquals(TEXT_CSV, response.getMediaType().toString());
assertEquals(DOWNLOAD_CONTENT_DISPOSITION, response.getHeaderString(CONTENT_DISPOSITION));
String contentDisposition = response.getHeaderString(CONTENT_DISPOSITION);
assertNotNull(contentDisposition);
assertTrue(contentDisposition.startsWith("attachment; filename=\"Datanode_Insights_" + CLUSTER_ID + "_"));
assertTrue(contentDisposition.endsWith(".csv\""));
String csv = readCsv(response);
for (String row : csvRows) {
assertTrue(csv.contains(row));
Expand All @@ -201,16 +207,16 @@ private List<String> mockStorageDistributionData(int numNodes) throws Exception
List<String> headers = Arrays.asList(
"HostName",
"Datanode UUID",
"Filesystem Capacity",
"Filesystem Used Space",
"Filesystem Remaining Space",
"Ozone Capacity",
"Ozone Used Space",
"Ozone Remaining Space",
"PreAllocated Container Space",
"Reserved Space",
"Minimum Free Space",
"Pending Block Size");
"Filesystem Capacity (Bytes)",
"Filesystem Used Space (Bytes)",
"Filesystem Remaining Space (Bytes)",
"Ozone Capacity (Bytes)",
"Ozone Used Space (Bytes)",
"Ozone Remaining Space (Bytes)",
"PreAllocated Container Space (Bytes)",
"Reserved Space (Bytes)",
"Minimum Free Space (Bytes)",
"Pending Block Size (Bytes)");
csvRows.add(String.join(",", headers));

List<DatanodePendingDeletionMetrics> pendingDeletionMetrics = new ArrayList<>();
Expand Down