From b61860c76e9cf1eebfb7d29dc4f4420955f62bb4 Mon Sep 17 00:00:00 2001 From: wolfenha Date: Thu, 10 Dec 2020 11:19:40 -0500 Subject: [PATCH] Fix nodetool ring, status output when DNS resolution or port printing are in use Patch by Adam Holmberg and Scott Wolfenhaut; reviewed by Berenguer Blasi, Brandon Williams and Ekaterina Dimitrova for CASSANDRA-16283 --- CHANGES.txt | 1 + .../cassandra/locator/SimpleSnitch.java | 3 +- .../cassandra/service/StorageService.java | 2 +- .../tools/nodetool/HostStatWithPort.java | 2 +- .../apache/cassandra/tools/nodetool/Ring.java | 240 +++++------------- .../tools/nodetool/SetHostStatWithPort.java | 2 +- .../cassandra/tools/nodetool/Status.java | 31 +-- .../distributed/test/NodeToolTest.java | 6 +- .../cassandra/tools/nodetool/RingTest.java} | 139 +++++----- .../cassandra/tools/nodetool/StatusTest.java | 93 +++++++ .../stats/NodetoolTableStatsTest.java | 12 - 11 files changed, 252 insertions(+), 279 deletions(-) rename test/{distributed/org/apache/cassandra/distributed/test/NodeToolRingTest.java => unit/org/apache/cassandra/tools/nodetool/RingTest.java} (58%) create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/StatusTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 92b0e83311..ba23707b52 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.0-beta5 + * Fix nodetool ring, status output when DNS resolution or port printing are in use (CASSANDRA-16283) * Upgrade Jacoco to 0.8.6 (for Java 11 support) (CASSANDRA-16365) * Move excessive repair debug loggings to trace level (CASSANDRA-16406) * Restore validation of each message's protocol version (CASSANDRA-16374) diff --git a/src/java/org/apache/cassandra/locator/SimpleSnitch.java b/src/java/org/apache/cassandra/locator/SimpleSnitch.java index f26375a1d7..f9bf215258 100644 --- a/src/java/org/apache/cassandra/locator/SimpleSnitch.java +++ b/src/java/org/apache/cassandra/locator/SimpleSnitch.java @@ -25,10 +25,11 @@ package org.apache.cassandra.locator; public class SimpleSnitch extends AbstractEndpointSnitch { public static final String DATA_CENTER_NAME = "datacenter1"; + public static final String RACK_NAME = "rack1"; public String getRack(InetAddressAndPort endpoint) { - return "rack1"; + return RACK_NAME; } public String getDatacenter(InetAddressAndPort endpoint) diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index 1b180c0f4f..da0c8ea1b6 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -5170,7 +5170,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { LinkedHashMap result = getEffectiveOwnership(keyspace); LinkedHashMap asStrings = new LinkedHashMap<>(); - result.entrySet().stream().forEachOrdered(entry -> asStrings.put(entry.getKey().toString(), entry.getValue())); + result.entrySet().stream().forEachOrdered(entry -> asStrings.put(entry.getKey().getHostAddressAndPort(), entry.getValue())); return asStrings; } diff --git a/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java b/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java index 4849fd1ffe..9cff725c51 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java +++ b/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java @@ -42,6 +42,6 @@ public class HostStatWithPort extends HostStat return resolveIp ? endpointWithPort.address.getHostName() + ':' + endpointWithPort.port : - endpointWithPort.toString(); + endpointWithPort.getHostAddressAndPort(); } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/Ring.java b/src/java/org/apache/cassandra/tools/nodetool/Ring.java index 134c85df87..ccc7912da8 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Ring.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Ring.java @@ -23,7 +23,6 @@ import io.airlift.airline.Command; import io.airlift.airline.Option; import java.io.PrintStream; -import java.net.InetAddress; import java.net.UnknownHostException; import java.text.DecimalFormat; import java.util.ArrayList; @@ -34,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.apache.cassandra.locator.EndpointSnitchInfoMBean; import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.NodeTool; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; @@ -49,192 +49,76 @@ public class Ring extends NodeToolCmd @Option(title = "resolve_ip", name = {"-r", "--resolve-ip"}, description = "Show node domain names instead of IPs") private boolean resolveIp = false; + private PrintStream out; + private EndpointSnitchInfoMBean epSnitchInfo; + private Collection liveNodes, deadNodes, joiningNodes, leavingNodes, movingNodes; + private Map loadMap; + @Override public void execute(NodeProbe probe) { - PrintStream out = probe.output().out; + out = probe.output().out; + liveNodes = probe.getLiveNodes(true); + deadNodes = probe.getUnreachableNodes(true); + joiningNodes = probe.getJoiningNodes(true); + leavingNodes = probe.getLeavingNodes(true); + movingNodes = probe.getMovingNodes(true); + loadMap = probe.getLoadMap(true); + epSnitchInfo = probe.getEndpointSnitchInfoProxy(); + + Map tokensToEndpoints = probe.getTokenToEndpointMap(true); + LinkedHashMultimap endpointsToTokens = LinkedHashMultimap.create(); + boolean haveVnodes = false; + for (Map.Entry entry : tokensToEndpoints.entrySet()) + { + haveVnodes |= endpointsToTokens.containsKey(entry.getValue()); + endpointsToTokens.put(entry.getValue(), entry.getKey()); + } + + int maxAddressLength = Collections.max(endpointsToTokens.keys(), + Comparator.comparingInt(String::length)).length(); + + String formatPlaceholder = "%%-%ds %%-12s%%-7s%%-8s%%-16s%%-20s%%-44s%%n"; + String format = format(formatPlaceholder, maxAddressLength); + + StringBuilder errors = new StringBuilder(); + boolean showEffectiveOwnership = true; + + // Calculate per-token ownership of the ring + Map ownerships; try { - Map tokensToEndpoints = probe.getTokenToEndpointMap(printPort); - LinkedHashMultimap endpointsToTokens = LinkedHashMultimap.create(); - boolean haveVnodes = false; - for (Map.Entry entry : tokensToEndpoints.entrySet()) - { - haveVnodes |= endpointsToTokens.containsKey(entry.getValue()); - endpointsToTokens.put(entry.getValue(), entry.getKey()); - } - - int maxAddressLength = Collections.max(endpointsToTokens.keys(), new Comparator() - { - @Override - public int compare(String first, String second) - { - return Integer.compare(first.length(), second.length()); - } - }).length(); - - String formatPlaceholder = "%%-%ds %%-12s%%-7s%%-8s%%-16s%%-20s%%-44s%%n"; - String format = format(formatPlaceholder, maxAddressLength); - - StringBuilder errors = new StringBuilder(); - boolean showEffectiveOwnership = true; - - if (printPort) - { - // Calculate per-token ownership of the ring - Map ownerships; - try - { - ownerships = probe.effectiveOwnershipWithPort(keyspace); - } - catch (IllegalStateException ex) - { - ownerships = probe.getOwnershipWithPort(); - errors.append("Note: ").append(ex.getMessage()).append("%n"); - showEffectiveOwnership = false; - } - catch (IllegalArgumentException ex) - { - out.printf("%nError: %s%n", ex.getMessage()); - return; - } - - - out.println(); - for (Entry entry : NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) - printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership); - - if (haveVnodes) - { - out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node."); - out.println(" To view status related info of a node use \"nodetool status\" instead.\n"); - } - - out.printf("%n " + errors.toString()); - } - else - { - // Calculate per-token ownership of the ring - Map ownerships; - try - { - ownerships = probe.effectiveOwnership(keyspace); - } - catch (IllegalStateException ex) - { - ownerships = probe.getOwnership(); - errors.append("Note: ").append(ex.getMessage()).append("%n"); - showEffectiveOwnership = false; - } - catch (IllegalArgumentException ex) - { - out.printf("%nError: %s%n", ex.getMessage()); - return; - } - - - out.println(); - for (Entry entry : NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) - printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership); - - if (haveVnodes) - { - out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node."); - out.println(" To view status related info of a node use \"nodetool status\" instead.\n"); - } - - out.printf("%n " + errors.toString()); - } - } catch (Exception e) - { - e.printStackTrace(); - throw e; + ownerships = probe.effectiveOwnershipWithPort(keyspace); } - } - - private void printDc(NodeProbe probe, String format, - String dc, - LinkedHashMultimap endpointsToTokens, - SetHostStat hoststats,boolean showEffectiveOwnership) - { - PrintStream out = probe.output().out; - Collection liveNodes = probe.getLiveNodes(false); - Collection deadNodes = probe.getUnreachableNodes(false); - Collection joiningNodes = probe.getJoiningNodes(false); - Collection leavingNodes = probe.getLeavingNodes(false); - Collection movingNodes = probe.getMovingNodes(false); - Map loadMap = probe.getLoadMap(false); - - out.println("Datacenter: " + dc); - out.println("=========="); - - // get the total amount of replicas for this dc and the last token in this dc's ring - List tokens = new ArrayList<>(); - String lastToken = ""; - - for (HostStat stat : hoststats) + catch (IllegalStateException ex) { - tokens.addAll(endpointsToTokens.get(stat.endpoint.getHostAddress())); - lastToken = tokens.get(tokens.size() - 1); + ownerships = probe.getOwnershipWithPort(); + errors.append("Note: ").append(ex.getMessage()).append("%n"); + showEffectiveOwnership = false; + } + catch (IllegalArgumentException ex) + { + out.printf("%nError: %s%n", ex.getMessage()); + return; } - out.printf(format, "Address", "Rack", "Status", "State", "Load", "Owns", "Token"); - - if (hoststats.size() > 1) - out.printf(format, "", "", "", "", "", "", lastToken); - else - out.println(); - - for (HostStat stat : hoststats) - { - String endpoint = stat.endpoint.getHostAddress(); - String rack; - try - { - rack = probe.getEndpointSnitchInfoProxy().getRack(endpoint); - } - catch (UnknownHostException e) - { - rack = "Unknown"; - } - - String status = liveNodes.contains(endpoint) - ? "Up" - : deadNodes.contains(endpoint) - ? "Down" - : "?"; - - String state = "Normal"; - - if (joiningNodes.contains(endpoint)) - state = "Joining"; - else if (leavingNodes.contains(endpoint)) - state = "Leaving"; - else if (movingNodes.contains(endpoint)) - state = "Moving"; - - String load = loadMap.containsKey(endpoint) - ? loadMap.get(endpoint) - : "?"; - String owns = stat.owns != null && showEffectiveOwnership? new DecimalFormat("##0.00%").format(stat.owns) : "?"; - out.printf(format, stat.ipOrDns(), rack, status, state, load, owns, stat.token); - } out.println(); + for (Entry entry : NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) + printDc(format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership); + + if (haveVnodes) + { + out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node."); + out.println(" To view status related info of a node use \"nodetool status\" instead.\n"); + } + + out.printf("%n " + errors.toString()); } - private void printDc(NodeProbe probe, String format, - String dc, + private void printDc(String format, String dc, LinkedHashMultimap endpointsToTokens, - SetHostStatWithPort hoststats,boolean showEffectiveOwnership) + SetHostStatWithPort hoststats, boolean showEffectiveOwnership) { - PrintStream out = probe.output().out; - Collection liveNodes = probe.getLiveNodes(true); - Collection deadNodes = probe.getUnreachableNodes(true); - Collection joiningNodes = probe.getJoiningNodes(true); - Collection leavingNodes = probe.getLeavingNodes(true); - Collection movingNodes = probe.getMovingNodes(true); - Map loadMap = probe.getLoadMap(true); - out.println("Datacenter: " + dc); out.println("=========="); @@ -244,9 +128,7 @@ public class Ring extends NodeToolCmd for (HostStatWithPort stat : hoststats) { - // Remove extra '/' from address - String addressNPort = stat.endpointWithPort.toString().replaceAll("^/", ""); - tokens.addAll(endpointsToTokens.get(addressNPort)); + tokens.addAll(endpointsToTokens.get(stat.endpointWithPort.getHostAddressAndPort())); lastToken = tokens.get(tokens.size() - 1); } @@ -259,11 +141,11 @@ public class Ring extends NodeToolCmd for (HostStatWithPort stat : hoststats) { - String endpoint = stat.endpoint.toString(); + String endpoint = stat.endpointWithPort.getHostAddressAndPort(); String rack; try { - rack = probe.getEndpointSnitchInfoProxy().getRack(endpoint); + rack = epSnitchInfo.getRack(endpoint); } catch (UnknownHostException e) { @@ -289,7 +171,7 @@ public class Ring extends NodeToolCmd ? loadMap.get(endpoint) : "?"; String owns = stat.owns != null && showEffectiveOwnership? new DecimalFormat("##0.00%").format(stat.owns) : "?"; - out.printf(format, stat.ipOrDns(), rack, status, state, load, owns, stat.token); + out.printf(format, stat.ipOrDns(printPort), rack, status, state, load, owns, stat.token); } out.println(); } diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java b/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java index 6ac02583b6..d2abe6e678 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java @@ -50,7 +50,7 @@ public class SetHostStatWithPort implements Iterable public void add(String token, String host, Map ownerships) throws UnknownHostException { InetAddressAndPort endpoint = InetAddressAndPort.getByName(host); - Float owns = ownerships.get(endpoint.toString()); + Float owns = ownerships.get(endpoint.getHostAddressAndPort()); hostStats.add(new HostStatWithPort(token, endpoint, resolveIp, owns)); } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/Status.java b/src/java/org/apache/cassandra/tools/nodetool/Status.java index 418493915e..369affc9bc 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Status.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Status.java @@ -57,14 +57,14 @@ public class Status extends NodeToolCmd public void execute(NodeProbe probe) { PrintStream out = probe.output().out; - joiningNodes = probe.getJoiningNodes(printPort); - leavingNodes = probe.getLeavingNodes(printPort); - movingNodes = probe.getMovingNodes(printPort); - loadMap = probe.getLoadMap(printPort); - Map tokensToEndpoints = probe.getTokenToEndpointMap(printPort); - liveNodes = probe.getLiveNodes(printPort); - unreachableNodes = probe.getUnreachableNodes(printPort); - hostIDMap = probe.getHostIdMap(printPort); + joiningNodes = probe.getJoiningNodes(true); + leavingNodes = probe.getLeavingNodes(true); + movingNodes = probe.getMovingNodes(true); + loadMap = probe.getLoadMap(true); + Map tokensToEndpoints = probe.getTokenToEndpointMap(true); + liveNodes = probe.getLiveNodes(true); + unreachableNodes = probe.getUnreachableNodes(true); + hostIDMap = probe.getHostIdMap(true); epSnitchInfo = probe.getEndpointSnitchInfoProxy(); StringBuilder errors = new StringBuilder(); @@ -102,14 +102,13 @@ public class Status extends NodeToolCmd ArrayListMultimap hostToTokens = ArrayListMultimap.create(); for (HostStatWithPort stat : dc.getValue()) - hostToTokens.put(stat.ipOrDns(printPort), stat); + hostToTokens.put(stat.endpointWithPort.getHostAddressAndPort(), stat); for (String endpoint : hostToTokens.keySet()) { Float owns = ownerships.get(endpoint); List tokens = hostToTokens.get(endpoint); - addNode(endpoint, owns, tokens.get(0).ipOrDns(printPort), tokens.get(0).token, tokens.size(), - hasEffectiveOwns, tableBuilder); + addNode(endpoint, owns, tokens.get(0), tokens.size(), hasEffectiveOwns, tableBuilder); } } @@ -146,10 +145,10 @@ public class Status extends NodeToolCmd tableBuilder.add("--", "Address", "Load", "Tokens", owns, "Host ID", "Rack"); } - private void addNode(String endpoint, Float owns, String epDns, String token, int size, boolean hasEffectiveOwns, + private void addNode(String endpoint, Float owns, HostStatWithPort hostStat, int size, boolean hasEffectiveOwns, TableBuilder tableBuilder) { - String status, state, load, strOwns, hostID, rack; + String status, state, load, strOwns, hostID, rack, epDns; if (liveNodes.contains(endpoint)) status = "U"; else if (unreachableNodes.contains(endpoint)) status = "D"; else status = "?"; @@ -166,14 +165,16 @@ public class Status extends NodeToolCmd try { rack = epSnitchInfo.getRack(endpoint); - } catch (UnknownHostException e) + } + catch (UnknownHostException e) { throw new RuntimeException(e); } + epDns = hostStat.ipOrDns(printPort); if (isTokenPerNode) { - tableBuilder.add(statusAndState, epDns, load, strOwns, hostID, token, rack); + tableBuilder.add(statusAndState, epDns, load, strOwns, hostID, hostStat.token, rack); } else { diff --git a/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java b/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java index 3e7f012f6c..7c760808ab 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java @@ -52,7 +52,7 @@ public class NodeToolTest extends TestBaseImpl } @Test - public void testCommands() throws Throwable + public void testCommands() { assertEquals(0, NODE.nodetool("help")); assertEquals(0, NODE.nodetool("flush")); @@ -60,11 +60,11 @@ public class NodeToolTest extends TestBaseImpl } @Test - public void testCaptureConsoleOutput() throws Throwable + public void testCaptureConsoleOutput() { NodeToolResult ringResult = NODE.nodetoolResult("ring"); ringResult.asserts().stdoutContains("Datacenter: datacenter0"); - ringResult.asserts().stdoutContains("127.0.0.1 rack0 Up Normal"); + ringResult.asserts().stdoutContains("127.0.0.1 rack0 Up Normal"); assertEquals("Non-empty error output", "", ringResult.getStderr()); } diff --git a/test/distributed/org/apache/cassandra/distributed/test/NodeToolRingTest.java b/test/unit/org/apache/cassandra/tools/nodetool/RingTest.java similarity index 58% rename from test/distributed/org/apache/cassandra/distributed/test/NodeToolRingTest.java rename to test/unit/org/apache/cassandra/tools/nodetool/RingTest.java index 74f4275209..83771f6314 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/NodeToolRingTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/RingTest.java @@ -16,44 +16,96 @@ * limitations under the License. */ -package org.apache.cassandra.distributed.test; +package org.apache.cassandra.tools.nodetool; -import java.io.IOException; import java.util.Arrays; -import org.junit.AfterClass; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; -import org.apache.cassandra.distributed.api.ICluster; +import org.apache.cassandra.OrderedJUnit4ClassRunner; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.locator.SimpleSnitch; +import org.apache.cassandra.service.StorageService; import org.apache.cassandra.tools.ToolRunner; -import org.apache.cassandra.tools.ToolRunner.ToolResult; +import org.apache.cassandra.utils.FBUtilities; import org.assertj.core.api.Assertions; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.matchesPattern; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -public class NodeToolRingTest extends TestBaseImpl +@RunWith(OrderedJUnit4ClassRunner.class) +public class RingTest extends CQLTester { - private static ICluster cluster; + private static String token; - @Before - public void setupEnv() throws IOException + @BeforeClass + public static void setup() throws Exception { - if (cluster == null) - cluster = init(builder().withNodes(1).start()); + StorageService.instance.initServer(); + token = StorageService.instance.getTokens().get(0); + startJMXServer(); } - @AfterClass - public static void teardownEnv() throws Exception + /** + * Validate output, making sure the table mappings work with various host-modifying arguments in use. + */ + @Test + public void testRingOutput() { - cluster.close(); + final HostStatWithPort host = new HostStatWithPort(null, FBUtilities.getBroadcastAddressAndPort(), + false, null); + validateRingOutput(host.ipOrDns(false), + "ring"); + Arrays.asList("-pp", "--print-port").forEach(arg -> { + validateRingOutput(host.ipOrDns(true), + "-pp", "ring"); + }); + + final HostStatWithPort hostResolved = new HostStatWithPort(null, FBUtilities.getBroadcastAddressAndPort(), + true, null); + Arrays.asList("-r", "--resolve-ip").forEach(arg -> { + validateRingOutput(hostResolved.ipOrDns(false), + "ring", "-r"); + }); + validateRingOutput(hostResolved.ipOrDns(true), + "-pp", "ring", "-r"); + } + + private void validateRingOutput(String hostForm, String... args) + { + ToolRunner.ToolResult nodetool = ToolRunner.invokeNodetool(args); + nodetool.assertOnCleanExit(); + /* + Datacenter: datacenter1 + ========== + Address Rack Status State Load Owns Token + + 127.0.0.1 rack1 Up Normal 45.71 KiB 100.00% 4652409154190094022 + + */ + String[] lines = nodetool.getStdout().split("\\R"); + assertThat(lines[1].trim(), endsWith(SimpleSnitch.DATA_CENTER_NAME)); + assertThat(lines[3], matchesPattern("Address *Rack *Status *State *Load *Owns *Token *")); + String hostRing = lines[lines.length-4].trim(); // this command has a couple extra newlines and an empty error message at the end. Not messing with it. + assertThat(hostRing, startsWith(hostForm)); + assertThat(hostRing, containsString(SimpleSnitch.RACK_NAME)); + assertThat(hostRing, containsString("Up")); + assertThat(hostRing, containsString("Normal")); + assertThat(hostRing, matchesPattern(".*\\d+\\.\\d+ KiB.*")); + assertThat(hostRing, matchesPattern(".*\\d+\\.\\d+%.*")); + assertThat(hostRing, endsWith(token)); + assertThat(hostRing, not(containsString("?"))); } @Test public void testWrongArgFailsAndPrintsHelp() { - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "--wrongarg", "ring"); + ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("--wrongarg", "ring"); Assertions.assertThat(tool.getStdout()).containsIgnoringCase("nodetool help"); assertEquals(1, tool.getExitCode()); assertTrue(tool.getCleanedStderr().isEmpty()); @@ -64,8 +116,8 @@ public class NodeToolRingTest extends TestBaseImpl { // If you added, modified options or help, please update docs if necessary - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "help", "ring"); - + ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "ring"); + String help = "NAME\n" + " nodetool ring - Print information about the token ring\n" + "\n" + "SYNOPSIS\n" @@ -110,63 +162,18 @@ public class NodeToolRingTest extends TestBaseImpl Assertions.assertThat(tool.getStdout()).isEqualTo(help); } - @Test - public void testRing() - { - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "ring"); - - Assertions.assertThat(tool.getStdout()) - .contains("Datacenter: datacenter0") - .contains("Address Rack Status State Load Owns Token") - .contains("127.0.0.1 rack0 Up Normal") - .contains("100.00% 9223372036854775807"); - assertEquals(0, tool.getExitCode()); - assertTrue(tool.getCleanedStderr().isEmpty()); - } - - @Test - public void testRingPrintPort() - { - Arrays.asList("-pp", "--print-port").forEach(arg -> { - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), arg, "ring"); - Assertions.assertThat(tool.getStdout()) - .contains("Datacenter: datacenter0") - .contains("Address Rack Status State Load Owns Token") - .contains("Unknown") - .contains("100.00% 9223372036854775807"); - assertEquals(0, tool.getExitCode()); - assertTrue(tool.getCleanedStderr().isEmpty()); - }); - } - - @Test - public void testRingResolve() - { - Arrays.asList("-r", "--resolve-ip").forEach(arg -> { - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "ring", arg); - Assertions.assertThat(tool.getStdout()) - .contains("Datacenter: datacenter0") - .contains("Address Rack Status State Load Owns Token") - .contains("localhost") - .contains("rack0 Up Normal") - .contains("100.00% 9223372036854775807"); - assertEquals(0, tool.getExitCode()); - assertTrue(tool.getCleanedStderr().isEmpty()); - }); - } - @Test public void testRingKeyspace() { // Bad KS - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "ring", "mockks"); + ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("ring", "mockks"); Assertions.assertThat(tool.getStdout()).contains("The keyspace mockks, does not exist"); assertEquals(0, tool.getExitCode()); assertTrue(tool.getCleanedStderr().isEmpty()); // Good KS - tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "ring", "system_schema"); - Assertions.assertThat(tool.getStdout()).contains("Datacenter: datacenter0"); + tool = ToolRunner.invokeNodetool("ring", "system_schema"); + Assertions.assertThat(tool.getStdout()).contains("Datacenter: datacenter1"); assertEquals(0, tool.getExitCode()); assertTrue(tool.getCleanedStderr().isEmpty()); } diff --git a/test/unit/org/apache/cassandra/tools/nodetool/StatusTest.java b/test/unit/org/apache/cassandra/tools/nodetool/StatusTest.java new file mode 100644 index 0000000000..a7f939c559 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/StatusTest.java @@ -0,0 +1,93 @@ +/* + * 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.cassandra.tools.nodetool; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.apache.cassandra.OrderedJUnit4ClassRunner; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.locator.SimpleSnitch; +import org.apache.cassandra.service.StorageService; +import org.apache.cassandra.tools.ToolRunner; +import org.apache.cassandra.utils.FBUtilities; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.matchesPattern; +import static org.junit.Assert.assertThat; + +@RunWith(OrderedJUnit4ClassRunner.class) +public class StatusTest extends CQLTester +{ + private static String localHostId; + private static String token; + + @BeforeClass + public static void setup() throws Exception + { + StorageService.instance.initServer(); + localHostId = StorageService.instance.getLocalHostId(); + token = StorageService.instance.getTokens().get(0); + startJMXServer(); + } + + /** + * Validate output, making sure the table mappings work with various host-modifying arguments in use. + */ + @Test + public void testStatusOutput() + { + HostStatWithPort host = new HostStatWithPort(null, FBUtilities.getBroadcastAddressAndPort(), false, null); + validateStatusOutput(host.ipOrDns(false), + "status"); + validateStatusOutput(host.ipOrDns(true), + "-pp", "status"); + host = new HostStatWithPort(null, FBUtilities.getBroadcastAddressAndPort(), true, null); + validateStatusOutput(host.ipOrDns(false), + "status", "-r"); + validateStatusOutput(host.ipOrDns(true), + "-pp", "status", "-r"); + } + + private void validateStatusOutput(String hostForm, String... args) + { + ToolRunner.ToolResult nodetool = ToolRunner.invokeNodetool(args); + nodetool.assertOnCleanExit(); + /* + Datacenter: datacenter1 + ======================= + Status=Up/Down + |/ State=Normal/Leaving/Joining/Moving + -- Address Load Owns (effective) Host ID Token Rack + UN localhost 45.71 KiB 100.0% 0b1b5e91-ad3b-444e-9c24-50578486978a 1849950853373272258 rack1 + */ + String[] lines = nodetool.getStdout().split("\\R"); + assertThat(lines[0].trim(), endsWith(SimpleSnitch.DATA_CENTER_NAME)); + String hostStatus = lines[lines.length-1].trim(); + assertThat(hostStatus, startsWith("UN")); + assertThat(hostStatus, containsString(hostForm)); + assertThat(hostStatus, matchesPattern(".*\\d+\\.\\d+ KiB.*")); + assertThat(hostStatus, matchesPattern(".*\\d+\\.\\d+%.*")); + assertThat(hostStatus, containsString(localHostId)); + assertThat(hostStatus, containsString(token)); + assertThat(hostStatus, endsWith(SimpleSnitch.RACK_NAME)); + assertThat(hostStatus, not(containsString("?"))); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java index 8edf2e0cc1..528a6ca5b3 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.tools.nodetool.stats; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -27,7 +26,6 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; -import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,7 +33,6 @@ import org.junit.runner.RunWith; import org.apache.cassandra.OrderedJUnit4ClassRunner; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.service.StorageService; -import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.ToolRunner; import org.apache.cassandra.tools.ToolRunner.ToolResult; import org.assertj.core.api.Assertions; @@ -49,20 +46,11 @@ import static org.junit.Assert.assertTrue; @RunWith(OrderedJUnit4ClassRunner.class) public class NodetoolTableStatsTest extends CQLTester { - private static NodeProbe probe; - @BeforeClass public static void setup() throws Exception { StorageService.instance.initServer(); startJMXServer(); - probe = new NodeProbe(jmxHost, jmxPort); - } - - @AfterClass - public static void teardown() throws IOException - { - probe.close(); } @Test