From 5727592a9c09177d1b7d7d24474540517b37e4d1 Mon Sep 17 00:00:00 2001 From: Sreeja Chintalapati Date: Thu, 8 May 2025 20:17:38 +0530 Subject: [PATCH 1/6] CLI command to list containers based on Health State --- .../container/ContainerLogController.java | 3 +- .../container/ListContainersByHealth.java | 79 ++++++++ .../utils/ContainerDatanodeDatabase.java | 180 ++++++++++++++++++ .../logs/container/utils/SQLDBConstants.java | 75 ++++++++ 4 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java index dd43119ea6ed..c89570f5e8ff 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java @@ -34,7 +34,8 @@ ContainerInfoCommand.class, ContainerLogParser.class, DuplicateOpenContainersCommand.class, - ListContainers.class + ListContainers.class, + ListContainersByHealth.class }, description = "Tool to parse and store container logs from datanodes into a temporary SQLite database." + " Supports querying state transitions of container replicas using various subcommands." diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java new file mode 100644 index 000000000000..86a7e975765d --- /dev/null +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java @@ -0,0 +1,79 @@ +/* + * 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.debug.logs.container; + +import java.nio.file.Path; +import java.util.concurrent.Callable; +import org.apache.hadoop.hdds.cli.AbstractSubcommand; +import org.apache.hadoop.hdds.scm.container.ReplicationManagerReport; +import org.apache.hadoop.ozone.debug.logs.container.utils.ContainerDatanodeDatabase; +import org.apache.hadoop.ozone.shell.ListOptions; +import picocli.CommandLine; + +/** + * Lists containers based on their health state. + */ +@CommandLine.Command( + name = "list-health", + description = "Lists containers based on health state like UNDER_REPLICATED, " + + "OVER_REPLICATED , UNHEALTHY, QUASI_CLOSED stuck." +) +public class ListContainersByHealth extends AbstractSubcommand implements Callable { + + @CommandLine.Option(names = {"--issue"}, + required = true, + description = "Health state of the container.") + private ReplicationManagerReport.HealthState healthState; + + @CommandLine.Mixin + private ListOptions listOptions; + + @CommandLine.ParentCommand + private ContainerLogController parent; + + @Override + public Void call() throws Exception { + + Path dbPath = parent.resolveDbPath(); + if (dbPath == null) { + return null; + } + + ContainerDatanodeDatabase cdd = new ContainerDatanodeDatabase(dbPath.toString()); + + switch (healthState) { + case UNDER_REPLICATED: + cdd.listReplicatedContainers("UNDER_REPLICATED", listOptions.getLimit()); + break; + case OVER_REPLICATED: + cdd.listReplicatedContainers("OVER_REPLICATED", listOptions.getLimit()); + break; + case UNHEALTHY: + cdd.listUnhealthyContainers(listOptions.getLimit()); + break; + case QUASI_CLOSED_STUCK: + cdd.listQuasiClosedStuckContainers(listOptions.getLimit()); + break; + default: + err().println("Unsupported health state: " + healthState); + } + + return null; + } +} + diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java index 1d44605249c8..b501027f2cf9 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java @@ -122,6 +122,7 @@ public void createIndexes() throws SQLException { createIdxDclContainerStateTime(stmt); createContainerLogIndex(stmt); createIdxContainerlogContainerId(stmt); + createIndexForQuasiClosedQuery(stmt); } catch (SQLException e) { throw new SQLException("Error while creating index: " + e.getMessage()); } catch (Exception e) { @@ -144,6 +145,11 @@ private void createIdxContainerlogContainerId(Statement stmt) throws SQLExceptio stmt.execute(createIndexSQL); } + private void createIndexForQuasiClosedQuery(Statement stmt) throws SQLException { + String createIndexSQL = SQLDBConstants.CREATE_DCL_STATE_CONTAINER_DATANODE_TIME_INDEX; + stmt.execute(createIndexSQL); + } + /** * Inserts a list of container log entries into the DatanodeContainerLogTable. * @@ -609,5 +615,179 @@ private List getContainerLogDataForOpenContainers(Long co return logEntries; } + + /** + * Lists container replicas with key details such as Container ID, Datanode ID, State, BCSID for over- + * or under-replicated containers. + */ + + public void listReplicatedContainers(String overOrUnder, Integer limit) throws SQLException { + String operator; + if ("OVER_REPLICATED".equalsIgnoreCase(overOrUnder)) { + operator = ">"; + } else if ("UNDER_REPLICATED".equalsIgnoreCase(overOrUnder)) { + operator = "<"; + } else { + out.println("Invalid type. Use OVER_REPLICATED or UNDER_REPLICATED."); + return; + } + + String rawQuery = SQLDBConstants.SELECT_REPLICATED_CONTAINERS; + + if (!rawQuery.contains("{operator}")) { + out.println("Query not defined correctly."); + return; + } + + String finalQuery = rawQuery.replace("{operator}", operator); + + boolean limitProvided = limit != Integer.MAX_VALUE; + if (limitProvided) { + finalQuery += " LIMIT ?"; + } + + try (Connection connection = getConnection(); + PreparedStatement pstmt = connection.prepareStatement(finalQuery)) { + + pstmt.setInt(1, DEFAULT_REPLICATION_FACTOR); + + if (limitProvided) { + pstmt.setInt(2, limit + 1); + } + + try (ResultSet rs = pstmt.executeQuery()) { + int count = 0; + + out.printf("| %-15s | %-35s | %-15s | %-15s %n", "Container ID", "Datanode ID", "State", "BCSID"); + out.println("+-----------------+-------------------------------------+-----------------+-----------------+"); + + while (rs.next()) { + if (limitProvided && count >= limit) { + out.println("Note: There might be more containers. Use -all option to list all entries."); + break; + } + out.printf("| %-15d | %-35s | %-15s | %-15d %n", + rs.getLong("container_id"), + rs.getString("datanode_id"), + rs.getString("latest_state"), + rs.getLong("latest_bcsid")); + count++; + } + + out.println("Number of containers listed: " + count); + + } + + } catch (SQLException e) { + throw new SQLException("Error while retrieving containers." + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException("Unexpected error: " + e); + } + } + + /** + * Lists container replicas with key details such as Container ID, Datanode ID, Timestamp, BCSID for + * containers that are UNHEALTHY. + */ + + public void listUnhealthyContainers(Integer limit) throws SQLException { + + String query = SQLDBConstants.SELECT_UNHEALTHY_CONTAINERS; + + boolean limitProvided = limit != Integer.MAX_VALUE; + if (limitProvided) { + query += " LIMIT ?"; + } + + try (Connection connection = getConnection(); + PreparedStatement stmt = connection.prepareStatement(query)) { + + if (limitProvided) { + stmt.setInt(1, limit + 1); + } + + try (ResultSet rs = stmt.executeQuery()) { + int count = 0; + + out.printf("| %-15s | %-35s | %-25s | %-10s %n", "Container ID", "Datanode ID", "Timestamp", "BCSID"); + out.println("+-----------------+-------------------------------------+---------------------------+----------"); + + while (rs.next()) { + if (limitProvided && count >= limit) { + out.println("Note: There might be more containers. Use -all option to list all entries."); + break; + } + + String containerId = rs.getString("container_id"); + String datanodeId = rs.getString("datanode_id"); + String timestamp = rs.getString("latest_unhealthy_timestamp"); + long bcsid = rs.getLong("bcsid"); + + out.printf("| %-15s | %-35s | %-25s | %-10d %n", containerId, datanodeId, timestamp, bcsid); + count++; + } + + out.println("Number of containers listed: " + count); + } + + } catch (SQLException e) { + throw new SQLException("Error while retrieving containers." + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException("Unexpected error: " + e); + } + } + + /** + * Lists container replicas with key details such as Container ID, Datanode ID, Timestamp, BCSID for + * containers that are QUASI_CLOSED stuck. + */ + + public void listQuasiClosedStuckContainers(Integer limit) throws SQLException { + + String query = SQLDBConstants.SELECT_QUASI_CLOSED_STUCK_CONTAINERS; + + boolean limitProvided = limit != Integer.MAX_VALUE; + if (limitProvided) { + query += " LIMIT ?"; + } + + try (Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(query)) { + + if (limitProvided) { + statement.setInt(1, limit + 1); + } + + try (ResultSet resultSet = statement.executeQuery()) { + int count = 0; + + out.printf("| %-15s | %-35s | %-30s | %-15s %n", "Container ID", "Datanode ID", "Timestamp", "BCSID"); + out.println("+-----------------+-------------------------------------+--------------------------" + + "----+-----------------+"); + + while (resultSet.next()) { + if (limitProvided && count >= limit) { + out.println("Note: There might be more containers. Use -all option to list all entries."); + break; + } + + String containerId = resultSet.getString("container_id"); + String datanodeId = resultSet.getString("datanode_id"); + String timestamp = resultSet.getString("latest_quasi_closed_timestamp"); + long bcsid = resultSet.getLong("bcsid"); + + out.printf("| %-15s | %-35s | %-30s | %-15d %n", containerId, datanodeId, timestamp, bcsid); + count++; + } + + out.println("Number of containers listed: " + count); + } + + } catch (SQLException e) { + throw new SQLException("Error while retrieving containers." + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException("Unexpected error: " + e); + } + } } diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java index 74822d35e3d3..6fee233e046b 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java @@ -75,6 +75,81 @@ public final class SQLDBConstants { public static final String SELECT_CONTAINER_DETAILS_OPEN_STATE = "SELECT d.timestamp, d.container_id, " + "d.datanode_id, d.container_state FROM DatanodeContainerLogTable d " + "WHERE d.container_id = ? AND d.container_state = 'OPEN' ORDER BY d.timestamp ASC;"; + public static final String CREATE_DCL_STATE_CONTAINER_DATANODE_TIME_INDEX = + "CREATE INDEX IF NOT EXISTS idx_dcl_state_container_datanode_time " + + "ON DatanodeContainerLogTable(container_state, container_id, datanode_id, timestamp DESC);"; + public static final String SELECT_REPLICATED_CONTAINERS = + "SELECT cl.datanode_id, cl.container_id, cl.latest_state, cl.latest_bcsid\n" + + "FROM ContainerLogTable cl\n" + + "JOIN (\n" + + " SELECT container_id\n" + + " FROM ContainerLogTable\n" + + " GROUP BY container_id\n" + + " HAVING COUNT(DISTINCT datanode_id) {operator} ? \n" + + ") AS grouped ON cl.container_id = grouped.container_id"; + /* "SELECT cl.datanode_id, cl.container_id, cl.latest_state, cl.latest_bcsid\n" + + "FROM ContainerLogTable cl\n" + + "JOIN (\n" + + " SELECT container_id\n" + + " FROM ContainerLogTable\n" + + " GROUP BY container_id\n" + + " HAVING COUNT(datanode_id) {operator} ?\n" + + ") AS grouped ON cl.container_id = grouped.container_id\n" + + "WHERE cl.container_id NOT IN (\n" + + " SELECT dcl.container_id\n" + + " FROM DatanodeContainerLogTable dcl\n" + + " LEFT JOIN DatanodeContainerLogTable other\n" + + " ON dcl.container_id = other.container_id AND other.container_state != 'CLOSED'\n" + + " WHERE dcl.container_state = 'CLOSED'\n" + + " GROUP BY dcl.container_id\n" + + " HAVING COUNT(dcl.container_id) = ?\n" + + " AND MIN(dcl.timestamp) > MAX(other.timestamp)\n" + + ")";*/ + public static final String SELECT_UNHEALTHY_CONTAINERS = + "SELECT u.container_id, u.datanode_id, u.latest_unhealthy_timestamp, u.bcsid\n" + + "FROM (\n" + + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_unhealthy_timestamp, bcsid\n" + + " FROM DatanodeContainerLogTable\n" + + " WHERE container_state = 'UNHEALTHY'\n" + + " GROUP BY container_id, datanode_id, bcsid\n" + + ") AS u\n" + + "LEFT JOIN (\n" + + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_closed_timestamp\n" + + " FROM DatanodeContainerLogTable\n" + + " WHERE container_state IN ('CLOSED', 'DELETED')\n" + + " GROUP BY container_id, datanode_id\n" + + ") AS c\n" + + "ON u.container_id = c.container_id AND u.datanode_id = c.datanode_id\n" + + "WHERE c.latest_closed_timestamp IS NULL \n" + + " OR u.latest_unhealthy_timestamp > c.latest_closed_timestamp\n" + + "ORDER BY u.container_id, u.datanode_id\n"; + public static final String SELECT_QUASI_CLOSED_STUCK_CONTAINERS = + "WITH quasi_closed_replicas AS ( " + + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_quasi_closed_timestamp, bcsid " + + " FROM DatanodeContainerLogTable " + + " WHERE container_state = 'QUASI_CLOSED' " + + " GROUP BY container_id, datanode_id, bcsid " + + "), " + + "container_with_enough_quasi_closed AS (\n" + + " SELECT container_id\n" + + " FROM quasi_closed_replicas\n" + + " GROUP BY container_id\n" + + " HAVING COUNT(DISTINCT datanode_id) >= 3\n" + + "),\n" + + "closed_or_deleted AS (\n" + + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_closed_timestamp\n" + + " FROM DatanodeContainerLogTable\n" + + " WHERE container_state IN ('CLOSED', 'DELETED')\n" + + " GROUP BY container_id, datanode_id\n" + + ")\n" + + "SELECT q.container_id, q.datanode_id, q.latest_quasi_closed_timestamp, q.bcsid\n" + + "FROM quasi_closed_replicas q\n" + + "JOIN container_with_enough_quasi_closed qc ON q.container_id = qc.container_id\n" + + "LEFT JOIN closed_or_deleted c \n" + + " ON q.container_id = c.container_id AND q.datanode_id = c.datanode_id\n" + + "WHERE c.latest_closed_timestamp IS NULL\n" + + " OR q.latest_quasi_closed_timestamp > c.latest_closed_timestamp\n" + + "ORDER BY q.container_id, q.datanode_id\n"; private SQLDBConstants() { //Never constructed From 058eb51d4f153a7373ad210326c86ee34f976305 Mon Sep 17 00:00:00 2001 From: Sreeja Chintalapati Date: Thu, 8 May 2025 21:47:55 +0530 Subject: [PATCH 2/6] Removed unnecessary comments --- .../logs/container/utils/SQLDBConstants.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java index 6fee233e046b..6a494dff91d3 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java @@ -87,24 +87,6 @@ public final class SQLDBConstants { " GROUP BY container_id\n" + " HAVING COUNT(DISTINCT datanode_id) {operator} ? \n" + ") AS grouped ON cl.container_id = grouped.container_id"; - /* "SELECT cl.datanode_id, cl.container_id, cl.latest_state, cl.latest_bcsid\n" + - "FROM ContainerLogTable cl\n" + - "JOIN (\n" + - " SELECT container_id\n" + - " FROM ContainerLogTable\n" + - " GROUP BY container_id\n" + - " HAVING COUNT(datanode_id) {operator} ?\n" + - ") AS grouped ON cl.container_id = grouped.container_id\n" + - "WHERE cl.container_id NOT IN (\n" + - " SELECT dcl.container_id\n" + - " FROM DatanodeContainerLogTable dcl\n" + - " LEFT JOIN DatanodeContainerLogTable other\n" + - " ON dcl.container_id = other.container_id AND other.container_state != 'CLOSED'\n" + - " WHERE dcl.container_state = 'CLOSED'\n" + - " GROUP BY dcl.container_id\n" + - " HAVING COUNT(dcl.container_id) = ?\n" + - " AND MIN(dcl.timestamp) > MAX(other.timestamp)\n" + - ")";*/ public static final String SELECT_UNHEALTHY_CONTAINERS = "SELECT u.container_id, u.datanode_id, u.latest_unhealthy_timestamp, u.bcsid\n" + "FROM (\n" + From 6e412da3624e35f574e2eaf34f664c0d4ec9e3dd Mon Sep 17 00:00:00 2001 From: Sreeja Chintalapati Date: Fri, 9 May 2025 13:56:16 +0530 Subject: [PATCH 3/6] Updated queries and command output, moved list health state to ListContainers command --- .../logs/container/ContainerInfoCommand.java | 3 - .../container/ContainerLogController.java | 11 ++- .../DuplicateOpenContainersCommand.java | 3 - .../debug/logs/container/ListContainers.java | 40 +++++++--- .../container/ListContainersByHealth.java | 79 ------------------- .../utils/ContainerDatanodeDatabase.java | 49 +++--------- .../logs/container/utils/SQLDBConstants.java | 47 ++++++----- 7 files changed, 76 insertions(+), 156 deletions(-) delete mode 100644 hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerInfoCommand.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerInfoCommand.java index 7c5f8b32dc0d..116cbe3f34c4 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerInfoCommand.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerInfoCommand.java @@ -49,9 +49,6 @@ public Void call() throws Exception { } Path dbPath = parent.resolveDbPath(); - if (dbPath == null) { - return null; - } ContainerDatanodeDatabase cdd = new ContainerDatanodeDatabase(dbPath.toString()); diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java index c89570f5e8ff..1a6cdafea630 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ContainerLogController.java @@ -34,8 +34,7 @@ ContainerInfoCommand.class, ContainerLogParser.class, DuplicateOpenContainersCommand.class, - ListContainers.class, - ListContainersByHealth.class + ListContainers.class }, description = "Tool to parse and store container logs from datanodes into a temporary SQLite database." + " Supports querying state transitions of container replicas using various subcommands." @@ -64,17 +63,17 @@ public Path resolveDbPath() { if (Files.exists(resolvedPath) && Files.isRegularFile(resolvedPath)) { out().println("Using default database file found in current directory: " + resolvedPath); } else { - err().println("No database path provided and default file '" + SQLDBConstants.DEFAULT_DB_FILENAME + "' not " + + throw new IllegalArgumentException("No database path provided and default file '" + + SQLDBConstants.DEFAULT_DB_FILENAME + "' not " + "found in current directory. Please provide a valid database path"); - return null; } } else { resolvedPath = Paths.get(dbPath); Path parentDir = resolvedPath.getParent(); if (parentDir != null && !Files.exists(parentDir)) { - err().println("The parent directory of the provided database path does not exist: " + parentDir); - return null; + throw new IllegalArgumentException("The parent directory of the provided database " + + "path does not exist: " + parentDir); } } diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/DuplicateOpenContainersCommand.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/DuplicateOpenContainersCommand.java index 820d5b5d2e41..a0fb8371ecd2 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/DuplicateOpenContainersCommand.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/DuplicateOpenContainersCommand.java @@ -39,9 +39,6 @@ public class DuplicateOpenContainersCommand implements Callable { @Override public Void call() throws Exception { Path dbPath = parent.resolveDbPath(); - if (dbPath == null) { - return null; - } ContainerDatanodeDatabase cdd = new ContainerDatanodeDatabase(dbPath.toString()); cdd.findDuplicateOpenContainer(); diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java index 25a5e7c003e3..db0e5ba19375 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java @@ -19,7 +19,9 @@ import java.nio.file.Path; import java.util.concurrent.Callable; +import org.apache.hadoop.hdds.cli.AbstractSubcommand; import org.apache.hadoop.hdds.protocol.proto.HddsProtos; +import org.apache.hadoop.hdds.scm.container.ReplicationManagerReport; import org.apache.hadoop.ozone.debug.logs.container.utils.ContainerDatanodeDatabase; import org.apache.hadoop.ozone.shell.ListOptions; import picocli.CommandLine; @@ -33,12 +35,15 @@ name = "list", description = "Finds containers from the database based on the option provided." ) -public class ListContainers implements Callable { +public class ListContainers extends AbstractSubcommand implements Callable { - @CommandLine.Option(names = {"--state"}, - description = "Life cycle state of the container.", - required = true) - private HddsProtos.LifeCycleState state; + @CommandLine.Option(names = {"--lifecycle"}, + description = "Life cycle state of the container.") + private HddsProtos.LifeCycleState lifecycleState; + + @CommandLine.Option(names = {"--health"}, + description = "Health state of the container.") + private ReplicationManagerReport.HealthState healthState; @CommandLine.Mixin private ListOptions listOptions; @@ -50,13 +55,30 @@ public class ListContainers implements Callable { public Void call() throws Exception { Path dbPath = parent.resolveDbPath(); - if (dbPath == null) { - return null; - } ContainerDatanodeDatabase cdd = new ContainerDatanodeDatabase(dbPath.toString()); - cdd.listContainersByState(state.name(), listOptions.getLimit()); + if (lifecycleState != null) { + cdd.listContainersByState(lifecycleState.name(), listOptions.getLimit()); + } else if (healthState != null) { + switch (healthState) { + case UNDER_REPLICATED: + case OVER_REPLICATED: + cdd.listReplicatedContainers(healthState.name(), listOptions.getLimit()); + break; + case UNHEALTHY: + cdd.listUnhealthyContainers(listOptions.getLimit()); + break; + case QUASI_CLOSED_STUCK: + cdd.listQuasiClosedStuckContainers(listOptions.getLimit()); + break; + default: + err().println("Unsupported health state: " + healthState); + } + } else { + err().println("Please provide either a lifecycle state (--lifecycle) or health state " + + "(--health) to filter the containers."); + } return null; } diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java deleted file mode 100644 index 86a7e975765d..000000000000 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainersByHealth.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.debug.logs.container; - -import java.nio.file.Path; -import java.util.concurrent.Callable; -import org.apache.hadoop.hdds.cli.AbstractSubcommand; -import org.apache.hadoop.hdds.scm.container.ReplicationManagerReport; -import org.apache.hadoop.ozone.debug.logs.container.utils.ContainerDatanodeDatabase; -import org.apache.hadoop.ozone.shell.ListOptions; -import picocli.CommandLine; - -/** - * Lists containers based on their health state. - */ -@CommandLine.Command( - name = "list-health", - description = "Lists containers based on health state like UNDER_REPLICATED, " + - "OVER_REPLICATED , UNHEALTHY, QUASI_CLOSED stuck." -) -public class ListContainersByHealth extends AbstractSubcommand implements Callable { - - @CommandLine.Option(names = {"--issue"}, - required = true, - description = "Health state of the container.") - private ReplicationManagerReport.HealthState healthState; - - @CommandLine.Mixin - private ListOptions listOptions; - - @CommandLine.ParentCommand - private ContainerLogController parent; - - @Override - public Void call() throws Exception { - - Path dbPath = parent.resolveDbPath(); - if (dbPath == null) { - return null; - } - - ContainerDatanodeDatabase cdd = new ContainerDatanodeDatabase(dbPath.toString()); - - switch (healthState) { - case UNDER_REPLICATED: - cdd.listReplicatedContainers("UNDER_REPLICATED", listOptions.getLimit()); - break; - case OVER_REPLICATED: - cdd.listReplicatedContainers("OVER_REPLICATED", listOptions.getLimit()); - break; - case UNHEALTHY: - cdd.listUnhealthyContainers(listOptions.getLimit()); - break; - case QUASI_CLOSED_STUCK: - cdd.listQuasiClosedStuckContainers(listOptions.getLimit()); - break; - default: - err().println("Unsupported health state: " + healthState); - } - - return null; - } -} - diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java index b501027f2cf9..a3595ddee07f 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java @@ -617,8 +617,7 @@ private List getContainerLogDataForOpenContainers(Long co } /** - * Lists container replicas with key details such as Container ID, Datanode ID, State, BCSID for over- - * or under-replicated containers. + * Lists containers that are over- or under-replicated also provides count of replicas. */ public void listReplicatedContainers(String overOrUnder, Integer limit) throws SQLException { @@ -658,19 +657,14 @@ public void listReplicatedContainers(String overOrUnder, Integer limit) throws S try (ResultSet rs = pstmt.executeQuery()) { int count = 0; - out.printf("| %-15s | %-35s | %-15s | %-15s %n", "Container ID", "Datanode ID", "State", "BCSID"); - out.println("+-----------------+-------------------------------------+-----------------+-----------------+"); - while (rs.next()) { if (limitProvided && count >= limit) { - out.println("Note: There might be more containers. Use -all option to list all entries."); + out.println("Note: There might be more containers. Use --all option to list all entries."); break; } - out.printf("| %-15d | %-35s | %-15s | %-15d %n", - rs.getLong("container_id"), - rs.getString("datanode_id"), - rs.getString("latest_state"), - rs.getLong("latest_bcsid")); + + out.printf("Container ID = %s - Count = %d%n", rs.getLong("container_id"), + rs.getInt("replica_count")); count++; } @@ -686,8 +680,7 @@ public void listReplicatedContainers(String overOrUnder, Integer limit) throws S } /** - * Lists container replicas with key details such as Container ID, Datanode ID, Timestamp, BCSID for - * containers that are UNHEALTHY. + * Lists containers that are UNHEALTHY also provides count of replicas which are in UNHEALTHY state. */ public void listUnhealthyContainers(Integer limit) throws SQLException { @@ -709,21 +702,14 @@ public void listUnhealthyContainers(Integer limit) throws SQLException { try (ResultSet rs = stmt.executeQuery()) { int count = 0; - out.printf("| %-15s | %-35s | %-25s | %-10s %n", "Container ID", "Datanode ID", "Timestamp", "BCSID"); - out.println("+-----------------+-------------------------------------+---------------------------+----------"); - while (rs.next()) { if (limitProvided && count >= limit) { - out.println("Note: There might be more containers. Use -all option to list all entries."); + out.println("Note: There might be more containers. Use --all option to list all entries."); break; } - String containerId = rs.getString("container_id"); - String datanodeId = rs.getString("datanode_id"); - String timestamp = rs.getString("latest_unhealthy_timestamp"); - long bcsid = rs.getLong("bcsid"); - - out.printf("| %-15s | %-35s | %-25s | %-10d %n", containerId, datanodeId, timestamp, bcsid); + out.printf("Container ID = %s - Count = %d%n", rs.getString("container_id"), + rs.getInt("unhealthy_replica_count")); count++; } @@ -738,8 +724,7 @@ public void listUnhealthyContainers(Integer limit) throws SQLException { } /** - * Lists container replicas with key details such as Container ID, Datanode ID, Timestamp, BCSID for - * containers that are QUASI_CLOSED stuck. + * Lists containers that are QUASI_CLOSED stuck also provides count of replicas which are in QUASI_CLOSED state. */ public void listQuasiClosedStuckContainers(Integer limit) throws SQLException { @@ -761,22 +746,14 @@ public void listQuasiClosedStuckContainers(Integer limit) throws SQLException { try (ResultSet resultSet = statement.executeQuery()) { int count = 0; - out.printf("| %-15s | %-35s | %-30s | %-15s %n", "Container ID", "Datanode ID", "Timestamp", "BCSID"); - out.println("+-----------------+-------------------------------------+--------------------------" + - "----+-----------------+"); - while (resultSet.next()) { if (limitProvided && count >= limit) { - out.println("Note: There might be more containers. Use -all option to list all entries."); + out.println("Note: There might be more containers. Use --all option to list all entries."); break; } - String containerId = resultSet.getString("container_id"); - String datanodeId = resultSet.getString("datanode_id"); - String timestamp = resultSet.getString("latest_quasi_closed_timestamp"); - long bcsid = resultSet.getLong("bcsid"); - - out.printf("| %-15s | %-35s | %-30s | %-15d %n", containerId, datanodeId, timestamp, bcsid); + out.printf("Container ID = %s - Count = %d%n", resultSet.getString("container_id"), + resultSet.getInt("quasi_closed_replica_count")); count++; } diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java index 6a494dff91d3..773e82129820 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/SQLDBConstants.java @@ -17,6 +17,9 @@ package org.apache.hadoop.ozone.debug.logs.container.utils; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos; +import org.apache.hadoop.hdds.scm.container.ReplicationManagerReport; + /** * Constants used for ContainerDatanodeDatabase. */ @@ -29,6 +32,11 @@ public final class SQLDBConstants { public static final int BATCH_SIZE = 2500; public static final String DATANODE_CONTAINER_LOG_TABLE_NAME = "DatanodeContainerLogTable"; public static final String CONTAINER_LOG_TABLE_NAME = "ContainerLogTable"; + public static final String CLOSED_STATE = HddsProtos.LifeCycleState.CLOSED.name(); + public static final String DELETED_STATE = HddsProtos.LifeCycleState.DELETED.name(); + public static final String UNHEALTHY_STATE = ReplicationManagerReport.HealthState.UNHEALTHY.name(); + public static final String QUASI_CLOSED_STATE = HddsProtos.LifeCycleState.QUASI_CLOSED.name(); + public static final String CREATE_DATANODE_CONTAINER_LOG_TABLE = "CREATE TABLE IF NOT EXISTS DatanodeContainerLogTable (datanode_id TEXT NOT NULL, " + "container_id INTEGER NOT NULL, timestamp TEXT NOT NULL, container_state TEXT, bcsid INTEGER, " + @@ -79,38 +87,36 @@ public final class SQLDBConstants { "CREATE INDEX IF NOT EXISTS idx_dcl_state_container_datanode_time " + "ON DatanodeContainerLogTable(container_state, container_id, datanode_id, timestamp DESC);"; public static final String SELECT_REPLICATED_CONTAINERS = - "SELECT cl.datanode_id, cl.container_id, cl.latest_state, cl.latest_bcsid\n" + - "FROM ContainerLogTable cl\n" + - "JOIN (\n" + - " SELECT container_id\n" + - " FROM ContainerLogTable\n" + - " GROUP BY container_id\n" + - " HAVING COUNT(DISTINCT datanode_id) {operator} ? \n" + - ") AS grouped ON cl.container_id = grouped.container_id"; + "SELECT container_id, COUNT(DISTINCT datanode_id) AS replica_count\n" + + "FROM ContainerLogTable\n" + + "WHERE latest_state != '" + DELETED_STATE + "'\n" + + " GROUP BY container_id\n" + + "HAVING COUNT(DISTINCT datanode_id) {operator} ?"; public static final String SELECT_UNHEALTHY_CONTAINERS = - "SELECT u.container_id, u.datanode_id, u.latest_unhealthy_timestamp, u.bcsid\n" + + "SELECT u.container_id, COUNT(*) AS unhealthy_replica_count\n" + "FROM (\n" + - " SELECT container_id, datanode_id, MAX(timestamp) AS latest_unhealthy_timestamp, bcsid\n" + + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_unhealthy_timestamp\n" + " FROM DatanodeContainerLogTable\n" + - " WHERE container_state = 'UNHEALTHY'\n" + - " GROUP BY container_id, datanode_id, bcsid\n" + + " WHERE container_state = '" + UNHEALTHY_STATE + "'\n" + + " GROUP BY container_id, datanode_id\n" + ") AS u\n" + "LEFT JOIN (\n" + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_closed_timestamp\n" + " FROM DatanodeContainerLogTable\n" + - " WHERE container_state IN ('CLOSED', 'DELETED')\n" + + " WHERE container_state IN ('" + CLOSED_STATE + "', '" + DELETED_STATE + "')\n" + " GROUP BY container_id, datanode_id\n" + ") AS c\n" + "ON u.container_id = c.container_id AND u.datanode_id = c.datanode_id\n" + "WHERE c.latest_closed_timestamp IS NULL \n" + " OR u.latest_unhealthy_timestamp > c.latest_closed_timestamp\n" + - "ORDER BY u.container_id, u.datanode_id\n"; + "GROUP BY u.container_id\n" + + "ORDER BY u.container_id"; public static final String SELECT_QUASI_CLOSED_STUCK_CONTAINERS = "WITH quasi_closed_replicas AS ( " + - " SELECT container_id, datanode_id, MAX(timestamp) AS latest_quasi_closed_timestamp, bcsid " + + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_quasi_closed_timestamp\n" + " FROM DatanodeContainerLogTable " + - " WHERE container_state = 'QUASI_CLOSED' " + - " GROUP BY container_id, datanode_id, bcsid " + + " WHERE container_state = '" + QUASI_CLOSED_STATE + "'\n" + + " GROUP BY container_id, datanode_id" + "), " + "container_with_enough_quasi_closed AS (\n" + " SELECT container_id\n" + @@ -121,17 +127,18 @@ public final class SQLDBConstants { "closed_or_deleted AS (\n" + " SELECT container_id, datanode_id, MAX(timestamp) AS latest_closed_timestamp\n" + " FROM DatanodeContainerLogTable\n" + - " WHERE container_state IN ('CLOSED', 'DELETED')\n" + + " WHERE container_state IN ('" + CLOSED_STATE + "', '" + DELETED_STATE + "')\n" + " GROUP BY container_id, datanode_id\n" + ")\n" + - "SELECT q.container_id, q.datanode_id, q.latest_quasi_closed_timestamp, q.bcsid\n" + + "SELECT q.container_id, COUNT(*) AS quasi_closed_replica_count\n" + "FROM quasi_closed_replicas q\n" + "JOIN container_with_enough_quasi_closed qc ON q.container_id = qc.container_id\n" + "LEFT JOIN closed_or_deleted c \n" + " ON q.container_id = c.container_id AND q.datanode_id = c.datanode_id\n" + "WHERE c.latest_closed_timestamp IS NULL\n" + " OR q.latest_quasi_closed_timestamp > c.latest_closed_timestamp\n" + - "ORDER BY q.container_id, q.datanode_id\n"; + "GROUP BY q.container_id\n" + + "ORDER BY q.container_id"; private SQLDBConstants() { //Never constructed From 392d13e36e068c0809f6e1d7907e879f3c463bc2 Mon Sep 17 00:00:00 2001 From: Sreeja Chintalapati Date: Fri, 9 May 2025 19:00:37 +0530 Subject: [PATCH 4/6] Redirect non-content messages to System.err --- .../utils/ContainerDatanodeDatabase.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java index a3595ddee07f..398fb98b28ec 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/utils/ContainerDatanodeDatabase.java @@ -49,15 +49,18 @@ public class ContainerDatanodeDatabase { private static final int DEFAULT_REPLICATION_FACTOR; private final PrintWriter out; + private final PrintWriter err; public ContainerDatanodeDatabase(String dbPath) { this.databasePath = dbPath; this.out = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8), true); + this.err = new PrintWriter(new OutputStreamWriter(System.err, StandardCharsets.UTF_8), true); } - public ContainerDatanodeDatabase(String dbPath, PrintWriter out) { + public ContainerDatanodeDatabase(String dbPath, PrintWriter out, PrintWriter err) { this.databasePath = dbPath; this.out = out; + this.err = err; } static { @@ -627,14 +630,14 @@ public void listReplicatedContainers(String overOrUnder, Integer limit) throws S } else if ("UNDER_REPLICATED".equalsIgnoreCase(overOrUnder)) { operator = "<"; } else { - out.println("Invalid type. Use OVER_REPLICATED or UNDER_REPLICATED."); + err.println("Invalid type. Use OVER_REPLICATED or UNDER_REPLICATED."); return; } String rawQuery = SQLDBConstants.SELECT_REPLICATED_CONTAINERS; if (!rawQuery.contains("{operator}")) { - out.println("Query not defined correctly."); + err.println("Query not defined correctly."); return; } @@ -659,7 +662,7 @@ public void listReplicatedContainers(String overOrUnder, Integer limit) throws S while (rs.next()) { if (limitProvided && count >= limit) { - out.println("Note: There might be more containers. Use --all option to list all entries."); + err.println("Note: There might be more containers. Use --all option to list all entries."); break; } @@ -704,7 +707,7 @@ public void listUnhealthyContainers(Integer limit) throws SQLException { while (rs.next()) { if (limitProvided && count >= limit) { - out.println("Note: There might be more containers. Use --all option to list all entries."); + err.println("Note: There might be more containers. Use --all option to list all entries."); break; } @@ -748,7 +751,7 @@ public void listQuasiClosedStuckContainers(Integer limit) throws SQLException { while (resultSet.next()) { if (limitProvided && count >= limit) { - out.println("Note: There might be more containers. Use --all option to list all entries."); + err.println("Note: There might be more containers. Use --all option to list all entries."); break; } From 994328cb159e5f5102863d7d407f516c9cd74b7f Mon Sep 17 00:00:00 2001 From: Sreeja Chintalapati Date: Mon, 12 May 2025 15:07:57 +0530 Subject: [PATCH 5/6] Made lifecycle and health state option in list command exclusive --- .../debug/logs/container/ListContainers.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java index 2519bf2c2884..8a3cd543af7f 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java @@ -37,17 +37,22 @@ ) public class ListContainers extends AbstractSubcommand implements Callable { - @CommandLine.Option(names = {"--lifecycle"}, - description = "Life cycle state of the container.") - private HddsProtos.LifeCycleState lifecycleState; - - @CommandLine.Option(names = {"--health"}, - description = "Health state of the container.") - private ReplicationManagerReport.HealthState healthState; + @CommandLine.ArgGroup(multiplicity = "1") + private ExclusiveOptions exclusiveOptions; @CommandLine.Mixin private ListLimitOptions listOptions; + private static final class ExclusiveOptions { + @CommandLine.Option(names = {"--lifecycle"}, + description = "Life cycle state of the container.") + private HddsProtos.LifeCycleState lifecycleState; + + @CommandLine.Option(names = {"--health"}, + description = "Health state of the container.") + private ReplicationManagerReport.HealthState healthState; + } + @CommandLine.ParentCommand private ContainerLogController parent; @@ -58,13 +63,13 @@ public Void call() throws Exception { ContainerDatanodeDatabase cdd = new ContainerDatanodeDatabase(dbPath.toString()); - if (lifecycleState != null) { - cdd.listContainersByState(lifecycleState.name(), listOptions.getLimit()); - } else if (healthState != null) { - switch (healthState) { + if (exclusiveOptions.lifecycleState != null) { + cdd.listContainersByState(exclusiveOptions.lifecycleState.name(), listOptions.getLimit()); + } else if (exclusiveOptions.healthState != null) { + switch (exclusiveOptions.healthState) { case UNDER_REPLICATED: case OVER_REPLICATED: - cdd.listReplicatedContainers(healthState.name(), listOptions.getLimit()); + cdd.listReplicatedContainers(exclusiveOptions.healthState.name(), listOptions.getLimit()); break; case UNHEALTHY: cdd.listUnhealthyContainers(listOptions.getLimit()); @@ -73,11 +78,8 @@ public Void call() throws Exception { cdd.listQuasiClosedStuckContainers(listOptions.getLimit()); break; default: - err().println("Unsupported health state: " + healthState); + err().println("Unsupported health state: " + exclusiveOptions.healthState); } - } else { - err().println("Please provide either a lifecycle state (--lifecycle) or health state " + - "(--health) to filter the containers."); } return null; From 921342ac473c80a094d9a2f47db93dca852e1405 Mon Sep 17 00:00:00 2001 From: Sreeja Chintalapati Date: Mon, 12 May 2025 15:40:31 +0530 Subject: [PATCH 6/6] Fixed pmd issue --- .../hadoop/ozone/debug/logs/container/ListContainers.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java index 8a3cd543af7f..2c9de2d64383 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/logs/container/ListContainers.java @@ -42,6 +42,9 @@ public class ListContainers extends AbstractSubcommand implements Callable @CommandLine.Mixin private ListLimitOptions listOptions; + + @CommandLine.ParentCommand + private ContainerLogController parent; private static final class ExclusiveOptions { @CommandLine.Option(names = {"--lifecycle"}, @@ -52,9 +55,6 @@ private static final class ExclusiveOptions { description = "Health state of the container.") private ReplicationManagerReport.HealthState healthState; } - - @CommandLine.ParentCommand - private ContainerLogController parent; @Override public Void call() throws Exception {