parser = builder.build();
-
- int status = 0;
- try
- {
- Runnable parse = parser.parse(args);
- parse.run();
- }
- catch (Throwable throwable)
- {
- err(throwable);
- status = 2;
- }
+ CommandLine cli = new CommandLine(StandaloneJournalUtil.class)
+ .setExecutionExceptionHandler((ex, cmd, parseResult) -> {
+ err(ex);
+ return 2;
+ });
+ int status = cli.execute(args);
System.exit(status);
}
+ @Override
+ public void run()
+ {
+ CommandLine.usage(this, output.out);
+ }
+
protected static void err(Throwable e)
{
output.err.println("error: " + e.getMessage());
@@ -132,19 +133,19 @@ public class StandaloneJournalUtil
@Command(name = "dump_segments", description = "Dump journal segments")
public static class DumpSegments implements Runnable
{
- @Option(name = {"-d", "--dir"}, description = "Directory to find journal segments")
+ @Option(names = {"-d", "--dir"}, description = "Directory to find journal segments")
public String dir;
- @Option(name = {"-p", "--pattern"}, description = "Kind to filter by")
+ @Option(names = {"-p", "--pattern"}, description = "Kind to filter by")
public String pattern;
- @Option(name = {"-k", "--kind"}, description = "Kind to filter by")
+ @Option(names = {"-k", "--kind"}, description = "Kind to filter by")
public String kind;
- @Option(name = {"-t", "--txnid"}, description = "Transaction id to filter by")
+ @Option(names = {"-t", "--txnid"}, description = "Transaction id to filter by")
public String txnId;
- @Option(name = {"-m", "--metadata-only"}, description = "Only dump metadata file contents")
+ @Option(names = {"-m", "--metadata-only"}, description = "Only dump metadata file contents")
public boolean metadataOnly;
public void run()
@@ -173,7 +174,8 @@ public class StandaloneJournalUtil
}
catch (Throwable t)
{
- t.printStackTrace(output.err);
+ throw new RuntimeException(String.format("Error reading key %s in segment %s at position %d: %s",
+ key, segment1, position, t.getMessage()), t);
}
});
});
@@ -189,28 +191,28 @@ public class StandaloneJournalUtil
@Command(name = "dump_journal", description = "Dump journal")
public static class DumpJournal implements Runnable
{
- @Option(name = {"-s", "--sstables"}, description = "Path to sstables")
+ @Option(names = {"-s", "--sstables"}, description = "Path to sstables")
public String sstables;
- @Option(name = {"-j", "--journal-segments"}, description = "Path to journal segments")
+ @Option(names = {"-j", "--journal-segments"}, description = "Path to journal segments")
public String journalSegments;
- @Option(name = {"-k", "--kind"}, description = "Kind to filter by")
+ @Option(names = {"-k", "--kind"}, description = "Kind to filter by")
public String kind;
- @Option(name = {"-t", "--txnid"}, description = "Transaction id to filter by")
+ @Option(names = {"-t", "--txnid"}, description = "Transaction id to filter by")
public String txnId;
- @Option(name = {"--since"}, description = "Filter transactions since this timestamp (inclusive)")
+ @Option(names = {"--since"}, description = "Filter transactions since this timestamp (inclusive)")
public String since;
- @Option(name = {"--until"}, description = "Filter transactions until this timestamp (inclusive)")
+ @Option(names = {"--until"}, description = "Filter transactions until this timestamp (inclusive)")
public String until;
- @Option(name = {"-e", "--skip-errors"}, description = "Skip errors: 'true' to skip all, or comma-separated exception class names to skip specific types")
+ @Option(names = {"-e", "--skip-errors"}, description = "Skip errors: 'true' to skip all, or comma-separated exception class names to skip specific types")
public String skipErrors;
- @Option(name = {"-c", "--construct"}, description = "Construct entry")
+ @Option(names = {"-c", "--construct"}, description = "Construct entry")
public boolean construct;
@@ -345,8 +347,7 @@ public class StandaloneJournalUtil
if (!shouldSkip)
throw t;
- output.out.println(String.format("Got error reading key %s", key));
- t.printStackTrace();
+ throw new RuntimeException(String.format("Error reading key %s: %s", key, t.getMessage()), t);
}
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/AbortBootstrap.java b/src/java/org/apache/cassandra/tools/nodetool/AbortBootstrap.java
index 4c180ad81c..00f44249e1 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/AbortBootstrap.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/AbortBootstrap.java
@@ -17,25 +17,22 @@
*/
package org.apache.cassandra.tools.nodetool;
-
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isEmpty;
@Command(name = "abortbootstrap", description = "Abort a failed bootstrap")
-public class AbortBootstrap extends NodeToolCmd
+public class AbortBootstrap extends AbstractCommand
{
- @Option(title = "node id", name = "--node", description = "Node ID of the node that failed bootstrap", required = false)
+ @Option(paramLabel = "node_id", names = "--node", description = "Node ID of the node that failed bootstrap")
private String nodeId = EMPTY;
- @Option(title = "ip", name = "--ip", description = "IP of the node that failed bootstrap", required = false)
+ @Option(paramLabel = "ip", names = "--ip", description = "IP of the node that failed bootstrap")
private String endpoint = EMPTY;
-
@Override
public void execute(NodeProbe probe)
{
diff --git a/src/java/org/apache/cassandra/tools/nodetool/AbstractCommand.java b/src/java/org/apache/cassandra/tools/nodetool/AbstractCommand.java
new file mode 100644
index 0000000000..5c25307563
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/AbstractCommand.java
@@ -0,0 +1,104 @@
+/*
+ * 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 javax.inject.Inject;
+
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.Output;
+
+import picocli.CommandLine;
+import picocli.CommandLine.ExecutionException;
+
+/**
+ * Abstract class for all nodetool commands, which provides common methods and fields
+ * for running commands and outputting results.
+ *
+ * The command is executed by calling {@link #execute(NodeProbe)}, in all other cases
+ * it should not contain any fields or methods that are specific to a particular API
+ * being executed, or common methods that are shared across multiple commands.
+ *
+ * Commands must work only with the API provided by the {@link NodeProbe} instance.
+ */
+public abstract class AbstractCommand implements Runnable
+{
+ @Inject
+ protected Output output;
+
+ private NodeProbe probe;
+
+ public void probe(NodeProbe probe)
+ {
+ this.probe = probe;
+ }
+
+ public NodeProbe probe()
+ {
+ return probe;
+ }
+
+ public void logger(Output output)
+ {
+ this.output = output;
+ }
+
+ @Override
+ public void run()
+ {
+ execute(probe());
+ }
+
+ /**
+ * Prepare a command for execution. This method is called before the command is executed and
+ * can be used to perform any necessary setup or validation. If this method returns {@code false},
+ * the command will not initiate connection and will be executed locally. The default implementation
+ * returns {@code true} so that the command initiates connection to the node before execution.
+ *
+ * @return {@code true} if the command is required to connect to the node, {@code false} otherwise.
+ * @throws ExecutionException if an error occurs during preparation and execution must be aborted.
+ */
+ protected boolean shouldConnect() throws ExecutionException
+ {
+ return true;
+ }
+
+ /**
+ * Execute the command using the supplied {@link NodeProbe} instance, which is already connected
+ * to the node and ready to use. If the {@link NodeProbe} is not {@code null}, it is guaranteed that it
+ * is connected to the node, and the command can use it to perform operations on the node.
+ *
+ * WARNING:
+ *
+ * Due to the backwards compatibility with the previous Airline-based implementation of
+ * the nodetool commands, for most of the commands this method is also used to validate the input
+ * arguments and perform necessary checks before the command is executed. This implies that
+ * the command throws an exception during execution to avoid unexpected behavior or errors,
+ * instead of validating the input arguments within the Picocli framework on the Parser stage.
+ * For this reason, the {@link CommandLine.Parameters#arity()} and {@link CommandLine.Option#arity()}
+ * which are normally used to validate the input arguments, set to {@code "0..*"} or {@code "0..1"}
+ * making the arguments optional, and passing the validation to the command's
+ * {@code execute(NodeProbe probe)} method.
+ *
+ * New commands should not rely on the behavior described above and should validate the input arguments
+ * using Picocli annotations such as {@link CommandLine.Parameters} and {@link CommandLine.Option}.
+ *
+ * @param probe The {@link NodeProbe} instance to use, or {@code null} if no connection is required.
+ */
+ protected abstract void execute(NodeProbe probe);
+}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/AccordAdmin.java b/src/java/org/apache/cassandra/tools/nodetool/AccordAdmin.java
index ff7e88ca9f..e07f714824 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/AccordAdmin.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/AccordAdmin.java
@@ -21,15 +21,30 @@ package org.apache.cassandra.tools.nodetool;
import java.util.List;
import java.util.Map;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
-public abstract class AccordAdmin extends NodeTool.NodeToolCmd
+@Command(name = "accord",
+ description = "Manage the operation of Accord",
+ subcommands = {
+ AccordAdmin.Describe.class,
+ AccordAdmin.MarkStale.class,
+ AccordAdmin.MarkRejoining.class
+ })
+public class AccordAdmin extends AbstractCommand
{
+ @Override
+ protected void execute(NodeProbe probe)
+ {
+ AbstractCommand cmd = new AccordAdmin.Describe();
+ cmd.probe(probe);
+ cmd.logger(output);
+ cmd.run();
+ }
+
@Command(name = "describe", description = "Describe current cluster metadata relating to Accord")
- public static class Describe extends NodeTool.NodeToolCmd
+ public static class Describe extends AbstractCommand
{
@Override
protected void execute(NodeProbe probe)
@@ -42,9 +57,9 @@ public abstract class AccordAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "mark_stale", description = "Mark a replica as being stale and no longer able to participate in durability status coordination")
- public static class MarkStale extends AccordAdmin
+ public static class MarkStale extends AbstractCommand
{
- @Arguments(required = true, description = "One or more node IDs to mark stale", usage = "+")
+ @Parameters(arity = "1..*", description = "One or more node IDs to mark stale")
public List nodeIds;
@Override
@@ -55,9 +70,9 @@ public abstract class AccordAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "mark_rejoining", description = "Mark a stale replica as being allowed to participate in durability status coordination again")
- public static class MarkRejoining extends AccordAdmin
+ public static class MarkRejoining extends AbstractCommand
{
- @Arguments(required = true, description = "One or more node IDs to mark no longer stale", usage = "+")
+ @Parameters(arity = "1", description = "One or more node IDs to mark no longer stale")
public List nodeIds;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/AlterTopology.java b/src/java/org/apache/cassandra/tools/nodetool/AlterTopology.java
index e8078d3d57..62390228d9 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/AlterTopology.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/AlterTopology.java
@@ -20,17 +20,16 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
-
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
import static com.google.common.base.Preconditions.checkArgument;
@Command(name = "altertopology", description = "Modify the datacenter and/or rack of one or more nodes")
-public class AlterTopology extends NodeToolCmd
+public class AlterTopology extends AbstractCommand
{
- @Arguments(usage = " [...]", description = "One or more node identifiers, which may be either a node id, host id or broadcast address, each with a target dc:rack")
+ @Parameters(description = { "One or more node identifiers, which may be either a node id, host id or broadcast address, each with a target dc:rack",
+ " [...]" })
private List args = new ArrayList<>();
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Assassinate.java b/src/java/org/apache/cassandra/tools/nodetool/Assassinate.java
index 2639ec81b3..22f56c5709 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Assassinate.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Assassinate.java
@@ -17,19 +17,18 @@
*/
package org.apache.cassandra.tools.nodetool;
-import static org.apache.commons.lang3.StringUtils.EMPTY;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.net.UnknownHostException;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
+import static org.apache.commons.lang3.StringUtils.EMPTY;
@Command(name = "assassinate", description = "Forcefully remove a dead node without re-replicating any data. Use as a last resort if you cannot removenode")
-public class Assassinate extends NodeToolCmd
+public class Assassinate extends AbstractCommand
{
- @Arguments(title = "ip address", usage = "", description = "IP address of the endpoint to assassinate", required = true)
+ @Parameters(paramLabel = "ip_address", description = "IP address of the endpoint to assassinate", arity = "1")
private String endpoint = EMPTY;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/AutoRepairStatus.java b/src/java/org/apache/cassandra/tools/nodetool/AutoRepairStatus.java
index bb594a010f..951be1ab37 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/AutoRepairStatus.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/AutoRepairStatus.java
@@ -23,11 +23,10 @@ import java.util.Set;
import com.google.common.annotations.VisibleForTesting;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
import static com.google.common.base.Preconditions.checkArgument;
@@ -35,10 +34,10 @@ import static com.google.common.base.Preconditions.checkArgument;
* Provides currently running auto-repair tasks.
*/
@Command(name = "autorepairstatus", description = "Print autorepair status")
-public class AutoRepairStatus extends NodeTool.NodeToolCmd
+public class AutoRepairStatus extends AbstractCommand
{
@VisibleForTesting
- @Option(title = "repair type", name = { "-t", "--repair-type" }, description = "Repair type")
+ @Option(names = { "-t", "--repair-type" }, description = "Repair type")
protected String repairType;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Bootstrap.java b/src/java/org/apache/cassandra/tools/nodetool/Bootstrap.java
new file mode 100644
index 0000000000..f350f8e5bb
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/Bootstrap.java
@@ -0,0 +1,28 @@
+/*
+ * 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 picocli.CommandLine.Command;
+
+@Command(name = "bootstrap",
+ subcommands = { BootstrapResume.class },
+ description = "Monitor/manage node's bootstrap process")
+public class Bootstrap
+{
+}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/BootstrapResume.java b/src/java/org/apache/cassandra/tools/nodetool/BootstrapResume.java
index a55cded6ac..9f000b9e07 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/BootstrapResume.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/BootstrapResume.java
@@ -17,24 +17,24 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import java.io.IOError;
import java.io.IOException;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
import static org.apache.cassandra.config.CassandraRelevantProperties.RESET_BOOTSTRAP_PROGRESS;
@Command(name = "resume", description = "Resume bootstrap streaming")
-public class BootstrapResume extends NodeToolCmd
+public class BootstrapResume extends AbstractCommand
{
- @Option(title = "force",
- name = { "-f", "--force"},
- description = "Use --force to resume bootstrap regardless of cassandra.reset_bootstrap_progress environment variable. WARNING: This is potentially dangerous, see CASSANDRA-17679")
- boolean force = false;
+ @Option(paramLabel = "force",
+ names = { "-f", "--force" },
+ description = { "Use --force to resume bootstrap regardless of ",
+ "cassandra.reset_bootstrap_progress environment variable. WARNING:",
+ "This is potentially dangerous, see CASSANDRA-17679" })
+ private boolean force = false;
@Override
protected void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/CIDRFilteringStats.java b/src/java/org/apache/cassandra/tools/nodetool/CIDRFilteringStats.java
index 9b01dc7894..c01bf46b41 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/CIDRFilteringStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/CIDRFilteringStats.java
@@ -22,18 +22,17 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import io.airlift.airline.Command;
import org.apache.cassandra.db.virtual.CIDRFilteringMetricsTable.CIDRFilteringMetricsCountsTable;
import org.apache.cassandra.db.virtual.CIDRFilteringMetricsTable.CIDRFilteringMetricsLatenciesTable;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import picocli.CommandLine.Command;
/**
* Nodetool command to view stats related to CIDR filtering
*/
@Command(name = "cidrfilteringstats", description = "Print statistics on CIDR filtering")
-public class CIDRFilteringStats extends NodeToolCmd
+public class CIDRFilteringStats extends AbstractCommand
{
private void printCountsMetrics(NodeProbe probe, PrintStream out)
{
diff --git a/src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java b/src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java
index 84d23dc3f8..8c5dc0b0dc 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java
@@ -27,12 +27,12 @@ import java.util.Map;
import com.google.common.collect.ImmutableList;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tcm.Epoch;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
import static org.apache.cassandra.tcm.CMSOperations.COMMITS_PAUSED;
import static org.apache.cassandra.tcm.CMSOperations.EPOCH;
@@ -44,10 +44,29 @@ import static org.apache.cassandra.tcm.CMSOperations.NEEDS_RECONFIGURATION;
import static org.apache.cassandra.tcm.CMSOperations.REPLICATION_FACTOR;
import static org.apache.cassandra.tcm.CMSOperations.SERVICE_STATE;
-public abstract class CMSAdmin extends NodeTool.NodeToolCmd
+@Command(name = "cms", description = "Manage cluster metadata",
+ subcommands = { CMSAdmin.DescribeCMS.class,
+ CMSAdmin.InitializeCMS.class,
+ CMSAdmin.ReconfigureCMS.class,
+ CMSAdmin.Snapshot.class,
+ CMSAdmin.Unregister.class,
+ CMSAdmin.AbortInitialization.class,
+ CMSAdmin.DumpDirectory.class,
+ CMSAdmin.DumpLog.class,
+ CMSAdmin.ResumeDropAccordTable.class })
+public class CMSAdmin extends AbstractCommand
{
+ @Override
+ protected void execute(NodeProbe probe)
+ {
+ AbstractCommand cmd = new DescribeCMS();
+ cmd.probe(probe);
+ cmd.logger(output);
+ cmd.run();
+ }
+
@Command(name = "describe", description = "Describe the current Cluster Metadata Service")
- public static class DescribeCMS extends NodeTool.NodeToolCmd
+ public static class DescribeCMS extends AbstractCommand
{
@Override
protected void execute(NodeProbe probe)
@@ -67,9 +86,9 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "initialize", description = "Upgrade from gossip and initialize CMS")
- public static class InitializeCMS extends NodeTool.NodeToolCmd
+ public static class InitializeCMS extends AbstractCommand
{
- @Option(title = "ignored endpoints", name = { "-i", "--ignore"}, description = "Hosts to ignore due to them being down")
+ @Option(paramLabel = "ignored_endpoints", names = { "-i", "--ignore" }, description = "Hosts to ignore due to them being down")
private List endpoint = new ArrayList<>();
@Override
@@ -80,24 +99,25 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "reconfigure", description = "Reconfigure replication factor of CMS")
- public static class ReconfigureCMS extends NodeTool.NodeToolCmd
+ public static class ReconfigureCMS extends AbstractCommand
{
- @Option(title = "status",
- name = {"--status"},
- description = "Poll status of the reconfigure command. All other flags and arguments are ignored when this one is used.")
+ @Option(paramLabel = "status",
+ names = { "--status" },
+ description = "Poll status of the reconfigure command. All other flags and arguments are ignored when this one is used.")
private boolean status = false;
- @Option(title = "resume",
- name = {"-r", "--resume"},
- description = "Whether or not a previously interrupted sequence should be resumed")
+ @Option(paramLabel = "resume",
+ names = { "-r", "--resume" },
+ description = "Whether or not a previously interrupted sequence should be resumed")
private boolean resume = false;
- @Option(title = "cancel",
- name = {"-c", "--cancel"},
- description = "Cancels any in progress CMS reconfiguration")
+ @Option(paramLabel = "cancel",
+ names = { "-c", "--cancel" },
+ description = "Cancels any in progress CMS reconfiguration")
private boolean cancel = false;
- @Arguments(usage = "[] or : ... ", description = "Replication factor of new CMS")
+ @CassandraUsage(usage = "[] or : ... ", description = "Replication factor of new CMS")
+ @Parameters(paramLabel = "replication_factor", description = "Replication factors of new CMS in format or :")
private List args = new ArrayList<>();
@Override
@@ -178,7 +198,7 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "snapshot", description = "Request a checkpointing snapshot of cluster metadata")
- public static class Snapshot extends NodeTool.NodeToolCmd
+ public static class Snapshot extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
@@ -188,10 +208,10 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "unregister", description = "Unregister nodes in LEFT state")
- public static class Unregister extends NodeTool.NodeToolCmd
+ public static class Unregister extends AbstractCommand
{
- @Arguments(required = true, title = "Unregister nodes in LEFT state", description = "One or more nodeIds to unregister, they all need to be in LEFT state", usage = "+")
- public List nodeIds;
+ @Parameters(paramLabel = "nodeId", description = "One or more nodeIds to unregister, they all need to be in LEFT state", arity = "1..*")
+ private List nodeIds;
@Override
protected void execute(NodeProbe probe)
@@ -201,9 +221,9 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "abortinitialization", description = "Abort an incomplete initialization")
- public static class AbortInitialization extends NodeTool.NodeToolCmd
+ public static class AbortInitialization extends AbstractCommand
{
- @Option(required = true, name = "--initiator", title = "Initiator", description = "The address of the node where `cms initialize` was run.")
+ @Option(required = true, names = { "--initiator" }, description = "The address of the node where `cms initialize` was run.")
public String initiator;
@Override
@@ -214,10 +234,11 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "dumpdirectory", description = "Dump the directory from the current ClusterMetadata")
- public static class DumpDirectory extends NodeTool.NodeToolCmd
+ public static class DumpDirectory extends AbstractCommand
{
- @Option(name = "--tokens", title = "Include tokens", description = "Include tokens in output")
+ @Option(names = { "--tokens" }, description = "Include tokens in output")
public boolean tokens = false;
+
@Override
protected void execute(NodeProbe probe)
{
@@ -226,12 +247,13 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "dumplog", description = "Dump the metadata log")
- public static class DumpLog extends NodeTool.NodeToolCmd
+ public static class DumpLog extends AbstractCommand
{
- @Option(name = "--start", title = "Start epoch")
+ @Option(names = { "--start" }, description = "Start epoch")
long startEpoch = Epoch.FIRST.getEpoch();
- @Option(name = "--end", title = "End epoch")
+ @Option(names = { "--end" }, description = "End epoch")
long endEpoch = Long.MAX_VALUE;
+
@Override
protected void execute(NodeProbe probe)
{
@@ -259,10 +281,11 @@ public abstract class CMSAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "resumedropaccordtable", description = "Resume a drop accord table operation which has stalled")
- public static class ResumeDropAccordTable extends NodeTool.NodeToolCmd
+ public static class ResumeDropAccordTable extends AbstractCommand
{
- @Arguments(usage = "[tableId]", description = "Table id of the table being dropped")
+ @Parameters(description = "Table id of the table being dropped")
private String tableId;
+
@Override
public void execute(NodeProbe probe)
{
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Cleanup.java b/src/java/org/apache/cassandra/tools/nodetool/Cleanup.java
index 8c402093d7..b8e722d86b 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Cleanup.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Cleanup.java
@@ -17,32 +17,43 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Option;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspaceNonLocal;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables;
@Command(name = "cleanup", description = "Triggers the immediate cleanup of keys no longer belonging to a node. By default, clean all keyspaces")
-public class Cleanup extends NodeToolCmd
+public class Cleanup extends AbstractCommand
{
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
+ @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables")
private List args = new ArrayList<>();
- @Option(title = "jobs",
- name = {"-j", "--jobs"},
+ @Parameters(index = "0", description = "The keyspace followed by one or many tables", arity = "0..1")
+ private String keyspace;
+
+ @Parameters (index = "1..*", description = "The tables to cleanup", arity = "0..*")
+ private String[] tables;
+
+ @Option(paramLabel = "jobs",
+ names = {"-j", "--jobs"},
description = "Number of sstables to cleanup simultanously, set to 0 to use all available compaction threads")
private int jobs = 2;
@Override
public void execute(NodeProbe probe)
{
- List keyspaces = parseOptionalKeyspace(args, probe, KeyspaceSet.NON_LOCAL_STRATEGY);
+ args = concatArgs(keyspace, tables);
+
+ List keyspaces = parseOptionalKeyspaceNonLocal(args, probe);
String[] tableNames = parseOptionalTables(args);
for (String keyspace : keyspaces)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/ClearSnapshot.java b/src/java/org/apache/cassandra/tools/nodetool/ClearSnapshot.java
index 866b85d3d5..869531961c 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/ClearSnapshot.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/ClearSnapshot.java
@@ -17,13 +17,6 @@
*/
package org.apache.cassandra.tools.nodetool;
-import static com.google.common.collect.Iterables.toArray;
-import static org.apache.commons.lang3.StringUtils.EMPTY;
-import static org.apache.commons.lang3.StringUtils.join;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import java.io.IOException;
import java.time.Instant;
import java.time.format.DateTimeParseException;
@@ -34,25 +27,33 @@ import java.util.Map;
import org.apache.cassandra.config.DurationSpec;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+import static com.google.common.collect.Iterables.toArray;
+import static org.apache.commons.lang3.StringUtils.EMPTY;
+import static org.apache.commons.lang3.StringUtils.join;
@Command(name = "clearsnapshot", description = "Remove the snapshot with the given name from the given keyspaces")
-public class ClearSnapshot extends NodeToolCmd
+public class ClearSnapshot extends AbstractCommand
{
- @Arguments(usage = "[...] ", description = "Remove snapshots from the given keyspaces")
+ @CassandraUsage(usage = "[...]", description = "Remove snapshots from the given keyspaces")
+ @Parameters(description = "Remove snapshots from the given keyspaces", arity = "0..*")
private List keyspaces = new ArrayList<>();
- @Option(title = "snapshot_name", name = "-t", description = "Remove the snapshot with a given name")
+ @Option(paramLabel = "snapshot_name", names = "-t", description = "Remove the snapshot with a given name")
private String snapshotName = EMPTY;
- @Option(title = "clear_all_snapshots", name = "--all", description = "Removes all snapshots")
+ @Option(paramLabel = "clear_all_snapshots", names = "--all", description = "Removes all snapshots")
private boolean clearAllSnapshots = false;
- @Option(title = "older_than", name = "--older-than", description = "Clear snapshots older than specified time period.")
+ @Option(paramLabel = "older_than", names = "--older-than", description = "Clear snapshots older than specified time period.")
private String olderThan;
- @Option(title = "older_than_timestamp", name = "--older-than-timestamp",
- description = "Clear snapshots older than specified timestamp. It has to be a string in ISO format, for example '2022-12-03T10:15:30Z'")
+ @Option(paramLabel = "older_than_timestamp", names = "--older-than-timestamp",
+ description = "Clear snapshots older than specified timestamp. It has to be a string in ISO format, for example '2022-12-03T10:15:30Z'")
private String olderThanTimestamp;
@Override
@@ -121,7 +122,8 @@ public class ClearSnapshot extends NodeToolCmd
parameters.put("older_than_timestamp", olderThanTimestamp);
probe.clearSnapshot(parameters, snapshotName, toArray(keyspaces, String.class));
- } catch (IOException e)
+ }
+ catch (IOException e)
{
throw new RuntimeException("Error during clearing snapshots", e);
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/ClientStats.java b/src/java/org/apache/cassandra/tools/nodetool/ClientStats.java
index fcc9d76629..10b29bbb56 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/ClientStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/ClientStats.java
@@ -26,30 +26,30 @@ import java.util.Map.Entry;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
+
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
import org.apache.cassandra.transport.ClientStat;
import org.apache.cassandra.transport.ConnectedClient;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "clientstats", description = "Print information about connected clients")
-public class ClientStats extends NodeToolCmd
+public class ClientStats extends AbstractCommand
{
- @Option(title = "list_connections", name = "--all", description = "Lists all connections")
+ @Option(paramLabel = "list_connections", names = "--all", description = "Lists all connections")
private boolean listConnections = false;
- @Option(title = "by_protocol", name = "--by-protocol", description = "Lists most recent client connections by protocol version")
+ @Option(paramLabel = "by_protocol", names = "--by-protocol", description = "Lists most recent client connections by protocol version")
private boolean connectionsByProtocolVersion = false;
- @Option(title = "clear_history", name = "--clear-history", description = "Clear the history of connected clients")
+ @Option(paramLabel = "clear_history", names = "--clear-history", description = "Clear the history of connected clients")
private boolean clearConnectionHistory = false;
- @Option(title = "list_connections_with_client_options", name = "--client-options", description = "Lists all connections and the client options")
+ @Option(paramLabel = "list_connections_with_client_options", names = "--client-options", description = "Lists all connections and the client options")
private boolean clientOptions = false;
- @Option(title = "verbose", name = "--verbose", description = "Lists all connections with additional details (client options, authenticator-specific metadata and more)")
+ @Option(paramLabel = "verbose", names = "--verbose", description = "Lists all connections with additional details (client options, authenticator-specific metadata and more)")
private boolean verbose = false;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/CommandUtils.java b/src/java/org/apache/cassandra/tools/nodetool/CommandUtils.java
new file mode 100644
index 0000000000..8685ba1ce8
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/CommandUtils.java
@@ -0,0 +1,259 @@
+/*
+ * 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 java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.google.common.collect.Maps;
+
+import org.apache.cassandra.locator.EndpointSnitchInfoMBean;
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import org.apache.cassandra.utils.Pair;
+
+import static com.google.common.collect.Iterables.toArray;
+import static java.util.stream.Stream.concat;
+import static java.util.stream.Stream.ofNullable;
+import static org.apache.commons.lang3.ArrayUtils.EMPTY_STRING_ARRAY;
+
+/**
+ * Utility methods for nodetool commands.
+ */
+public final class CommandUtils
+{
+ private CommandUtils() {}
+
+ public static int maxLength(Collection> any)
+ {
+ int result = 0;
+ for (Object value : any)
+ result = Math.max(result, String.valueOf(value).length());
+ return result;
+ }
+
+ public static Pair findCassandraBackwardCompatibleArgument(Object userObject)
+ {
+ Class> clazz = userObject.getClass();
+ do
+ {
+ for (Field field : clazz.getDeclaredFields())
+ {
+ if (field.isAnnotationPresent(CassandraUsage.class))
+ {
+ CassandraUsage ann = field.getAnnotation(CassandraUsage.class);
+ return Pair.create(ann.usage(), ann.description());
+ }
+ }
+ }
+ while ((clazz = clazz.getSuperclass()) != null);
+ return null;
+ }
+
+ public static String[] sortShortestFirst(String[] names)
+ {
+ Arrays.sort(names, Comparator.comparing(String::length));
+ return names;
+ }
+
+ public static List concatArgs(String first, String second)
+ {
+ return concat(ofNullable(first), ofNullable(second)).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, String second, String third)
+ {
+ return concat(ofNullable(first), concatArgs(second, third).stream()).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, String second, String third, String fourth)
+ {
+ return concat(ofNullable(first), concatArgs(second, third, fourth).stream()).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, String[] second)
+ {
+ return concat(ofNullable(first), (second == null ? Stream.empty() : Arrays.stream(second))).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, List second)
+ {
+ return concat(ofNullable(first), (second == null ? Stream.empty() : second.stream())).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, String second, String[] third)
+ {
+ return concat(ofNullable(first), concatArgs(second, third).stream()).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, String second, List third)
+ {
+ return concat(ofNullable(first), concatArgs(second, third).stream()).collect(Collectors.toList());
+ }
+
+ public static List concatArgs(String first, String second, String third, String[] fourth)
+ {
+ return concat(ofNullable(first), concatArgs(second, third, fourth).stream()).collect(Collectors.toList());
+ }
+
+ public static void printSet(PrintStream out, String colName, Set values)
+ {
+ if (values == null || values.isEmpty())
+ return;
+
+ TableBuilder table = new TableBuilder();
+
+ table.add(colName + ": ");
+
+ for (String value : values)
+ table.add(value);
+
+ table.printTo(out);
+ }
+
+ public static List parseOptionalKeyspaceAccordManaged(List cmdArgs, NodeProbe nodeProbe)
+ {
+ return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.ACCORD_MANAGED);
+ }
+
+ public static List parseOptionalKeyspace(List cmdArgs, NodeProbe nodeProbe)
+ {
+ return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.ALL);
+ }
+
+ public static List parseOptionalKeyspaceNonLocal(List cmdArgs, NodeProbe nodeProbe)
+ {
+ return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.NON_LOCAL_STRATEGY);
+ }
+
+ private static List parseOptionalKeyspace(List cmdArgs, NodeProbe nodeProbe, KeyspaceSet defaultKeyspaceSet)
+ {
+ List keyspaces = new ArrayList<>();
+
+
+ if (cmdArgs == null || cmdArgs.isEmpty())
+ {
+ if (defaultKeyspaceSet == KeyspaceSet.NON_LOCAL_STRATEGY)
+ keyspaces.addAll(keyspaces = nodeProbe.getNonLocalStrategyKeyspaces());
+ else if (defaultKeyspaceSet == KeyspaceSet.NON_SYSTEM)
+ keyspaces.addAll(keyspaces = nodeProbe.getNonSystemKeyspaces());
+ else if (defaultKeyspaceSet == KeyspaceSet.ACCORD_MANAGED)
+ keyspaces.addAll(nodeProbe.getAccordManagedKeyspaces());
+
+ else
+ keyspaces.addAll(nodeProbe.getKeyspaces());
+ }
+ else
+ {
+ keyspaces.add(cmdArgs.get(0));
+ }
+
+ for (String keyspace : keyspaces)
+ {
+ if (!nodeProbe.getKeyspaces().contains(keyspace))
+ throw new IllegalArgumentException("Keyspace [" + keyspace + "] does not exist.");
+ }
+
+ return Collections.unmodifiableList(keyspaces);
+ }
+
+ /**
+ * Parses the optional table names from the command arguments for nodetool commands.
+ *
+ * The nodetool commands can operate on either all tables within a keyspace, or on a specific
+ * subset of tables. This method extracts the table names from the provided cli arguments, assuming
+ * the first argument is the keyspace name and any subsequent arguments are table names.
+ *
+ * If no table names are provided (e.g. only the keyspace is specified), this method returns
+ * an empty array, which signals to the MBeans that the operation should apply to all tables
+ * in the keyspace. This approach provides flexibility to target either all tables or specific
+ * tables as needed.
+ *
+ * @param cmdArgs the list of command arguments, where the first argument is typically the
+ * keyspace name (ignored), and any subsequent arguments are table names
+ * @return an array of table names, or an empty array (meaning 'all tables') if no extra args are specified.
+ */
+ public static String[] parseOptionalTables(List cmdArgs)
+ {
+ return cmdArgs.size() <= 1 ? EMPTY_STRING_ARRAY : toArray(cmdArgs.subList(1, cmdArgs.size()), String.class);
+ }
+
+ /**
+ * Parses the optional partition key values from the command arguments for nodetool commands.
+ *
+ * Some nodetool commands can operate on a specific partition within a table, which requires
+ * specifying the partition key values after the keyspace and table names in the cli arguments.
+ * This method extracts the partition keys from the provided command arguments, assuming
+ * the first argument is the keyspace name, the second is the table name, and any subsequent
+ * arguments are partition key values.
+ *
+ * If no partition key values are provided (e.g. only keyspace and table are specified), this
+ * method returns an empty array, which signals to the MBeans or command logic that the operation
+ * should apply to all partitions.
+ *
+ * @param cmdArgs the list of command arguments, where the first argument is the keyspace name,
+ * the second is the table name, and any subsequent arguments are partition key values
+ * @return an array of partition key values, or an empty array if none are specified (meaning 'all partitions').
+ */
+ public static String[] parsePartitionKeys(List cmdArgs)
+ {
+ return cmdArgs.size() <= 2 ? EMPTY_STRING_ARRAY : toArray(cmdArgs.subList(2, cmdArgs.size()), String.class);
+ }
+
+ public static SortedMap getOwnershipByDcWithPort(NodeProbe probe, boolean resolveIp,
+ Map tokenToEndpoint,
+ Map ownerships)
+ {
+ SortedMap ownershipByDc = Maps.newTreeMap();
+ EndpointSnitchInfoMBean epSnitchInfo = probe.getEndpointSnitchInfoProxy();
+ try
+ {
+ for (Map.Entry tokenAndEndPoint : tokenToEndpoint.entrySet())
+ {
+ String dc = epSnitchInfo.getDatacenter(tokenAndEndPoint.getValue());
+ if (!ownershipByDc.containsKey(dc))
+ ownershipByDc.put(dc, new SetHostStatWithPort(resolveIp));
+ ownershipByDc.get(dc).add(tokenAndEndPoint.getKey(), tokenAndEndPoint.getValue(), ownerships);
+ }
+ }
+ catch (UnknownHostException e)
+ {
+ throw new RuntimeException(e);
+ }
+ return ownershipByDc;
+ }
+
+ private enum KeyspaceSet
+ {
+ ALL, NON_SYSTEM, NON_LOCAL_STRATEGY, ACCORD_MANAGED
+ }
+}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Compact.java b/src/java/org/apache/cassandra/tools/nodetool/Compact.java
index 1f8b4c4b46..c776592b65 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Compact.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Compact.java
@@ -17,41 +17,46 @@
*/
package org.apache.cassandra.tools.nodetool;
-import static org.apache.commons.lang3.StringUtils.EMPTY;
-
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspace;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables;
+import static org.apache.commons.lang3.StringUtils.EMPTY;
+
+// TODO CASSANDRA-20793 Types of input aguments shouldn't be mixed in the same command. The keyspace, table and SSTable file arguments should have their own commands.
@Command(name = "compact", description = "Force a (major) compaction on one or more tables or user-defined compaction on given SSTables")
-public class Compact extends NodeToolCmd
+public class Compact extends AbstractCommand
{
- @Arguments(usage = "[ ...] or ...", description = "The keyspace followed by one or many tables or list of SSTable data files when using --user-defined")
+ @CassandraUsage(usage = "[ ...] or ...",
+ description = "The keyspace followed by one or many tables or list of SSTable data files when using --user-defined")
+ @Parameters(index = "0..*", description = "The keyspace followed by one or many tables or " +
+ "list of SSTable data files when using --user-defined")
private List args = new ArrayList<>();
- @Option(title = "split_output", name = {"-s", "--split-output"}, description = "Use -s to not create a single big file")
+ @Option(paramLabel = "split_output", names = { "-s", "--split-output" }, description = "Use -s to not create a single big file")
private boolean splitOutput = false;
- @Option(title = "user-defined", name = {"--user-defined"}, description = "Use --user-defined to submit listed files for user-defined compaction")
+ @Option(paramLabel = "user_defined", names = { "--user-defined" }, description = "Use --user-defined to submit listed files for user-defined compaction")
private boolean userDefined = false;
- @Option(title = "start_token", name = {"-st", "--start-token"}, description = "Use -st to specify a token at which the compaction range starts (inclusive)")
+ @Option(paramLabel = "start_token", names = { "-st", "--start-token" }, description = "Use -st to specify a token at which the compaction range starts (inclusive)")
private String startToken = EMPTY;
- @Option(title = "end_token", name = {"-et", "--end-token"}, description = "Use -et to specify a token at which compaction range ends (inclusive)")
+ @Option(paramLabel = "end_token", names = { "-et", "--end-token" }, description = "Use -et to specify a token at which compaction range ends (inclusive)")
private String endToken = EMPTY;
- @Option(title = "partition_key", name = {"--partition"}, description = "String representation of the partition key")
+ @Option(paramLabel = "partition_key", names = { "--partition" }, description = "String representation of the partition key")
private String partitionKey = EMPTY;
- @Option(title = "jobs",
- name = {"-j", "--jobs"},
+ @Option(paramLabel = "jobs",
+ names = {"-j", "--jobs"},
description = "Use -j to specify the maximum number of threads to use for parallel compaction. " +
"If not set, up to half the compaction threads will be used. " +
"If set to 0, the major compaction will use all threads and will not permit other compactions to run until it completes (use with caution).")
diff --git a/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java b/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java
index e963d7a1c4..0eb4a32fda 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/CompactionHistory.java
@@ -17,26 +17,25 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import org.apache.cassandra.tools.nodetool.stats.CompactionHistoryHolder;
import org.apache.cassandra.tools.nodetool.stats.CompactionHistoryPrinter;
import org.apache.cassandra.tools.nodetool.stats.StatsHolder;
import org.apache.cassandra.tools.nodetool.stats.StatsPrinter;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "compactionhistory", description = "Print history of compaction")
-public class CompactionHistory extends NodeToolCmd
+public class CompactionHistory extends AbstractCommand
{
- @Option(title = "format",
- name = {"-F", "--format"},
+ @Option(paramLabel = "format",
+ names = { "-F", "--format" },
description = "Output format (json, yaml)")
private String outputFormat = "";
- @Option(title = "human_readable",
- name = {"-H", "--human-readable"},
- description = "Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB")
+ @Option(paramLabel = "human_readable",
+ names = { "-H", "--human-readable" },
+ description = "Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB")
private boolean humanReadable = false;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java b/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java
index f76d4d0191..51a675c5ac 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/CompactionStats.java
@@ -24,29 +24,27 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import org.apache.cassandra.db.compaction.CompactionInfo;
import org.apache.cassandra.db.compaction.CompactionInfo.Unit;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.metrics.CassandraMetricsRegistry;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
import static java.lang.String.format;
@Command(name = "compactionstats", description = "Print statistics on compactions")
-public class CompactionStats extends NodeToolCmd
+public class CompactionStats extends AbstractCommand
{
- @Option(title = "human_readable",
- name = {"-H", "--human-readable"},
+ @Option(paramLabel = "human_readable",
+ names = { "-H", "--human-readable" },
description = "Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB")
private boolean humanReadable = false;
- @Option(title = "vtable_output",
- name = {"-V", "--vtable"},
+ @Option(paramLabel = "vtable_output",
+ names = { "-V", "--vtable" },
description = "Display fields matching vtable output")
private boolean vtableOutput = false;
diff --git a/src/java/org/apache/cassandra/tools/nodetool/ConsensusMigrationAdmin.java b/src/java/org/apache/cassandra/tools/nodetool/ConsensusMigrationAdmin.java
index 6553ddf9fd..7135ee7158 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/ConsensusMigrationAdmin.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/ConsensusMigrationAdmin.java
@@ -24,35 +24,53 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.service.consensus.migration.ConsensusMigrationTarget;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
import org.apache.cassandra.tools.RepairRunner.RepairCmd;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Collections.singleton;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspaceAccordManaged;
/**
* For managing migration from one consensus protocol to another.
*
* Mark ranges as migrating, and list the migrating ranges.
*/
-public abstract class ConsensusMigrationAdmin extends NodeTool.NodeToolCmd
+@CommandLine.Command(name = "consensus_admin", description = "List and mark ranges as migrating between consensus protocols",
+ subcommands = { ConsensusMigrationAdmin.BeginMigration.class,
+ ConsensusMigrationAdmin.FinishMigration.class,
+ ConsensusMigrationAdmin.ListCmd.class })
+public class ConsensusMigrationAdmin extends AbstractCommand
{
- @Command(name = "list", description = "List migrating tables and ranges")
- public static class ListCmd extends ConsensusMigrationAdmin
+ @Override
+ protected void execute(NodeProbe probe)
{
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
- private List schemaArgs = new ArrayList<>();
+ AbstractCommand cmd = new CMSAdmin.DescribeCMS();
+ cmd.probe(probe);
+ cmd.logger(output);
+ cmd.run();
+ }
- @Option(title = "format", name = {"-f", "--format"}, description = "Output format, YAML and JSON are the only supported formats, default YAML, prefix with `minified-` to turn off pretty printing")
+ @Command(name = "list", description = "List migrating tables and ranges")
+ public static class ListCmd extends AbstractCommand
+ {
+ @Parameters(index = "0", arity = "0..1", description = "The keyspace followed by one or many tables")
+ public String keyspace;
+
+ @Parameters(index = "1..*", arity = "0..*", description = "The tables")
+ public String[] tables;
+
+ @Option(names = {"-f", "--format"}, description = "Output format, YAML and JSON are the only supported formats, default YAML, prefix with `minified-` to turn off pretty printing")
private String format = "yaml";
protected void execute(NodeProbe probe)
{
+ List schemaArgs = CommandUtils.concatArgs(keyspace, tables);
Set keyspaceNames = schemaArgs.size() > 0 ? singleton(schemaArgs.get(0)) : null;
Set tableNames = schemaArgs.size() > 1 ? new HashSet<>(schemaArgs.subList(1, schemaArgs.size())) : null;
String output = probe.getStorageService().listConsensusMigrations(keyspaceNames, tableNames, format);
@@ -61,22 +79,26 @@ public abstract class ConsensusMigrationAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "begin-migration", description = "Mark the range as migrating for the specified token range and tables")
- public static class BeginMigration extends ConsensusMigrationAdmin
+ public static class BeginMigration extends AbstractCommand
{
- @Option(title = "start_token", name = {"-st", "--start-token"}, description = "Use -st to specify a token at which the repair range starts")
+ @Option(paramLabel = "start_token", names = { "-st", "--start-token" }, description = "Use -st to specify a token at which the repair range starts")
private String startToken = null;
- @Option(title = "end_token", name = {"-et", "--end-token"}, description = "Use -et to specify a token at which repair range ends")
+ @Option(paramLabel = "end_token", names = { "-et", "--end-token" }, description = "Use -et to specify a token at which repair range ends")
private String endToken = null;
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
- private List schemaArgs = new ArrayList<>();
+ @Parameters(index = "0", arity = "0..1", description = "The keyspace followed by one or many tables")
+ private String keyspace;
+
+ @Parameters(index = "1..*", arity = "0..*", description = "The tables")
+ private String[] tables;
protected void execute(NodeProbe probe)
{
checkArgument((endToken != null && startToken != null) || (endToken == null && startToken == null), "Must specify start and end token together");
+ List schemaArgs = CommandUtils.concatArgs(keyspace, tables);
String maybeRangesStr = startToken != null ? startToken + ":" + endToken : null;
- List keyspaceNames = parseOptionalKeyspace(schemaArgs, probe, KeyspaceSet.ACCORD_MANAGED);
+ List keyspaceNames = parseOptionalKeyspaceAccordManaged(schemaArgs, probe);
List maybeTableNames = schemaArgs.size() > 1 ? schemaArgs.subList(1, schemaArgs.size()) : null;
probe.getStorageService().migrateConsensusProtocol(keyspaceNames, maybeTableNames, maybeRangesStr);
probe.output().out.println("Marked requested ranges as migrating. Repair needs to be run in order to complete the migration");
@@ -84,16 +106,19 @@ public abstract class ConsensusMigrationAdmin extends NodeTool.NodeToolCmd
}
@Command(name = "finish-migration", description = "Complete the migration for a range that has already begun migration")
- public static class FinishMigration extends ConsensusMigrationAdmin
+ public static class FinishMigration extends AbstractCommand
{
- @Option(title = "start_token", name = {"-st", "--start-token"}, description = "Use -st to specify a token at which the repair range starts (exclusive)")
+ @Option(paramLabel = "start_token", names = {"-st", "--start-token"}, description = "Use -st to specify a token at which the repair range starts (exclusive)")
private String startToken = null;
- @Option(title = "end_token", name = {"-et", "--end-token"}, description = "Use -et to specify a token at which repair range ends (inclusive)")
+ @Option(paramLabel = "end_token", names = {"-et", "--end-token"}, description = "Use -et to specify a token at which repair range ends (inclusive)")
private String endToken = null;
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
- private List schemaArgs = new ArrayList<>();
+ @Parameters(index = "0", arity = "0..1", description = "The keyspace followed by one or many tables")
+ private String keyspace;
+
+ @Parameters(index = "1..*", arity = "0..*", description = "The tables")
+ private String[] tables;
private static class FinishMigrationRepairCommand extends RepairCmd
{
@@ -123,8 +148,9 @@ public abstract class ConsensusMigrationAdmin extends NodeTool.NodeToolCmd
protected void execute(NodeProbe probe)
{
checkArgument((endToken != null) == (startToken != null), "Start and end token must be specified together");
+ List schemaArgs = CommandUtils.concatArgs(keyspace, tables);
String maybeRangesStr = startToken != null ? startToken + ":" + endToken : null;
- List keyspaceNames = parseOptionalKeyspace(schemaArgs, probe, KeyspaceSet.ACCORD_MANAGED);
+ List keyspaceNames = parseOptionalKeyspaceAccordManaged(schemaArgs, probe);
List maybeTableNames = schemaArgs.size() > 1 ? schemaArgs.subList(1, schemaArgs.size()) : null;
List repairCmds = new ArrayList<>(keyspaceNames.size() * 2);
// Finish can't actually finish with one set of repairs when migrating from Paxos -> Accord
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DataPaths.java b/src/java/org/apache/cassandra/tools/nodetool/DataPaths.java
index 10ae01e8da..2af65534aa 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DataPaths.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DataPaths.java
@@ -20,22 +20,23 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
import org.apache.cassandra.tools.nodetool.stats.DataPathsHolder;
import org.apache.cassandra.tools.nodetool.stats.DataPathsPrinter;
import org.apache.cassandra.tools.nodetool.stats.StatsPrinter;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
@Command(name = "datapaths", description = "Print all directories where data of tables are stored")
-public class DataPaths extends NodeToolCmd
+public class DataPaths extends AbstractCommand
{
- @Arguments(usage = "[...]", description = "List of table (or keyspace) names")
+ @CassandraUsage(usage = "[...]", description = "List of table (or keyspace) names")
+ @Parameters(paramLabel = "keyspace.table", description = "List of table (or keyspace) names")
private List tableNames = new ArrayList<>();
- @Option(title = "format", name = {"-F", "--format"}, description = "Output format (json, yaml)")
+ @Option(paramLabel = "format", names = { "-F", "--format" }, description = "Output format (json, yaml)")
private String outputFormat = "";
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Decommission.java b/src/java/org/apache/cassandra/tools/nodetool/Decommission.java
index 2c326c9b29..59b509037a 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Decommission.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Decommission.java
@@ -17,18 +17,17 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "decommission", description = "Decommission the *node I am connecting to*")
-public class Decommission extends NodeToolCmd
+public class Decommission extends AbstractCommand
{
- @Option(title = "force",
- name = {"-f", "--force"},
- description = "Force decommission of this node even when it reduces the number of replicas to below configured RF")
+
+ @Option(paramLabel = "force",
+ names = { "-f", "--force" },
+ description = "Force decommission of this node even when it reduces the number of replicas to below configured RF")
private boolean force = false;
@Override
@@ -57,9 +56,9 @@ public class Decommission extends NodeToolCmd
}
@Command(name = "abortdecommission", description = "Abort an ongoing, failed decommission")
- public static class Abort extends NodeToolCmd
+ public static class Abort extends AbstractCommand
{
- @Option(title = "node id", name = "--node")
+ @Option(paramLabel = "nodeId", names = { "--node" })
private String nodeId;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java b/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java
index 1904eeb50e..11bf299286 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java
@@ -25,16 +25,14 @@ import java.util.SortedMap;
import com.google.common.collect.ArrayListMultimap;
-import io.airlift.airline.Command;
import org.apache.cassandra.locator.DynamicEndpointSnitch;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.LocationInfoMBean;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "describecluster", description = "Print the name, snitch, partitioner and schema version of a cluster")
-public class DescribeCluster extends NodeToolCmd
+public class DescribeCluster extends WithPortDisplayAbstractCommand
{
private boolean resolveIp = false;
private String keyspace = null;
@@ -114,7 +112,7 @@ public class DescribeCluster extends NodeToolCmd
System.exit(1);
}
- SortedMap dcs = NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships);
+ SortedMap dcs = CommandUtils.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships);
out.println("\nData Centers: ");
for (Map.Entry dc : dcs.entrySet())
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java b/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java
index d90e6bad9c..a975b7638a 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java
@@ -17,21 +17,20 @@
*/
package org.apache.cassandra.tools.nodetool;
-import static org.apache.commons.lang3.StringUtils.EMPTY;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.io.IOException;
import java.io.PrintStream;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
+import static org.apache.commons.lang3.StringUtils.EMPTY;
@Command(name = "describering", description = "Shows the token ranges info of a given keyspace")
-public class DescribeRing extends NodeToolCmd
+public class DescribeRing extends WithPortDisplayAbstractCommand
{
- @Arguments(description = "The keyspace name", required = true)
- String keyspace = EMPTY;
+ @Parameters(description = "The keyspace name", arity = "1")
+ private String keyspace = EMPTY;
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableAuditLog.java b/src/java/org/apache/cassandra/tools/nodetool/DisableAuditLog.java
index 6d878a0b16..9ad15c21f8 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableAuditLog.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableAuditLog.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "disableauditlog", description = "Disable the audit log")
-public class DisableAuditLog extends NodeToolCmd
+public class DisableAuditLog extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableAutoCompaction.java b/src/java/org/apache/cassandra/tools/nodetool/DisableAutoCompaction.java
index 39a4c76352..ecba7441ba 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableAutoCompaction.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableAutoCompaction.java
@@ -17,25 +17,36 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspace;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "disableautocompaction", description = "Disable autocompaction for the given keyspace and table")
-public class DisableAutoCompaction extends NodeToolCmd
+public class DisableAutoCompaction extends AbstractCommand
{
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
+ @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables")
private List args = new ArrayList<>();
+ @Parameters(index = "0", description = "The keyspace name", arity = "0..1")
+ private String keyspace;
+
+ @Parameters(index = "1..*", description = "The table names", arity = "0..*")
+ private String[] tables;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, tables);
+
List keyspaces = parseOptionalKeyspace(args, probe);
String[] tablenames = parseOptionalTables(args);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableBackup.java b/src/java/org/apache/cassandra/tools/nodetool/DisableBackup.java
index 4ee6340da8..f10f65a2e2 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableBackup.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableBackup.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "disablebackup", description = "Disable incremental backup")
-public class DisableBackup extends NodeToolCmd
+public class DisableBackup extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableBinary.java b/src/java/org/apache/cassandra/tools/nodetool/DisableBinary.java
index 11e024f289..28513a2b54 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableBinary.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableBinary.java
@@ -17,15 +17,14 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "disablebinary", description = "Disable native transport (binary protocol)")
-public class DisableBinary extends NodeToolCmd
+public class DisableBinary extends AbstractCommand
{
- @Option(title = "force", name = { "-f", "--force"}, description = "Use -f to interrupt client requests that have already started")
+ @Option(paramLabel = "force", names = { "-f", "--force" }, description = "Use -f to interrupt client requests that have already started")
private boolean force = false;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableFullQueryLog.java b/src/java/org/apache/cassandra/tools/nodetool/DisableFullQueryLog.java
index aa5d0b7092..d3a41d9faf 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableFullQueryLog.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableFullQueryLog.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "disablefullquerylog", description = "Disable the full query log")
-public class DisableFullQueryLog extends NodeToolCmd
+public class DisableFullQueryLog extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableGossip.java b/src/java/org/apache/cassandra/tools/nodetool/DisableGossip.java
index 7b6c348549..f60ae02bcd 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableGossip.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableGossip.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "disablegossip", description = "Disable gossip (effectively marking the node down)")
-public class DisableGossip extends NodeToolCmd
+public class DisableGossip extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableHandoff.java b/src/java/org/apache/cassandra/tools/nodetool/DisableHandoff.java
index 62465a395b..00a8251389 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableHandoff.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableHandoff.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "disablehandoff", description = "Disable storing hinted handoffs")
-public class DisableHandoff extends NodeToolCmd
+public class DisableHandoff extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableHintsForDC.java b/src/java/org/apache/cassandra/tools/nodetool/DisableHintsForDC.java
index 3615a99730..b78d6ab909 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableHintsForDC.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableHintsForDC.java
@@ -20,17 +20,17 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
import static com.google.common.base.Preconditions.checkArgument;
@Command(name = "disablehintsfordc", description = "Disable hints for a data center")
-public class DisableHintsForDC extends NodeTool.NodeToolCmd
+public class DisableHintsForDC extends AbstractCommand
{
- @Arguments(usage = "", description = "The data center to disable")
+ @Parameters(paramLabel = "datacenter", description = "The data center to disable")
private List args = new ArrayList<>();
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DisableOldProtocolVersions.java b/src/java/org/apache/cassandra/tools/nodetool/DisableOldProtocolVersions.java
index 875647127c..8577d0f4bb 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DisableOldProtocolVersions.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DisableOldProtocolVersions.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import picocli.CommandLine.Command;
@Command(name = "disableoldprotocolversions", description = "Disable old protocol versions")
-public class DisableOldProtocolVersions extends NodeTool.NodeToolCmd
+public class DisableOldProtocolVersions extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Drain.java b/src/java/org/apache/cassandra/tools/nodetool/Drain.java
index a152057798..3cbc2e6a4d 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Drain.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Drain.java
@@ -17,16 +17,15 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "drain", description = "Drain the node (stop accepting writes and flush all tables)")
-public class Drain extends NodeToolCmd
+public class Drain extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/DropCIDRGroup.java b/src/java/org/apache/cassandra/tools/nodetool/DropCIDRGroup.java
index 2b432ea9f7..c2ccae560a 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/DropCIDRGroup.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/DropCIDRGroup.java
@@ -17,34 +17,26 @@
*/
package org.apache.cassandra.tools.nodetool;
-import java.util.ArrayList;
-import java.util.List;
-
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.auth.AuthKeyspace;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
-import static com.google.common.base.Preconditions.checkArgument;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
/**
* Nodetool command to drop a CIDR group and associated mapping from the table {@link AuthKeyspace#CIDR_GROUPS}
*/
@Command(name = "dropcidrgroup", description = "Drop an existing cidr group")
-public class DropCIDRGroup extends NodeToolCmd
+public class DropCIDRGroup extends AbstractCommand
{
- @Arguments(usage = "", description = "Requires a cidr group name")
- private List args = new ArrayList<>();
+ @Parameters(paramLabel = "cidrGroup", description = "Requires a cidr group name", index = "0", arity = "1")
+ private String cidrGroup;
@Override
public void execute(NodeProbe probe)
{
- checkArgument(args.size() == 1, "dropcidrgroup command requires a cidr group name");
+ probe.dropCidrGroup(cidrGroup);
- String cidrGroupName = args.get(0);
- probe.dropCidrGroup(cidrGroupName);
-
- probe.output().out.println("Deleted CIDR group " + cidrGroupName);
+ probe.output().out.println("Deleted CIDR group " + cidrGroup);
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableAuditLog.java b/src/java/org/apache/cassandra/tools/nodetool/EnableAuditLog.java
index cf9d05ee64..265aada48f 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableAuditLog.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableAuditLog.java
@@ -20,54 +20,53 @@ package org.apache.cassandra.tools.nodetool;
import java.util.Collections;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "enableauditlog", description = "Enable the audit log")
-public class EnableAuditLog extends NodeToolCmd
+public class EnableAuditLog extends AbstractCommand
{
- @Option(title = "logger", name = { "--logger" }, description = "Logger name to be used for AuditLogging. Default BinAuditLogger. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "logger", names = { "--logger" }, description = "Logger name to be used for AuditLogging. Default BinAuditLogger. If not set the value from cassandra.yaml will be used")
private String logger = null;
- @Option(title = "included_keyspaces", name = { "--included-keyspaces" }, description = "Comma separated list of keyspaces to be included for audit log. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "included_keyspaces", names = { "--included-keyspaces" }, description = "Comma separated list of keyspaces to be included for audit log. If not set the value from cassandra.yaml will be used")
private String included_keyspaces = null;
- @Option(title = "excluded_keyspaces", name = { "--excluded-keyspaces" }, description = "Comma separated list of keyspaces to be excluded for audit log. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "excluded_keyspaces", names = { "--excluded-keyspaces" }, description = "Comma separated list of keyspaces to be excluded for audit log. If not set the value from cassandra.yaml will be used")
private String excluded_keyspaces = null;
- @Option(title = "included_categories", name = { "--included-categories" }, description = "Comma separated list of Audit Log Categories to be included for audit log. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "included_categories", names = { "--included-categories" }, description = "Comma separated list of Audit Log Categories to be included for audit log. If not set the value from cassandra.yaml will be used")
private String included_categories = null;
- @Option(title = "excluded_categories", name = { "--excluded-categories" }, description = "Comma separated list of Audit Log Categories to be excluded for audit log. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "excluded_categories", names = { "--excluded-categories" }, description = "Comma separated list of Audit Log Categories to be excluded for audit log. If not set the value from cassandra.yaml will be used")
private String excluded_categories = null;
- @Option(title = "included_users", name = { "--included-users" }, description = "Comma separated list of users to be included for audit log. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "included_users", names = { "--included-users" }, description = "Comma separated list of users to be included for audit log. If not set the value from cassandra.yaml will be used")
private String included_users = null;
- @Option(title = "excluded_users", name = { "--excluded-users" }, description = "Comma separated list of users to be excluded for audit log. If not set the value from cassandra.yaml will be used")
+ @Option(paramLabel = "excluded_users", names = { "--excluded-users" }, description = "Comma separated list of users to be excluded for audit log. If not set the value from cassandra.yaml will be used")
private String excluded_users = null;
- @Option(title = "roll_cycle", name = {"--roll-cycle"}, description = "How often to roll the log file (MINUTELY, HOURLY, DAILY).")
+ @Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file (MINUTELY, HOURLY, DAILY).")
private String rollCycle = null;
- @Option(title = "blocking", name = {"--blocking"}, description = "If the queue is full whether to block producers or drop samples [true|false].")
+ @Option(paramLabel = "blocking", names = { "--blocking" }, description = "If the queue is full whether to block producers or drop samples [true|false].")
private String blocking = null;
- @Option(title = "max_queue_weight", name = {"--max-queue-weight"}, description = "Maximum number of bytes of query data to queue to disk before blocking or dropping samples.")
+ @Option(paramLabel = "max_queue_weight", names = { "--max-queue-weight" }, description = "Maximum number of bytes of query data to queue to disk before blocking or dropping samples.")
private int maxQueueWeight = Integer.MIN_VALUE;
- @Option(title = "max_log_size", name = {"--max-log-size"}, description = "How many bytes of log data to store before dropping segments. Might not be respected if a log file hasn't rolled so it can be deleted.")
+ @Option(paramLabel = "max_log_size", names = { "--max-log-size" }, description = "How many bytes of log data to store before dropping segments. Might not be respected if a log file hasn't rolled so it can be deleted.")
private long maxLogSize = Long.MIN_VALUE;
- @Option(title = "archive_command", name = {"--archive-command"}, description = "Command that will handle archiving rolled audit log files." +
- " Format is \"/path/to/script.sh %path\" where %path will be replaced with the file to archive" +
+ @Option(paramLabel = "archive_command", names = { "--archive-command" }, description = "Command that will handle archiving rolled audit log files." +
+ " Format is \"/path/to/script.sh %%path\" where %%path will be replaced with the file to archive" +
" Enable this by setting the audit_logging_options.allow_nodetool_archive_command: true in the config.")
private String archiveCommand = null;
- @Option(title = "archive_retries", name = {"--max-archive-retries"}, description = "Max number of archive retries.")
+ @Option(paramLabel = "archive_retries", names = { "--max-archive-retries" }, description = "Max number of archive retries.")
private int archiveRetries = Integer.MIN_VALUE;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableAutoCompaction.java b/src/java/org/apache/cassandra/tools/nodetool/EnableAutoCompaction.java
index f8b98ff289..7c098e3f32 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableAutoCompaction.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableAutoCompaction.java
@@ -17,25 +17,37 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspace;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "enableautocompaction", description = "Enable autocompaction for the given keyspace and table")
-public class EnableAutoCompaction extends NodeToolCmd
+public class EnableAutoCompaction extends AbstractCommand
{
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
+ @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables")
private List args = new ArrayList<>();
+ @Parameters(index = "0", description = "The keyspace to enable auto-compaction on", arity = "0..1")
+ private String keyspace;
+
+ @Parameters(index = "1..*", description = "The tables to enable auto-compaction on", arity = "0..*")
+ private String[] tables;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, tables);
+
List keyspaces = parseOptionalKeyspace(args, probe);
String[] tableNames = parseOptionalTables(args);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableBackup.java b/src/java/org/apache/cassandra/tools/nodetool/EnableBackup.java
index 7ebad8a4d8..9e5611ee4e 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableBackup.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableBackup.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "enablebackup", description = "Enable incremental backup")
-public class EnableBackup extends NodeToolCmd
+public class EnableBackup extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableBinary.java b/src/java/org/apache/cassandra/tools/nodetool/EnableBinary.java
index 2e37e6ff4c..17fd640f4f 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableBinary.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableBinary.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "enablebinary", description = "Reenable native transport (binary protocol)")
-public class EnableBinary extends NodeToolCmd
+public class EnableBinary extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableFullQueryLog.java b/src/java/org/apache/cassandra/tools/nodetool/EnableFullQueryLog.java
index 511730d269..7f8f2ddfd4 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableFullQueryLog.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableFullQueryLog.java
@@ -18,35 +18,34 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "enablefullquerylog", description = "Enable full query logging, defaults for the options are configured in cassandra.yaml")
-public class EnableFullQueryLog extends NodeToolCmd
+public class EnableFullQueryLog extends AbstractCommand
{
- @Option(title = "roll_cycle", name = {"--roll-cycle"}, description = "How often to roll the log file (MINUTELY, HOURLY, DAILY).")
+ @Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file (MINUTELY, HOURLY, DAILY).")
private String rollCycle = null;
- @Option(title = "blocking", name = {"--blocking"}, description = "If the queue is full whether to block producers or drop samples [true|false].")
+ @Option(paramLabel = "blocking", names = { "--blocking" }, description = "If the queue is full whether to block producers or drop samples [true|false].")
private String blocking = null;
- @Option(title = "max_queue_weight", name = {"--max-queue-weight"}, description = "Maximum number of bytes of query data to queue to disk before blocking or dropping samples.")
+ @Option(paramLabel = "max_queue_weight", names = { "--max-queue-weight" }, description = "Maximum number of bytes of query data to queue to disk before blocking or dropping samples.")
private int maxQueueWeight = Integer.MIN_VALUE;
- @Option(title = "max_log_size", name = {"--max-log-size"}, description = "How many bytes of log data to store before dropping segments. Might not be respected if a log file hasn't rolled so it can be deleted.")
+ @Option(paramLabel = "max_log_size", names = { "--max-log-size" }, description = "How many bytes of log data to store before dropping segments. Might not be respected if a log file hasn't rolled so it can be deleted.")
private long maxLogSize = Long.MIN_VALUE;
- @Option(title = "path", name = {"--path"}, description = "Path to store the full query log at. Will have it's contents recursively deleted.")
+ @Option(paramLabel = "path", names = { "--path" }, description = "Path to store the full query log at. Will have it's contents recursively deleted.")
private String path = null;
- @Option(title = "archive_command", name = {"--archive-command"}, description = "Command that will handle archiving rolled full query log files." +
- " Format is \"/path/to/script.sh %path\" where %path will be replaced with the file to archive" +
+ @Option(paramLabel = "archive_command", names = { "--archive-command" }, description = "Command that will handle archiving rolled full query log files." +
+ " Format is \"/path/to/script.sh %%path\" where %%path will be replaced with the file to archive" +
" Enable this by setting the full_query_logging_options.allow_nodetool_archive_command: true in the config.")
private String archiveCommand = null;
- @Option(title = "archive_retries", name = {"--max-archive-retries"}, description = "Max number of archive retries.")
+ @Option(paramLabel = "archive_retries", names = { "--max-archive-retries" }, description = "Max number of archive retries.")
private int archiveRetries = Integer.MIN_VALUE;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableGossip.java b/src/java/org/apache/cassandra/tools/nodetool/EnableGossip.java
index 3433c3ec61..a0c53e21b7 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableGossip.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableGossip.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "enablegossip", description = "Reenable gossip")
-public class EnableGossip extends NodeToolCmd
+public class EnableGossip extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableHandoff.java b/src/java/org/apache/cassandra/tools/nodetool/EnableHandoff.java
index be64e120eb..cf77ead527 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableHandoff.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableHandoff.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "enablehandoff", description = "Reenable future hints storing on the current node")
-public class EnableHandoff extends NodeToolCmd
+public class EnableHandoff extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableHintsForDC.java b/src/java/org/apache/cassandra/tools/nodetool/EnableHintsForDC.java
index 97e40e0856..3ad7068708 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableHintsForDC.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableHintsForDC.java
@@ -17,26 +17,19 @@
*/
package org.apache.cassandra.tools.nodetool;
-import java.util.ArrayList;
-import java.util.List;
-
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
-import static com.google.common.base.Preconditions.checkArgument;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "enablehintsfordc", description = "Enable hints for a data center that was previsouly disabled")
-public class EnableHintsForDC extends NodeTool.NodeToolCmd
+public class EnableHintsForDC extends AbstractCommand
{
- @Arguments(usage = "", description = "The data center to enable")
- private List args = new ArrayList<>();
+ @Parameters(paramLabel = "datacenter", description = "The data center to enable", index = "0", arity = "1")
+ private String datacenter;
public void execute(NodeProbe probe)
{
- checkArgument(args.size() == 1, "enablehintsfordc requires exactly one data center");
-
- probe.enableHintsForDC(args.get(0));
+ probe.enableHintsForDC(datacenter);
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/EnableOldProtocolVersions.java b/src/java/org/apache/cassandra/tools/nodetool/EnableOldProtocolVersions.java
index 06c9f8d02f..abdfe0094b 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/EnableOldProtocolVersions.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/EnableOldProtocolVersions.java
@@ -18,13 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
-
+import picocli.CommandLine.Command;
@Command(name = "enableoldprotocolversions", description = "Enable old protocol versions")
-public class EnableOldProtocolVersions extends NodeTool.NodeToolCmd
+public class EnableOldProtocolVersions extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java b/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java
index 6431e0510d..185406e7f4 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java
@@ -17,18 +17,15 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import java.util.List;
-
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "failuredetector", description = "Shows the failure detector information for the cluster")
-public class FailureDetectorInfo extends NodeToolCmd
+public class FailureDetectorInfo extends WithPortDisplayAbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Flush.java b/src/java/org/apache/cassandra/tools/nodetool/Flush.java
index fb2446d22f..79e352fe14 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Flush.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Flush.java
@@ -17,24 +17,35 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspace;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "flush", description = "Flush one or more tables")
-public class Flush extends NodeToolCmd
+public class Flush extends AbstractCommand
{
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
+ @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables")
private List args = new ArrayList<>();
+ @Parameters(index = "0", description = "The keyspace followed by one or many tables to flush", arity = "0..1")
+ private String keyspace;
+
+ @Parameters(index = "1..*", description = "The tables to flush", arity = "0..*")
+ private String[] tables;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, tables);
+
List keyspaces = parseOptionalKeyspace(args, probe);
String[] tableNames = parseOptionalTables(args);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/ForceCompact.java b/src/java/org/apache/cassandra/tools/nodetool/ForceCompact.java
index 99265e7bf0..a00c033dbe 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/ForceCompact.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/ForceCompact.java
@@ -18,26 +18,37 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parsePartitionKeys;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "forcecompact", description = "Force a (major) compaction on a table")
-public class ForceCompact extends NodeToolCmd
+public class ForceCompact extends AbstractCommand
{
- @Arguments(usage = "[ ]", description = "The keyspace, table, and a list of partition keys ignoring the gc_grace_seconds")
+ @CassandraUsage(usage = "[ ]", description = "The keyspace, table, and a list of partition keys ignoring the gc_grace_seconds")
private List args = new ArrayList<>();
+ @Parameters(index = "0", description = "The keyspace name to compact", arity = "0..1")
+ private String keyspace;
+
+ @Parameters(index = "1", description = "The table name to compact", arity = "0..1")
+ private String table;
+
+ @Parameters(index = "2..*", description = "The partition keys to compact", arity = "0..*")
+ private String[] keys;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, table, keys);
// Check if the input has valid size
checkArgument(args.size() >= 3, "forcecompact requires keyspace, table and keys args");
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GarbageCollect.java b/src/java/org/apache/cassandra/tools/nodetool/GarbageCollect.java
index 5d24d84d20..8d9073e402 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GarbageCollect.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GarbageCollect.java
@@ -17,30 +17,40 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import java.util.ArrayList;
import java.util.List;
+import org.apache.cassandra.schema.CompactionParams;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspace;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "garbagecollect", description = "Remove deleted data from one or more tables")
-public class GarbageCollect extends NodeToolCmd
+public class GarbageCollect extends AbstractCommand
{
- @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables")
+ @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables")
private List args = new ArrayList<>();
- @Option(title = "granularity",
- name = {"-g", "--granularity"},
- allowedValues = {"ROW", "CELL"},
- description = "Granularity of garbage removal. ROW (default) removes deleted partitions and rows, CELL also removes overwritten or deleted cells.")
- private String tombstoneOption = "ROW";
+ @Parameters(index = "0", description = "The keyspace followed by one or many tables to garbage collect", arity = "0..1")
+ private String keyspace;
- @Option(title = "jobs",
- name = {"-j", "--jobs"},
+ @Parameters(index = "1..*", description = "The tables to garbage collect", arity = "0..*")
+ private String[] tables;
+
+ @Option(paramLabel = "granularity",
+ names = { "-g", "--granularity" },
+ description = "Granularity of garbage removal. ROW (default) removes deleted partitions and rows, CELL also removes overwritten or deleted cells.")
+ private CompactionParams.TombstoneOption tombstoneOption = CompactionParams.TombstoneOption.ROW;
+
+ @Option(paramLabel = "jobs",
+ names = { "-j", "--jobs" },
description = "Number of sstables to cleanup simultanously, set to 0 to use all available compaction " +
"threads. Defaults to 1 so that collections of newer tables can see the data is deleted " +
"and also remove tombstones.")
@@ -49,6 +59,8 @@ public class GarbageCollect extends NodeToolCmd
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, tables);
+
List keyspaces = parseOptionalKeyspace(args, probe);
String[] tableNames = parseOptionalTables(args);
@@ -56,7 +68,7 @@ public class GarbageCollect extends NodeToolCmd
{
try
{
- probe.garbageCollect(probe.output().out, tombstoneOption, jobs, keyspace, tableNames);
+ probe.garbageCollect(probe.output().out, tombstoneOption.toString(), jobs, keyspace, tableNames);
} catch (Exception e)
{
throw new RuntimeException("Error occurred during garbage collection", e);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GcStats.java b/src/java/org/apache/cassandra/tools/nodetool/GcStats.java
index 2378226f75..59f1c752a7 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GcStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GcStats.java
@@ -17,24 +17,23 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import org.apache.cassandra.tools.nodetool.stats.GcStatsHolder;
import org.apache.cassandra.tools.nodetool.stats.GcStatsPrinter;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "gcstats", description = "Print GC Statistics")
-public class GcStats extends NodeToolCmd
+public class GcStats extends AbstractCommand
{
- @Option(title = "format",
- name = { "-F", "--format" },
- description = "Output format (json, yaml, table)")
+ @Option(paramLabel = "format",
+ names = { "-F", "--format" },
+ description = "Output format (json, yaml, table)")
private String outputFormat = "";
- @Option(title = "human_readable",
- name = { "-H", "--human-readable" },
- description = "Display gcstats with human-readable units")
+ @Option(paramLabel = "human_readable",
+ names = { "-H", "--human-readable" },
+ description = "Display gcstats with human-readable units")
private boolean humanReadable = false;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetAuditLog.java b/src/java/org/apache/cassandra/tools/nodetool/GetAuditLog.java
index 1e08984c7b..410141ff24 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetAuditLog.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetAuditLog.java
@@ -18,14 +18,13 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.audit.AuditLogOptions;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import picocli.CommandLine.Command;
@Command(name = "getauditlog", description = "Print configuration of audit log if enabled, otherwise the configuration reflected in cassandra.yaml")
-public class GetAuditLog extends NodeTool.NodeToolCmd
+public class GetAuditLog extends AbstractCommand
{
@Override
protected void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetAuthCacheConfig.java b/src/java/org/apache/cassandra/tools/nodetool/GetAuthCacheConfig.java
index 347500bfd3..ba2e6584ca 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetAuthCacheConfig.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetAuthCacheConfig.java
@@ -18,18 +18,16 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.auth.AuthCacheMBean;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "getauthcacheconfig", description = "Get configuration of Auth cache")
-public class GetAuthCacheConfig extends NodeTool.NodeToolCmd
+public class GetAuthCacheConfig extends AbstractCommand
{
- @SuppressWarnings("unused")
- @Option(title = "cache-name",
- name = {"--cache-name"},
+ @Option(paramLabel = "cache-name",
+ names = { "--cache-name" },
description = "Name of Auth cache (required)",
required = true)
private String cacheName;
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetAutoRepairConfig.java b/src/java/org/apache/cassandra/tools/nodetool/GetAutoRepairConfig.java
index 9744498de7..11cb2fd31d 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetAutoRepairConfig.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetAutoRepairConfig.java
@@ -21,15 +21,14 @@ import java.io.PrintStream;
import com.google.common.annotations.VisibleForTesting;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
/**
* Prints all the configurations for AutoRepair through nodetool.
*/
@Command(name = "getautorepairconfig", description = "Print autorepair configurations")
-public class GetAutoRepairConfig extends NodeToolCmd
+public class GetAutoRepairConfig extends AbstractCommand
{
@VisibleForTesting
protected static PrintStream out = System.out;
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetBatchlogReplayTrottle.java b/src/java/org/apache/cassandra/tools/nodetool/GetBatchlogReplayTrottle.java
index 6497aa1b06..7a1f706da8 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetBatchlogReplayTrottle.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetBatchlogReplayTrottle.java
@@ -17,13 +17,12 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getbatchlogreplaythrottle", description = "Print batchlog replay throttle in KB/s. " +
"This is reduced proportionally to the number of nodes in the cluster.")
-public class GetBatchlogReplayTrottle extends NodeToolCmd
+public class GetBatchlogReplayTrottle extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIP.java b/src/java/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIP.java
index 85c5194ed9..28cc36530b 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIP.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIP.java
@@ -17,34 +17,24 @@
*/
package org.apache.cassandra.tools.nodetool;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
-import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.printSet;
/**
* Nodetool command to get CIDR groups(s) of given IP
*/
@Command(name = "getcidrgroupsofip", description = "Print CIDR groups associated with given IP")
-public class GetCIDRGroupsOfIP extends NodeToolCmd
+public class GetCIDRGroupsOfIP extends AbstractCommand
{
- @Arguments(usage = "", description = "Requires IP address as a string")
- private List args = new ArrayList<>();
+ @Parameters(paramLabel = "ip_address", description = "Requires IP address as a string", arity = "1", index = "0")
+ private String ipStr;
@Override
public void execute(NodeProbe probe)
{
- PrintStream out = probe.output().out;
-
- checkArgument(args.size() == 1, "Requires IP address as input");
-
- String ipStr = args.get(0);
- probe.printSet(out, "CIDR Groups", probe.getCidrGroupsOfIp(ipStr));
+ printSet(probe.output().out, "CIDR Groups", probe.getCidrGroupsOfIp(ipStr));
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java b/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java
index 6925932077..c820fd0995 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getcolumnindexsize", description = "Print the granularity of the collation index of rows within a partition in KiB")
-public class GetColumnIndexSize extends NodeToolCmd
+public class GetColumnIndexSize extends AbstractCommand
{
@Override
protected void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThreshold.java b/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThreshold.java
index c0ccbbff57..c5e8c3b1ad 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThreshold.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThreshold.java
@@ -18,25 +18,34 @@
package org.apache.cassandra.tools.nodetool;
import static com.google.common.base.Preconditions.checkArgument;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.db.ColumnFamilyStoreMBean;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "getcompactionthreshold", description = "Print min and max compaction thresholds for a given table")
-public class GetCompactionThreshold extends NodeToolCmd
+public class GetCompactionThreshold extends AbstractCommand
{
- @Arguments(usage = " ", description = "The keyspace with a table")
+ @CassandraUsage(usage = " ", description = "The keyspace with a table")
private List args = new ArrayList<>();
+ @Parameters(index = "0", arity = "1", description = "The keyspace to get the compaction threshold for")
+ private String keyspace;
+
+ @Parameters(index = "1", arity = "1", description = "The table to get the compaction threshold for")
+ private String table;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, table);
+
checkArgument(args.size() == 2, "getcompactionthreshold requires ks and cf args");
String ks = args.get(0);
String cf = args.get(1);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThroughput.java b/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThroughput.java
index 8296511c0a..16b867e7e3 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThroughput.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetCompactionThroughput.java
@@ -21,17 +21,14 @@ import java.util.Map;
import com.google.common.math.DoubleMath;
-import io.airlift.airline.Command;
-
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "getcompactionthroughput", description = "Print the MiB/s throughput cap for compaction in the system as a rounded number")
-public class GetCompactionThroughput extends NodeToolCmd
+public class GetCompactionThroughput extends AbstractCommand
{
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-d", "--precise-mib" }, description = "Print the MiB/s throughput cap for compaction in the system as a precise number (double)")
+ @Option(names = { "-d", "--precise-mib" }, description = "Print the MiB/s throughput cap for compaction in the system as a precise number (double)")
private boolean compactionThroughputAsDouble;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetConcurrency.java b/src/java/org/apache/cassandra/tools/nodetool/GetConcurrency.java
index ddf7b6ae35..fa35559560 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetConcurrency.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetConcurrency.java
@@ -21,17 +21,16 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Command;
@Command(name = "getconcurrency", description = "Get maximum concurrency for processing stages")
-public class GetConcurrency extends NodeToolCmd
+public class GetConcurrency extends AbstractCommand
{
- @Arguments(title = "[stage-names]",
- usage = "[stage-names]",
- description = "optional list of stage names, otherwise display all stages")
+ @Parameters(paramLabel = "stage-names",
+ description = "Optional list of stage names, otherwise display all stages",
+ index = "0..*")
private List args = new ArrayList<>();
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentCompactors.java b/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentCompactors.java
index 3176c3edb1..77674c1d6d 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentCompactors.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentCompactors.java
@@ -18,16 +18,15 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getconcurrentcompactors", description = "Get the number of concurrent compactors in the system.")
-public class GetConcurrentCompactors extends NodeToolCmd
+public class GetConcurrentCompactors extends AbstractCommand
{
protected void execute(NodeProbe probe)
{
probe.output().out.println("Current concurrent compactors in the system is: \n" +
- probe.getConcurrentCompactors());
+ probe.getConcurrentCompactors());
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentViewBuilders.java b/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentViewBuilders.java
index 0fe1830813..536e892b86 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentViewBuilders.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetConcurrentViewBuilders.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getconcurrentviewbuilders", description = "Get the number of concurrent view builders in the system")
-public class GetConcurrentViewBuilders extends NodeToolCmd
+public class GetConcurrentViewBuilders extends AbstractCommand
{
protected void execute(NodeProbe probe)
{
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRF.java b/src/java/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRF.java
index 0ba7d37874..53e9124041 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRF.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRF.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import picocli.CommandLine.Command;
@Command(name = "getdefaultrf", description = "Gets default keyspace replication factor.")
-public class GetDefaultKeyspaceRF extends NodeTool.NodeToolCmd
+public class GetDefaultKeyspaceRF extends AbstractCommand
{
protected void execute(NodeProbe probe)
{
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java b/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java
index a834e28c99..7d89bb678f 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java
@@ -17,26 +17,38 @@
*/
package org.apache.cassandra.tools.nodetool;
-import static com.google.common.base.Preconditions.checkArgument;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Command;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "getendpoints", description = "Print the end points that owns the key")
-public class GetEndpoints extends NodeToolCmd
+public class GetEndpoints extends WithPortDisplayAbstractCommand
{
- @Arguments(usage = " ", description = "The keyspace, the table, and the partition key for which we need to find the endpoint")
+ @CassandraUsage(usage = " ", description = "The keyspace, the table, and the partition key for which we need to find the endpoint")
private List args = new ArrayList<>();
+ @Parameters(index = "0", arity = "0..1", description = "The keyspace for which we need to find the endpoint")
+ private String keyspace;
+
+ @Parameters(index = "1", arity = "0..1", description = "The table for which we need to find the endpoint")
+ private String table;
+
+ @Parameters(index = "2", arity = "0..1", description = "The partition key for which we need to find the endpoint")
+ private String key;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, table, key);
+
checkArgument(args.size() == 3, "getendpoints requires keyspace, table and partition key arguments");
String ks = args.get(0);
String table = args.get(1);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetFullQueryLog.java b/src/java/org/apache/cassandra/tools/nodetool/GetFullQueryLog.java
index 1dc4c3ff2e..72c2d6dad8 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetFullQueryLog.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetFullQueryLog.java
@@ -18,14 +18,13 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.fql.FullQueryLoggerOptions;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import picocli.CommandLine.Command;
@Command(name = "getfullquerylog", description = "Print configuration of fql if enabled, otherwise the configuration reflected in cassandra.yaml")
-public class GetFullQueryLog extends NodeToolCmd
+public class GetFullQueryLog extends AbstractCommand
{
protected void execute(NodeProbe probe)
{
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetInterDCStreamThroughput.java b/src/java/org/apache/cassandra/tools/nodetool/GetInterDCStreamThroughput.java
index 50098a5393..5d727470ed 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetInterDCStreamThroughput.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetInterDCStreamThroughput.java
@@ -19,25 +19,21 @@ package org.apache.cassandra.tools.nodetool;
import com.google.common.math.DoubleMath;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "getinterdcstreamthroughput", description = "Print the throughput cap for inter-datacenter streaming and entire SSTable inter-datacenter streaming in the system" +
"in rounded megabits. For precise number, please, use option -d")
-public class GetInterDCStreamThroughput extends NodeToolCmd
+public class GetInterDCStreamThroughput extends AbstractCommand
{
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-e", "--entire-sstable-throughput" }, description = "Print entire SSTable streaming throughput in MiB/s")
+ @Option(names = { "-e", "--entire-sstable-throughput" }, description = "Print entire SSTable streaming throughput in MiB/s")
private boolean entireSSTableThroughput;
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-m", "--mib" }, description = "Print the throughput cap for inter-datacenter streaming in MiB/s")
+ @Option(names = { "-m", "--mib" }, description = "Print the throughput cap for inter-datacenter streaming in MiB/s")
private boolean interDCStreamThroughputMiB;
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-d", "--precise-mbit" }, description = "Print the throughput cap for inter-datacenter streaming in precise Mbits (double)")
+ @Option(names = { "-d", "--precise-mbit" }, description = "Print the throughput cap for inter-datacenter streaming in precise Mbits (double)")
private boolean interDCStreamThroughputDoubleMbit;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetLoggingLevels.java b/src/java/org/apache/cassandra/tools/nodetool/GetLoggingLevels.java
index 3cadcebebd..fedac6f091 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetLoggingLevels.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetLoggingLevels.java
@@ -17,15 +17,13 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import java.util.Map;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getlogginglevels", description = "Get the runtime logging levels")
-public class GetLoggingLevels extends NodeToolCmd
+public class GetLoggingLevels extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetMaxHintWindow.java b/src/java/org/apache/cassandra/tools/nodetool/GetMaxHintWindow.java
index 54e73ab3fa..7eba1315ac 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetMaxHintWindow.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetMaxHintWindow.java
@@ -18,12 +18,11 @@
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
+import picocli.CommandLine.Command;
@Command(name = "getmaxhintwindow", description = "Print the max hint window in ms")
-public class GetMaxHintWindow extends NodeTool.NodeToolCmd
+public class GetMaxHintWindow extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetSSTables.java b/src/java/org/apache/cassandra/tools/nodetool/GetSSTables.java
index 657e0ec619..37395730c9 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetSSTables.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetSSTables.java
@@ -17,36 +17,48 @@
*/
package org.apache.cassandra.tools.nodetool;
-import static com.google.common.base.Preconditions.checkArgument;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "getsstables", description = "Print the sstable filenames that own the key")
-public class GetSSTables extends NodeToolCmd
+public class GetSSTables extends AbstractCommand
{
- @Option(title = "hex_format",
- name = {"-hf", "--hex-format"},
- description = "Specify the key in hexadecimal string format")
+ @Option(paramLabel = "hex_format",
+ names = { "-hf", "--hex-format" },
+ description = "Specify the key in hexadecimal string format")
private boolean hexFormat = false;
- @Option(name={"-l", "--show-levels"}, description="If the table is using leveled compaction the level of each sstable will be included in the output (Default: false)")
+ @Option(names = { "-l", "--show-levels" }, description = "If the table is using leveled compaction the level of each sstable will be included in the output (Default: false)")
private boolean showLevels = false;
- @Arguments(usage = " ", description = "The keyspace, the column family, and the key")
+ @CassandraUsage(usage = " ", description = "The keyspace, the column family, and the key")
private List args = new ArrayList<>();
+ @Parameters(paramLabel = "keyspace", index = "0", arity = "0..1", description = "The keyspace")
+ private String keyspace;
+
+ @Parameters(paramLabel = "cfname", index = "1", arity = "0..1", description = "The column family")
+ private String cfname;
+
+ @Parameters(paramLabel = "key", index = "2", arity = "0..1", description = "The key")
+ private String key;
+
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, cfname, key);
+
checkArgument(args.size() == 3, "getsstables requires ks, cf and key args");
String ks = args.get(0);
String cf = args.get(1);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetSeeds.java b/src/java/org/apache/cassandra/tools/nodetool/GetSeeds.java
index faf5e8d5bd..1d92673300 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetSeeds.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetSeeds.java
@@ -19,13 +19,11 @@ package org.apache.cassandra.tools.nodetool;
import java.util.List;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getseeds", description = "Get the currently in use seed node IP list excluding the node IP")
-public class GetSeeds extends NodeToolCmd
+public class GetSeeds extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetSnapshotThrottle.java b/src/java/org/apache/cassandra/tools/nodetool/GetSnapshotThrottle.java
index bd98d34306..06a4260ca5 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetSnapshotThrottle.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetSnapshotThrottle.java
@@ -17,20 +17,19 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "getsnapshotthrottle", description = "Print the snapshot_links_per_second throttle for snapshot/clearsnapshot")
-public class GetSnapshotThrottle extends NodeToolCmd
+public class GetSnapshotThrottle extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
{
long throttle = probe.getSnapshotLinksPerSecond();
if (throttle > 0)
- System.out.println("Current snapshot throttle: " + throttle + " links/s");
+ output.printInfo("Current snapshot throttle: %s links/s", throttle);
else
- System.out.println("Snapshot throttle is disabled");
+ output.printInfo("Snapshot throttle is disabled");
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetStreamThroughput.java b/src/java/org/apache/cassandra/tools/nodetool/GetStreamThroughput.java
index 94cf298b65..2de2db9844 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetStreamThroughput.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetStreamThroughput.java
@@ -19,25 +19,21 @@ package org.apache.cassandra.tools.nodetool;
import com.google.common.math.DoubleMath;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "getstreamthroughput", description = "Print the throughput cap for streaming and entire SSTable streaming in the system in rounded megabits. " +
"For precise number, please, use option -d")
-public class GetStreamThroughput extends NodeToolCmd
+public class GetStreamThroughput extends AbstractCommand
{
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-e", "--entire-sstable-throughput" }, description = "Print entire SSTable streaming throughput in MiB/s")
+ @Option(names = { "-e", "--entire-sstable-throughput" }, description = "Print entire SSTable streaming throughput in MiB/s")
private boolean entireSSTableThroughput;
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-m", "--mib" }, description = "Print the throughput cap for streaming in MiB/s")
+ @Option(names = { "-m", "--mib" }, description = "Print the throughput cap for streaming in MiB/s")
private boolean streamThroughputMiB;
- @SuppressWarnings("UnusedDeclaration")
- @Option(name = { "-d", "--precise-mbit" }, description = "Print the throughput cap for streaming in precise Mbits (double)")
+ @Option(names = { "-d", "--precise-mbit" }, description = "Print the throughput cap for streaming in precise Mbits (double)")
private boolean streamThroughputDoubleMbit;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetTimeout.java b/src/java/org/apache/cassandra/tools/nodetool/GetTimeout.java
index bd720ab6be..3ac0b9a60e 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetTimeout.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetTimeout.java
@@ -17,32 +17,32 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
-import static com.google.common.base.Preconditions.checkArgument;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "gettimeout", description = "Print the timeout of the given type in ms")
-public class GetTimeout extends NodeToolCmd
+public class GetTimeout extends AbstractCommand
{
public static final String TIMEOUT_TYPES = "read, range, write, counterwrite, cascontention, truncate, internodeconnect, internodeuser, internodestreaminguser, misc (general rpc_timeout_in_ms)";
- @Arguments(usage = "", description = "The timeout type, one of (" + TIMEOUT_TYPES + ")")
+ @CassandraUsage(usage = "", description = "The timeout type, one of (" + TIMEOUT_TYPES + ")")
private List args = new ArrayList<>();
+ @Parameters (index = "0", description = "The timeout type, one of (" + TIMEOUT_TYPES + ')', arity = "1")
+ private String timeout_type;
+
@Override
public void execute(NodeProbe probe)
{
- checkArgument(args.size() == 1, "gettimeout requires a timeout type, one of (" + TIMEOUT_TYPES + ")");
try
{
- probe.output().out.println("Current timeout for type " + args.get(0) + ": " + probe.getTimeout(args.get(0)) + " ms");
+ probe.output().out.println("Current timeout for type " + timeout_type + ": " + probe.getTimeout(timeout_type) + " ms");
} catch (Exception e)
{
throw new IllegalArgumentException(e.getMessage());
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetTraceProbability.java b/src/java/org/apache/cassandra/tools/nodetool/GetTraceProbability.java
index 007b813fad..da8c15a275 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GetTraceProbability.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GetTraceProbability.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "gettraceprobability", description = "Print the current trace probability value")
-public class GetTraceProbability extends NodeToolCmd
+public class GetTraceProbability extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java b/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java
index a297f762cf..0d5f5a1253 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java
@@ -17,16 +17,14 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "gossipinfo", description = "Shows the gossip information for the cluster")
-public class GossipInfo extends NodeToolCmd
+public class GossipInfo extends WithPortDisplayAbstractCommand
{
- @Option(title = "resolve_ip", name = {"-r", "--resolve-ip"}, description = "Show node domain names instead of IPs")
+ @Option(paramLabel = "resolve_ip", names = { "-r", "--resolve-ip" }, description = "Show node domain names instead of IPs")
private boolean resolveIp = false;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommand.java b/src/java/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommand.java
index e71713641c..b6fec26676 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommand.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommand.java
@@ -18,7 +18,6 @@
package org.apache.cassandra.tools.nodetool;
-import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -37,48 +36,41 @@ import java.util.stream.Stream;
import com.google.common.annotations.VisibleForTesting;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.db.guardrails.GuardrailsMBean;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool;
import org.apache.cassandra.tools.nodetool.formatter.TableBuilder;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
import org.apache.cassandra.utils.LocalizeString;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
import static java.lang.String.format;
import static java.util.Arrays.stream;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
-public abstract class GuardrailsConfigCommand extends NodeTool.NodeToolCmd
+public abstract class GuardrailsConfigCommand extends AbstractCommand
{
@Command(name = "getguardrailsconfig", description = "Print runtime configuration of guardrails.")
public static class GetGuardrailsConfig extends GuardrailsConfigCommand
{
- @Option(name = { "--category", "-c" },
- description = "Category of guardrails to filter, can be one of 'values', 'thresholds', 'flags', 'others'.",
- allowedValues = { "values", "thresholds", "flags", "others" })
- private String guardrailCategory;
+ @Option(names = { "--category", "-c" },
+ description = "Category of guardrails to filter, can be one of 'values', 'thresholds', 'flags', 'others'.")
+ private GuardrailCategory guardrailCategory;
- @Option(name = { "--expand" },
+ @Option(names = { "--expand" },
description = "Expand all guardrail names so they reflect their counterparts in cassandra.yaml")
private boolean expand = false;
- @Arguments(description = "Specific name of a guardrail to get configuration of.")
- private List args = new ArrayList<>();
+ @Parameters(index = "0", arity = "0..1", description = "Specific name of a guardrail to get configuration of or all guardrails if not specified.")
+ private String guardrailName;
@Override
public void execute(NodeProbe probe)
{
- GuardrailCategory categoryEnum = GuardrailCategory.parseCategory(guardrailCategory, probe.output().out);
-
- if (args.size() > 1)
- throw new IllegalStateException("Specify only one guardrail name to get the configuration of or no name to get the configuration of all of them.");
-
- String guardrailName = !args.isEmpty() ? args.get(0) : null;
-
- if (guardrailName != null && categoryEnum != null)
+ if (guardrailName != null && guardrailCategory != null)
throw new IllegalStateException("Do not specify additional arguments when --category/-c is set.");
Map> allGetters = parseGuardrailNames(probe.getGuardrailsMBean().getClass().getDeclaredMethods(), guardrailName);
@@ -89,7 +81,7 @@ public abstract class GuardrailsConfigCommand extends NodeTool.NodeToolCmd
throw new IllegalStateException(format("Guardrail %s not found.", guardrailName));
}
- display(probe, allGetters, categoryEnum, expand);
+ display(probe, allGetters, guardrailCategory, expand);
}
@VisibleForTesting
@@ -179,17 +171,28 @@ public abstract class GuardrailsConfigCommand extends NodeTool.NodeToolCmd
{
private static final Pattern SETTER_PATTERN = Pattern.compile("^set");
- @Arguments(usage = "[ ...]",
- description = "For flags, possible values are 'true' or 'false'. " +
- "For thresholds, two values are expected, first for failure, second for warning. " +
- "For values, enumeration of values expected or one value where multiple items are separated by comma. " +
- "Setting for thresholds accepting strings and value guardrails are reset by specifying 'null' or '[]' value. " +
- "For thresholds accepting integers, the reset value is -1.")
- private final List args = new ArrayList<>();
+ @CassandraUsage(usage = "[ ...]",
+ description = "For flags, possible values are 'true' or 'false'. " +
+ "For thresholds, two values are expected, first for failure, second for warning. " +
+ "For values, enumeration of values expected or one value where multiple items are separated by comma. " +
+ "Setting for thresholds accepting strings and value guardrails are reset by specifying 'null' or '[]' value. " +
+ "For thresholds accepting integers, the reset value is -1.")
+ private List args = new ArrayList<>();
+
+ @Parameters(index = "0", arity = "0..1")
+ private String setterName;
+
+ @Parameters(index = "1..*", arity = "0..*", description = "Arguments for the setter. For flags, possible values are 'true' or 'false'. " +
+ "For thresholds, two values are expected, first for failure, second for warning. " +
+ "For values, enumeration of values expected or one value where multiple items are separated by comma. " +
+ "Setting for thresholds accepting strings and value guardrails are reset by specifying 'null' or '[]' value. " +
+ "For thresholds accepting integers, the reset value is -1.")
+ private List setterArgs = new ArrayList<>();
@Override
public void execute(NodeProbe probe)
{
+ args = CommandUtils.concatArgs(setterName, setterArgs);
if (args.isEmpty())
throw new IllegalStateException("No arguments.");
@@ -376,27 +379,6 @@ public abstract class GuardrailsConfigCommand extends NodeTool.NodeToolCmd
thresholds,
flags,
others;
-
- public static GuardrailCategory parseCategory(String category, PrintStream out)
- {
- if (category == null)
- return null;
-
- try
- {
- return GuardrailCategory.valueOf(LocalizeString.toLowerCaseLocalized(category));
- }
- catch (IllegalArgumentException ex)
- {
- String enabledValues = Arrays.stream(GuardrailCategory.values())
- .map(GuardrailCategory::name)
- .collect(Collectors.joining(","));
- out.printf("%nError: Illegal value for -c/--category used: '"
- + category + "'. Supported values are " + enabledValues + ".%n");
- System.exit(1);
- return null;
- }
- }
}
void display(NodeProbe probe, Map> methods, GuardrailCategory userCategory, boolean verbose)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Help.java b/src/java/org/apache/cassandra/tools/nodetool/Help.java
new file mode 100644
index 0000000000..2491fde5a6
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/Help.java
@@ -0,0 +1,162 @@
+/*
+ * 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 java.io.PrintWriter;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+import org.apache.cassandra.tools.nodetool.layout.CassandraCliHelpLayout;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.IHelpCommandInitializable2;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST_HEADING;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_EXIT_CODE_LIST;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_EXIT_CODE_LIST_HEADING;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_FOOTER;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_FOOTER_HEADING;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_HEADER;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_HEADER_HEADING;
+import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_SYNOPSIS;
+
+@Command(name = "help",
+ helpCommand = true,
+ description = "Display help information")
+public class Help implements IHelpCommandInitializable2, Runnable
+{
+ @Option(names = { "--help" }, hidden = true, usageHelp = true, descriptionKey = "helpCommand.help",
+ description = "Show usage help for the help command and exit.")
+ private boolean helpRequested;
+
+ @Parameters(paramLabel = "command", arity = "1..*", descriptionKey = "helpCommand.command",
+ description = "The COMMAND to display the usage help message for.")
+ private List commands;
+
+ private CommandLine self;
+ private PrintWriter out;
+ private CommandLine.Help.ColorScheme colorScheme;
+
+ /**
+ * Invokes {@code #usage(PrintStream, CommandLine.Help.ColorScheme) usage} for the specified command,
+ * or for the parent command.
+ */
+ @Override
+ public void run()
+ {
+ CommandLine parent = self == null ? null : self.getParent();
+ if (parent == null)
+ return;
+
+ CommandLine.Help.ColorScheme colors = colorScheme == null ?
+ CommandLine.Help.defaultColorScheme(CommandLine.Help.Ansi.AUTO) :
+ colorScheme;
+
+ if (commands == null)
+ {
+ // If the parent command is the top-level command, print help for the top-level command.
+ printTopCommandUsage(parent, colors, out);
+ return;
+ }
+
+ if (parent.isAbbreviatedSubcommandsAllowed())
+ throw new CommandLine.ParameterException(parent, "Abbreviated subcommands are not allowed.");
+
+ // Print help for the last command in the list of commands.
+ CommandLine subcommand = parent;
+ for (String command : commands)
+ {
+ subcommand = subcommand.getSubcommands().get(command);
+ if (subcommand == null)
+ throw new CommandLine.ParameterException(parent, "Unknown subcommand '" + command + "'.", null, command);
+ }
+
+ subcommand.usage(out, colors);
+ }
+
+ public static void printTopCommandUsage(CommandLine command, CommandLine.Help.ColorScheme colors, PrintWriter writer)
+ {
+ if (command == null)
+ return;
+
+ StringBuilder sb = new StringBuilder();
+ CommandLine.Help help = command.getHelpFactory().create(command.getCommandSpec(), colors);
+ if (!(help instanceof CassandraCliHelpLayout))
+ {
+ command.usage(writer, colors);
+ return;
+ }
+
+ Map helpSectionMap = cassandraTopLevelHelpSectionKeys((CassandraCliHelpLayout) help);
+ for (String key : command.getHelpSectionKeys())
+ {
+ CommandLine.IHelpSectionRenderer renderer = helpSectionMap.get(key);
+ if (renderer == null)
+ continue;
+ String rendered = renderer.render(help);
+ if (!Strings.isNullOrEmpty(rendered))
+ sb.append(rendered);
+ }
+
+ writer.println(sb);
+ writer.flush();
+ }
+
+ /**
+ * Top-level help command (includes all the available nodetool commands) has a different layout, so we need to
+ * provide a different set of keys for the help sections.
+ * @param layout The help class layout.
+ * @return Map of supported keys for the help sections.
+ */
+ public static Map cassandraTopLevelHelpSectionKeys(CassandraCliHelpLayout layout)
+ {
+ Map sectionMap = new LinkedHashMap<>();
+ sectionMap.put(SECTION_KEY_HEADER_HEADING, CommandLine.Help::headerHeading);
+ sectionMap.put(SECTION_KEY_HEADER, CommandLine.Help::header);
+ sectionMap.put(SECTION_KEY_SYNOPSIS, layout::topLevelSynopsis);
+ sectionMap.put(SECTION_KEY_COMMAND_LIST_HEADING, layout::topLevelCommandListHeading);
+ sectionMap.put(SECTION_KEY_COMMAND_LIST, layout::topLevelCommandList);
+ sectionMap.put(SECTION_KEY_EXIT_CODE_LIST_HEADING, CommandLine.Help::exitCodeListHeading);
+ sectionMap.put(SECTION_KEY_EXIT_CODE_LIST, CommandLine.Help::exitCodeList);
+ sectionMap.put(SECTION_KEY_FOOTER_HEADING, CommandLine.Help::footerHeading);
+ sectionMap.put(SECTION_KEY_FOOTER, CommandLine.Help::footer);
+ return sectionMap;
+ }
+
+ /**
+ * The printHelpIfRequested method calls the init method on commands marked
+ * as helpCommand before the help command's run or call method is called.
+ */
+ public void init(CommandLine helpCommandLine,
+ CommandLine.Help.ColorScheme colorScheme,
+ PrintWriter out,
+ PrintWriter err)
+ {
+ this.self = Preconditions.checkNotNull(helpCommandLine, "helpCommandLine");
+ this.colorScheme = Preconditions.checkNotNull(colorScheme, "colorScheme");
+ this.out = Preconditions.checkNotNull(out, "outWriter");
+ }
+}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Import.java b/src/java/org/apache/cassandra/tools/nodetool/Import.java
index f64c7893dd..8868599936 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Import.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Import.java
@@ -18,13 +18,6 @@
package org.apache.cassandra.tools.nodetool;
-import static com.google.common.base.Preconditions.checkArgument;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-
-import io.airlift.airline.Option;
-
-import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -32,67 +25,87 @@ import java.util.List;
import com.google.common.collect.Lists;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs;
@Command(name = "import", description = "Import new SSTables to the system")
-public class Import extends NodeToolCmd
+public class Import extends AbstractCommand
{
- @Arguments(usage = " ...", description = "The keyspace, table name and directories to import sstables from")
+ public static final String IMPORT_FAIL_MESSAGE = "Some directories failed to import, check server logs for details";
+
+ @CassandraUsage(usage = " ...", description = "The keyspace, table name and directories to import sstables from")
private List args = new ArrayList<>();
- @Option(title = "keep_level",
- name = {"-l", "--keep-level"},
+ @Parameters(index = "0", paramLabel = "keyspace", description = "The keyspace name")
+ private String keyspace;
+
+ @Parameters(index = "1", paramLabel = "table", description = "The table name")
+ private String table;
+
+ @Parameters(index = "2..*", paramLabel = "directories", description = "The directories to import sstables from")
+ private String[] directories;
+
+ @Option(paramLabel = "keep_level",
+ names = { "-l", "--keep-level" },
description = "Keep the level on the new sstables")
private boolean keepLevel = false;
- @Option(title = "keep_repaired",
- name = {"-r", "--keep-repaired"},
+ @Option(paramLabel = "keep_repaired",
+ names = { "-r", "--keep-repaired" },
description = "Keep any repaired information from the sstables")
private boolean keepRepaired = false;
- @Option(title = "no_verify_sstables",
- name = {"-v", "--no-verify"},
+ @Option(paramLabel = "no_verify_sstables",
+ names = { "-v", "--no-verify" },
description = "Don't verify new sstables")
private boolean noVerify = false;
- @Option(title = "no_verify_tokens",
- name = {"-t", "--no-tokens"},
+ @Option(paramLabel = "no_verify_tokens",
+ names = { "-t", "--no-tokens" },
description = "Don't verify that all tokens in the new sstable are owned by the current node")
private boolean noVerifyTokens = false;
- @Option(title = "no_invalidate_caches",
- name = {"-c", "--no-invalidate-caches"},
+ @Option(paramLabel = "no_invalidate_caches",
+ names = { "-c", "--no-invalidate-caches" },
description = "Don't invalidate the row cache when importing")
private boolean noInvalidateCaches = false;
- @Option(title = "quick",
- name = {"-q", "--quick"},
+ @Option(paramLabel = "quick",
+ names = { "-q", "--quick" },
description = "Do a quick import without verifying sstables, clearing row cache or checking in which data directory to put the file")
private boolean quick = false;
- @Option(title = "extended_verify",
- name = {"-e", "--extended-verify"},
+ @Option(paramLabel = "extended_verify",
+ names = { "-e", "--extended-verify" },
description = "Run an extended verify, verifying all values in the new sstables")
private boolean extendedVerify = false;
- @Option(title = "copy_data",
- name = {"-p", "--copy-data"},
+ // The previous option -p collides with the --port in the JMX, so we need to alter it to -cd.
+ // It is safe to alter the name since the option is not used by users as it doesn't work.
+ @Option(paramLabel = "copy_data",
+ names = { "-cd", "--copy-data" },
description = "Copy data from source directories instead of moving them")
private boolean copyData = false;
- @Option(title = "require_index_components",
- name = {"-ri", "--require-index-components"},
+ @Option(paramLabel = "require_index_components",
+ names = { "-ri", "--require-index-components" },
description = "Require existing index components for SSTables with attached indexes")
private boolean failOnMissingIndex = false;
- @Option(title = "no_index_validation",
- name = {"-niv", "--no-index-validation"},
+ @Option(paramLabel = "no_index_validation",
+ names = { "-niv", "--no-index-validation" },
description = "Skip SSTable-attached index checksum validation")
private boolean noIndexValidation = false;
@Override
public void execute(NodeProbe probe)
{
+ args = concatArgs(keyspace, table, directories);
checkArgument(args.size() >= 3, "import requires keyspace, table name and directories");
if (quick)
@@ -110,11 +123,9 @@ public class Import extends NodeToolCmd
extendedVerify, copyData, failOnMissingIndex, !noIndexValidation);
if (!failedDirs.isEmpty())
{
- PrintStream err = probe.output().err;
- err.println("Some directories failed to import, check server logs for details:");
for (String directory : failedDirs)
- err.println(directory);
- System.exit(1);
+ output.printError(directory);
+ throw new RuntimeException(IMPORT_FAIL_MESSAGE);
}
}
}
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Info.java b/src/java/org/apache/cassandra/tools/nodetool/Info.java
index 950616833a..8f7598b064 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Info.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Info.java
@@ -17,28 +17,25 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
-
import java.io.PrintStream;
import java.lang.management.MemoryUsage;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-
import javax.management.InstanceNotFoundException;
import org.apache.cassandra.db.ColumnFamilyStoreMBean;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.service.CacheServiceMBean;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
@Command(name = "info", description = "Print node information (uptime, load, ...)")
-public class Info extends NodeToolCmd
+public class Info extends AbstractCommand
{
- @Option(name = {"-T", "--tokens"}, description = "Display all tokens")
+ @Option(names = { "-T", "--tokens" }, description = "Display all tokens")
private boolean tokens = false;
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCache.java
index 622bed3519..d4e66bcf7c 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCache.java
@@ -20,18 +20,19 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Command;
/**
* Nodetool command to invalidate CIDR permissions cache, for a give role or for all roles in the cache.
*/
@Command(name = "invalidatecidrpermissionscache", description = "Invalidate the cidr permissions cache")
-public class InvalidateCIDRPermissionsCache extends NodeToolCmd
+public class InvalidateCIDRPermissionsCache extends AbstractCommand
{
- @Arguments(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @CassandraUsage(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @Parameters(paramLabel = "roles", description = "List of roles to invalidate. By default, all roles", index = "0..*")
private List args = new ArrayList<>();
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCounterCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCounterCache.java
index 3cba8e0cd2..b05f87dd64 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCounterCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCounterCache.java
@@ -17,13 +17,12 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "invalidatecountercache", description = "Invalidate the counter cache")
-public class InvalidateCounterCache extends NodeToolCmd
+public class InvalidateCounterCache extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java
index 0f9079f351..58dede10a6 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java
@@ -20,15 +20,16 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "invalidatecredentialscache", description = "Invalidate the credentials cache")
-public class InvalidateCredentialsCache extends NodeToolCmd
+public class InvalidateCredentialsCache extends AbstractCommand
{
- @Arguments(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @CassandraUsage(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @Parameters(paramLabel = "roles", description = "List of roles to invalidate. By default, all roles", index = "0..*")
private List args = new ArrayList<>();
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java
index c242b03b62..abf67aa68b 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java
@@ -20,15 +20,16 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "invalidatejmxpermissionscache", description = "Invalidate the JMX permissions cache")
-public class InvalidateJmxPermissionsCache extends NodeToolCmd
+public class InvalidateJmxPermissionsCache extends AbstractCommand
{
- @Arguments(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @CassandraUsage(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @Parameters(paramLabel = "roles", description = "List of roles to invalidate. By default, all roles", index = "0..*")
private List args = new ArrayList<>();
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateKeyCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateKeyCache.java
index 4414b42cc9..af7149f64a 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateKeyCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateKeyCache.java
@@ -17,13 +17,11 @@
*/
package org.apache.cassandra.tools.nodetool;
-import io.airlift.airline.Command;
-
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Command;
@Command(name = "invalidatekeycache", description = "Invalidate the key cache")
-public class InvalidateKeyCache extends NodeToolCmd
+public class InvalidateKeyCache extends AbstractCommand
{
@Override
public void execute(NodeProbe probe)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java
index 8b58060743..c68569b132 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java
@@ -20,15 +20,16 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "invalidatenetworkpermissionscache", description = "Invalidate the network permissions cache")
-public class InvalidateNetworkPermissionsCache extends NodeToolCmd
+public class InvalidateNetworkPermissionsCache extends AbstractCommand
{
- @Arguments(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @CassandraUsage(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @Parameters(paramLabel = "roles", description = "List of roles to invalidate. By default, all roles", index = "0..*")
private List args = new ArrayList<>();
@Override
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java
index cc66c987df..e659dc4d9a 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java
@@ -22,89 +22,88 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
-import io.airlift.airline.Option;
import org.apache.cassandra.auth.DataResource;
import org.apache.cassandra.auth.FunctionResource;
import org.apache.cassandra.auth.JMXResource;
import org.apache.cassandra.auth.RoleResource;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
import static com.google.common.base.Preconditions.checkArgument;
@Command(name = "invalidatepermissionscache", description = "Invalidate the permissions cache")
-public class InvalidatePermissionsCache extends NodeToolCmd
+public class InvalidatePermissionsCache extends AbstractCommand
{
- @Arguments(usage = "[]", description = "A role for which permissions to specified resources need to be invalidated")
- private List args = new ArrayList<>();
+ @Parameters(paramLabel = "role", description = "A role for which permissions to specified resources need to be invalidated", arity = "0..1", index = "0")
+ private String roleName;
// Data Resources
- @Option(title = "all-keyspaces",
- name = {"--all-keyspaces"},
+ @Option(paramLabel = "all-keyspaces",
+ names = { "--all-keyspaces" },
description = "Invalidate permissions for 'ALL KEYSPACES'")
private boolean allKeyspaces;
- @Option(title = "keyspace",
- name = {"--keyspace"},
+ @Option(paramLabel = "keyspace",
+ names = { "--keyspace" },
description = "Keyspace to invalidate permissions for")
private String keyspace;
- @Option(title = "all-tables",
- name = {"--all-tables"},
+ @Option(paramLabel = "all-tables",
+ names = { "--all-tables" },
description = "Invalidate permissions for 'ALL TABLES'")
private boolean allTables;
- @Option(title = "table",
- name = {"--table"},
+ @Option(paramLabel = "table",
+ names = { "--table" },
description = "Table to invalidate permissions for (you must specify --keyspace for using this option)")
private String table;
// Roles Resources
- @Option(title = "all-roles",
- name = {"--all-roles"},
+ @Option(paramLabel = "all-roles",
+ names = { "--all-roles" },
description = "Invalidate permissions for 'ALL ROLES'")
private boolean allRoles;
- @Option(title = "role",
- name = {"--role"},
+ @Option(paramLabel = "role",
+ names = { "--role" },
description = "Role to invalidate permissions for")
private String role;
// Functions Resources
- @Option(title = "all-functions",
- name = {"--all-functions"},
+ @Option(paramLabel = "all-functions",
+ names = { "--all-functions" },
description = "Invalidate permissions for 'ALL FUNCTIONS'")
private boolean allFunctions;
- @Option(title = "functions-in-keyspace",
- name = {"--functions-in-keyspace"},
+ @Option(paramLabel = "functions-in-keyspace",
+ names = { "--functions-in-keyspace" },
description = "Keyspace to invalidate permissions for")
private String functionsInKeyspace;
- @Option(title = "function",
- name = {"--function"},
+ @Option(paramLabel = "function",
+ names = { "--function" },
description = "Function to invalidate permissions for (you must specify --functions-in-keyspace for using " +
- "this option; function format: name[arg1^..^agrN], for example: foo[Int32Type^DoubleType])")
+ "this option; function format: name[arg1^..^agrN], for example: foo[Int32Type^DoubleType])")
private String function;
// MBeans Resources
- @Option(title = "all-mbeans",
- name = {"--all-mbeans"},
+ @Option(paramLabel = "all-mbeans",
+ names = { "--all-mbeans" },
description = "Invalidate permissions for 'ALL MBEANS'")
private boolean allMBeans;
- @Option(title = "mbean",
- name = {"--mbean"},
+ @Option(paramLabel = "mbean",
+ names = { "--mbean" },
description = "MBean to invalidate permissions for")
private String mBean;
@Override
public void execute(NodeProbe probe)
{
- if (args.isEmpty())
+ if (StringUtils.isEmpty(roleName))
{
checkArgument(!allKeyspaces && StringUtils.isEmpty(keyspace) && StringUtils.isEmpty(table)
&& !allRoles && StringUtils.isEmpty(role)
@@ -116,8 +115,6 @@ public class InvalidatePermissionsCache extends NodeToolCmd
}
else
{
- checkArgument(args.size() == 1,
- "A single is only supported / you have a typo in the resource options spelling");
List resourceNames = new ArrayList<>();
// Data Resources
@@ -166,8 +163,6 @@ public class InvalidatePermissionsCache extends NodeToolCmd
if (StringUtils.isNotEmpty(mBean))
resourceNames.add(JMXResource.mbean(mBean).getName());
- String roleName = args.get(0);
-
if (resourceNames.isEmpty())
throw new IllegalArgumentException("No resource options specified");
diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java
index 4fca5c377b..5ce39bbbcb 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java
@@ -20,16 +20,17 @@ package org.apache.cassandra.tools.nodetool;
import java.util.ArrayList;
import java.util.List;
-import io.airlift.airline.Arguments;
-import io.airlift.airline.Command;
import org.apache.cassandra.tools.NodeProbe;
-import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
@Command(name = "invalidaterolescache", description = "Invalidate the roles cache")
-public class InvalidateRolesCache extends NodeToolCmd
+public class InvalidateRolesCache extends AbstractCommand
{
- @Arguments(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @CassandraUsage(usage = "[...]", description = "List of roles to invalidate. By default, all roles")
+ @Parameters(paramLabel = "role", description = "List of roles to invalidate. By default, all roles")
private List