You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ed...@apache.org on 2021/02/03 14:37:22 UTC

[cassandra] branch trunk updated: 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

This is an automated email from the ASF dual-hosted git repository.

edimitrova pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new b61860c  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
b61860c is described below

commit b61860c76e9cf1eebfb7d29dc4f4420955f62bb4
Author: wolfenha <wo...@adobe.com>
AuthorDate: Thu Dec 10 11:19:40 2020 -0500

    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 +
 .../org/apache/cassandra/locator/SimpleSnitch.java |   3 +-
 .../apache/cassandra/service/StorageService.java   |   2 +-
 .../cassandra/tools/nodetool/HostStatWithPort.java |   2 +-
 .../org/apache/cassandra/tools/nodetool/Ring.java  | 228 +++++----------------
 .../tools/nodetool/SetHostStatWithPort.java        |   2 +-
 .../apache/cassandra/tools/nodetool/Status.java    |  31 +--
 .../cassandra/distributed/test/NodeToolTest.java   |   6 +-
 .../apache/cassandra/tools/nodetool/RingTest.java} | 139 +++++++------
 .../cassandra/tools/nodetool/StatusTest.java       |  93 +++++++++
 .../nodetool/stats/NodetoolTableStatsTest.java     |  12 --
 11 files changed, 246 insertions(+), 273 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 92b0e83..ba23707 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 f26375a..f9bf215 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 1b180c0..da0c8ea 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<InetAddressAndPort, Float> result = getEffectiveOwnership(keyspace);
         LinkedHashMap<String, Float> 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 4849fd1..9cff725 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 134c85d..ccc7912 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<String> liveNodes, deadNodes, joiningNodes, leavingNodes, movingNodes;
+    private Map<String, String> loadMap;
+
     @Override
     public void execute(NodeProbe probe)
     {
-        PrintStream out = probe.output().out;
-        try
+        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<String, String> tokensToEndpoints = probe.getTokenToEndpointMap(true);
+        LinkedHashMultimap<String, String> endpointsToTokens = LinkedHashMultimap.create();
+        boolean haveVnodes = false;
+        for (Map.Entry<String, String> entry : tokensToEndpoints.entrySet())
         {
-            Map<String, String> tokensToEndpoints = probe.getTokenToEndpointMap(printPort);
-            LinkedHashMultimap<String, String> endpointsToTokens = LinkedHashMultimap.create();
-            boolean haveVnodes = false;
-            for (Map.Entry<String, String> entry : tokensToEndpoints.entrySet())
-            {
-                haveVnodes |= endpointsToTokens.containsKey(entry.getValue());
-                endpointsToTokens.put(entry.getValue(), entry.getKey());
-            }
-
-            int maxAddressLength = Collections.max(endpointsToTokens.keys(), new Comparator<String>()
-            {
-                @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<String, Float> 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<String, SetHostStatWithPort> 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<InetAddress, Float> 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;
-                }
+            haveVnodes |= endpointsToTokens.containsKey(entry.getValue());
+            endpointsToTokens.put(entry.getValue(), entry.getKey());
+        }
 
+        int maxAddressLength = Collections.max(endpointsToTokens.keys(),
+                                               Comparator.comparingInt(String::length)).length();
 
-                out.println();
-                for (Entry<String, SetHostStat> entry : NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships).entrySet())
-                    printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership);
+        String formatPlaceholder = "%%-%ds  %%-12s%%-7s%%-8s%%-16s%%-20s%%-44s%%n";
+        String format = format(formatPlaceholder, maxAddressLength);
 
-                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");
-                }
+        StringBuilder errors = new StringBuilder();
+        boolean showEffectiveOwnership = true;
 
-                out.printf("%n  " + errors.toString());
-            }
-        } catch (Exception e)
+        // Calculate per-token ownership of the ring
+        Map<String, Float> ownerships;
+        try
         {
-            e.printStackTrace();
-            throw e;
+            ownerships = probe.effectiveOwnershipWithPort(keyspace);
         }
-    }
-
-    private void printDc(NodeProbe probe, String format,
-                         String dc,
-                         LinkedHashMultimap<String, String> endpointsToTokens,
-                         SetHostStat hoststats,boolean showEffectiveOwnership)
-    {
-        PrintStream out = probe.output().out;
-        Collection<String> liveNodes = probe.getLiveNodes(false);
-        Collection<String> deadNodes = probe.getUnreachableNodes(false);
-        Collection<String> joiningNodes = probe.getJoiningNodes(false);
-        Collection<String> leavingNodes = probe.getLeavingNodes(false);
-        Collection<String> movingNodes = probe.getMovingNodes(false);
-        Map<String, String> 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<String> 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;
         }
-
-        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)
+        catch (IllegalArgumentException ex)
         {
-            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";
+            out.printf("%nError: %s%n", ex.getMessage());
+            return;
+        }
 
-            if (joiningNodes.contains(endpoint))
-                state = "Joining";
-            else if (leavingNodes.contains(endpoint))
-                state = "Leaving";
-            else if (movingNodes.contains(endpoint))
-                state = "Moving";
+        out.println();
+        for (Entry<String, SetHostStatWithPort> entry : NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships).entrySet())
+            printDc(format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership);
 
-            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);
+        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.println();
+
+        out.printf("%n  " + errors.toString());
     }
 
-    private void printDc(NodeProbe probe, String format,
-                         String dc,
+    private void printDc(String format, String dc,
                          LinkedHashMultimap<String, String> endpointsToTokens,
-                         SetHostStatWithPort hoststats,boolean showEffectiveOwnership)
+                         SetHostStatWithPort hoststats, boolean showEffectiveOwnership)
     {
-        PrintStream out = probe.output().out;
-        Collection<String> liveNodes = probe.getLiveNodes(true);
-        Collection<String> deadNodes = probe.getUnreachableNodes(true);
-        Collection<String> joiningNodes = probe.getJoiningNodes(true);
-        Collection<String> leavingNodes = probe.getLeavingNodes(true);
-        Collection<String> movingNodes = probe.getMovingNodes(true);
-        Map<String, String> 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 6ac0258..d2abe6e 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<HostStatWithPort>
     public void add(String token, String host, Map<String, Float> 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 4184939..369affc 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<String, String> 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<String, String> 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<String, HostStatWithPort> 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<HostStatWithPort> 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 3e7f012..7c76080 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 74f4275..83771f6 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()
+    {
+        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)
     {
-        cluster.close();
+        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"
@@ -111,62 +163,17 @@ public class NodeToolRingTest extends TestBaseImpl
     }
 
     @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 0000000..a7f939c
--- /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 8edf2e0..528a6ca 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


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org