Add required -f option to nodetool verify

Patch by Josh McKenzie; reviewed by Marcus Eriksson, Berenguer Blasi, and Caleb Rackliffe for CASSANDRA-17017

Co-authored by Josh McKenzie <jmckenzie@apache.org>
Co-authored by Chris Lohfink <clohfink@apple.com>
This commit is contained in:
Josh McKenzie 2021-09-30 12:10:46 -04:00
parent a64e218d0f
commit 10103d353c
11 changed files with 259 additions and 57 deletions

View File

@ -1,4 +1,5 @@
4.1
* Add required -f option to use nodetool verify and standalone sstableverify (CASSANDRA-17017)
* Add support for UUID based sstable generation identifiers (CASSANDRA-17048)
* Log largest memtable flush at info instead of debug (CASSANDRA-17472)
* Add native transport rate limiter options to example cassandra.yaml, and expose metric for dispatch rate (CASSANDRA-17423)

View File

@ -83,11 +83,11 @@ New features
mechanism (see below) and with some minor modifications to applications using LWTs.
The new implementation may be enabled at any time by setting paxos_variant: v2, and disabled by setting to v1,
and this alone will reduce the number of WAN round-trips by between one and two for reads, and one for writes.
- A new Paxos Repair mechanism has been introduced as part of Repair, that permits further reducing the number of WAN
round-trips for write LWTs. This process may be manually executed for v1 and is run automatically alongside normal
repairs for v2. Once users are running regular repairs that include paxos repairs they are encouraged to set
paxos_state_purging: repaired. Once this has been set across the cluster, users are encouraged to set their
applications to supply a Commit consistency level of ANY with their LWT write operations, saving one additional WAN
- A new Paxos Repair mechanism has been introduced as part of Repair, that permits further reducing the number of WAN
round-trips for write LWTs. This process may be manually executed for v1 and is run automatically alongside normal
repairs for v2. Once users are running regular repairs that include paxos repairs they are encouraged to set
paxos_state_purging: repaired. Once this has been set across the cluster, users are encouraged to set their
applications to supply a Commit consistency level of ANY with their LWT write operations, saving one additional WAN
round-trip. See upgrade notes below.
- Warn/abort thresholds added to read queries notifying clients when these thresholds trigger (by
emitting a client warning or aborting the query). This feature is disabled by default, scheduled
@ -164,6 +164,11 @@ Upgrading
restore default commit consistency to ensure correctness.
- Applications may now safely be updated to use ANY commit consistency level (or LOCAL_QUORUM, as preferred).
Uncontended writes should now take 2 round-trips, and uncontended reads should typically take one round-trip.
- A required [f|force] flag has been added to both "nodetool verify" and the standalone "sstableverify" tools.
These tools have some subtleties and should not be used unless the operator is familiar with what they do
and do not do, as well as the edge cases associated with their use.
NOTE: ANY SCRIPTS THAT RELY ON sstableverify OR nodetool verify WILL STOP WORKING UNTIL MODIFIED.
Please see CASSANDRA-17017 for details: https://issues.apache.org/jira/browse/CASSANDRA-17017
Deprecation
-----------

View File

@ -8,6 +8,11 @@ Cassandra must be stopped before this tool is executed, or unexpected
results will occur. Note: the script does not verify that Cassandra is
stopped.
== WARNING
See CASSANDRA-9947 and CASSANDRA-17017 for discussion around risks with this tool. Specifically: "We mark sstables that fail verification as unrepaired, but that's not going to do what you think. What it means is that the local node will use that sstable in the next repair, but other nodes will not. So all we'll end up doing is streaming whatever data we can read from it, to the other replicas. If we could magically mark whatever sstables correspond on the remote nodes, to the data in the local sstable, that would work, but we can't."
This tool requires the use of a -f or --force flag to indicate that the user understands the risks and would like to attempt its usage anyway.
== Usage
sstableverify <options> <keyspace> <table>
@ -18,6 +23,7 @@ sstableverify <options> <keyspace> <table>
|-e, --extended |extended verification
|-h, --help |display this help message
|-v, --verbose |verbose output
|-f, --force |allow use of tool (see CASSANDRA-17017 for risks)
|===
== Basic Verification

View File

@ -62,11 +62,18 @@ public class StandaloneVerifier
private static final String CHECK_VERSION = "check_version";
private static final String MUTATE_REPAIR_STATUS = "mutate_repair_status";
private static final String QUICK = "quick";
private static final String FORCE = "force";
private static final String TOKEN_RANGE = "token_range";
public static void main(String args[])
{
Options options = Options.parseArgs(args);
if (!options.force)
{
System.err.println("verify will not run without -f or --force. See CASSANDRA-17017 for details.");
Options.printUsage(Options.getCmdLineOptions());
System.exit(1);
}
initDatabaseDescriptorForTool();
System.out.println("sstableverify using the following options: " + options);
@ -165,6 +172,7 @@ public class StandaloneVerifier
public boolean checkVersion;
public boolean mutateRepairStatus;
public boolean quick;
public boolean force;
public Collection<Range<Token>> tokens;
private Options(String keyspaceName, String cfName)
@ -190,8 +198,8 @@ public class StandaloneVerifier
String[] args = cmd.getArgs();
if (args.length != 2)
{
String msg = args.length < 2 ? "Missing arguments" : "Too many arguments";
System.err.println(msg);
String prefix = args.length < 2 ? "Missing" : "Too many";
System.err.println(prefix + " arguments");
printUsage(options);
System.exit(1);
}
@ -207,6 +215,7 @@ public class StandaloneVerifier
opts.checkVersion = cmd.hasOption(CHECK_VERSION);
opts.mutateRepairStatus = cmd.hasOption(MUTATE_REPAIR_STATUS);
opts.quick = cmd.hasOption(QUICK);
opts.force = cmd.hasOption(FORCE);
if (cmd.hasOption(TOKEN_RANGE))
{
@ -260,17 +269,24 @@ public class StandaloneVerifier
options.addOption("c", CHECK_VERSION, "make sure sstables are the latest version");
options.addOption("r", MUTATE_REPAIR_STATUS, "don't mutate repair status");
options.addOption("q", QUICK, "do a quick check, don't read all data");
options.addOption("f", FORCE, "force verify - see CASSANDRA-17017");
options.addOptionList("t", TOKEN_RANGE, "range", "long token range of the format left,right. This may be provided multiple times to define multiple different ranges");
return options;
}
public static void printUsage(CmdLineOptions options)
{
String usage = String.format("%s [options] <keyspace> <column_family>", TOOL_NAME);
String usage = String.format("%s [options] <keyspace> <column_family> force", TOOL_NAME);
StringBuilder header = new StringBuilder();
header.append("--\n");
header.append("Verify the sstable for the provided table." );
header.append("\n--\n");
header.append("NOTE: There are significant risks associated with using this tool; it likely doesn't do what " +
"you expect and there are known edge cases. You must provide a -f or --force argument in " +
"order to allow usage of the tool -> see CASSANDRA-9947 and CASSANDRA-17017 for known risks.\n");
header.append("https://issues.apache.org/jira/browse/CASSANDRA-9947\n");
header.append("https://issues.apache.org/jira/browse/CASSANDRA-17017");
header.append("\n--\n");
header.append("Options are:");
new HelpFormatter().printHelp(usage, header.toString(), options, "");
}

View File

@ -44,6 +44,11 @@ public class Verify extends NodeToolCmd
description = "Also check that all sstables are the latest version")
private boolean checkVersion = false;
@Option(title = "override-disable",
name = {"-f", "--force"},
description = "Override disabling of verify tool - see CASSANDRA-9947 for caveats")
private boolean overrideDisable = false;
@Option(title = "dfp",
name = {"-d", "--dfp"},
description = "Invoke the disk failure policy if a corrupt sstable is found")
@ -68,6 +73,12 @@ public class Verify extends NodeToolCmd
public void execute(NodeProbe probe)
{
PrintStream out = probe.output().out;
if (!overrideDisable)
{
out.println("verify is disabled unless a [-f|--force] override flag is provided. See CASSANDRA-9947 and CASSANDRA-17017 for details.");
System.exit(1);
}
List<String> keyspaces = parseOptionalKeyspace(args, probe);
String[] tableNames = parseOptionalTables(args);

View File

@ -73,7 +73,7 @@ public class NodeToolTest extends TestBaseImpl
public void testNodetoolSystemExit()
{
// Verify currently calls System.exit, this test uses that knowlege to test System.exit behavior in jvm-dtest
NODE.nodetoolResult("verify", "--check-tokens")
NODE.nodetoolResult("verify", "--check-tokens", "--force")
.asserts()
.failure()
.stdoutContains("Token verification requires --extended-verify");

View File

@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@ -54,6 +53,7 @@ import org.apache.cassandra.io.FSWriteError;
import org.apache.cassandra.io.sstable.Component;
import org.apache.cassandra.io.sstable.CorruptSSTableException;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileInputStreamPlus;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.io.util.RandomAccessReader;
@ -63,21 +63,7 @@ import org.apache.cassandra.schema.CompressionParams;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.commons.lang3.StringUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.*;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import org.apache.cassandra.io.util.File;
import static org.apache.cassandra.SchemaLoader.counterCFMD;
import static org.apache.cassandra.SchemaLoader.createKeyspace;
import static org.apache.cassandra.SchemaLoader.loadSchema;

View File

@ -71,6 +71,7 @@ public abstract class OfflineToolUtils
"ObjectCleanerThread",
"process reaper", // spawned by the jvm when executing external processes
// and may still be active when we check
"Attach Listener", // spawned in intellij IDEA
};
public void assertNoUnexpectedThreadsStarted(String[] optionalThreadNames)

View File

@ -83,7 +83,7 @@ public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
createAndPopulateTable(keyspaceName, workingTable, x -> {});
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, workingTable, "-c");
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, workingTable, "--force", "-c");
assertEquals(0, tool.getExitCode());
tool.assertOnCleanExit();
}
@ -103,7 +103,7 @@ public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
}
});
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspace, tableName, "-c");
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspace, tableName, "-f", "-c");
assertEquals(1, tool.getExitCode());
Assertions.assertThat(tool.getStdout()).contains("is not the latest version, run upgradesstables");
@ -117,7 +117,7 @@ public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
createAndPopulateTable(keyspaceName, workingTable, x -> {});
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, workingTable);
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, workingTable, "--force");
assertEquals(0, tool.getExitCode());
tool.assertOnCleanExit();
}
@ -136,7 +136,7 @@ public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
}
});
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, corruptStatsTable);
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, corruptStatsTable, "-f");
assertEquals(1, tool.getExitCode());
Assertions.assertThat(tool.getStderr()).contains("Error Loading", corruptStatsTable);
@ -161,7 +161,7 @@ public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
}
});
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, corruptDataTable);
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, keyspaceName, corruptDataTable, "--force");
assertEquals(1, tool.getExitCode());
Assertions.assertThat(tool.getStdout()).contains("Invalid SSTable", corruptDataTable);
}

View File

@ -42,15 +42,23 @@ public class StandaloneVerifierTest extends OfflineToolUtils
{
// If you added, modified options or help, please update docs if necessary
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "-h");
String help = "usage: sstableverify [options] <keyspace> <column_family>\n" +
String help = "usage: sstableverify [options] <keyspace> <column_family> force\n" +
"--\n" +
"Verify the sstable for the provided table.\n" +
"--\n" +
"Options are:\n" +
"--\n" +
"NOTE: There are significant risks associated with using this tool; it\n" +
"likely doesn't do what you expect and there are known edge cases. You must\n" +
"provide a -f or --force argument in order to allow usage of the tool ->\n" +
"see CASSANDRA-9947 and CASSANDRA-17017 for known risks.\n" +
"https://issues.apache.org/jira/browse/CASSANDRA-9947\n" +
"https://issues.apache.org/jira/browse/CASSANDRA-17017\n" +
"--\n" +
"Options are:\n" +
" -c,--check_version make sure sstables are the latest version\n" +
" --debug display stack traces\n" +
" -e,--extended extended verification\n" +
" -h,--help display this help message\n" +
" -e,--extended extended verification\n" +
" -f,--force force verify - see CASSANDRA-17017\n" +
" -h,--help display this help message\n" +
" -q,--quick do a quick check, don't read all data\n" +
" -r,--mutate_repair_status don't mutate repair status\n" +
" -t,--token_range <range> long token range of the format left,right.\n" +
@ -62,7 +70,7 @@ public class StandaloneVerifierTest extends OfflineToolUtils
@Test
public void testWrongArgFailsAndPrintsHelp()
{
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "--debugwrong", "system_schema", "tables");
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "--debugwrong", "system_schema", "tables", "-f");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
assertEquals(1, tool.getExitCode());
@ -71,24 +79,24 @@ public class StandaloneVerifierTest extends OfflineToolUtils
@Test
public void testDefaultCall()
{
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "system_schema", "tables");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("using the following options"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertEquals(0, tool.getExitCode());
assertCorrectEnvPostTest();
tool.assertOnCleanExit();
Arrays.asList("-f", "--force").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "system_schema", "tables", arg);
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("using the following options"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertEquals(0, tool.getExitCode());
assertCorrectEnvPostTest();
tool.assertOnCleanExit();
});
}
@Test
public void testDebugArg()
{
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "--debug", "system_schema", "tables");
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "--debug", "system_schema", "tables", "-f");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("debug=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertCorrectEnvPostTest();
tool.assertOnCleanExit();
}
@Test
@ -96,9 +104,10 @@ public class StandaloneVerifierTest extends OfflineToolUtils
{
Arrays.asList("-e", "--extended").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
arg,
"system_schema",
"tables");
arg,
"system_schema",
"tables",
"--force");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("extended=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertCorrectEnvPostTest();
@ -111,9 +120,10 @@ public class StandaloneVerifierTest extends OfflineToolUtils
{
Arrays.asList("-q", "--quick").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
arg,
"system_schema",
"tables");
arg,
"system_schema",
"tables",
"-f");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("quick=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertCorrectEnvPostTest();
@ -126,9 +136,10 @@ public class StandaloneVerifierTest extends OfflineToolUtils
{
Arrays.asList("-r", "--mutate_repair_status").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
arg,
"system_schema",
"tables");
arg,
"system_schema",
"tables",
"--force");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("mutateRepairStatus=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertCorrectEnvPostTest();
@ -153,9 +164,10 @@ public class StandaloneVerifierTest extends OfflineToolUtils
{
Arrays.asList("-v", "--verbose").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
arg,
"system_schema",
"tables");
arg,
"system_schema",
"tables",
"-f");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("verbose=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertCorrectEnvPostTest();
@ -163,12 +175,48 @@ public class StandaloneVerifierTest extends OfflineToolUtils
});
}
@Test
public void testTooFewArgs()
{
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "one_arg");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
assertEquals(1, tool.getExitCode());
}
@Test
public void testTooManyArgs()
{
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "another arg", "system_schema", "tables");
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "one_arg", "two_arg", "toomany_arg");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Too many arguments"));
assertEquals(1, tool.getExitCode());
}
@Test
public void testFailsWithoutForce()
{
Arrays.asList("-r", "--mutate_repair_status").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
arg,
"system_schema",
"tables",
"debug");
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertEquals(1, tool.getExitCode());
});
}
@Test
public void testBadForceArgument()
{
Arrays.asList("bf", "badforce", "garbage", "forrce").forEach(arg -> {
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
"system_schema",
"tables",
arg);
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage"));
assertEquals(1, tool.getExitCode());
});
}
}

View File

@ -0,0 +1,128 @@
/*
* 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.apache.cassandra.SchemaLoader;
import org.apache.cassandra.auth.AuthTestUtils;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
import static org.assertj.core.api.Assertions.assertThat;
public class VerifyTest extends CQLTester
{
@BeforeClass
public static void setup() throws Exception
{
SchemaLoader.prepareServer();
AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager();
SchemaLoader.setupAuth(roleManager,
new AuthTestUtils.LocalPasswordAuthenticator(),
new AuthTestUtils.LocalCassandraAuthorizer(),
new AuthTestUtils.LocalCassandraNetworkAuthorizer());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());
startJMXServer();
}
/**
* We calcify the help file as last seen as a "trigger" to notify a developer that, upon addition of a new flag or
* functionality to this tool option, they will need to update help output and/or documentation as necessary.
*/
@Test
public void testMaybeChangeDocs()
{
// If you added, modified options or help, please update docs if necessary
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "verify");
tool.assertOnCleanExit();
String help =
"NAME\n" +
" nodetool verify - Verify (check data checksum for) one or more tables\n" +
"\n" +
"SYNOPSIS\n" +
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
" [(-u <username> | --username <username>)] verify\n" +
" [(-c | --check-version)] [(-d | --dfp)] [(-e | --extended-verify)]\n" +
" [(-f | --force)] [(-q | --quick)] [(-r | --rsc)] [(-t | --check-tokens)]\n" +
" [--] [<keyspace> <tables>...]\n" +
"\n" +
"OPTIONS\n" +
" -c, --check-version\n" +
" Also check that all sstables are the latest version\n" +
"\n" +
" -d, --dfp\n" +
" Invoke the disk failure policy if a corrupt sstable is found\n" +
"\n" +
" -e, --extended-verify\n" +
" Verify each cell data, beyond simply checking sstable checksums\n" +
"\n" +
" -f, --force\n" +
" Override disabling of verify tool - see CASSANDRA-9947 for caveats\n" +
"\n" +
" -h <host>, --host <host>\n" +
" Node hostname or ip address\n" +
"\n" +
" -p <port>, --port <port>\n" +
" Remote jmx agent port number\n" +
"\n" +
" -pp, --print-port\n" +
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
"\n" +
" -pw <password>, --password <password>\n" +
" Remote jmx agent password\n" +
"\n" +
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
" Path to the JMX password file\n" +
"\n" +
" -q, --quick\n" +
" Do a quick check - avoid reading all data to verify checksums\n" +
"\n" +
" -r, --rsc\n" +
" Mutate the repair status on corrupt sstables\n" +
"\n" +
" -t, --check-tokens\n" +
" Verify that all tokens in sstables are owned by this node\n" +
"\n" +
" -u <username>, --username <username>\n" +
" Remote jmx agent username\n" +
"\n" +
" --\n" +
" This option can be used to separate command-line options from the\n" +
" list of argument, (useful when arguments might be mistaken for\n" +
" command-line options\n" +
"\n" +
" [<keyspace> <tables>...]\n" +
" The keyspace followed by one or many tables\n" +
"\n\n";
assertThat(tool.getStdout()).isEqualTo(help);
}
}