Skip to content

HDDS-14108. Provide option in ‘scm safemode status’ to show status of all SCM nodes - #9611

Merged
sumitagrawl merged 12 commits into
apache:masterfrom
sreejasahithi:HDDS-14108
Feb 25, 2026
Merged

HDDS-14108. Provide option in ‘scm safemode status’ to show status of all SCM nodes#9611
sumitagrawl merged 12 commits into
apache:masterfrom
sreejasahithi:HDDS-14108

Conversation

@sreejasahithi

@sreejasahithi sreejasahithi commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

This PR provides an option --all to show the safemode status of each SCM node in the cluster.
If verbose, It also provides the status of each safemode exit rule for each SCM node.

This PR also fixes the bug stated in HDDS-13832 where when --scm option is used in HA it always shows the status of the leader SCM and silently ignores the node specified via the option.

What is the link to the Apache JIRA

HDDS-14108

How was this patch tested?

This patch was tested locally in a docker ozone-ha cluster:

bash-5.1$ ozone admin safemode status --all
Service ID: scmservice
scm1:9860 [scm1]: out of safe mode
scm2:9860 [scm2]: out of safe mode
scm3:9860 [scm3]: out of safe mode
bash-5.1$ ozone admin safemode status --all --verbose
Service ID: scmservice
scm1:9860 [scm1]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
scm2:9860 [scm2]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
scm3:9860 [scm3]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)

bash-5.1$ ozone admin safemode status --scm=scm2:9860
Service ID: scmservice
scm2:9860 [scm2]: out of safe mode
bash-5.1$ ozone admin safemode status --scm=scm2:9860 --verbose
Service ID: scmservice
scm2:9860 [scm2]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)

bash-5.1$ ozone admin safemode status      
Service ID: scmservice
scm1:9860 [scm1]: out of safe mode
bash-5.1$ ozone admin safemode status --verbose
Service ID: scmservice
scm1:9860 [scm1]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)

if scm1 goes down then we can see that the command curretly picks up the new leader when we run the command with no explicit option (--all or --scm) provided.

bash-5.1$ ozone admin safemode status
Service ID: scmservice
scm2:9860 [scm2]: out of safe mode
bash-5.1$ ozone admin safemode status --verbose
Service ID: scmservice
scm2:9860 [scm2]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
bash-5.1$  ozone admin scm roles
scm1:9894:FOLLOWER:bae9a4cf-ce85-47f3-91d3-f90fa1989e2e:
scm3:9894:FOLLOWER:04822469-9758-4429-9aec-b38c5c707882:172.18.0.6
scm2:9894:LEADER:9cda6813-c047-4af5-b1f6-25b6a3ac7bfa:172.18.0.11

bash-5.1$ ozone admin safemode status --all
Service ID: scmservice
scm1:9860 [scm1]: ERROR: Failed to get safe mode status for SCM node: Invalid host name: local host is: "om1/172.18.0.2"; destination host is: "scm1":9860; java.net.UnknownHostException: Invalid host name: local host is: "om1/172.18.0.2"; destination host is: "scm1":9860; java.net.UnknownHostException; For more details see:  http://wiki.apache.org/hadoop/UnknownHost; For more details see:  http://wiki.apache.org/hadoop/UnknownHost
scm2:9860 [scm2]: out of safe mode
scm3:9860 [scm3]: out of safe mode
bash-5.1$ ozone admin safemode status --all --verbose
Service ID: scmservice
scm1:9860 [scm1]: ERROR: Failed to get safe mode status for SCM node: Invalid host name: local host is: "om1/172.18.0.2"; destination host is: "scm1":9860; java.net.UnknownHostException: Invalid host name: local host is: "om1/172.18.0.2"; destination host is: "scm1":9860; java.net.UnknownHostException; For more details see:  http://wiki.apache.org/hadoop/UnknownHost; For more details see:  http://wiki.apache.org/hadoop/UnknownHost
scm2:9860 [scm2]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
scm3:9860 [scm3]: out of safe mode
validated:true, DataNodeSafeModeRule, registered datanodes (=3) >= required datanodes (=3)
validated:true, RatisContainerSafeModeRule, 100.00% of [RATIS] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)
validated:true, HealthyPipelineSafeModeRule, healthy Ratis/THREE pipelines (=1) >= healthyPipelineThresholdCount (=1)
validated:true, StateMachineReadyRule, Refreshed SCM State Machine after leader ready: true
validated:true, OneReplicaPipelineSafeModeRule, reported Ratis/THREE pipelines with at least one datanode (=0) >= threshold (=0)
validated:true, ECContainerSafeModeRule, 100.00% of [EC] Containers(0 / 0) with at least N reported replica (=1.00) >= safeModeCutoff (=0.99)

if scm node scm2 also goes down then it clearly states that leader could not be determined.

bash-5.1$ ozone admin safemode status
Service ID: scmservice
Could not determine leader node

when invalid scm node provided:

bash-5.1$ ozone admin safemode status --scm=scm10:9860          
Service ID: scmservice
Specified --scm address scm10:9860 does not match any node in service scmservice. Nodes: scm1:9860 [scm1], scm2:9860 [scm2], scm3:9860 [scm3]

Green CI : https://github.com/sreejasahithi/ozone/actions/runs/20842284515

@dombizita
dombizita self-requested a review January 10, 2026 10:35
@dombizita

Copy link
Copy Markdown
Contributor

@octachoron would you like to take a look at it if you have time? It's related to what we discussed recently :)

@octachoron

Copy link
Copy Markdown
Contributor

@dombizita, absolutely, thank you! I don't think my vote is enough to merge though.

@octachoron octachoron left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @sreejasahithi for the patch. I added a few thoughts and questions inline. 🙂

@jojochuang
jojochuang requested review from errose28 and sumitagrawl and removed request for octachoron January 12, 2026 17:27

@octachoron octachoron left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, the changes look good to me. Do you think there is a good way to write tests for the feature? (I do not see straightforward precedent other than actual integration tests, but that does not mean there isn't a way. 🙂)

@ashishkumar50 ashishkumar50 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sreejasahithi Thanks for working on this.

}

private void executeForSpecificNodeInHA(ScmClient scmClient, String serviceId) throws IOException {
String scmAddress = getScmOption().getScm();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scmAddress is not mandatory option.

} else if (StringUtils.isNotEmpty(getScmOption().getScm()) && serviceId != null) {
executeForSpecificNodeInHA(scmClient, serviceId);
} else {
executeForSingleNode(scmClient);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In normal or existing behaviour we need safemode status from leader node most of the time. When no scm address is passed, whether we are getting safe mode status from leader node or not? Because now follower also can accept safemode and can return the status.

@sreejasahithi sreejasahithi Jan 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ashishkumar50 for finding this bug, you are right now that we are allowing follower to also accept status command there can be a possibility where when we run safemode status command with no additional option it can return the status of the follower.

I have fixed this issue.

@priyeshkaratha priyeshkaratha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sreejasahithi for working on this. I have one minor comment on handling audit logs.

target.getAddress().equals(nodeAddr.getAddress());
} catch (Exception e) {
// If address resolution fails, no match
return false;

@sadanand48 sadanand48 Jan 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit : Log the exception here before returning false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the logging here because it creates unwanted noise in the CLI output. Instead, I have ensured that actual errors are properly surfaced when no leader can be determined or when the node specified in --scm option doesn't match, clear error messages are thrown to the user.

@priyeshkaratha priyeshkaratha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sreejasahithi for improving the PR. Changes looks good to me.

@aryangupta1998 aryangupta1998 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch @sreejasahithi, some minor comments inline!

@aryangupta1998 aryangupta1998 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the patch @sreejasahithi, LGTM!

@sreejasahithi

Copy link
Copy Markdown
Contributor Author

@sumitagrawl could you please review this patch.

@sumitagrawl sumitagrawl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sumitagrawl
sumitagrawl merged commit 4779f60 into apache:master Feb 25, 2026
46 checks passed
Comment on lines 866 to 889
public Map<String, Pair<Boolean, String>> getSafeModeRuleStatuses()
throws IOException {
GetSafeModeRuleStatusesRequestProto request =
GetSafeModeRuleStatusesRequestProto.getDefaultInstance();
GetSafeModeRuleStatusesResponseProto response =
submitRequest(Type.GetSafeModeRuleStatuses,
builder -> builder.setGetSafeModeRuleStatusesRequest(request))
.getGetSafeModeRuleStatusesResponse();
Map<String, Pair<Boolean, String>> map = new HashMap();
for (SafeModeRuleStatusProto statusProto :
response.getSafeModeRuleStatusesProtoList()) {
map.put(statusProto.getRuleName(),
return buildSafeModeRuleStatusesMap(response);
}

/**
* Helper method to build a map from GetSafeModeRuleStatusesResponseProto.
* Extracts rule names and their status information.
*/
private Map<String, Pair<Boolean, String>> buildSafeModeRuleStatusesMap(
GetSafeModeRuleStatusesResponseProto response) {
Map<String, Pair<Boolean, String>> ruleStatuses = new HashMap<>();
for (SafeModeRuleStatusProto statusProto : response.getSafeModeRuleStatusesProtoList()) {
ruleStatuses.put(statusProto.getRuleName(),
Pair.of(statusProto.getValidate(), statusProto.getStatusText()));
}
return map;
return ruleStatuses;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sreejasahithi , Using Map + Pair is an incorrect choice of data structure. Just want to let you know but not blaming your since it was the existing code before this PR. See

@sreejasahithi sreejasahithi Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @szetszwo for pointing this out. Agreed Map<String, Pair<Boolean, String>> is not needed given the use of SafeModeRuleStatusProto.
Will take a look at HDDS-16130

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants