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 a1ce3b9d900 HDDS-15774. Make SCM Ratis roles string parsing IPv6-safe
(#10831)
a1ce3b9d900 is described below
commit a1ce3b9d9006d4790bf72d9751d5351938626ae2
Author: Aditya Chechani <[email protected]>
AuthorDate: Wed Aug 12 05:33:32 2026 +0530
HDDS-15774. Make SCM Ratis roles string parsing IPv6-safe (#10831)
---
.../java/org/apache/hadoop/hdds/HddsUtils.java | 63 +++++++++++++++++++++
.../apache/hadoop/hdds/scm/net/HostAndPort.java | 3 +-
.../java/org/apache/hadoop/hdds/TestHddsUtils.java | 62 +++++++++++++++++++++
.../hadoop/hdds/scm/net/TestHostAndPort.java | 6 ++
.../hadoop/hdds/scm/ha/SCMRatisServerImpl.java | 31 ++++++++---
.../hdds/scm/server/StorageContainerManager.java | 2 +-
.../hadoop/hdds/scm/ha/TestSCMRatisServerImpl.java | 62 +++++++++++++++++++++
.../hdds/scm/cli/SafeModeCheckSubcommand.java | 49 +++++++++-------
.../admin/scm/GetScmRatisRolesSubcommand.java | 51 ++++++++++-------
.../ozone/scm/TestGetScmRatisRolesSubcommand.java | 65 ++++++++++++++++++++++
.../impl/StorageContainerServiceProviderImpl.java | 3 +-
11 files changed, 347 insertions(+), 50 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
index 3e6715b11c3..3b29663fedd 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/HddsUtils.java
@@ -243,6 +243,69 @@ public static String getHostPortString(String host, int
port) {
return HostAndPort.fromParts(host, port).toString();
}
+ /**
+ * Parse a Ratis role string produced by
+ * {@code SCMRatisServerImpl.getRatisRoles()} into its constituent fields.
+ * The format is {@code [host]:port:ROLE:id:hostIP} where host and hostIP
+ * may be bracketed IPv6 literals.
+ *
+ * @param roleString the encoded role string
+ * @return a 5-element array: {host, port, role, id, hostIP}
+ */
+ public static String[] parseRatisRoleString(String roleString) {
+ Preconditions.checkArgument(roleString != null && !roleString.isEmpty(),
+ "Role string must not be null or empty");
+
+ // Parse from the right: the last field is hostIP (possibly bracketed),
+ // then id (uuid, no colons), then role (LEADER/FOLLOWER, no colons),
+ // and the remainder is host:port (which may be bracketed IPv6).
+ int idx = roleString.length();
+
+ // Field 5: hostIP — may be bracketed IPv6 like [2001:db8::1]
+ String hostIp;
+ if (roleString.charAt(idx - 1) == ']') {
+ int bracket = roleString.lastIndexOf('[');
+ Preconditions.checkArgument(bracket > 0,
+ "Malformed role string (unmatched bracket): %s", roleString);
+ hostIp = roleString.substring(bracket + 1, idx - 1);
+ Preconditions.checkArgument(roleString.charAt(bracket - 1) == ':',
+ "Malformed role string (expected ':' before '['): %s", roleString);
+ idx = bracket - 1;
+ } else {
+ int sep = roleString.lastIndexOf(':');
+ Preconditions.checkArgument(sep > 0,
+ "Malformed role string (expected host:port:role:id:hostIP): %s",
roleString);
+ hostIp = roleString.substring(sep + 1);
+ idx = sep;
+ }
+
+ // Field 4: id (uuid or peer id, no colons)
+ int sep3 = roleString.lastIndexOf(':', idx - 1);
+ Preconditions.checkArgument(sep3 > 0,
+ "Malformed role string (cannot find id field): %s", roleString);
+ String id = roleString.substring(sep3 + 1, idx);
+ idx = sep3;
+
+ // Field 3: role (LEADER/FOLLOWER, no colons)
+ int sep2 = roleString.lastIndexOf(':', idx - 1);
+ Preconditions.checkArgument(sep2 > 0,
+ "Malformed role string (cannot find role field): %s", roleString);
+ String role = roleString.substring(sep2 + 1, idx);
+ idx = sep2;
+
+ // Remainder is host:port — use HostAndPort to parse safely
+ String hostPort = roleString.substring(0, idx);
+ Preconditions.checkArgument(!hostPort.isEmpty(),
+ "Malformed role string (empty host:port): %s", roleString);
+ HostAndPort hp = HostAndPort.fromString(hostPort);
+ Preconditions.checkArgument(hp.hasPort(),
+ "Malformed role string (missing port): %s", roleString);
+ String host = hp.getHost();
+ String port = String.valueOf(hp.getPort());
+
+ return new String[]{host, port, role, id, hostIp};
+ }
+
/**
* Retrieve a number, trying the supplied config keys in order.
* Each config value may be absent
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/HostAndPort.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/HostAndPort.java
index 2a142ea8a11..75400ff707e 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/HostAndPort.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/HostAndPort.java
@@ -20,6 +20,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Objects;
+import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.net.NetUtils;
/**
@@ -37,7 +38,7 @@ public class HostAndPort {
public HostAndPort(String host, int port) {
this.host = host;
this.port = port;
- this.hostAndPortString = host + ":" + port;
+ this.hostAndPortString = HddsUtils.getHostPortString(host, port);
this.hash = host.hashCode() ^ Integer.hashCode(port);
this.address = NetUtils.createSocketAddr(hostAndPortString);
}
diff --git
a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/TestHddsUtils.java
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/TestHddsUtils.java
index 36bc8bed95f..8fdf6de7b50 100644
--- a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/TestHddsUtils.java
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/TestHddsUtils.java
@@ -102,6 +102,68 @@ void testGetHostPortString() {
assertEquals("[2001:db8::1]:9858",
HddsUtils.getHostPortString("[2001:db8::1]", 9858));
}
+ @Test
+ void testParseRatisRoleStringIPv4() {
+ String input = "hostname1:9894:LEADER:peer-uuid-123:192.168.1.1";
+ String[] result = HddsUtils.parseRatisRoleString(input);
+ assertEquals("hostname1", result[0]);
+ assertEquals("9894", result[1]);
+ assertEquals("LEADER", result[2]);
+ assertEquals("peer-uuid-123", result[3]);
+ assertEquals("192.168.1.1", result[4]);
+ }
+
+ @Test
+ void testParseRatisRoleStringIPv6() {
+ String input = "[2001:db8::1]:9894:LEADER:peer1:[2001:db8:0:0:0:0:0:1]";
+ String[] result = HddsUtils.parseRatisRoleString(input);
+ assertEquals("2001:db8::1", result[0]);
+ assertEquals("9894", result[1]);
+ assertEquals("LEADER", result[2]);
+ assertEquals("peer1", result[3]);
+ assertEquals("2001:db8:0:0:0:0:0:1", result[4]);
+ }
+
+ @Test
+ void testParseRatisRoleStringIPv6Follower() {
+ String input = "[::1]:9894:FOLLOWER:abc-def:[0:0:0:0:0:0:0:1]";
+ String[] result = HddsUtils.parseRatisRoleString(input);
+ assertEquals("::1", result[0]);
+ assertEquals("9894", result[1]);
+ assertEquals("FOLLOWER", result[2]);
+ assertEquals("abc-def", result[3]);
+ assertEquals("0:0:0:0:0:0:0:1", result[4]);
+ }
+
+ @Test
+ void testParseRatisRoleStringEmptyHostIp() {
+ String input = "scm-host:9894:LEADER:uuid123:";
+ String[] result = HddsUtils.parseRatisRoleString(input);
+ assertEquals("scm-host", result[0]);
+ assertEquals("9894", result[1]);
+ assertEquals("LEADER", result[2]);
+ assertEquals("uuid123", result[3]);
+ assertEquals("", result[4]);
+ }
+
+ @Test
+ void testParseRatisRoleStringRejectsNull() {
+ assertThrows(IllegalArgumentException.class,
+ () -> HddsUtils.parseRatisRoleString(null));
+ }
+
+ @Test
+ void testParseRatisRoleStringRejectsEmpty() {
+ assertThrows(IllegalArgumentException.class,
+ () -> HddsUtils.parseRatisRoleString(""));
+ }
+
+ @Test
+ void testParseRatisRoleStringRejectsTooFewFields() {
+ assertThrows(IllegalArgumentException.class,
+ () -> HddsUtils.parseRatisRoleString("host:9894"));
+ }
+
static List<Arguments> validPaths() {
return Arrays.asList(
Arguments.of("/", "/"),
diff --git
a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestHostAndPort.java
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestHostAndPort.java
index 9be015f5ef9..134e89aa34e 100644
---
a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestHostAndPort.java
+++
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestHostAndPort.java
@@ -42,6 +42,12 @@ public void setAddressRejectsNull() {
assertThrows(NullPointerException.class, () -> address.setAddress(null));
}
+ @Test
+ public void ipv6HostAndPortString() {
+ HostAndPort address = new HostAndPort("2001:db8::1", 9894);
+ assertEquals("[2001:db8::1]:9894", address.getHostAndPortString());
+ }
+
@Test
public void setAddressDoesNotChangeIdentity() throws Exception {
HostAndPort address = new HostAndPort("127.0.0.1", 9861);
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisServerImpl.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisServerImpl.java
index ac9564ca95d..94b381ccdd9 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisServerImpl.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisServerImpl.java
@@ -21,6 +21,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.net.InetAddresses;
import jakarta.annotation.Nullable;
import java.io.IOException;
import java.net.InetAddress;
@@ -28,6 +29,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.OptionalInt;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -289,13 +291,28 @@ public List<String> getRatisRoles() {
LOG.error("SCM Ratis PeerInetAddress {} is unresolvable",
peer.getAddress());
}
- ratisRoles.add((peer.getAddress() == null ? "" :
- peer.getAddress().concat(peer.equals(leader) ?
- ":".concat(RaftProtos.RaftPeerRole.LEADER.toString()) :
- ":".concat(RaftProtos.RaftPeerRole.FOLLOWER.toString()))
- .concat(":".concat(peer.getId().toString()))
- .concat(":".concat(peerInetAddress == null ? "" :
- peerInetAddress.getHostAddress()))));
+ if (peer.getAddress() == null) {
+ ratisRoles.add("");
+ continue;
+ }
+ String host = HddsUtils.getHostName(peer.getAddress()).orElse("");
+ OptionalInt portOpt = HddsUtils.getHostPort(peer.getAddress());
+ if (!portOpt.isPresent()) {
+ LOG.error("SCM Ratis peer address {} has no port, skipping",
+ peer.getAddress());
+ continue;
+ }
+ String normalizedAddress = HddsUtils.getHostPortString(host,
portOpt.getAsInt());
+ String role = peer.equals(leader)
+ ? RaftProtos.RaftPeerRole.LEADER.toString()
+ : RaftProtos.RaftPeerRole.FOLLOWER.toString();
+ String hostIp = "";
+ if (peerInetAddress != null) {
+ hostIp = InetAddresses.toUriString(peerInetAddress);
+ }
+ String roleEntry = normalizedAddress + ":" + role + ":"
+ + peer.getId().toString() + ":" + hostIp;
+ ratisRoles.add(roleEntry);
}
return ratisRoles;
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
index 100c9feb9b6..93df74a6ea7 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
@@ -2190,7 +2190,7 @@ public List<List<String>> getScmRatisRoles() {
List<String> ratisRoles = server.getRatisRoles();
List<List<String>> result = new ArrayList<>();
for (String role : ratisRoles) {
- String[] roleArr = role.split(":");
+ String[] roleArr = HddsUtils.parseRatisRoleString(role);
List<String> scmInfo = new ArrayList<>();
// Host Name
scmInfo.add(roleArr[0]);
diff --git
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisServerImpl.java
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisServerImpl.java
index 6b601868c32..56b99fb8f65 100644
---
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisServerImpl.java
+++
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisServerImpl.java
@@ -19,6 +19,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -27,11 +28,15 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import java.util.List;
import java.util.UUID;
+import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.protocol.RaftGroup;
+import org.apache.ratis.protocol.RaftGroupId;
import org.apache.ratis.protocol.RaftPeer;
import org.apache.ratis.protocol.RaftPeerId;
import org.apache.ratis.server.RaftServer;
@@ -103,4 +108,61 @@ public void testGetLeaderId() throws Exception {
}
}
+ @Test
+ public void testGetRatisRolesWithIPv6() throws Exception {
+ try (
+ MockedConstruction<SecurityConfig> mockedSecurityConfigConstruction =
mockConstruction(SecurityConfig.class);
+ MockedStatic<RaftServer> staticMockedRaftServer =
mockStatic(RaftServer.class);
+ MockedStatic<RatisUtil> staticMockedRatisUtil =
mockStatic(RatisUtil.class);
+ ) {
+ ConfigurationSource conf = mock(ConfigurationSource.class);
+ StorageContainerManager scm = mock(StorageContainerManager.class);
+ when(scm.getClusterId()).thenReturn("CID-" + UUID.randomUUID());
+ SCMHADBTransactionBuffer dbTransactionBuffer =
mock(SCMHADBTransactionBuffer.class);
+
+ RaftServer.Builder raftServerBuilder = mock(RaftServer.Builder.class);
+ when(raftServerBuilder.setServerId(any())).thenReturn(raftServerBuilder);
+
when(raftServerBuilder.setProperties(any())).thenReturn(raftServerBuilder);
+
when(raftServerBuilder.setStateMachineRegistry(any())).thenReturn(raftServerBuilder);
+ when(raftServerBuilder.setOption(any())).thenReturn(raftServerBuilder);
+ when(raftServerBuilder.setGroup(any())).thenReturn(raftServerBuilder);
+
when(raftServerBuilder.setParameters(any())).thenReturn(raftServerBuilder);
+
+ RaftServer raftServer = mock(RaftServer.class);
+ RaftServer.Division division = mock(RaftServer.Division.class);
+ when(raftServer.getDivision(any())).thenReturn(division);
+ when(raftServerBuilder.build()).thenReturn(raftServer);
+
staticMockedRaftServer.when(RaftServer::newBuilder).thenReturn(raftServerBuilder);
+
+ RaftProperties raftProperties = mock(RaftProperties.class);
+ staticMockedRatisUtil.when(() ->
RatisUtil.newRaftProperties(conf)).thenReturn(raftProperties);
+
+ SecurityConfig sc = new SecurityConfig(conf);
+ when(sc.isSecurityEnabled()).thenReturn(false);
+
+ SCMRatisServerImpl scmRatisServer = spy(new SCMRatisServerImpl(conf,
scm, dbTransactionBuffer));
+
+ // IPv6 peer address in the bracketed format that getRatisHostPortStr()
produces
+ RaftPeer ipv6Peer = RaftPeer.newBuilder()
+ .setId(RaftPeerId.valueOf("peer1"))
+ .setAddress("[2001:db8::1]:9894")
+ .build();
+
+ RaftGroup raftGroup = RaftGroup.valueOf(RaftGroupId.randomId(),
ipv6Peer);
+ when(division.getGroup()).thenReturn(raftGroup);
+ doReturn(ipv6Peer).when(scmRatisServer).getLeader();
+
+ List<String> roles = scmRatisServer.getRatisRoles();
+ assertEquals(1, roles.size());
+
+ String roleString = roles.get(0);
+ String[] parsed = HddsUtils.parseRatisRoleString(roleString);
+ assertEquals("2001:db8::1", parsed[0]);
+ assertEquals("9894", parsed[1]);
+ assertEquals("LEADER", parsed[2]);
+ assertEquals("peer1", parsed[3]);
+ assertTrue(!parsed[4].isEmpty(), "hostIP should be resolved");
+ }
+ }
+
}
diff --git
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/SafeModeCheckSubcommand.java
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/SafeModeCheckSubcommand.java
index 1fb97d6e01a..7af5ea3d0c6 100644
---
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/SafeModeCheckSubcommand.java
+++
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/SafeModeCheckSubcommand.java
@@ -18,8 +18,10 @@
package org.apache.hadoop.hdds.scm.cli;
import java.io.IOException;
+import java.net.InetAddress;
import java.util.List;
import java.util.Map;
+import java.util.OptionalInt;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
@@ -104,16 +106,21 @@ private SCMNodeInfo findLeaderNode(ScmClient scmClient)
throws IOException {
try {
List<String> roles = scmClient.getScmRoles();
for (String role : roles) {
- String[] parts = role.split(":");
- if (parts.length < 3 || !"LEADER".equalsIgnoreCase(parts[2])) {
+ String[] parts;
+ try {
+ parts = HddsUtils.parseRatisRoleString(role);
+ } catch (IllegalArgumentException e) {
+ continue;
+ }
+ if (!"LEADER".equalsIgnoreCase(parts[2])) {
continue;
}
String leaderHost = parts[0];
- String leaderIp = parts.length >= 5 ? parts[4] : null;
+ String leaderIp = parts[4];
for (SCMNodeInfo node : nodes) {
- String nodeHost = node.getScmClientAddress().split(":")[0];
+ String nodeHost =
HddsUtils.getHostName(node.getScmClientAddress()).orElse("");
- if (matchesAddress(leaderHost, nodeHost) || (leaderIp != null &&
!leaderIp.isEmpty() &&
+ if (matchesAddress(leaderHost, nodeHost) || (!leaderIp.isEmpty() &&
matchesAddress(leaderIp, nodeHost))) {
return node;
}
@@ -176,8 +183,9 @@ private void queryNode(ScmClient scmClient, ScmNodeTarget
targetScmNode, SCMNode
}
/**
- * Check if the given SCMNodeInfo matches the target address.
- * Tries to match by direct string comparison and by resolved address.
+ * Check if the given addresses match by comparing host portions and ports.
+ * Inputs may be bare hosts or host:port strings. Handles IPv6 equivalence
+ * (e.g. 2001:db8::1 vs 2001:db8:0:0:0:0:0:1) by resolving to InetAddress.
*/
private boolean matchesAddress(String address1, String address2) {
if (address1.equalsIgnoreCase(address2)) {
@@ -185,23 +193,24 @@ private boolean matchesAddress(String address1, String
address2) {
}
try {
- // Parse both addresses into host:port components
- String[] parts1 = address1.split(":", 2);
- String[] parts2 = address2.split(":", 2);
-
- String host1 = parts1[0];
- String host2 = parts2[0];
-
- // Hostnames must match
- if (!host1.equalsIgnoreCase(host2)) {
+ String host1 = HddsUtils.getHostName(address1).orElse(address1);
+ String host2 = HddsUtils.getHostName(address2).orElse(address2);
+
+ boolean hostsMatch = host1.equalsIgnoreCase(host2);
+ if (!hostsMatch) {
+ InetAddress inet1 = InetAddress.getByName(host1);
+ InetAddress inet2 = InetAddress.getByName(host2);
+ hostsMatch = inet1.equals(inet2);
+ }
+ if (!hostsMatch) {
return false;
}
- // If both have ports specified, they must match
- if (parts1.length > 1 && parts2.length > 1) {
- return parts1[1].equals(parts2[1]);
+ OptionalInt port1 = HddsUtils.getHostPort(address1);
+ OptionalInt port2 = HddsUtils.getHostPort(address2);
+ if (port1.isPresent() && port2.isPresent()) {
+ return port1.getAsInt() == port2.getAsInt();
}
-
return true;
} catch (Exception e) {
// If address resolution fails, no match
diff --git
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/scm/GetScmRatisRolesSubcommand.java
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/scm/GetScmRatisRolesSubcommand.java
index da3076306d7..14021c4261e 100644
---
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/scm/GetScmRatisRolesSubcommand.java
+++
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/scm/GetScmRatisRolesSubcommand.java
@@ -17,14 +17,13 @@
package org.apache.hadoop.ozone.admin.scm;
-import static java.lang.System.err;
-
+import com.google.common.net.HostAndPort;
import java.io.IOException;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.hadoop.hdds.scm.cli.ScmSubcommand;
import org.apache.hadoop.hdds.scm.client.ScmClient;
@@ -73,11 +72,18 @@ public void execute(ScmClient scmClient) throws IOException
{
formattingCLIUtils.addHeaders(SCM_ROLES_HEADER);
for (String role : peerRoles) {
- String[] roleItems = role.split(":");
- if (roleItems.length < 2) {
- err.println("Invalid response received for ScmRatisRoles.");
+ try {
+ String[] roleItems = HddsUtils.parseRatisRoleString(role);
+ formattingCLIUtils.addLine(roleItems);
+ } catch (IllegalArgumentException e) {
+ try {
+ HostAndPort hp = HostAndPort.fromString(role);
+ String port = hp.hasPort() ? String.valueOf(hp.getPort()) : "";
+ formattingCLIUtils.addLine(new String[]{hp.getHost(), port, "",
"", ""});
+ } catch (IllegalArgumentException ex) {
+ formattingCLIUtils.addLine(new String[]{role, "", "", "", ""});
+ }
}
- formattingCLIUtils.addLine(roleItems);
}
System.out.println(formattingCLIUtils.render());
} else {
@@ -92,20 +98,25 @@ private Map<String, Map<String, String>> parseScmRoles(
Map<String, Map<String, String>> allRoles = new HashMap<>();
for (String role : peerRoles) {
Map<String, String> roleDetails = new HashMap<>();
- String[] roles = role.split(":");
- if (roles.length < 2) {
- err.println("Invalid response received for ScmRatisRoles.");
- return Collections.emptyMap();
- }
- // In case, there is no ratis, there is no ratis role.
- // This will just print the hostname with ratis port as the address
- roleDetails.put("address", roles[0].concat(":").concat(roles[1]));
- if (roles.length == 5) {
- roleDetails.put("raftPeerRole", roles[2]);
- roleDetails.put("ID", roles[3]);
- roleDetails.put("InetAddress", roles[4]);
+ try {
+ String[] fields = HddsUtils.parseRatisRoleString(role);
+ roleDetails.put("address", HddsUtils.getHostPortString(
+ fields[0], Integer.parseInt(fields[1])));
+ roleDetails.put("raftPeerRole", fields[2]);
+ roleDetails.put("ID", fields[3]);
+ roleDetails.put("InetAddress", fields[4]);
+ allRoles.put(fields[0], roleDetails);
+ } catch (IllegalArgumentException e) {
+ try {
+ HostAndPort hp = HostAndPort.fromString(role);
+ String addr = hp.hasPort() ?
HddsUtils.getHostPortString(hp.getHost(), hp.getPort()) : hp.getHost();
+ roleDetails.put("address", addr);
+ allRoles.put(hp.getHost(), roleDetails);
+ } catch (IllegalArgumentException ex) {
+ roleDetails.put("address", role);
+ allRoles.put(role, roleDetails);
+ }
}
- allRoles.put(roles[0], roleDetails);
}
return allRoles;
}
diff --git
a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/ozone/scm/TestGetScmRatisRolesSubcommand.java
b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/ozone/scm/TestGetScmRatisRolesSubcommand.java
index c622a1ff3d1..52c4a4cda2e 100644
---
a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/ozone/scm/TestGetScmRatisRolesSubcommand.java
+++
b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/ozone/scm/TestGetScmRatisRolesSubcommand.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hdds.scm.client.ScmClient;
+import org.apache.hadoop.hdds.server.JsonUtils;
import org.apache.hadoop.ozone.admin.scm.GetScmRatisRolesSubcommand;
import org.apache.ozone.test.GenericTestUtils;
import org.junit.jupiter.api.Test;
@@ -61,4 +62,68 @@ public void testGetScmHARatisRoles() throws Exception {
}
}
+ @Test
+ public void testGetScmRolesNonRatisShortStringTable() throws Exception {
+ GetScmRatisRolesSubcommand cmd = new GetScmRatisRolesSubcommand();
+ ScmClient client = mock(ScmClient.class);
+ CommandLine c = new CommandLine(cmd);
+ c.parseArgs("--table");
+
+ List<String> result = new ArrayList<>();
+ result.add("host:9894");
+
+ when(client.getScmRoles()).thenAnswer(invocation -> result);
+
+ try (GenericTestUtils.SystemOutCapturer capture =
+ new GenericTestUtils.SystemOutCapturer()) {
+ cmd.execute(client);
+ assertThat(capture.getOutput())
+ .containsPattern("\\|\\s+host\\s+\\|\\s+9894\\s+\\|");
+ }
+ }
+
+ @Test
+ public void testGetScmRolesNonRatisShortStringJson() throws Exception {
+ GetScmRatisRolesSubcommand cmd = new GetScmRatisRolesSubcommand();
+ ScmClient client = mock(ScmClient.class);
+ CommandLine c = new CommandLine(cmd);
+ c.parseArgs("--json");
+
+ List<String> result = new ArrayList<>();
+ result.add("host:9894");
+
+ when(client.getScmRoles()).thenAnswer(invocation -> result);
+
+ try (GenericTestUtils.SystemOutCapturer capture =
+ new GenericTestUtils.SystemOutCapturer()) {
+ cmd.execute(client);
+ assertThat(JsonUtils.readTree(capture.getOutput())).isEqualTo(
+ JsonUtils.readTree("{\"host\":{\"address\":\"host:9894\"}}"));
+ }
+ }
+
+ @Test
+ public void testGetScmHARatisRolesIPv6() throws Exception {
+ GetScmRatisRolesSubcommand cmd = new GetScmRatisRolesSubcommand();
+ ScmClient client = mock(ScmClient.class);
+ CommandLine c = new CommandLine(cmd);
+ c.parseArgs("--table");
+
+ List<String> result = new ArrayList<>();
+
result.add("[2001:db8::1]:9894:LEADER:e428ca07-b2a3-4756-bf9b-a4abb033c7d1:[2001:db8:0:0:0:0:0:1]");
+
result.add("[2001:db8::2]:9894:FOLLOWER:61b1c8e5-da40-4567-8a17-96a0234ba14e:[2001:db8:0:0:0:0:0:2]");
+
+ when(client.getScmRoles()).thenAnswer(invocation -> result);
+
+ try (GenericTestUtils.SystemOutCapturer capture =
+ new GenericTestUtils.SystemOutCapturer()) {
+ cmd.execute(client);
+ assertThat(capture.getOutput()).contains("2001:db8::1");
+ assertThat(capture.getOutput()).contains("9894");
+ assertThat(capture.getOutput()).contains("LEADER");
+
assertThat(capture.getOutput()).contains("e428ca07-b2a3-4756-bf9b-a4abb033c7d1");
+ assertThat(capture.getOutput()).contains("FOLLOWER");
+ }
+ }
+
}
diff --git
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/StorageContainerServiceProviderImpl.java
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/StorageContainerServiceProviderImpl.java
index 96aca5feed7..be3d917f42c 100644
---
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/StorageContainerServiceProviderImpl.java
+++
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/StorageContainerServiceProviderImpl.java
@@ -31,6 +31,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import
org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
@@ -138,7 +139,7 @@ public DBCheckpoint getSCMDBSnapshot() {
try {
List<String> ratisRoles = scmClient.getScmInfo().getPeerRoles();
for (String ratisRole : ratisRoles) {
- String[] role = ratisRole.split(":");
+ String[] role = HddsUtils.parseRatisRoleString(ratisRole);
if (role[2].equals(RaftProtos.RaftPeerRole.LEADER.toString())) {
String hostAddress = role[4].trim();
int grpcPort = configuration.getInt(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]