From 94b251f3ce4489af0bc697ebbd9464a45bde4265 Mon Sep 17 00:00:00 2001 From: Maxim Muzafarov Date: Sat, 26 Jul 2025 18:07:24 +0200 Subject: [PATCH] Migrate all nodetool commands from airline to picocli Replace io.airlift:airline dependency with info.picocli:picocli across all nodetool command implementations. This migration includes: - Convert 160+ nodetool command classes from airline to picocli - Add AbstractCommand base class for commands and utility methods - Add NodetoolCommand top-level class for the cli utility - Update all command classes to use @Command, @Option, and @Parameters - Add plain text files to test command help output - Add mock test classes for improved test coverage and parse validation - Modify test infrastructure to work with picocli-based commands - Add new layouts for cli formatting to preserve backwards compatibility The migration maintains backward compatibility while providing improved command-line parsing, better help system, and more robust argument validation through picocli enhanced features. patch by Maxim Muzafarov; reviewed by Caleb Rackliffe, Dmitry Konstantinov, Stefan Miklosovic for CASSANDRA-17445 --- .build/build-rat.xml | 1 + .build/cassandra-build-maven-pom.xml | 4 + .build/cassandra-deps-maven-pom.xml | 4 +- .build/parent-maven-pom.xml | 18 +- CHANGES.txt | 1 + .../config/CassandraRelevantEnv.java | 28 +- .../config/CassandraRelevantProperties.java | 15 +- .../apache/cassandra/db/SSTableImporter.java | 26 +- .../org/apache/cassandra/tools/JMXTool.java | 103 +-- .../org/apache/cassandra/tools/NodeProbe.java | 40 +- .../org/apache/cassandra/tools/NodeTool.java | 590 ++++--------- .../org/apache/cassandra/tools/Output.java | 10 + .../tools/StandaloneJournalUtil.java | 97 +-- .../tools/nodetool/AbortBootstrap.java | 13 +- .../tools/nodetool/AbstractCommand.java | 104 +++ .../cassandra/tools/nodetool/AccordAdmin.java | 33 +- .../tools/nodetool/AlterTopology.java | 11 +- .../cassandra/tools/nodetool/Assassinate.java | 13 +- .../tools/nodetool/AutoRepairStatus.java | 9 +- .../cassandra/tools/nodetool/Bootstrap.java | 28 + .../tools/nodetool/BootstrapResume.java | 18 +- .../tools/nodetool/CIDRFilteringStats.java | 5 +- .../cassandra/tools/nodetool/CMSAdmin.java | 87 +- .../cassandra/tools/nodetool/Cleanup.java | 31 +- .../tools/nodetool/ClearSnapshot.java | 34 +- .../cassandra/tools/nodetool/ClientStats.java | 18 +- .../tools/nodetool/CommandUtils.java | 259 ++++++ .../cassandra/tools/nodetool/Compact.java | 37 +- .../tools/nodetool/CompactionHistory.java | 17 +- .../tools/nodetool/CompactionStats.java | 16 +- .../nodetool/ConsensusMigrationAdmin.java | 70 +- .../cassandra/tools/nodetool/DataPaths.java | 15 +- .../tools/nodetool/Decommission.java | 19 +- .../tools/nodetool/DescribeCluster.java | 8 +- .../tools/nodetool/DescribeRing.java | 15 +- .../tools/nodetool/DisableAuditLog.java | 5 +- .../tools/nodetool/DisableAutoCompaction.java | 23 +- .../tools/nodetool/DisableBackup.java | 6 +- .../tools/nodetool/DisableBinary.java | 9 +- .../tools/nodetool/DisableFullQueryLog.java | 5 +- .../tools/nodetool/DisableGossip.java | 6 +- .../tools/nodetool/DisableHandoff.java | 6 +- .../tools/nodetool/DisableHintsForDC.java | 10 +- .../nodetool/DisableOldProtocolVersions.java | 5 +- .../cassandra/tools/nodetool/Drain.java | 5 +- .../tools/nodetool/DropCIDRGroup.java | 22 +- .../tools/nodetool/EnableAuditLog.java | 35 +- .../tools/nodetool/EnableAutoCompaction.java | 24 +- .../tools/nodetool/EnableBackup.java | 6 +- .../tools/nodetool/EnableBinary.java | 6 +- .../tools/nodetool/EnableFullQueryLog.java | 23 +- .../tools/nodetool/EnableGossip.java | 6 +- .../tools/nodetool/EnableHandoff.java | 6 +- .../tools/nodetool/EnableHintsForDC.java | 19 +- .../nodetool/EnableOldProtocolVersions.java | 6 +- .../tools/nodetool/FailureDetectorInfo.java | 7 +- .../cassandra/tools/nodetool/Flush.java | 23 +- .../tools/nodetool/ForceCompact.java | 23 +- .../tools/nodetool/GarbageCollect.java | 42 +- .../cassandra/tools/nodetool/GcStats.java | 19 +- .../cassandra/tools/nodetool/GetAuditLog.java | 5 +- .../tools/nodetool/GetAuthCacheConfig.java | 12 +- .../tools/nodetool/GetAutoRepairConfig.java | 5 +- .../nodetool/GetBatchlogReplayTrottle.java | 5 +- .../tools/nodetool/GetCIDRGroupsOfIP.java | 24 +- .../tools/nodetool/GetColumnIndexSize.java | 5 +- .../nodetool/GetCompactionThreshold.java | 19 +- .../nodetool/GetCompactionThroughput.java | 11 +- .../tools/nodetool/GetConcurrency.java | 13 +- .../nodetool/GetConcurrentCompactors.java | 7 +- .../nodetool/GetConcurrentViewBuilders.java | 5 +- .../tools/nodetool/GetDefaultKeyspaceRF.java | 5 +- .../tools/nodetool/GetEndpoints.java | 26 +- .../tools/nodetool/GetFullQueryLog.java | 5 +- .../nodetool/GetInterDCStreamThroughput.java | 16 +- .../tools/nodetool/GetLoggingLevels.java | 6 +- .../tools/nodetool/GetMaxHintWindow.java | 5 +- .../cassandra/tools/nodetool/GetSSTables.java | 36 +- .../cassandra/tools/nodetool/GetSeeds.java | 6 +- .../tools/nodetool/GetSnapshotThrottle.java | 9 +- .../tools/nodetool/GetStreamThroughput.java | 16 +- .../cassandra/tools/nodetool/GetTimeout.java | 18 +- .../tools/nodetool/GetTraceProbability.java | 6 +- .../cassandra/tools/nodetool/GossipInfo.java | 10 +- .../nodetool/GuardrailsConfigCommand.java | 82 +- .../apache/cassandra/tools/nodetool/Help.java | 162 ++++ .../cassandra/tools/nodetool/Import.java | 79 +- .../apache/cassandra/tools/nodetool/Info.java | 11 +- .../InvalidateCIDRPermissionsCache.java | 11 +- .../nodetool/InvalidateCounterCache.java | 5 +- .../nodetool/InvalidateCredentialsCache.java | 11 +- .../InvalidateJmxPermissionsCache.java | 11 +- .../tools/nodetool/InvalidateKeyCache.java | 6 +- .../InvalidateNetworkPermissionsCache.java | 11 +- .../nodetool/InvalidatePermissionsCache.java | 65 +- .../tools/nodetool/InvalidateRolesCache.java | 11 +- .../tools/nodetool/InvalidateRowCache.java | 6 +- .../cassandra/tools/nodetool/JmxConnect.java | 243 ++++++ .../apache/cassandra/tools/nodetool/Join.java | 6 +- .../tools/{ => nodetool}/ListCIDRGroups.java | 29 +- .../tools/nodetool/ListPendingHints.java | 5 +- .../tools/nodetool/ListSnapshots.java | 37 +- .../apache/cassandra/tools/nodetool/Move.java | 21 +- .../cassandra/tools/nodetool/NetStats.java | 12 +- .../tools/nodetool/NodetoolCommand.java | 232 ++++++ .../tools/nodetool/PauseHandoff.java | 5 +- .../cassandra/tools/nodetool/ProfileLoad.java | 38 +- .../tools/nodetool/ProxyHistograms.java | 9 +- .../tools/nodetool/RangeKeySample.java | 6 +- .../cassandra/tools/nodetool/Rebuild.java | 32 +- .../tools/nodetool/RebuildIndex.java | 30 +- .../tools/nodetool/RecompressSSTables.java | 29 +- .../cassandra/tools/nodetool/Refresh.java | 25 +- .../tools/nodetool/RefreshSizeEstimates.java | 5 +- .../tools/nodetool/ReloadCIDRGroupsCache.java | 5 +- .../tools/nodetool/ReloadLocalSchema.java | 5 +- .../cassandra/tools/nodetool/ReloadSeeds.java | 6 +- .../{ => nodetool}/ReloadSslCertificates.java | 7 +- .../tools/nodetool/ReloadTriggers.java | 6 +- .../tools/nodetool/RelocateSSTables.java | 28 +- .../cassandra/tools/nodetool/RemoveNode.java | 62 +- .../cassandra/tools/nodetool/Repair.java | 90 +- .../cassandra/tools/nodetool/RepairAdmin.java | 95 ++- .../tools/nodetool/ReplayBatchlog.java | 5 +- .../tools/nodetool/ResetFullQueryLog.java | 5 +- .../tools/nodetool/ResetLocalSchema.java | 6 +- .../tools/nodetool/ResumeHandoff.java | 6 +- .../apache/cassandra/tools/nodetool/Ring.java | 26 +- .../tools/nodetool/SSTableRepairedSet.java | 41 +- .../cassandra/tools/nodetool/Scrub.java | 50 +- .../tools/nodetool/SetAuthCacheConfig.java | 37 +- .../tools/nodetool/SetAutoRepairConfig.java | 43 +- .../nodetool/SetBatchlogReplayThrottle.java | 9 +- .../tools/nodetool/SetCacheCapacity.java | 32 +- .../tools/nodetool/SetCacheKeysToSave.java | 32 +- .../tools/nodetool/SetColumnIndexSize.java | 10 +- .../nodetool/SetCompactionThreshold.java | 31 +- .../nodetool/SetCompactionThroughput.java | 10 +- .../tools/nodetool/SetConcurrency.java | 19 +- .../nodetool/SetConcurrentCompactors.java | 12 +- .../nodetool/SetConcurrentViewBuilders.java | 9 +- .../tools/nodetool/SetDefaultKeyspaceRF.java | 9 +- .../SetHintedHandoffThrottleInKB.java | 10 +- .../nodetool/SetInterDCStreamThroughput.java | 18 +- .../tools/nodetool/SetLoggingLevel.java | 26 +- .../tools/nodetool/SetMaxHintWindow.java | 9 +- .../tools/nodetool/SetSnapshotThrottle.java | 9 +- .../tools/nodetool/SetStreamThroughput.java | 18 +- .../cassandra/tools/nodetool/SetTimeout.java | 22 +- .../tools/nodetool/SetTraceProbability.java | 13 +- .../apache/cassandra/tools/nodetool/Sjk.java | 95 +-- .../cassandra/tools/nodetool/Snapshot.java | 21 +- .../cassandra/tools/nodetool/Status.java | 59 +- .../tools/nodetool/StatusAutoCompaction.java | 25 +- .../tools/nodetool/StatusBackup.java | 5 +- .../tools/nodetool/StatusBinary.java | 5 +- .../tools/nodetool/StatusGossip.java | 6 +- .../tools/nodetool/StatusHandoff.java | 5 +- .../apache/cassandra/tools/nodetool/Stop.java | 25 +- .../cassandra/tools/nodetool/StopDaemon.java | 7 +- .../tools/nodetool/TableHistograms.java | 27 +- .../cassandra/tools/nodetool/TableStats.java | 46 +- .../tools/nodetool/TopPartitions.java | 2 +- .../cassandra/tools/nodetool/TpStats.java | 22 +- .../tools/nodetool/TruncateHints.java | 15 +- .../tools/nodetool/UpdateCIDRGroup.java | 20 +- .../tools/nodetool/UpgradeSSTable.java | 35 +- .../cassandra/tools/nodetool/Verify.java | 56 +- .../cassandra/tools/nodetool/Version.java | 11 +- .../tools/nodetool/ViewBuildStatus.java | 31 +- .../WithPortDisplayAbstractCommand.java | 40 + .../layout/CassandraCliHelpLayout.java | 781 ++++++++++++++++++ .../tools/nodetool/layout/CassandraUsage.java | 38 + .../cassandra/distributed/impl/Instance.java | 2 +- .../nodetool/InternalNodeProbeFactory.java | 17 +- .../test/AbstractNodetoolStatusTest.java | 4 +- .../distributed/test/BooleanTest.java | 3 + .../distributed/test/GetEndpointsTest.java | 33 +- .../distributed/test/GossipTest.java | 6 + .../test/HintedHandoffNodetoolTest.java | 8 + .../distributed/test/IncRepairAdminTest.java | 3 + .../test/MultipleDataDirectoryTest.java | 3 + .../test/NodeToolEnableDisableBinaryTest.java | 78 +- .../distributed/test/NodeToolTest.java | 7 + .../distributed/test/RemoveNodeTest.java | 6 + .../distributed/test/TableEstimatesTest.java | 3 + .../distributed/test/UpgradeSSTablesTest.java | 3 + .../test/fql/FqlReplayDDLExclusionTest.java | 2 - .../test/gossip/GossipShutdownTest.java | 1 + .../test/log/ReconfigureCMSTest.java | 3 + .../distributed/test/ring/BootstrapTest.java | 4 +- .../ClusterMetadataSingleNodeUpgradeTest.java | 3 + test/resources/nodetool/help/abortbootstrap | 34 + .../resources/nodetool/help/abortdecommission | 31 + test/resources/nodetool/help/abortmove | 31 + test/resources/nodetool/help/abortremovenode | 31 + test/resources/nodetool/help/accord | 56 ++ test/resources/nodetool/help/accord$describe | 28 + .../nodetool/help/accord$mark_rejoining | 37 + .../resources/nodetool/help/accord$mark_stale | 37 + test/resources/nodetool/help/altertopology | 37 + test/resources/nodetool/help/assassinate | 36 + test/resources/nodetool/help/autorepairstatus | 31 + test/resources/nodetool/help/bootstrap | 43 + test/resources/nodetool/help/bootstrap$resume | 33 + .../nodetool/help/cidrfilteringstats | 27 + test/resources/nodetool/help/cleanup | 41 + test/resources/nodetool/help/clearsnapshot | 52 ++ test/resources/nodetool/help/clientstats | 44 + test/resources/nodetool/help/cms | 119 +++ .../nodetool/help/cms$abortinitialization | 31 + test/resources/nodetool/help/cms$describe | 27 + .../resources/nodetool/help/cms$dumpdirectory | 31 + test/resources/nodetool/help/cms$dumplog | 34 + test/resources/nodetool/help/cms$initialize | 31 + test/resources/nodetool/help/cms$reconfigure | 47 ++ .../nodetool/help/cms$resumedropaccordtable | 37 + test/resources/nodetool/help/cms$snapshot | 28 + test/resources/nodetool/help/cms$unregister | 35 + test/resources/nodetool/help/compact | 67 ++ .../resources/nodetool/help/compactionhistory | 34 + test/resources/nodetool/help/compactionstats | 34 + test/resources/nodetool/help/consensus_admin | 78 ++ .../help/consensus_admin$begin-migration | 49 ++ .../help/consensus_admin$finish-migration | 50 ++ .../nodetool/help/consensus_admin$list | 43 + test/resources/nodetool/help/datapaths | 40 + test/resources/nodetool/help/decommission | 31 + test/resources/nodetool/help/describecluster | 28 + test/resources/nodetool/help/describering | 35 + test/resources/nodetool/help/disableauditlog | 27 + .../nodetool/help/disableautocompaction | 37 + test/resources/nodetool/help/disablebackup | 27 + test/resources/nodetool/help/disablebinary | 31 + .../nodetool/help/disablefullquerylog | 27 + test/resources/nodetool/help/disablegossip | 28 + test/resources/nodetool/help/disablehandoff | 27 + .../resources/nodetool/help/disablehintsfordc | 36 + .../nodetool/help/disableoldprotocolversions | 27 + test/resources/nodetool/help/drain | 28 + test/resources/nodetool/help/dropcidrgroup | 35 + test/resources/nodetool/help/enableauditlog | 89 ++ .../nodetool/help/enableautocompaction | 37 + test/resources/nodetool/help/enablebackup | 27 + test/resources/nodetool/help/enablebinary | 27 + .../nodetool/help/enablefullquerylog | 61 ++ test/resources/nodetool/help/enablegossip | 27 + test/resources/nodetool/help/enablehandoff | 28 + test/resources/nodetool/help/enablehintsfordc | 37 + .../nodetool/help/enableoldprotocolversions | 27 + test/resources/nodetool/help/failuredetector | 28 + test/resources/nodetool/help/flush | 36 + test/resources/nodetool/help/forcecompact | 37 + test/resources/nodetool/help/garbagecollect | 46 ++ test/resources/nodetool/help/gcstats | 34 + test/resources/nodetool/help/getauditlog | 28 + .../nodetool/help/getauthcacheconfig | 31 + .../nodetool/help/getautorepairconfig | 27 + .../nodetool/help/getbatchlogreplaythrottle | 29 + .../resources/nodetool/help/getcidrgroupsofip | 36 + .../nodetool/help/getcolumnindexsize | 28 + .../nodetool/help/getcompactionthreshold | 37 + .../nodetool/help/getcompactionthroughput | 33 + test/resources/nodetool/help/getconcurrency | 36 + .../nodetool/help/getconcurrentcompactors | 28 + .../nodetool/help/getconcurrentviewbuilders | 28 + test/resources/nodetool/help/getdefaultrf | 27 + test/resources/nodetool/help/getendpoints | 37 + test/resources/nodetool/help/getfullquerylog | 28 + .../nodetool/help/getguardrailsconfig | 47 ++ .../nodetool/help/getinterdcstreamthroughput | 42 + test/resources/nodetool/help/getlogginglevels | 27 + test/resources/nodetool/help/getmaxhintwindow | 27 + test/resources/nodetool/help/getseeds | 28 + .../nodetool/help/getsnapshotthrottle | 28 + test/resources/nodetool/help/getsstables | 44 + .../nodetool/help/getstreamthroughput | 40 + test/resources/nodetool/help/gettimeout | 37 + .../nodetool/help/gettraceprobability | 27 + test/resources/nodetool/help/gossipinfo | 31 + test/resources/nodetool/help/help | 14 + test/resources/nodetool/help/import | 73 ++ test/resources/nodetool/help/info | 30 + .../help/invalidatecidrpermissionscache | 37 + .../nodetool/help/invalidatecountercache | 27 + .../nodetool/help/invalidatecredentialscache | 36 + .../help/invalidatejmxpermissionscache | 37 + .../nodetool/help/invalidatekeycache | 27 + .../help/invalidatenetworkpermissionscache | 37 + .../nodetool/help/invalidatepermissionscache | 77 ++ .../nodetool/help/invalidaterolescache | 36 + .../nodetool/help/invalidaterowcache | 27 + test/resources/nodetool/help/join | 27 + test/resources/nodetool/help/listcidrgroups | 36 + test/resources/nodetool/help/listpendinghints | 27 + test/resources/nodetool/help/listsnapshots | 49 ++ test/resources/nodetool/help/move | 39 + test/resources/nodetool/help/netstats | 32 + test/resources/nodetool/help/nodetool | 164 ++++ test/resources/nodetool/help/pausehandoff | 27 + test/resources/nodetool/help/profileload | 60 ++ test/resources/nodetool/help/proxyhistograms | 28 + test/resources/nodetool/help/rangekeysample | 28 + test/resources/nodetool/help/rebuild | 58 ++ test/resources/nodetool/help/rebuild_index | 37 + .../nodetool/help/recompress_sstables | 41 + test/resources/nodetool/help/refresh | 37 + .../nodetool/help/refreshsizeestimates | 27 + .../nodetool/help/reloadcidrgroupscache | 28 + .../resources/nodetool/help/reloadlocalschema | 27 + test/resources/nodetool/help/reloadseeds | 28 + test/resources/nodetool/help/reloadssl | 27 + test/resources/nodetool/help/reloadtriggers | 27 + test/resources/nodetool/help/relocatesstables | 40 + test/resources/nodetool/help/removenode | 51 ++ .../resources/nodetool/help/removenode$status | 28 + test/resources/nodetool/help/repair | 131 +++ test/resources/nodetool/help/repair_admin | 123 +++ .../nodetool/help/repair_admin$cancel | 36 + .../nodetool/help/repair_admin$cleanup | 50 ++ .../resources/nodetool/help/repair_admin$list | 38 + .../help/repair_admin$summarize-pending | 50 ++ .../help/repair_admin$summarize-repaired | 50 ++ test/resources/nodetool/help/replaybatchlog | 27 + .../resources/nodetool/help/resetfullquerylog | 29 + test/resources/nodetool/help/resetlocalschema | 27 + test/resources/nodetool/help/resumehandoff | 27 + test/resources/nodetool/help/ring | 40 + test/resources/nodetool/help/scrub | 61 ++ .../nodetool/help/setauthcacheconfig | 49 ++ .../nodetool/help/setautorepairconfig | 43 + .../nodetool/help/setbatchlogreplaythrottle | 38 + test/resources/nodetool/help/setcachecapacity | 37 + .../nodetool/help/setcachekeystosave | 39 + .../nodetool/help/setcolumnindexsize | 37 + .../nodetool/help/setcompactionthreshold | 37 + .../nodetool/help/setcompactionthroughput | 37 + test/resources/nodetool/help/setconcurrency | 38 + .../nodetool/help/setconcurrentcompactors | 37 + .../nodetool/help/setconcurrentviewbuilders | 37 + test/resources/nodetool/help/setdefaultrf | 35 + .../nodetool/help/setguardrailsconfig | 42 + .../nodetool/help/sethintedhandoffthrottlekb | 37 + .../nodetool/help/setinterdcstreamthroughput | 45 + test/resources/nodetool/help/setlogginglevel | 40 + test/resources/nodetool/help/setmaxhintwindow | 36 + .../nodetool/help/setsnapshotthrottle | 38 + .../nodetool/help/setstreamthroughput | 44 + test/resources/nodetool/help/settimeout | 40 + .../nodetool/help/settraceprobability | 38 + test/resources/nodetool/help/sjk | 36 + test/resources/nodetool/help/snapshot | 57 ++ .../nodetool/help/sstablerepairedset | 53 ++ test/resources/nodetool/help/status | 50 ++ .../nodetool/help/statusautocompaction | 40 + test/resources/nodetool/help/statusbackup | 27 + test/resources/nodetool/help/statusbinary | 27 + test/resources/nodetool/help/statusgossip | 27 + test/resources/nodetool/help/statushandoff | 28 + test/resources/nodetool/help/stop | 45 + test/resources/nodetool/help/stopdaemon | 27 + test/resources/nodetool/help/tablehistograms | 36 + test/resources/nodetool/help/tablestats | 79 ++ test/resources/nodetool/help/toppartitions | 59 ++ test/resources/nodetool/help/tpstats | 34 + test/resources/nodetool/help/truncatehints | 38 + test/resources/nodetool/help/updatecidrgroup | 37 + test/resources/nodetool/help/upgradesstables | 52 ++ test/resources/nodetool/help/verify | 59 ++ test/resources/nodetool/help/version | 30 + test/resources/nodetool/help/viewbuildstatus | 36 + .../cassandra/simulator/SimulationRunner.java | 162 ++-- .../paxos/AccordClusterSimulation.java | 4 +- .../paxos/AccordSimulationRunner.java | 24 +- .../paxos/PaxosSimulationRunner.java | 119 ++- .../simulator/test/HarrySimulatorTest.java | 41 +- .../apache/cassandra/db/CounterCacheTest.java | 27 +- .../org/apache/cassandra/db/ImportTest.java | 97 ++- .../apache/cassandra/hints/HintsMaker.java | 51 +- .../apache/cassandra/repair/FuzzTestBase.java | 21 +- .../service/StorageServiceDrainTest.java | 25 +- .../apache/cassandra/tools/JMXToolTest.java | 115 +-- .../apache/cassandra/tools/ToolRunner.java | 138 +++- .../nodetool/CIDRFilteringStatsTest.java | 40 - .../tools/nodetool/ClientStatsTest.java | 55 -- .../cassandra/tools/nodetool/CompactTest.java | 9 +- .../tools/nodetool/CompactionHistoryTest.java | 6 + .../tools/nodetool/CompactionStatsTest.java | 47 -- .../tools/nodetool/DataPathsTest.java | 51 -- .../tools/nodetool/DropCIDRGroupTest.java | 48 -- .../tools/nodetool/ForceCompactionTest.java | 21 +- .../cassandra/tools/nodetool/GcStatsTest.java | 47 -- .../tools/nodetool/GetAuditLogTest.java | 4 + .../nodetool/GetAuthCacheConfigTest.java | 48 +- .../tools/nodetool/GetCIDRGroupsOfIPTest.java | 49 -- .../nodetool/GetDefaultKeyspaceRFTest.java | 40 - .../tools/nodetool/GetFullQueryLogTest.java | 8 + .../tools/nodetool/GossipInfoTest.java | 47 +- .../GuardrailsConfigCommandsTest.java | 4 +- .../InvalidateCIDRPermissionsCacheTest.java | 50 -- .../InvalidateCredentialsCacheTest.java | 52 +- .../InvalidateJmxPermissionsCacheTest.java | 53 +- ...InvalidateNetworkPermissionsCacheTest.java | 50 -- .../InvalidatePermissionsCacheTest.java | 92 +-- .../nodetool/InvalidateRolesCacheTest.java | 49 -- .../tools/nodetool/ListCIDRGroupTest.java | 49 -- .../tools/nodetool/NetStatsTest.java | 45 - .../nodetool/NodetoolClassHierarchyTest.java | 73 ++ .../NodetoolHelpCommandsOutputTest.java | 183 ++++ .../tools/nodetool/NodetoolHelpGenerator.java | 166 ++++ .../nodetool/ReloadCIDRGroupsCacheTest.java | 41 - .../cassandra/tools/nodetool/RingTest.java | 60 +- .../nodetool/SSTableRepairedSetTest.java | 2 +- .../nodetool/SetAuthCacheConfigTest.java | 66 +- .../nodetool/SetDefaultKeyspaceRFTest.java | 48 -- .../nodetool/SetGetColumnIndexSizeTest.java | 6 +- .../SetGetCompactionThroughputTest.java | 6 +- ...ireSSTableInterDCStreamThroughputTest.java | 9 +- ...tGetEntireSSTableStreamThroughputTest.java | 6 +- .../SetGetInterDCStreamThroughputTest.java | 10 +- .../nodetool/SetGetStreamThroughputTest.java | 13 +- .../tools/nodetool/TableHistogramsTest.java | 56 +- .../tools/nodetool/TableStatsTest.java | 95 +-- .../cassandra/tools/nodetool/TpStatsTest.java | 47 -- .../tools/nodetool/UpdateCIDRGroupTest.java | 50 -- .../cassandra/tools/nodetool/VerifyTest.java | 128 --- .../cassandra/tools/nodetool/VersionTest.java | 41 - .../nodetool/mock/AbortBootstrapMockTest.java | 45 + .../nodetool/mock/AbstractNodetoolMock.java | 180 ++++ .../nodetool/mock/AccordAdminMockTest.java | 38 + .../nodetool/mock/AssassinateMockTest.java | 41 + .../mock/BootstrapResumeMockTest.java | 52 ++ .../tools/nodetool/mock/CleanupMockTest.java | 52 ++ .../tools/nodetool/mock/CompactMockTest.java | 77 ++ .../nodetool/mock/DecommissionMockTest.java | 63 ++ .../mock/DescribeClusterMockTest.java | 72 ++ .../nodetool/mock/DescribeRingMockTest.java | 50 ++ .../mock/DisableAuditLogMockTest.java | 35 + .../mock/DisableAutoCompactionMockTest.java | 70 ++ .../nodetool/mock/DisableBackupMockTest.java | 37 + .../mock/DisableFullQueryLogMockTest.java | 37 + .../DisableOldProtocolVersionsMockTest.java | 37 + .../tools/nodetool/mock/DrainMockTest.java | 35 + .../mock/EnableAutoCompactionMockTest.java | 50 ++ .../nodetool/mock/EnableBackupMockTest.java | 37 + .../EnableOldProtocolVersionsMockTest.java | 35 + .../mock/FailureDetectorInfoMockTest.java | 66 ++ .../nodetool/mock/GarbageCollectMockTest.java | 61 ++ .../GetBatchlogReplayTrottleMockTest.java | 37 + .../mock/GetCompactionThresholdMockTest.java | 51 ++ .../nodetool/mock/GetConcurrencyMockTest.java | 48 ++ .../mock/GetConcurrentCompactorsMockTest.java | 37 + .../GetConcurrentViewBuildersMockTest.java | 35 + .../mock/GetLoggingLevelsMockTest.java | 40 + .../nodetool/mock/GetSSTablesMockTest.java | 55 ++ .../tools/nodetool/mock/GetSeedsTest.java | 44 + .../mock/GetSnapshotThrottleMockTest.java | 40 + .../mock/GetTraceProbabilityMockTest.java | 35 + .../mock/InvalidateKeyCacheMockTest.java | 35 + .../mock/InvalidateRowCacheMockTest.java | 35 + .../mock/ListPendingHintsMockTest.java | 42 + .../nodetool/mock/RangeKeySampleMockTest.java | 40 + .../nodetool/mock/RebuildIndexMockTest.java | 35 + .../mock/ReloadLocalSchemaMockTest.java | 35 + .../nodetool/mock/ReloadSeedsMockTest.java | 42 + .../mock/ReloadSslCertificatesMockTest.java | 35 + .../nodetool/mock/ReloadTriggersMockTest.java | 35 + .../mock/RelocateSSTablesMockTest.java | 43 + .../nodetool/mock/ReplayBatchlogMockTest.java | 35 + .../mock/ResetLocalSchemaMockTest.java | 37 + .../SetBatchlogReplayThrottleMockTest.java | 35 + .../mock/SetCacheKeysToSaveMockTest.java | 37 + .../mock/SetCompactionThresholdMockTest.java | 35 + .../nodetool/mock/SetConcurrencyMockTest.java | 58 ++ .../SetConcurrentViewBuildersMockTest.java | 35 + .../mock/SetLoggingLevelMockTest.java | 35 + .../mock/SetSnapshotThrottleMockTest.java | 35 + .../mock/SetTraceProbabilityMockTest.java | 35 + .../tools/nodetool/mock/SjkMockTest.java | 56 ++ .../tools/nodetool/mock/SnapshotMockTest.java | 43 + .../mock/StatusAutoCompactionMockTest.java | 44 + .../nodetool/mock/StatusBackupMockTest.java | 40 + .../nodetool/mock/StatusBinaryMockTest.java | 40 + .../nodetool/mock/StatusGossipMockTest.java | 40 + .../nodetool/mock/StopDaemonMockTest.java | 35 + .../tools/nodetool/mock/StopMockTest.java | 53 ++ .../nodetool/mock/TruncateHintsMockTest.java | 43 + .../tools/nodetool/mock/VersionMockTest.java | 51 ++ .../mock/ViewBuildStatusMockTest.java | 56 ++ .../cassandra/fqltool/FullQueryLogTool.java | 76 +- .../cassandra/fqltool/commands/Compare.java | 16 +- .../cassandra/fqltool/commands/Dump.java | 16 +- .../cassandra/fqltool/commands/Replay.java | 23 +- .../cassandra/stress/CompactionStress.java | 71 +- 494 files changed, 14954 insertions(+), 4071 deletions(-) create mode 100644 src/java/org/apache/cassandra/tools/nodetool/AbstractCommand.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/Bootstrap.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/CommandUtils.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/Help.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/JmxConnect.java rename src/java/org/apache/cassandra/tools/{ => nodetool}/ListCIDRGroups.java (61%) create mode 100644 src/java/org/apache/cassandra/tools/nodetool/NodetoolCommand.java rename src/java/org/apache/cassandra/tools/{ => nodetool}/ReloadSslCertificates.java (86%) create mode 100644 src/java/org/apache/cassandra/tools/nodetool/WithPortDisplayAbstractCommand.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/layout/CassandraCliHelpLayout.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/layout/CassandraUsage.java create mode 100644 test/resources/nodetool/help/abortbootstrap create mode 100644 test/resources/nodetool/help/abortdecommission create mode 100644 test/resources/nodetool/help/abortmove create mode 100644 test/resources/nodetool/help/abortremovenode create mode 100644 test/resources/nodetool/help/accord create mode 100644 test/resources/nodetool/help/accord$describe create mode 100644 test/resources/nodetool/help/accord$mark_rejoining create mode 100644 test/resources/nodetool/help/accord$mark_stale create mode 100644 test/resources/nodetool/help/altertopology create mode 100644 test/resources/nodetool/help/assassinate create mode 100644 test/resources/nodetool/help/autorepairstatus create mode 100644 test/resources/nodetool/help/bootstrap create mode 100644 test/resources/nodetool/help/bootstrap$resume create mode 100644 test/resources/nodetool/help/cidrfilteringstats create mode 100644 test/resources/nodetool/help/cleanup create mode 100644 test/resources/nodetool/help/clearsnapshot create mode 100644 test/resources/nodetool/help/clientstats create mode 100644 test/resources/nodetool/help/cms create mode 100644 test/resources/nodetool/help/cms$abortinitialization create mode 100644 test/resources/nodetool/help/cms$describe create mode 100644 test/resources/nodetool/help/cms$dumpdirectory create mode 100644 test/resources/nodetool/help/cms$dumplog create mode 100644 test/resources/nodetool/help/cms$initialize create mode 100644 test/resources/nodetool/help/cms$reconfigure create mode 100644 test/resources/nodetool/help/cms$resumedropaccordtable create mode 100644 test/resources/nodetool/help/cms$snapshot create mode 100644 test/resources/nodetool/help/cms$unregister create mode 100644 test/resources/nodetool/help/compact create mode 100644 test/resources/nodetool/help/compactionhistory create mode 100644 test/resources/nodetool/help/compactionstats create mode 100644 test/resources/nodetool/help/consensus_admin create mode 100644 test/resources/nodetool/help/consensus_admin$begin-migration create mode 100644 test/resources/nodetool/help/consensus_admin$finish-migration create mode 100644 test/resources/nodetool/help/consensus_admin$list create mode 100644 test/resources/nodetool/help/datapaths create mode 100644 test/resources/nodetool/help/decommission create mode 100644 test/resources/nodetool/help/describecluster create mode 100644 test/resources/nodetool/help/describering create mode 100644 test/resources/nodetool/help/disableauditlog create mode 100644 test/resources/nodetool/help/disableautocompaction create mode 100644 test/resources/nodetool/help/disablebackup create mode 100644 test/resources/nodetool/help/disablebinary create mode 100644 test/resources/nodetool/help/disablefullquerylog create mode 100644 test/resources/nodetool/help/disablegossip create mode 100644 test/resources/nodetool/help/disablehandoff create mode 100644 test/resources/nodetool/help/disablehintsfordc create mode 100644 test/resources/nodetool/help/disableoldprotocolversions create mode 100644 test/resources/nodetool/help/drain create mode 100644 test/resources/nodetool/help/dropcidrgroup create mode 100644 test/resources/nodetool/help/enableauditlog create mode 100644 test/resources/nodetool/help/enableautocompaction create mode 100644 test/resources/nodetool/help/enablebackup create mode 100644 test/resources/nodetool/help/enablebinary create mode 100644 test/resources/nodetool/help/enablefullquerylog create mode 100644 test/resources/nodetool/help/enablegossip create mode 100644 test/resources/nodetool/help/enablehandoff create mode 100644 test/resources/nodetool/help/enablehintsfordc create mode 100644 test/resources/nodetool/help/enableoldprotocolversions create mode 100644 test/resources/nodetool/help/failuredetector create mode 100644 test/resources/nodetool/help/flush create mode 100644 test/resources/nodetool/help/forcecompact create mode 100644 test/resources/nodetool/help/garbagecollect create mode 100644 test/resources/nodetool/help/gcstats create mode 100644 test/resources/nodetool/help/getauditlog create mode 100644 test/resources/nodetool/help/getauthcacheconfig create mode 100644 test/resources/nodetool/help/getautorepairconfig create mode 100644 test/resources/nodetool/help/getbatchlogreplaythrottle create mode 100644 test/resources/nodetool/help/getcidrgroupsofip create mode 100644 test/resources/nodetool/help/getcolumnindexsize create mode 100644 test/resources/nodetool/help/getcompactionthreshold create mode 100644 test/resources/nodetool/help/getcompactionthroughput create mode 100644 test/resources/nodetool/help/getconcurrency create mode 100644 test/resources/nodetool/help/getconcurrentcompactors create mode 100644 test/resources/nodetool/help/getconcurrentviewbuilders create mode 100644 test/resources/nodetool/help/getdefaultrf create mode 100644 test/resources/nodetool/help/getendpoints create mode 100644 test/resources/nodetool/help/getfullquerylog create mode 100644 test/resources/nodetool/help/getguardrailsconfig create mode 100644 test/resources/nodetool/help/getinterdcstreamthroughput create mode 100644 test/resources/nodetool/help/getlogginglevels create mode 100644 test/resources/nodetool/help/getmaxhintwindow create mode 100644 test/resources/nodetool/help/getseeds create mode 100644 test/resources/nodetool/help/getsnapshotthrottle create mode 100644 test/resources/nodetool/help/getsstables create mode 100644 test/resources/nodetool/help/getstreamthroughput create mode 100644 test/resources/nodetool/help/gettimeout create mode 100644 test/resources/nodetool/help/gettraceprobability create mode 100644 test/resources/nodetool/help/gossipinfo create mode 100644 test/resources/nodetool/help/help create mode 100644 test/resources/nodetool/help/import create mode 100644 test/resources/nodetool/help/info create mode 100644 test/resources/nodetool/help/invalidatecidrpermissionscache create mode 100644 test/resources/nodetool/help/invalidatecountercache create mode 100644 test/resources/nodetool/help/invalidatecredentialscache create mode 100644 test/resources/nodetool/help/invalidatejmxpermissionscache create mode 100644 test/resources/nodetool/help/invalidatekeycache create mode 100644 test/resources/nodetool/help/invalidatenetworkpermissionscache create mode 100644 test/resources/nodetool/help/invalidatepermissionscache create mode 100644 test/resources/nodetool/help/invalidaterolescache create mode 100644 test/resources/nodetool/help/invalidaterowcache create mode 100644 test/resources/nodetool/help/join create mode 100644 test/resources/nodetool/help/listcidrgroups create mode 100644 test/resources/nodetool/help/listpendinghints create mode 100644 test/resources/nodetool/help/listsnapshots create mode 100644 test/resources/nodetool/help/move create mode 100644 test/resources/nodetool/help/netstats create mode 100644 test/resources/nodetool/help/nodetool create mode 100644 test/resources/nodetool/help/pausehandoff create mode 100644 test/resources/nodetool/help/profileload create mode 100644 test/resources/nodetool/help/proxyhistograms create mode 100644 test/resources/nodetool/help/rangekeysample create mode 100644 test/resources/nodetool/help/rebuild create mode 100644 test/resources/nodetool/help/rebuild_index create mode 100644 test/resources/nodetool/help/recompress_sstables create mode 100644 test/resources/nodetool/help/refresh create mode 100644 test/resources/nodetool/help/refreshsizeestimates create mode 100644 test/resources/nodetool/help/reloadcidrgroupscache create mode 100644 test/resources/nodetool/help/reloadlocalschema create mode 100644 test/resources/nodetool/help/reloadseeds create mode 100644 test/resources/nodetool/help/reloadssl create mode 100644 test/resources/nodetool/help/reloadtriggers create mode 100644 test/resources/nodetool/help/relocatesstables create mode 100644 test/resources/nodetool/help/removenode create mode 100644 test/resources/nodetool/help/removenode$status create mode 100644 test/resources/nodetool/help/repair create mode 100644 test/resources/nodetool/help/repair_admin create mode 100644 test/resources/nodetool/help/repair_admin$cancel create mode 100644 test/resources/nodetool/help/repair_admin$cleanup create mode 100644 test/resources/nodetool/help/repair_admin$list create mode 100644 test/resources/nodetool/help/repair_admin$summarize-pending create mode 100644 test/resources/nodetool/help/repair_admin$summarize-repaired create mode 100644 test/resources/nodetool/help/replaybatchlog create mode 100644 test/resources/nodetool/help/resetfullquerylog create mode 100644 test/resources/nodetool/help/resetlocalschema create mode 100644 test/resources/nodetool/help/resumehandoff create mode 100644 test/resources/nodetool/help/ring create mode 100644 test/resources/nodetool/help/scrub create mode 100644 test/resources/nodetool/help/setauthcacheconfig create mode 100644 test/resources/nodetool/help/setautorepairconfig create mode 100644 test/resources/nodetool/help/setbatchlogreplaythrottle create mode 100644 test/resources/nodetool/help/setcachecapacity create mode 100644 test/resources/nodetool/help/setcachekeystosave create mode 100644 test/resources/nodetool/help/setcolumnindexsize create mode 100644 test/resources/nodetool/help/setcompactionthreshold create mode 100644 test/resources/nodetool/help/setcompactionthroughput create mode 100644 test/resources/nodetool/help/setconcurrency create mode 100644 test/resources/nodetool/help/setconcurrentcompactors create mode 100644 test/resources/nodetool/help/setconcurrentviewbuilders create mode 100644 test/resources/nodetool/help/setdefaultrf create mode 100644 test/resources/nodetool/help/setguardrailsconfig create mode 100644 test/resources/nodetool/help/sethintedhandoffthrottlekb create mode 100644 test/resources/nodetool/help/setinterdcstreamthroughput create mode 100644 test/resources/nodetool/help/setlogginglevel create mode 100644 test/resources/nodetool/help/setmaxhintwindow create mode 100644 test/resources/nodetool/help/setsnapshotthrottle create mode 100644 test/resources/nodetool/help/setstreamthroughput create mode 100644 test/resources/nodetool/help/settimeout create mode 100644 test/resources/nodetool/help/settraceprobability create mode 100644 test/resources/nodetool/help/sjk create mode 100644 test/resources/nodetool/help/snapshot create mode 100644 test/resources/nodetool/help/sstablerepairedset create mode 100644 test/resources/nodetool/help/status create mode 100644 test/resources/nodetool/help/statusautocompaction create mode 100644 test/resources/nodetool/help/statusbackup create mode 100644 test/resources/nodetool/help/statusbinary create mode 100644 test/resources/nodetool/help/statusgossip create mode 100644 test/resources/nodetool/help/statushandoff create mode 100644 test/resources/nodetool/help/stop create mode 100644 test/resources/nodetool/help/stopdaemon create mode 100644 test/resources/nodetool/help/tablehistograms create mode 100644 test/resources/nodetool/help/tablestats create mode 100644 test/resources/nodetool/help/toppartitions create mode 100644 test/resources/nodetool/help/tpstats create mode 100644 test/resources/nodetool/help/truncatehints create mode 100644 test/resources/nodetool/help/updatecidrgroup create mode 100644 test/resources/nodetool/help/upgradesstables create mode 100644 test/resources/nodetool/help/verify create mode 100644 test/resources/nodetool/help/version create mode 100644 test/resources/nodetool/help/viewbuildstatus create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/NodetoolClassHierarchyTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpCommandsOutputTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpGenerator.java delete mode 100644 test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/AbortBootstrapMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/AbstractNodetoolMock.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/AccordAdminMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/AssassinateMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/BootstrapResumeMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/CleanupMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/CompactMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DecommissionMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeClusterMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeRingMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAuditLogMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAutoCompactionMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DisableBackupMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DisableFullQueryLogMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DisableOldProtocolVersionsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/DrainMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/EnableAutoCompactionMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/EnableBackupMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/EnableOldProtocolVersionsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/FailureDetectorInfoMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GarbageCollectMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetBatchlogReplayTrottleMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetCompactionThresholdMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrencyMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentCompactorsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentViewBuildersMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetLoggingLevelsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetSSTablesMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetSeedsTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetSnapshotThrottleMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/GetTraceProbabilityMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateKeyCacheMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateRowCacheMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ListPendingHintsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/RangeKeySampleMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/RebuildIndexMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadLocalSchemaMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSeedsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSslCertificatesMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadTriggersMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/RelocateSSTablesMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ReplayBatchlogMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ResetLocalSchemaMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetBatchlogReplayThrottleMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetCacheKeysToSaveMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetCompactionThresholdMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrencyMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrentViewBuildersMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetLoggingLevelMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetSnapshotThrottleMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SetTraceProbabilityMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SjkMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/SnapshotMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/StatusAutoCompactionMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBackupMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBinaryMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/StatusGossipMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/StopDaemonMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/StopMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/TruncateHintsMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/VersionMockTest.java create mode 100644 test/unit/org/apache/cassandra/tools/nodetool/mock/ViewBuildStatusMockTest.java diff --git a/.build/build-rat.xml b/.build/build-rat.xml index 32bf3d7369..4ce8f3bd78 100644 --- a/.build/build-rat.xml +++ b/.build/build-rat.xml @@ -57,6 +57,7 @@ + diff --git a/.build/cassandra-build-maven-pom.xml b/.build/cassandra-build-maven-pom.xml index c6b56955e0..bf2adfd9f8 100644 --- a/.build/cassandra-build-maven-pom.xml +++ b/.build/cassandra-build-maven-pom.xml @@ -98,6 +98,10 @@ org.openjdk.jmh jmh-generator-annprocess + + io.github.java-diff-utils + java-diff-utils + net.ju-n.compile-command-annotations compile-command-annotations diff --git a/.build/cassandra-deps-maven-pom.xml b/.build/cassandra-deps-maven-pom.xml index be58faa2f4..c38a171fa2 100644 --- a/.build/cassandra-deps-maven-pom.xml +++ b/.build/cassandra-deps-maven-pom.xml @@ -121,8 +121,8 @@ cassandra-accord - io.airlift - airline + info.picocli + picocli io.dropwizard.metrics diff --git a/.build/parent-maven-pom.xml b/.build/parent-maven-pom.xml index a42991bca4..8f56231bfc 100644 --- a/.build/parent-maven-pom.xml +++ b/.build/parent-maven-pom.xml @@ -662,6 +662,12 @@ 1.37 test + + io.github.java-diff-utils + java-diff-utils + 4.12 + test + org.apache.ant ant-junit @@ -752,15 +758,9 @@ - io.airlift - airline - 0.8 - - - jsr305 - com.google.code.findbugs - - + info.picocli + picocli + 4.7.7 io.netty diff --git a/CHANGES.txt b/CHANGES.txt index fd0d30a969..d34add029e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.1 + * Migrate all nodetool commands from airline to picocli (CASSANDRA-17445) * Journal.TopologyUpdate should not store the local topology as it can be inferred from the global on (CASSANDRA-20785) * Accord: Topology serializer has a lot of repeated data, can dedup to shrink the cost (CASSANDRA-20715) * Stream individual files in their own transactions and hand over ownership to a parent transaction on completion (CASSANDRA-20728) diff --git a/src/java/org/apache/cassandra/config/CassandraRelevantEnv.java b/src/java/org/apache/cassandra/config/CassandraRelevantEnv.java index 0fdcd6c819..d970cf7fde 100644 --- a/src/java/org/apache/cassandra/config/CassandraRelevantEnv.java +++ b/src/java/org/apache/cassandra/config/CassandraRelevantEnv.java @@ -20,6 +20,12 @@ package org.apache.cassandra.config; // checkstyle: suppress below 'blockSystemPropertyUsage' +import java.util.Arrays; + +import org.apache.cassandra.exceptions.ConfigurationException; + +import static org.apache.cassandra.utils.LocalizeString.toUpperCaseLocalized; + public enum CassandraRelevantEnv { /** @@ -28,8 +34,10 @@ public enum CassandraRelevantEnv */ JAVA_HOME ("JAVA_HOME"), CIRCLECI("CIRCLECI"), - CASSANDRA_SKIP_SYNC("CASSANDRA_SKIP_SYNC") - + CASSANDRA_SKIP_SYNC("CASSANDRA_SKIP_SYNC"), + /** By default, the standard Cassandra CLI layout is used for backward compatibility, however, + * the new Picocli layout can be enabled by setting this property to the {@code "picocli"}. */ + CASSANDRA_CLI_LAYOUT("CASSANDRA_CLI_LAYOUT"), ; CassandraRelevantEnv(String key) @@ -56,4 +64,20 @@ public enum CassandraRelevantEnv public String getKey() { return key; } + + public > T getEnum(boolean toUppercase, Class enumClass, String defaultVal) + { + String value = System.getenv(key); + value = value == null ? defaultVal : value; + try + { + return Enum.valueOf(enumClass, toUppercase ? toUpperCaseLocalized(value) : value); + } + catch (IllegalArgumentException e) + { + throw new ConfigurationException(String.format("Invalid value for environment variable '%s': " + + "expected one of %s (case-insensitive) but was '%s'", + key, Arrays.toString(enumClass.getEnumConstants()), value)); + } + } } diff --git a/src/java/org/apache/cassandra/config/CassandraRelevantProperties.java b/src/java/org/apache/cassandra/config/CassandraRelevantProperties.java index 0d411d7e81..a518497e8f 100644 --- a/src/java/org/apache/cassandra/config/CassandraRelevantProperties.java +++ b/src/java/org/apache/cassandra/config/CassandraRelevantProperties.java @@ -18,6 +18,7 @@ package org.apache.cassandra.config; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -87,6 +88,9 @@ public enum CassandraRelevantProperties CACHEABLE_MUTATION_SIZE_LIMIT("cassandra.cacheable_mutation_size_limit_bytes", convertToString(1_000_000)), CASSANDRA_ALLOW_SIMPLE_STRATEGY("cassandra.allow_simplestrategy"), CASSANDRA_AVAILABLE_PROCESSORS("cassandra.available_processors"), + /** By default, the standard Cassandra CLI layout is used for backward compatibility, however, + * the new Picocli layout can be enabled by setting this property to the {@code "picocli"}. */ + CASSANDRA_CLI_LAYOUT("cassandra.cli.layout", "airline"), /** The classpath storage configuration file. */ CASSANDRA_CONFIG("cassandra.config", "cassandra.yaml"), /** @@ -1002,7 +1006,16 @@ public enum CassandraRelevantProperties public > T getEnum(boolean toUppercase, Class enumClass) { String value = System.getProperty(key, defaultVal); - return Enum.valueOf(enumClass, toUppercase ? toUpperCaseLocalized(value) : value); + try + { + return Enum.valueOf(enumClass, toUppercase ? toUpperCaseLocalized(value) : value); + } + catch (IllegalArgumentException e) + { + throw new ConfigurationException(String.format("Invalid value for system propery '%s': " + + "expected one of %s (case-insensitive) but was '%s'", + key, Arrays.toString(enumClass.getEnumConstants()), value)); + } } /** diff --git a/src/java/org/apache/cassandra/db/SSTableImporter.java b/src/java/org/apache/cassandra/db/SSTableImporter.java index 8ad79003d3..09f4fb522e 100644 --- a/src/java/org/apache/cassandra/db/SSTableImporter.java +++ b/src/java/org/apache/cassandra/db/SSTableImporter.java @@ -469,16 +469,16 @@ public class SSTableImporter public static class Options { - private final Set srcPaths; - private final boolean resetLevel; - private final boolean clearRepaired; - private final boolean verifySSTables; - private final boolean verifyTokens; - private final boolean invalidateCaches; - private final boolean extendedVerify; - private final boolean copyData; - private final boolean failOnMissingIndex; - public final boolean validateIndexChecksum; + final Set srcPaths; + final boolean resetLevel; + final boolean clearRepaired; + final boolean verifySSTables; + final boolean verifyTokens; + final boolean invalidateCaches; + final boolean extendedVerify; + final boolean copyData; + final boolean failOnMissingIndex; + final boolean validateIndexChecksum; public Options(Set srcPaths, boolean resetLevel, boolean clearRepaired, boolean verifySSTables, boolean verifyTokens, boolean invalidateCaches, @@ -523,9 +523,9 @@ public class SSTableImporter ", verifyTokens=" + verifyTokens + ", invalidateCaches=" + invalidateCaches + ", extendedVerify=" + extendedVerify + - ", copyData= " + copyData + - ", failOnMissingIndex= " + failOnMissingIndex + - ", validateIndexChecksum= " + validateIndexChecksum + + ", copyData=" + copyData + + ", failOnMissingIndex=" + failOnMissingIndex + + ", validateIndexChecksum=" + validateIndexChecksum + '}'; } diff --git a/src/java/org/apache/cassandra/tools/JMXTool.java b/src/java/org/apache/cassandra/tools/JMXTool.java index 3e2fab5b3b..aaab04ee76 100644 --- a/src/java/org/apache/cassandra/tools/JMXTool.java +++ b/src/java/org/apache/cassandra/tools/JMXTool.java @@ -40,7 +40,6 @@ import java.util.function.BiConsumer; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.inject.Inject; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; @@ -62,14 +61,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import com.fasterxml.jackson.core.type.TypeReference; -import io.airlift.airline.Arguments; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; -import io.airlift.airline.Help; -import io.airlift.airline.HelpOption; -import io.airlift.airline.Option; import org.apache.cassandra.config.YamlConfigurationLoader; -import org.apache.cassandra.io.util.File; import org.apache.cassandra.io.util.FileInputStreamPlus; import org.apache.cassandra.utils.JsonUtils; import org.yaml.snakeyaml.DumperOptions; @@ -80,8 +72,14 @@ import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Representer; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; -public class JMXTool +@Command(name = "jmxtool", description = "JMX tool for Apache Cassandra", + mixinStandardHelpOptions = true, + subcommands = { CommandLine.HelpCommand.class, JMXTool.Dump.class, JMXTool.Diff.class }) +public class JMXTool implements Callable { private static final List METRIC_PACKAGES = Arrays.asList("org.apache.cassandra.metrics", "org.apache.cassandra.db", @@ -109,16 +107,20 @@ public class JMXTool return rc; }; - @Command(name = "dump", description = "Dump the Apache Cassandra JMX objects and metadata.") + @Override + public Void call() throws Exception + { + CommandLine.usage(this, System.out); + return null; + } + + @Command(name = "dump", description = "Dump the Apache Cassandra JMX objects and metadata.", mixinStandardHelpOptions = true) public static final class Dump implements Callable { - @Inject - private HelpOption helpOption; - - @Option(title = "url", name = { "-u", "--url" }, description = "JMX url to target") + @Option(paramLabel = "url", names = { "-u", "--url" }, description = "JMX url to target") private String targetUrl = "service:jmx:rmi:///jndi/rmi://localhost:7199/jmxrmi"; - @Option(title = "format", name = { "-f", "--format" }, description = "What format to dump content as; supported values are console (default), json, and yaml") + @Option(paramLabel = "format", names = { "-f", "--format" }, description = "What format to dump content as; supported values are console (default), json, and yaml") private Format format = Format.console; public Void call() throws Exception @@ -188,41 +190,32 @@ public class JMXTool } } - @Command(name = "diff", description = "Diff two jmx dump files and report their differences") + @Command(name = "diff", description = "Diff two jmx dump files and report their differences", mixinStandardHelpOptions = true) public static final class Diff implements Callable { - @Inject - private HelpOption helpOption; + @CommandLine.Parameters(description = "Files to diff") + private List files; - @Arguments(title = "files", usage = " ", description = "Files to diff") - private List files; - - @Option(title = "format", name = { "-f", "--format" }, description = "What format the files are in; only support json and yaml as format") + @Option(paramLabel = "format", names = { "-f", "--format" }, description = "What format the files are in; only support json and yaml as format") private Format format = Format.yaml; - @Option(title = "ignore left", name = { "--ignore-missing-on-left" }, description = "Ignore results missing on the left") + @Option(paramLabel = "ignore_left", names = { "--ignore-missing-on-left" }, description = "Ignore results missing on the left") private boolean ignoreMissingLeft; - @Option(title = "ignore right", name = { "--ignore-missing-on-right" }, description = "Ignore results missing on the right") + @Option(paramLabel = "ignore_right", names = { "--ignore-missing-on-right" }, description = "Ignore results missing on the right") private boolean ignoreMissingRight; - @Option(title = "exclude objects", name = "--exclude-object", description - = "Ignores processing specific objects. " + - "Each usage should take a single object, " + - "but can use this flag multiple times.") - private List excludeObjects = new ArrayList<>(); + @Option(paramLabel = "exclude_objects", names = "--exclude-object", converter = PatternConverter.class, + description = "Ignores processing specific objects. Each usage should take a single object, but can use this flag multiple times.") + private List excludeObjects = new ArrayList<>(); - @Option(title = "exclude attributes", name = "--exclude-attribute", description - = "Ignores processing specific attributes. " + - "Each usage should take a single attribute, " + - "but can use this flag multiple times.") - private List excludeAttributes = new ArrayList<>(); + @Option(paramLabel = "exclude_attributes", names = "--exclude-attribute", converter = PatternConverter.class, + description = "Ignores processing specific attributes. Each usage should take a single attribute, but can use this flag multiple times.") + private List excludeAttributes = new ArrayList<>(); - @Option(title = "exclude operations", name = "--exclude-operation", description - = "Ignores processing specific operations. " + - "Each usage should take a single operation, " + - "but can use this flag multiple times.") - private List excludeOperations = new ArrayList<>(); + @Option(paramLabel = "exclud_operations", names = "--exclude-operation", converter = PatternConverter.class, + description = "Ignores processing specific operations. Each usage should take a single operation, but can use this flag multiple times.") + private List excludeOperations = new ArrayList<>(); public Void call() throws Exception { @@ -243,9 +236,9 @@ public class JMXTool private void diff(Map left, Map right) { DiffResult objectNames = diff(left.keySet(), right.keySet(), name -> { - for (CliPattern p : excludeObjects) + for (Pattern p : excludeObjects) { - if (p.pattern.matcher(name).matches()) + if (p.matcher(name).matches()) return false; } return true; @@ -280,9 +273,9 @@ public class JMXTool Info leftInfo = left.get(key); Info rightInfo = right.get(key); DiffResult attributes = diff(leftInfo.attributeSet(), rightInfo.attributeSet(), attribute -> { - for (CliPattern p : excludeAttributes) + for (Pattern p : excludeAttributes) { - if (p.pattern.matcher(attribute.name).matches()) + if (p.matcher(attribute.name).matches()) return false; } return true; @@ -301,10 +294,10 @@ public class JMXTool } DiffResult operations = diff(leftInfo.operationSet(), rightInfo.operationSet(), operation -> { - for (CliPattern p : excludeOperations) + for (Pattern p : excludeOperations) { - if (p.pattern.matcher(operation.name).matches() || - p.pattern.matcher(operation.toString().replaceAll(" +", "")).matches()) + if (p.matcher(operation.name).matches() || + p.matcher(operation.toString().replaceAll(" +", "")).matches()) return false; } return true; @@ -844,24 +837,18 @@ public class JMXTool } } - public static final class CliPattern + public static class PatternConverter implements CommandLine.ITypeConverter { - private final Pattern pattern; - - public CliPattern(String pattern) + @Override + public Pattern convert(String pattern) throws Exception { - this.pattern = Pattern.compile(pattern); + return Pattern.compile(pattern); } } public static void main(String[] args) throws Exception { - Cli.CliBuilder> builder = Cli.builder("jmxtool"); - builder.withDefaultCommand(Help.class); - builder.withCommands(Help.class, Dump.class, Diff.class); - - Cli> parser = builder.build(); - Callable command = parser.parse(args); - command.call(); + CommandLine commandLine = new CommandLine(JMXTool.class); + commandLine.execute(args); } } diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java b/src/java/org/apache/cassandra/tools/NodeProbe.java index c716392541..dcab5048fa 100644 --- a/src/java/org/apache/cassandra/tools/NodeProbe.java +++ b/src/java/org/apache/cassandra/tools/NodeProbe.java @@ -118,7 +118,6 @@ import org.apache.cassandra.service.AutoRepairService; import org.apache.cassandra.service.AutoRepairServiceMBean; import org.apache.cassandra.service.CacheService; import org.apache.cassandra.service.CacheServiceMBean; -import org.apache.cassandra.service.snapshot.SnapshotManagerMBean; import org.apache.cassandra.service.GCInspector; import org.apache.cassandra.service.GCInspectorMXBean; import org.apache.cassandra.service.StorageProxy; @@ -126,6 +125,7 @@ import org.apache.cassandra.service.StorageProxyMBean; import org.apache.cassandra.service.StorageServiceMBean; import org.apache.cassandra.service.accord.AccordOperations; import org.apache.cassandra.service.accord.AccordOperationsMBean; +import org.apache.cassandra.service.snapshot.SnapshotManagerMBean; import org.apache.cassandra.streaming.StreamManagerMBean; import org.apache.cassandra.streaming.StreamState; import org.apache.cassandra.streaming.management.StreamStateCompositeData; @@ -133,7 +133,6 @@ import org.apache.cassandra.tcm.CMSOperations; import org.apache.cassandra.tcm.CMSOperationsMBean; import org.apache.cassandra.tools.RepairRunner.RepairCmd; import org.apache.cassandra.tools.nodetool.GetTimeout; -import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; import org.apache.cassandra.utils.NativeLibrary; import static org.apache.cassandra.config.CassandraRelevantProperties.NODETOOL_JMX_NOTIFICATION_POLL_INTERVAL_SECONDS; @@ -146,7 +145,7 @@ public class NodeProbe implements AutoCloseable { private static final String fmtUrl = "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi"; private static final String ssObjName = "org.apache.cassandra.db:type=StorageService"; - private static final int defaultPort = 7199; + public static final int defaultPort = 7199; static long JMX_NOTIFICATION_POLL_INTERVAL_SECONDS = NODETOOL_JMX_NOTIFICATION_POLL_INTERVAL_SECONDS.getLong(); @@ -185,8 +184,7 @@ public class NodeProbe implements AutoCloseable protected RolesCacheMBean rcProxy; protected AutoRepairServiceMBean autoRepairProxy; protected GuardrailsMBean grProxy; - protected Output output; - private boolean failed; + protected volatile Output output; protected CIDRFilteringMetricsTableMBean cfmProxy; @@ -469,9 +467,8 @@ public class NodeProbe implements AutoCloseable jobName, ks); break; case 2: - failed = true; - out.printf("Failed marking some sstables compacting in keyspace %s, check server logs for more information.\n", - ks); + out.printf("Failed marking some sstables compacting in keyspace %s, check server logs for more information.\n", ks); + throw new RuntimeException(String.format("Failed marking some sstables compacting in keyspace %s, check server logs for more information.\n", ks)); } } @@ -479,8 +476,8 @@ public class NodeProbe implements AutoCloseable { if (garbageCollect(tombstoneOption, jobs, keyspaceName, tableNames) != 0) { - failed = true; out.println("Aborted garbage collection for at least one table in keyspace " + keyspaceName + ", check server logs for more information."); + throw new RuntimeException("Aborted garbage collection for at least one table in keyspace " + keyspaceName + ", check server logs for more information."); } } @@ -1819,16 +1816,6 @@ public class NodeProbe implements AutoCloseable ssProxy.reloadLocalSchema(); } - public boolean isFailed() - { - return failed; - } - - public void failed() - { - this.failed = true; - } - public long getReadRepairAttempted() { return spProxy.getReadRepairAttempted(); @@ -2546,21 +2533,6 @@ public class NodeProbe implements AutoCloseable return ssProxy.getDefaultKeyspaceReplicationFactor(); } - public 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 void abortBootstrap(String nodeId, String endpoint) { ssProxy.abortBootstrap(nodeId, endpoint); diff --git a/src/java/org/apache/cassandra/tools/NodeTool.java b/src/java/org/apache/cassandra/tools/NodeTool.java index 62cbbd892b..0244b49596 100644 --- a/src/java/org/apache/cassandra/tools/NodeTool.java +++ b/src/java/org/apache/cassandra/tools/NodeTool.java @@ -17,54 +17,34 @@ */ package org.apache.cassandra.tools; -import java.io.Console; -import java.io.FileNotFoundException; import java.io.IOError; import java.io.IOException; -import java.net.UnknownHostException; +import java.io.PrintWriter; +import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Scanner; -import java.util.SortedMap; +import javax.inject.Inject; import javax.management.InstanceNotFoundException; import com.google.common.base.Joiner; import com.google.common.base.Throwables; -import com.google.common.collect.Maps; - -import io.airlift.airline.Cli; -import io.airlift.airline.Help; -import io.airlift.airline.Option; -import io.airlift.airline.OptionType; -import io.airlift.airline.ParseArgumentsMissingException; -import io.airlift.airline.ParseArgumentsUnexpectedException; -import io.airlift.airline.ParseCommandMissingException; -import io.airlift.airline.ParseCommandUnrecognizedException; -import io.airlift.airline.ParseOptionConversionException; -import io.airlift.airline.ParseOptionMissingException; -import io.airlift.airline.ParseOptionMissingValueException; +import org.apache.cassandra.config.CassandraRelevantEnv; +import org.apache.cassandra.config.CassandraRelevantProperties; +import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.util.File; import org.apache.cassandra.io.util.FileWriter; -import org.apache.cassandra.locator.EndpointSnitchInfoMBean; -import org.apache.cassandra.tools.nodetool.*; +import org.apache.cassandra.tools.nodetool.JmxConnect; +import org.apache.cassandra.tools.nodetool.NodetoolCommand; +import org.apache.cassandra.tools.nodetool.layout.CassandraCliHelpLayout; import org.apache.cassandra.utils.FBUtilities; +import picocli.CommandLine; import static com.google.common.base.Throwables.getStackTraceAsString; -import static com.google.common.collect.Iterables.toArray; -import static com.google.common.collect.Lists.newArrayList; -import static java.lang.Integer.parseInt; -import static java.lang.String.format; import static org.apache.cassandra.io.util.File.WriteMode.APPEND; -import static org.apache.commons.lang3.ArrayUtils.EMPTY_STRING_ARRAY; -import static org.apache.commons.lang3.StringUtils.EMPTY; -import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.apache.commons.lang3.StringUtils.isNotEmpty; +import static org.apache.cassandra.utils.LocalizeString.toUpperCaseLocalized; public class NodeTool { @@ -89,237 +69,59 @@ public class NodeTool this.output = output; } + /** + * Execute the command line utility with the given arguments via the JMX connection. + * + * @param args command line arguments + * @return 0 on success, 1 on bad use, 2 on execution error + */ public int execute(String... args) { - List> commands = newArrayList( - AbortBootstrap.class, - AlterTopology.class, - Assassinate.class, - AutoRepairStatus.class, - CassHelp.class, - CIDRFilteringStats.class, - Cleanup.class, - ClearSnapshot.class, - ClientStats.class, - Compact.class, - CompactionHistory.class, - CompactionStats.class, - DataPaths.class, - Decommission.class, - Decommission.Abort.class, - DescribeCluster.class, - DescribeRing.class, - DisableAuditLog.class, - DisableAutoCompaction.class, - DisableBackup.class, - DisableBinary.class, - DisableFullQueryLog.class, - DisableGossip.class, - DisableHandoff.class, - DisableHintsForDC.class, - DisableOldProtocolVersions.class, - Drain.class, - DropCIDRGroup.class, - EnableAuditLog.class, - EnableAutoCompaction.class, - EnableBackup.class, - EnableBinary.class, - EnableFullQueryLog.class, - EnableGossip.class, - EnableHandoff.class, - EnableHintsForDC.class, - EnableOldProtocolVersions.class, - FailureDetectorInfo.class, - Flush.class, - GarbageCollect.class, - GcStats.class, - GetAuditLog.class, - GetAuthCacheConfig.class, - GetAutoRepairConfig.class, - GetBatchlogReplayTrottle.class, - GetCIDRGroupsOfIP.class, - GetColumnIndexSize.class, - GetCompactionThreshold.class, - GetCompactionThroughput.class, - GetConcurrency.class, - GetConcurrentCompactors.class, - GetConcurrentViewBuilders.class, - GetDefaultKeyspaceRF.class, - GetEndpoints.class, - GetFullQueryLog.class, - GuardrailsConfigCommand.GetGuardrailsConfig.class, - GetInterDCStreamThroughput.class, - GetLoggingLevels.class, - GetMaxHintWindow.class, - GetSSTables.class, - GetSeeds.class, - GetSnapshotThrottle.class, - GetStreamThroughput.class, - GetTimeout.class, - GetTraceProbability.class, - GossipInfo.class, - Import.class, - Info.class, - InvalidateCIDRPermissionsCache.class, - InvalidateCounterCache.class, - InvalidateCredentialsCache.class, - InvalidateJmxPermissionsCache.class, - ReloadCIDRGroupsCache.class, - InvalidateKeyCache.class, - InvalidateNetworkPermissionsCache.class, - InvalidatePermissionsCache.class, - InvalidateRolesCache.class, - InvalidateRowCache.class, - Join.class, - ListCIDRGroups.class, - ListPendingHints.class, - ListSnapshots.class, - Move.class, - Move.Abort.class, - NetStats.class, - PauseHandoff.class, - ProfileLoad.class, - ProxyHistograms.class, - RangeKeySample.class, - Rebuild.class, - RebuildIndex.class, - RecompressSSTables.class, - Refresh.class, - RefreshSizeEstimates.class, - ReloadLocalSchema.class, - ReloadSeeds.class, - ReloadSslCertificates.class, - ReloadTriggers.class, - RelocateSSTables.class, - RemoveNode.class, - RemoveNode.Abort.class, - Repair.class, - ReplayBatchlog.class, - ResetFullQueryLog.class, - ResetLocalSchema.class, - ResumeHandoff.class, - Ring.class, - Scrub.class, - SetAuthCacheConfig.class, - SetAutoRepairConfig.class, - SetBatchlogReplayThrottle.class, - SetCacheCapacity.class, - SetCacheKeysToSave.class, - SetColumnIndexSize.class, - SetCompactionThreshold.class, - SetCompactionThroughput.class, - SetConcurrency.class, - SetConcurrentCompactors.class, - SetConcurrentViewBuilders.class, - SetDefaultKeyspaceRF.class, - GuardrailsConfigCommand.SetGuardrailsConfig.class, - SetHintedHandoffThrottleInKB.class, - SetInterDCStreamThroughput.class, - SetLoggingLevel.class, - SetMaxHintWindow.class, - SetSnapshotThrottle.class, - SetStreamThroughput.class, - SetTimeout.class, - SetTraceProbability.class, - Sjk.class, - Snapshot.class, - SSTableRepairedSet.class, - Status.class, - StatusAutoCompaction.class, - StatusBackup.class, - StatusBinary.class, - StatusGossip.class, - StatusHandoff.class, - Stop.class, - StopDaemon.class, - TableHistograms.class, - TableStats.class, - TopPartitions.class, - TpStats.class, - TruncateHints.class, - UpdateCIDRGroup.class, - UpgradeSSTable.class, - Verify.class, - Version.class, - ViewBuildStatus.class, - ForceCompact.class - ); - - Cli.CliBuilder builder = Cli.builder("nodetool"); - - builder.withDescription("Manage your Cassandra cluster") - .withDefaultCommand(CassHelp.class) - .withCommands(commands); - - // bootstrap commands - builder.withGroup("bootstrap") - .withDescription("Monitor/manage node's bootstrap process") - .withDefaultCommand(CassHelp.class) - .withCommand(BootstrapResume.class); - - builder.withGroup("repair_admin") - .withDescription("list and fail incremental repair sessions") - .withDefaultCommand(RepairAdmin.ListCmd.class) - .withCommand(RepairAdmin.ListCmd.class) - .withCommand(RepairAdmin.CancelCmd.class) - .withCommand(RepairAdmin.CleanupDataCmd.class) - .withCommand(RepairAdmin.SummarizePendingCmd.class) - .withCommand(RepairAdmin.SummarizeRepairedCmd.class); - - builder.withGroup("cms") - .withDescription("Manage cluster metadata") - .withDefaultCommand(CMSAdmin.DescribeCMS.class) - .withCommand(CMSAdmin.DescribeCMS.class) - .withCommand(CMSAdmin.InitializeCMS.class) - .withCommand(CMSAdmin.ReconfigureCMS.class) - .withCommand(CMSAdmin.Snapshot.class) - .withCommand(CMSAdmin.Unregister.class) - .withCommand(CMSAdmin.AbortInitialization.class) - .withCommand(CMSAdmin.DumpDirectory.class) - .withCommand(CMSAdmin.DumpLog.class) - .withCommand(CMSAdmin.ResumeDropAccordTable.class); - - builder.withGroup("consensus_admin") - .withDescription("List and mark ranges as migrating between consensus protocols") - .withDefaultCommand(CassHelp.class) - .withCommand(ConsensusMigrationAdmin.BeginMigration.class) - .withCommands(ConsensusMigrationAdmin.ListCmd.class) - .withCommands(ConsensusMigrationAdmin.FinishMigration.class); - - builder.withGroup("accord") - .withDescription("Manage the operation of Accord") - .withDefaultCommand(AccordAdmin.Describe.class) - .withCommand(AccordAdmin.Describe.class) - .withCommand(AccordAdmin.MarkStale.class) - .withCommand(AccordAdmin.MarkRejoining.class); - - Cli parser = builder.build(); - - int status = 0; try { - NodeToolCmdRunnable parse = parser.parse(args); + CommandLine commandLine = createCommandLine(new CassandraCliFactory(nodeProbeFactory, output)); + commandLine.setOut(new PrintWriter(output.out, true)); + commandLine.setErr(new PrintWriter(output.err, true)); + + configureCliLayout(commandLine); + commandLine.setExecutionStrategy(JmxConnect::executionStrategy) + .setExecutionExceptionHandler((ex, c, arg) -> { + // Used for backward compatibility, some commands are validated when a command is run. + if (ex instanceof IllegalArgumentException | + ex instanceof IllegalStateException) + { + badUse(ex); + return 1; + } + + err(Throwables.getRootCause(ex)); + return 2; + }) + .setParameterExceptionHandler((ex, arg) -> { + badUse(ex); + return 1; + }) + // Some of the Cassandra commands don't comply with the POSIX standard, so we need to disable such options. + // Example: ./nodetool -h localhost -p 7100 repair mykeyspayce -hosts 127.0.0.1,127.0.0.2 + // + // This also means that option parameters must be separated from the option name by whitespace + // or the = separator character, so -D key=value and -D=key=value will be recognized but + // -Dkey=value will not. + .setPosixClusteredShortOptionsAllowed(false); + printHistory(args); - parse.run(nodeProbeFactory, output); - } catch (IllegalArgumentException | - IllegalStateException | - ParseArgumentsMissingException | - ParseArgumentsUnexpectedException | - ParseOptionConversionException | - ParseOptionMissingException | - ParseOptionMissingValueException | - ParseCommandMissingException | - ParseCommandUnrecognizedException e) + return commandLine.execute(args); + } + catch (ConfigurationException e) { badUse(e); - status = 1; - } catch (Throwable throwable) - { - err(Throwables.getRootCause(throwable)); - status = 2; + return 1; + } + catch (Throwable e) + { + err(Throwables.getRootCause(e)); + return 2; } - - return status; } private static void printHistory(String... args) @@ -342,6 +144,59 @@ public class NodeTool } } + + public static List getCommandsWithoutRoot(String separator) + { + List commands = new ArrayList<>(); + try + { + getCommandsWithoutRoot(createCommandLine(new CassandraCliFactory(new NodeProbeFactory(), Output.CONSOLE)), commands, separator); + return commands; + } + catch (Exception e) + { + throw new RuntimeException("Failed to initialize command line hierarchy", e); + } + } + + private static void getCommandsWithoutRoot(CommandLine cli, List commands, String separator) + { + String name = cli.getCommandSpec().qualifiedName(separator); + // Skip the root command as it's not a real command. + if (cli.getCommandSpec().root() != cli.getCommandSpec()) + commands.add(name.replace(cli.getCommandSpec().root().qualifiedName() + separator, "")); + for (CommandLine sub : cli.getSubcommands().values()) + getCommandsWithoutRoot(sub, commands, separator); + } + + public static CommandLine createCommandLine(CommandLine.IFactory factory) throws Exception + { + return new CommandLine(new NodetoolCommand(), factory) + .addMixin(JmxConnect.MIXIN_KEY, factory.create(JmxConnect.class)); + } + + private static void configureCliLayout(CommandLine commandLine) + { + CliLayout defaultLayout = CliLayout.valueOf(toUpperCaseLocalized(CassandraRelevantProperties.CASSANDRA_CLI_LAYOUT.getDefaultValue())); + CliLayout layoutEnv = CassandraRelevantEnv.CASSANDRA_CLI_LAYOUT.getEnum(true, CliLayout.class, + CassandraRelevantProperties.CASSANDRA_CLI_LAYOUT.getDefaultValue()); + CliLayout layoutSys = CassandraRelevantProperties.CASSANDRA_CLI_LAYOUT.getEnum(true, CliLayout.class); + CliLayout layout = layoutEnv != defaultLayout ? layoutEnv : layoutSys; + + switch (layout) + { + case AIRLINE: + commandLine.setHelpFactory(CassandraCliHelpLayout::new) + .setUsageHelpWidth(CassandraCliHelpLayout.DEFAULT_USAGE_HELP_WIDTH) + .setHelpSectionKeys(CassandraCliHelpLayout.cassandraHelpSectionKeys()); + break; + case PICOCLI: + break; + default: + throw new IllegalStateException("Unknown CLI layout: " + layout); + } + } + protected void badUse(Exception e) { output.out.println("nodetool: " + e.getMessage()); @@ -359,208 +214,63 @@ public class NodeTool output.err.println(getStackTraceAsString(e)); } - public static class CassHelp extends Help implements NodeToolCmdRunnable + private enum CliLayout { - public void run(INodeProbeFactory nodeProbeFactory, Output output) - { - run(); - } + AIRLINE, + PICOCLI } - interface NodeToolCmdRunnable + private static class CassandraCliFactory implements CommandLine.IFactory { - void run(INodeProbeFactory nodeProbeFactory, Output output); - } + private final CommandLine.IFactory fallback; + private final INodeProbeFactory nodeProbeFactory; + private final Output output; - public static abstract class NodeToolCmd implements NodeToolCmdRunnable - { - - @Option(type = OptionType.GLOBAL, name = {"-h", "--host"}, description = "Node hostname or ip address") - private String host = "127.0.0.1"; - - @Option(type = OptionType.GLOBAL, name = {"-p", "--port"}, description = "Remote jmx agent port number") - private String port = "7199"; - - @Option(type = OptionType.GLOBAL, name = {"-u", "--username"}, description = "Remote jmx agent username") - private String username = EMPTY; - - @Option(type = OptionType.GLOBAL, name = {"-pw", "--password"}, description = "Remote jmx agent password") - private String password = EMPTY; - - @Option(type = OptionType.GLOBAL, name = {"-pwf", "--password-file"}, description = "Path to the JMX password file") - private String passwordFilePath = EMPTY; - - @Option(type = OptionType.GLOBAL, name = { "-pp", "--print-port"}, description = "Operate in 4.0 mode with hosts disambiguated by port number", arity = 0) - protected boolean printPort = false; - - private INodeProbeFactory nodeProbeFactory; - protected Output output; - - @Override - public void run(INodeProbeFactory nodeProbeFactory, Output output) + public CassandraCliFactory(INodeProbeFactory nodeProbeFactory, Output output) { + this.fallback = CommandLine.defaultFactory(); this.nodeProbeFactory = nodeProbeFactory; this.output = output; - runInternal(); } - public void runInternal() + public K create(Class cls) { - if (isNotEmpty(username)) { - if (isNotEmpty(passwordFilePath)) - password = readUserPasswordFromFile(username, passwordFilePath); - - if (isEmpty(password)) - password = promptAndReadPassword(); - } - - try (NodeProbe probe = connect()) - { - execute(probe); - if (probe.isFailed()) - throw new RuntimeException("nodetool failed, check server logs"); - } - catch (IOException e) - { - throw new RuntimeException("Error while closing JMX connection", e); - } - - } - - private String readUserPasswordFromFile(String username, String passwordFilePath) { - String password = EMPTY; - - File passwordFile = new File(passwordFilePath); - try (Scanner scanner = new Scanner(passwordFile.toJavaIOFile()).useDelimiter("\\s+")) - { - while (scanner.hasNextLine()) - { - if (scanner.hasNext()) - { - String jmxRole = scanner.next(); - if (jmxRole.equals(username) && scanner.hasNext()) - { - password = scanner.next(); - break; - } - } - scanner.nextLine(); - } - } - catch (FileNotFoundException e) - { - throw new RuntimeException(e); - } - - return password; - } - - private String promptAndReadPassword() - { - String password = EMPTY; - - Console console = System.console(); - if (console != null) - password = String.valueOf(console.readPassword("Password:")); - - return password; - } - - protected abstract void execute(NodeProbe probe); - - private NodeProbe connect() - { - NodeProbe nodeClient = null; - try { - if (username.isEmpty()) - nodeClient = nodeProbeFactory.create(host, parseInt(port)); - else - nodeClient = nodeProbeFactory.create(host, parseInt(port), username, password); - - nodeClient.setOutput(output); - } catch (IOException | SecurityException e) - { - Throwable rootCause = Throwables.getRootCause(e); - output.err.println(format("nodetool: Failed to connect to '%s:%s' - %s: '%s'.", host, port, rootCause.getClass().getSimpleName(), rootCause.getMessage())); - System.exit(1); + K bean = this.fallback.create(cls); + Class beanClass = bean.getClass(); + do + { + Field[] fields = beanClass.getDeclaredFields(); + for (Field field : fields) + { + if (!field.isAnnotationPresent(Inject.class)) + continue; + if (field.getType().equals(INodeProbeFactory.class)) + { + field.setAccessible(true); + field.set(bean, nodeProbeFactory); + } + else if (field.getType().equals(Output.class)) + { + field.setAccessible(true); + field.set(bean, output); + } + else + { + throw new RuntimeException("Unsupported injectable field type: " + field.getType() + + " in class " + beanClass.getName() + ". " + + "Only INodeProbeFactory and Output are supported."); + } + } + } + while ((beanClass = beanClass.getSuperclass()) != null); + return bean; } - - return nodeClient; - } - - protected enum KeyspaceSet - { - ALL, NON_SYSTEM, NON_LOCAL_STRATEGY, ACCORD_MANAGED - } - - protected List parseOptionalKeyspace(List cmdArgs, NodeProbe nodeProbe) - { - return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.ALL); - } - - protected List parseOptionalKeyspace(List cmdArgs, NodeProbe nodeProbe, KeyspaceSet defaultKeyspaceSet) - { - List keyspaces = new ArrayList<>(); - - - if (cmdArgs == null || cmdArgs.isEmpty()) + catch (Exception e) { - 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); - } - - protected String[] parseOptionalTables(List cmdArgs) - { - return cmdArgs.size() <= 1 ? EMPTY_STRING_ARRAY : toArray(cmdArgs.subList(1, cmdArgs.size()), String.class); - } - - protected 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 (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); + throw new CommandLine.InitializationException("Failed to create instance of " + cls, e); } } - catch (UnknownHostException e) - { - throw new RuntimeException(e); - } - return ownershipByDc; } } diff --git a/src/java/org/apache/cassandra/tools/Output.java b/src/java/org/apache/cassandra/tools/Output.java index 1d2fcb3a63..eef75766b5 100644 --- a/src/java/org/apache/cassandra/tools/Output.java +++ b/src/java/org/apache/cassandra/tools/Output.java @@ -32,4 +32,14 @@ public class Output this.out = out; this.err = err; } + + public void printInfo(String msg, Object... args) + { + out.printf(msg, args); + } + + public void printError(String msg, Object... args) + { + err.printf(msg, args); + } } diff --git a/src/java/org/apache/cassandra/tools/StandaloneJournalUtil.java b/src/java/org/apache/cassandra/tools/StandaloneJournalUtil.java index 871031acee..83db8e23ce 100644 --- a/src/java/org/apache/cassandra/tools/StandaloneJournalUtil.java +++ b/src/java/org/apache/cassandra/tools/StandaloneJournalUtil.java @@ -21,9 +21,19 @@ package org.apache.cassandra.tools; import accord.local.RedundantBefore; import accord.primitives.Timestamp; import accord.primitives.TxnId; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; -import io.airlift.airline.Option; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + import org.apache.cassandra.config.AccordSpec; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.DurationSpec; @@ -44,17 +54,9 @@ import org.apache.cassandra.service.accord.JournalKey; import org.apache.cassandra.service.accord.serializers.Version; import org.apache.cassandra.tcm.ClusterMetadataService; -import java.io.IOException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; import static accord.api.Journal.Load.ALL; import static com.google.common.base.Throwables.getStackTraceAsString; @@ -91,7 +93,11 @@ import static org.apache.cassandra.config.DatabaseDescriptor.setAccordJournalDir * **/ -public class StandaloneJournalUtil +@Command(name = "journal_util", + mixinStandardHelpOptions = true, + description = "Standalone Journal Util", + subcommands = { StandaloneJournalUtil.DumpSegments.class, StandaloneJournalUtil.DumpJournal.class }) +public class StandaloneJournalUtil implements Runnable { private static final Output output = Output.CONSOLE; @@ -101,27 +107,22 @@ public class StandaloneJournalUtil DatabaseDescriptor.setPartitioner("org.apache.cassandra.dht.Murmur3Partitioner"); AccordKeyspace.TABLES = Tables.of(AccordKeyspace.journalMetadata("journal", false)); ClusterMetadataService.empty(Keyspaces.of(AccordKeyspace.metadata())); - Cli.CliBuilder builder = Cli.builder("util"); - - builder.withDescription("Dump journal").withCommand(DumpSegments.class).withCommand(DumpJournal.class); - - Cli 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 args = new ArrayList<>(); @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/InvalidateRowCache.java b/src/java/org/apache/cassandra/tools/nodetool/InvalidateRowCache.java index 1a10ed00f2..f3676730d8 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/InvalidateRowCache.java +++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateRowCache.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 = "invalidaterowcache", description = "Invalidate the row cache") -public class InvalidateRowCache extends NodeToolCmd +public class InvalidateRowCache extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/JmxConnect.java b/src/java/org/apache/cassandra/tools/nodetool/JmxConnect.java new file mode 100644 index 0000000000..4fec76e348 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/JmxConnect.java @@ -0,0 +1,243 @@ +/* + * 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.Console; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.Scanner; + +import javax.inject.Inject; + +import com.google.common.base.Throwables; + +import org.apache.cassandra.io.util.File; +import org.apache.cassandra.tools.INodeProbeFactory; +import org.apache.cassandra.tools.NodeProbe; + +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExecutionException; +import picocli.CommandLine.IExecutionStrategy; +import picocli.CommandLine.InitializationException; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.ParseResult; +import picocli.CommandLine.RunLast; +import picocli.CommandLine.Spec; + +import static java.lang.Integer.parseInt; +import static org.apache.cassandra.tools.NodeProbe.defaultPort; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + +/** + * Command options for NodeTool commands that are executed via JMX. + */ +@Command(name = "connect", description = "Connect to a Cassandra node via JMX") +public class JmxConnect extends AbstractCommand implements AutoCloseable +{ + public static final String MIXIN_KEY = "jmx"; + + /** The command specification, used to access command-specific properties. */ + @Spec + protected CommandSpec spec; // injected by picocli + + @Option(names = { "-h", "--host" }, description = "Node hostname or ip address", arity = "0..1") + private String host = "127.0.0.1"; + + @Option(names = { "-p", "--port" }, description = "Remote jmx agent port number", arity = "0..1") + private String port = String.valueOf(defaultPort); + + @Option(names = { "-u", "--username" }, description = "Remote jmx agent username", arity = "0..1") + private String username = EMPTY; + + @Option(names = { "-pw", "--password" }, description = "Remote jmx agent password", arity = "0..1") + private String password = EMPTY; + + @Option(names = { "-pwf", "--password-file" }, description = "Path to the JMX password file", arity = "0..1") + private String passwordFilePath = EMPTY; + + @Inject + private INodeProbeFactory nodeProbeFactory; + + /** + * This method is called by picocli and used depending on the execution strategy. + * @param parseResult The parsed command line. + * @return The exit code. + */ + public static int executionStrategy(ParseResult parseResult) + { + CommandSpec jmx = parseResult.commandSpec().mixins().get(MIXIN_KEY); + if (jmx == null) + throw new InitializationException("No JmxConnect command found in the top-level hierarchy"); + + try (JmxConnectionCommandInvoker invoker = new JmxConnectionCommandInvoker((JmxConnect) jmx.userObject())) + { + return invoker.execute(parseResult); + } + catch (JmxConnectionCommandInvoker.CloseException e) + { + jmx.commandLine() + .getErr() + .println("Failed to connect to JMX: " + e.getMessage()); + return jmx.commandLine().getExitCodeExceptionMapper().getExitCode(e); + } + } + + /** + * Initialize the JMX connection to the Cassandra node using the provided options. + */ + @Override + protected void execute(NodeProbe probe) + { + assert probe == null; + try + { + if (isNotEmpty(username)) + { + if (isNotEmpty(passwordFilePath)) + password = readUserPasswordFromFile(username, passwordFilePath); + + if (isEmpty(password)) + password = promptAndReadPassword(); + } + + probe(username.isEmpty() ? nodeProbeFactory.create(host, parseInt(port)) + : nodeProbeFactory.create(host, parseInt(port), username, password)); + } + catch (IOException | SecurityException e) + { + Throwable rootCause = Throwables.getRootCause(e); + output.printError("nodetool: Failed to connect to '%s:%s' - %s: '%s'.%n", host, port, + rootCause.getClass().getSimpleName(), rootCause.getMessage()); + throw new InitializationException("Failed to connect to JMX", e); + } + } + + @Override + public void close() throws Exception + { + if (probe() == null) + return; + ((AutoCloseable) probe()).close(); + } + + private static String readUserPasswordFromFile(String username, String passwordFilePath) + { + String password = EMPTY; + + File passwordFile = new File(passwordFilePath); + try (Scanner scanner = new Scanner(passwordFile.toJavaIOFile()).useDelimiter("\\s+")) + { + while (scanner.hasNextLine()) + { + if (scanner.hasNext()) + { + String jmxRole = scanner.next(); + if (jmxRole.equals(username) && scanner.hasNext()) + { + password = scanner.next(); + break; + } + } + scanner.nextLine(); + } + } + catch (FileNotFoundException e) + { + throw new UncheckedIOException(e); + } + + return password; + } + + private static String promptAndReadPassword() + { + String password = EMPTY; + + Console console = System.console(); + if (console != null) + password = String.valueOf(console.readPassword("Password:")); + + return password; + } + + private static class JmxConnectionCommandInvoker implements IExecutionStrategy, AutoCloseable + { + private final JmxConnect connect; + + public JmxConnectionCommandInvoker(JmxConnect connect) + { + this.connect = connect; + } + + @Override + public int execute(ParseResult parseResult) throws ExecutionException, ParameterException + { + CommandSpec lastParent = lastExecutableSubcommandWithSameParent(parseResult.asCommandLineList()); + if (lastParent.userObject() instanceof AbstractCommand) + { + AbstractCommand command = (AbstractCommand) lastParent.userObject(); + if (command.shouldConnect()) + connect.run(); + command.probe(connect.probe()); + } + return new RunLast().execute(parseResult); + } + + @Override + public void close() throws CloseException + { + try + { + if (connect.probe() != null) + ((AutoCloseable) connect.probe()).close(); + } + catch (Exception e) + { + throw new CloseException("Failed to close JMX connection", e); + } + } + + private static CommandLine.Model.CommandSpec lastExecutableSubcommandWithSameParent(List parsedCommands) + { + int start = parsedCommands.size() - 1; + for (int i = parsedCommands.size() - 2; i >= 0; i--) + { + if (parsedCommands.get(i).getParent() != parsedCommands.get(i + 1).getParent()) + break; + start = i; + } + return parsedCommands.get(start).getCommandSpec(); + } + + private static class CloseException extends RuntimeException + { + public CloseException(String message, Throwable cause) + { + super(message, cause); + } + } + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/Join.java b/src/java/org/apache/cassandra/tools/nodetool/Join.java index 74f9ad96f5..6c14be165a 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Join.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Join.java @@ -18,15 +18,15 @@ package org.apache.cassandra.tools.nodetool; import static com.google.common.base.Preconditions.checkState; -import io.airlift.airline.Command; import java.io.IOException; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; + @Command(name = "join", description = "Join the ring") -public class Join extends NodeToolCmd +public class Join extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/ListCIDRGroups.java b/src/java/org/apache/cassandra/tools/nodetool/ListCIDRGroups.java similarity index 61% rename from src/java/org/apache/cassandra/tools/ListCIDRGroups.java rename to src/java/org/apache/cassandra/tools/nodetool/ListCIDRGroups.java index 4cc3097512..330dfeb9f9 100644 --- a/src/java/org/apache/cassandra/tools/ListCIDRGroups.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ListCIDRGroups.java @@ -15,38 +15,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.tools; +package org.apache.cassandra.tools.nodetool; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.List; +import org.apache.commons.lang3.StringUtils; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; import org.apache.cassandra.auth.AuthKeyspace; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import org.apache.cassandra.tools.NodeProbe; +import picocli.CommandLine.Command; +import picocli.CommandLine.Parameters; + +import static org.apache.cassandra.tools.nodetool.CommandUtils.printSet; /** * Nodetool command to list available CIDR groups, in the table {@link AuthKeyspace#CIDR_GROUPS} */ @Command(name = "listcidrgroups", description = "List existing cidr groups") -public class ListCIDRGroups extends NodeToolCmd +public class ListCIDRGroups extends AbstractCommand { - @Arguments(usage = "[]", description = "LIST operation can be invoked with or without cidr group name") - private List args = new ArrayList<>(); + @Parameters(paramLabel = "cidrGroup", description = "LIST operation can be invoked with or without cidr group name", arity = "0..1") + private String cidrGroup; @Override public void execute(NodeProbe probe) { - PrintStream out = probe.output().out; - - if (args.size() < 1) + if (StringUtils.isEmpty(cidrGroup)) { - probe.printSet(out, "CIDR Groups", probe.listAvailableCidrGroups()); + printSet(probe.output().out, "CIDR Groups", probe.listAvailableCidrGroups()); return; } - String cidrGroup = args.get(0); - probe.printSet(out, "CIDRs", probe.listCidrsOfCidrGroup(cidrGroup)); + printSet(probe.output().out, "CIDRs", probe.listCidrsOfCidrGroup(cidrGroup)); } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/ListPendingHints.java b/src/java/org/apache/cassandra/tools/nodetool/ListPendingHints.java index af414d82ce..008c8b716e 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ListPendingHints.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ListPendingHints.java @@ -26,16 +26,15 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; -import io.airlift.airline.Command; import org.apache.cassandra.hints.PendingHintsInfo; import org.apache.cassandra.locator.EndpointSnitchInfoMBean; import org.apache.cassandra.locator.InetAddressAndPort; 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 = "listpendinghints", description = "Print all pending hints that this node has") -public class ListPendingHints extends NodeTool.NodeToolCmd +public class ListPendingHints extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java b/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java index 97ea809fdd..49ebf140dc 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java @@ -24,39 +24,38 @@ import java.util.Map; import java.util.Set; import javax.management.openmbean.TabularData; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.io.util.FileUtils; 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; @Command(name = "listsnapshots", description = "Lists all the snapshots along with the size on disk and true size. True size is the total size of all SSTables which are not backed up to disk. Size on disk is total size of the snapshot on disk. Total TrueDiskSpaceUsed does not make any SSTable deduplication.") -public class ListSnapshots extends NodeToolCmd +public class ListSnapshots extends AbstractCommand { - @Option(title = "no_ttl", - name = { "-nt", "--no-ttl" }, - description = "Skip snapshots with TTL") + @Option(paramLabel = "no_ttl", + names = { "-nt", "--no-ttl" }, + description = "Skip snapshots with TTL") private boolean noTTL = false; - @Option(title = "ephemeral", - name = { "-e", "--ephemeral" }, - description = "Include ephememeral snapshots") + @Option(paramLabel = "ephemeral", + names = { "-e", "--ephemeral" }, + description = "Include ephememeral snapshots") private boolean includeEphemeral = false; - @Option(title = "keyspace", - name = { "-k", "--keyspace" }, - description = "Include snapshots of specified keyspace name") + @Option(paramLabel = "keyspace", + names = { "-k", "--keyspace" }, + description = "Include snapshots of specified keyspace name") private String keyspace = null; - @Option(title = "table", - name = { "-t", "--table" }, - description = "Include snapshots of specified table name") + @Option(paramLabel = "table", + names = { "-t", "--table" }, + description = "Include snapshots of specified table name") private String table = null; - @Option(title = "snapshot", - name = { "-n", "--snapshot"}, - description = "Include snapshots of specified name") + @Option(paramLabel = "snapshot", + names = { "-n", "--snapshot" }, + description = "Include snapshots of specified name") private String snapshotName = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/Move.java b/src/java/org/apache/cassandra/tools/nodetool/Move.java index ef05bf89b6..d4ef54cdfc 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Move.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Move.java @@ -17,23 +17,22 @@ */ 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 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 picocli.CommandLine.Parameters; + +import static org.apache.commons.lang3.StringUtils.EMPTY; @Command(name = "move", description = "Move node on the token ring to a new token") -public class Move extends NodeToolCmd +public class Move extends AbstractCommand { - @Arguments(usage = "", description = "The new token.") + @Parameters(paramLabel = "newToken", description = "The new token.", arity = "0..1", index = "0") private String newToken = EMPTY; - @Option(title = "Resume an ongoing move operation", name = "--resume") + @Option(description = "Resume an ongoing move operation", names = { "--resume" }) private boolean resume; @Override @@ -59,9 +58,9 @@ public class Move extends NodeToolCmd } @Command(name = "abortmove", description = "Abort a failed move operation for this or a remote node") - 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/NetStats.java b/src/java/org/apache/cassandra/tools/nodetool/NetStats.java index aacc0714fd..fcee02cf03 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/NetStats.java +++ b/src/java/org/apache/cassandra/tools/nodetool/NetStats.java @@ -17,9 +17,6 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.airline.Command; -import io.airlift.airline.Option; - import java.io.PrintStream; import java.util.Set; @@ -32,13 +29,14 @@ import org.apache.cassandra.streaming.ProgressInfo; import org.apache.cassandra.streaming.SessionInfo; import org.apache.cassandra.streaming.StreamState; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Command(name = "netstats", description = "Print network information on provided host (connecting node by default)") -public class NetStats extends NodeToolCmd +public class NetStats extends WithPortDisplayAbstractCommand { - @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; diff --git a/src/java/org/apache/cassandra/tools/nodetool/NodetoolCommand.java b/src/java/org/apache/cassandra/tools/nodetool/NodetoolCommand.java new file mode 100644 index 0000000000..88b86a20b1 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/NodetoolCommand.java @@ -0,0 +1,232 @@ +/* + * 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; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Option; +import picocli.CommandLine.Spec; + +import static org.apache.cassandra.tools.nodetool.Help.printTopCommandUsage; + +/** + * The top-level command class for the Cassandra nodetool utility. + *

+ * This class serves as the main entry point for the nodetool command-line interface, + * which provides administrative and monitoring capabilities for Apache Cassandra clusters. + * It uses the PicoCLI framework to define and manage a comprehensive set of subcommands + * that allow users to perform various cluster management operations. + *

+ * + *

Usage:

+ *
+ * nodetool [global-options] <subcommand> [subcommand-options] [arguments]
+ * 
+ * + *

Global Options:

+ *
    + *
  • {@code --print-port (-pp)}: Operate in 4.0 mode with hosts disambiguated by port number
  • + *
+ * + *

+ * When executed without any subcommand, this class displays the general usage information + * and lists all available subcommands. Each subcommand is implemented as a separate class + * that handles specific administrative tasks. + *

+ * + * @see Help For displaying command help and usage information + * @see AbstractCommand Base class for most nodetool subcommands + * @see JmxConnect For JMX connection management used by subcommands + */ +@Command(name = "nodetool", + description = "Manage your Cassandra cluster", + subcommands = { Help.class, + AbortBootstrap.class, + AccordAdmin.class, + AlterTopology.class, + Assassinate.class, + AutoRepairStatus.class, + Bootstrap.class, + CIDRFilteringStats.class, + CMSAdmin.class, + Cleanup.class, + ClearSnapshot.class, + ClientStats.class, + Compact.class, + CompactionHistory.class, + CompactionStats.class, + ConsensusMigrationAdmin.class, + DataPaths.class, + Decommission.Abort.class, + Decommission.class, + DescribeCluster.class, + DescribeRing.class, + DisableAuditLog.class, + DisableAutoCompaction.class, + DisableBackup.class, + DisableBinary.class, + DisableFullQueryLog.class, + DisableGossip.class, + DisableHandoff.class, + DisableHintsForDC.class, + DisableOldProtocolVersions.class, + Drain.class, + DropCIDRGroup.class, + EnableAuditLog.class, + EnableAutoCompaction.class, + EnableBackup.class, + EnableBinary.class, + EnableFullQueryLog.class, + EnableGossip.class, + EnableHandoff.class, + EnableHintsForDC.class, + EnableOldProtocolVersions.class, + FailureDetectorInfo.class, + Flush.class, + ForceCompact.class, + GarbageCollect.class, + GcStats.class, + GetAuditLog.class, + GetAuthCacheConfig.class, + GetAutoRepairConfig.class, + GetBatchlogReplayTrottle.class, + GetCIDRGroupsOfIP.class, + GetColumnIndexSize.class, + GetCompactionThreshold.class, + GetCompactionThroughput.class, + GetConcurrency.class, + GetConcurrentCompactors.class, + GetConcurrentViewBuilders.class, + GetDefaultKeyspaceRF.class, + GetEndpoints.class, + GetFullQueryLog.class, + GetInterDCStreamThroughput.class, + GetLoggingLevels.class, + GetMaxHintWindow.class, + GetSSTables.class, + GetSeeds.class, + GetSnapshotThrottle.class, + GetStreamThroughput.class, + GetTimeout.class, + GetTraceProbability.class, + GossipInfo.class, + GuardrailsConfigCommand.GetGuardrailsConfig.class, + GuardrailsConfigCommand.SetGuardrailsConfig.class, + Import.class, + Info.class, + InvalidateCIDRPermissionsCache.class, + InvalidateCounterCache.class, + InvalidateCredentialsCache.class, + InvalidateJmxPermissionsCache.class, + InvalidateKeyCache.class, + InvalidateNetworkPermissionsCache.class, + InvalidatePermissionsCache.class, + InvalidateRolesCache.class, + InvalidateRowCache.class, + Join.class, + ListCIDRGroups.class, + ListPendingHints.class, + ListSnapshots.class, + Move.Abort.class, + Move.class, + NetStats.class, + PauseHandoff.class, + ProfileLoad.class, + ProxyHistograms.class, + RangeKeySample.class, + Rebuild.class, + RebuildIndex.class, + RecompressSSTables.class, + Refresh.class, + RefreshSizeEstimates.class, + ReloadCIDRGroupsCache.class, + ReloadLocalSchema.class, + ReloadSeeds.class, + ReloadSslCertificates.class, + ReloadTriggers.class, + RelocateSSTables.class, + RemoveNode.Abort.class, + RemoveNode.class, + Repair.class, + RepairAdmin.class, + ReplayBatchlog.class, + ResetFullQueryLog.class, + ResetLocalSchema.class, + ResumeHandoff.class, + Ring.class, + SSTableRepairedSet.class, + Scrub.class, + SetAuthCacheConfig.class, + SetAutoRepairConfig.class, + SetBatchlogReplayThrottle.class, + SetCacheCapacity.class, + SetCacheKeysToSave.class, + SetColumnIndexSize.class, + SetCompactionThreshold.class, + SetCompactionThroughput.class, + SetConcurrency.class, + SetConcurrentCompactors.class, + SetConcurrentViewBuilders.class, + SetDefaultKeyspaceRF.class, + SetHintedHandoffThrottleInKB.class, + SetInterDCStreamThroughput.class, + SetLoggingLevel.class, + SetMaxHintWindow.class, + SetSnapshotThrottle.class, + SetStreamThroughput.class, + SetTimeout.class, + SetTraceProbability.class, + Sjk.class, + Snapshot.class, + Status.class, + StatusAutoCompaction.class, + StatusBackup.class, + StatusBinary.class, + StatusGossip.class, + StatusHandoff.class, + Stop.class, + StopDaemon.class, + TableHistograms.class, + TableStats.class, + TopPartitions.class, + TpStats.class, + TruncateHints.class, + UpdateCIDRGroup.class, + UpgradeSSTable.class, + Verify.class, + Version.class, + ViewBuildStatus.class }) +public class NodetoolCommand implements Runnable +{ + @Spec + public CommandSpec spec; + + // TODO CASSANDRA-20790 this option is used only in several commands and should not be the global option. + // It should be pushed down to specific commands to clean up the global hierarchy, while maintaining backwards compatibility. + // Calls such as './nodetool --print-port subcommand', and './nodetool subcommand --print-port' should work as expected. + @Option(names = { "-pp", "--print-port" }, description = "Operate in 4.0 mode with hosts disambiguated by port number") + public boolean printPort = false; + + public void run() + { + printTopCommandUsage(spec.commandLine(), + spec.commandLine().getColorScheme(), + spec.commandLine().getOut()); + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/PauseHandoff.java b/src/java/org/apache/cassandra/tools/nodetool/PauseHandoff.java index fde9eef271..e650048441 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/PauseHandoff.java +++ b/src/java/org/apache/cassandra/tools/nodetool/PauseHandoff.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 = "pausehandoff", description = "Pause hints delivery process") -public class PauseHandoff extends NodeToolCmd +public class PauseHandoff extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ProfileLoad.java b/src/java/org/apache/cassandra/tools/nodetool/ProfileLoad.java index 01af271fcb..711d259c78 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ProfileLoad.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ProfileLoad.java @@ -30,48 +30,60 @@ import javax.management.openmbean.OpenDataException; import com.google.common.collect.Lists; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.metrics.Sampler.SamplerType; import org.apache.cassandra.metrics.SamplingManager; 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.utils.Pair; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs; import static org.apache.cassandra.utils.LocalizeString.toUpperCaseLocalized; import static org.apache.commons.lang3.StringUtils.join; @Command(name = "profileload", description = "Low footprint profiling of activity for a period of time") -public class ProfileLoad extends NodeToolCmd +public class ProfileLoad extends AbstractCommand { - @Arguments(usage = " ", description = "The keyspace, column family name, and duration in milliseconds (Default: 10000)") + @CassandraUsage(usage = " ", description = "The keyspace, column family name, and duration in milliseconds (Default: 10000)") private List args = new ArrayList<>(); - @Option(name = "-s", description = "Capacity of the sampler, higher for more accuracy (Default: 256)") + @Parameters(index = "0", description = "The keyspace name", arity = "0..1") + private String keyspace; + + @Parameters(index = "1", description = "The cloumn family name", arity = "0..1") + private String cfname; + + @Parameters(index = "2", description = "The duration in milliseconds (Default: 10000)", arity = "0..1") + private String duration; + + @Option(names = "-s", description = "Capacity of the sampler, higher for more accuracy (Default: 256)") private int capacity = 256; - @Option(name = "-k", description = "Number of the top samples to list (Default: 10)") + @Option(names = "-k", description = "Number of the top samples to list (Default: 10)") private int topCount = 10; - @Option(name = "-a", description = "Comma separated list of samplers to use (Default: all)") + @Option(names = "-a", description = "Comma separated list of samplers to use (Default: all)") private String samplers = join(SamplerType.values(), ','); - @Option(name = {"-i", "--interval"}, description = "Schedule a new job that samples every interval milliseconds (Default: disabled) in the background") + @Option(names = { "-i", "--interval" }, description = "Schedule a new job that samples every interval milliseconds (Default: disabled) in the background") private int intervalMillis = -1; // -1 for disabled. - @Option(name = {"-t", "--stop"}, description = "Stop the scheduled sampling job identified by and . Jobs are stopped until the last schedules complete.") + @Option(names = { "-t", "--stop" }, description = "Stop the scheduled sampling job identified by and . Jobs are stopped until the last schedules complete.") private boolean shouldStop = false; - @Option(name = {"-l", "--list"}, description = "List the scheduled sampling jobs") + @Option(names = { "-l", "--list" }, description = "List the scheduled sampling jobs") private boolean shouldList = false; @Override public void execute(NodeProbe probe) { - checkArgument(args.size() == 3 || args.size() == 2 || args.size() == 1 || args.size() == 0, + args = concatArgs(keyspace, cfname, duration); + + checkArgument(args.size() == 3 || args.size() == 2 || args.size() == 1 || args.isEmpty(), "Invalid arguments, either [keyspace table/* duration] or [keyspace table/*] or [duration] or no args.\n" + "Optionally, use * to represent all tables under the keyspace."); checkArgument(topCount > 0, "TopK count (-k) option must have positive value"); diff --git a/src/java/org/apache/cassandra/tools/nodetool/ProxyHistograms.java b/src/java/org/apache/cassandra/tools/nodetool/ProxyHistograms.java index c736a67b87..aff4a294b2 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ProxyHistograms.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ProxyHistograms.java @@ -19,14 +19,13 @@ package org.apache.cassandra.tools.nodetool; import java.io.PrintStream; -import static java.lang.String.format; -import io.airlift.airline.Command; - import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; + +import static java.lang.String.format; @Command(name = "proxyhistograms", description = "Print statistic histograms for network operations") -public class ProxyHistograms extends NodeToolCmd +public class ProxyHistograms extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/RangeKeySample.java b/src/java/org/apache/cassandra/tools/nodetool/RangeKeySample.java index fb44d2ca68..96003a5a34 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RangeKeySample.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RangeKeySample.java @@ -17,15 +17,13 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.airline.Command; - import java.util.List; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; @Command(name = "rangekeysample", description = "Shows the sampled keys held across all keyspaces") -public class RangeKeySample extends NodeToolCmd +public class RangeKeySample extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/Rebuild.java b/src/java/org/apache/cassandra/tools/nodetool/Rebuild.java index ed4e97c0f7..a68a3d5d9a 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Rebuild.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Rebuild.java @@ -17,37 +17,37 @@ */ package org.apache.cassandra.tools.nodetool; -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 picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; @Command(name = "rebuild", description = "Rebuild data by streaming from other nodes (similarly to bootstrap)") -public class Rebuild extends NodeToolCmd +public class Rebuild extends AbstractCommand { - @Arguments(usage = "", - description = "Name of DC from which to select sources for streaming. By default, pick any DC (except local DC when --exclude-local-dc is set)") + @Parameters(paramLabel = "src-dc-name", + description = "Name of DC from which to select sources for streaming. By default, pick any DC (except local DC when --exclude-local-dc is set)", + index = "0", + arity = "0..1") private String sourceDataCenterName = null; - @Option(title = "specific_keyspace", - name = {"-ks", "--keyspace"}, + @Option(paramLabel = "specific_keyspace", + names = {"-ks", "--keyspace"}, description = "Use -ks to rebuild specific keyspace.") private String keyspace = null; - @Option(title = "specific_tokens", - name = {"-ts", "--tokens"}, + @Option(paramLabel = "specific_tokens", + names = {"-ts", "--tokens"}, description = "Use -ts to rebuild specific token ranges, in the format of \"(start_token_1,end_token_1],(start_token_2,end_token_2],...(start_token_n,end_token_n]\".") private String tokens = null; - @Option(title = "specific_sources", - name = {"-s", "--sources"}, + @Option(paramLabel = "specific_sources", + names = {"-s", "--sources"}, description = "Use -s to specify hosts that this node should stream from when -ts is used. Multiple hosts should be separated using commas (e.g. 127.0.0.1,127.0.0.2,...)") private String specificSources = null; - @Option(title = "exclude_local_dc", - name = {"--exclude-local-dc"}, + @Option(paramLabel = "exclude_local_dc", + names = {"--exclude-local-dc"}, description = "Use --exclude-local-dc to exclude nodes in local data center as source for streaming.") private boolean excludeLocalDatacenterNodes = false; diff --git a/src/java/org/apache/cassandra/tools/nodetool/RebuildIndex.java b/src/java/org/apache/cassandra/tools/nodetool/RebuildIndex.java index f7a3b6f1ee..02975244cc 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RebuildIndex.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RebuildIndex.java @@ -17,26 +17,38 @@ */ package org.apache.cassandra.tools.nodetool; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Iterables.toArray; -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 com.google.common.collect.Iterables.toArray; +import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "rebuild_index", description = "A full rebuild of native secondary indexes for a given table") -public class RebuildIndex extends NodeToolCmd +public class RebuildIndex extends AbstractCommand { - @Arguments(usage = "
", description = "The keyspace and table name followed by a list of index names") - List args = new ArrayList<>(); + @CassandraUsage(usage = "
", description = "The keyspace and table name followed by a list of index names") + private List args = new ArrayList<>(); + + @Parameters(index = "0", description = "The keyspace name", arity = "0..1") + private String keyspace; + + @Parameters(index = "1", description = "The table name", arity = "0..1") + private String table; + + @Parameters(index = "2..*", description = "The index names", arity = "1..*") + private List indexNames; @Override public void execute(NodeProbe probe) { + args = concatArgs(keyspace, table, indexNames); + checkArgument(args.size() >= 3, "rebuild_index requires ks, cf and idx args"); probe.rebuildIndex(args.get(0), args.get(1), toArray(args.subList(2, args.size()), String.class)); } diff --git a/src/java/org/apache/cassandra/tools/nodetool/RecompressSSTables.java b/src/java/org/apache/cassandra/tools/nodetool/RecompressSSTables.java index 78ed7ecc23..3b65615469 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RecompressSSTables.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RecompressSSTables.java @@ -17,30 +17,41 @@ */ 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.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.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "recompress_sstables", description = "Rewrite sstables (for the requested tables) that have compression configuration different from the current") -public class RecompressSSTables extends NodeToolCmd +public class RecompressSSTables 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 name", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "The keyspace and table names", arity = "0..*") + private List tables; + + @Option(paramLabel = "jobs", + names = { "-j", "--jobs" }, description = "Number of sstables to upgrade simultanously, set to 0 to use all available compaction threads") private int jobs = 2; @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/Refresh.java b/src/java/org/apache/cassandra/tools/nodetool/Refresh.java index b32e7b6210..10bf07c650 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Refresh.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Refresh.java @@ -17,25 +17,36 @@ */ 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 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.concatArgs; + +/** @deprecated See CASSANDRA-6719 */ +@Deprecated(since = "4.0") @Command(name = "refresh", description = "Load newly placed SSTables to the system without restart") -public class Refresh extends NodeToolCmd +public class Refresh extends AbstractCommand { - @Arguments(usage = "
", description = "The keyspace and table name") + @CassandraUsage(usage = "
", description = "The keyspace and table name") private List args = new ArrayList<>(); + @Parameters(index = "0", description = "The keyspace name", arity = "0..1") + private String keyspace; + + @Parameters(index = "1", description = "The table name", arity = "0..1") + private String table; + @Override public void execute(NodeProbe probe) { + args = concatArgs(keyspace, table); + probe.output().out.println("nodetool refresh is deprecated, use nodetool import instead"); checkArgument(args.size() == 2, "refresh requires ks and cf args"); probe.loadNewSSTables(args.get(0), args.get(1)); diff --git a/src/java/org/apache/cassandra/tools/nodetool/RefreshSizeEstimates.java b/src/java/org/apache/cassandra/tools/nodetool/RefreshSizeEstimates.java index 586b4514d7..b8d4d948d3 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RefreshSizeEstimates.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RefreshSizeEstimates.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 = "refreshsizeestimates", description = "Refresh system.size_estimates") -public class RefreshSizeEstimates extends NodeTool.NodeToolCmd +public class RefreshSizeEstimates extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCache.java b/src/java/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCache.java index 0cf44e7cf8..c5b8091974 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCache.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCache.java @@ -17,15 +17,14 @@ */ 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; /** * Nodetool command to reload CIDR groups cache */ @Command(name = "reloadcidrgroupscache", description = "Reload CIDR groups cache with latest entries in cidr_groups table, when CIDR authorizer is enabled") -public class ReloadCIDRGroupsCache extends NodeToolCmd +public class ReloadCIDRGroupsCache extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ReloadLocalSchema.java b/src/java/org/apache/cassandra/tools/nodetool/ReloadLocalSchema.java index d4b1c69eba..8ae6daeef7 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ReloadLocalSchema.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ReloadLocalSchema.java @@ -17,12 +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 = "reloadlocalschema", description = "Reload local node schema from system tables") -public class ReloadLocalSchema extends NodeToolCmd +public class ReloadLocalSchema extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ReloadSeeds.java b/src/java/org/apache/cassandra/tools/nodetool/ReloadSeeds.java index e054abd616..1c0680fc2c 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ReloadSeeds.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ReloadSeeds.java @@ -20,13 +20,11 @@ package org.apache.cassandra.tools.nodetool; import java.io.PrintStream; 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 = "reloadseeds", description = "Reload the seed node list from the seed node provider") -public class ReloadSeeds extends NodeToolCmd +public class ReloadSeeds extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/ReloadSslCertificates.java b/src/java/org/apache/cassandra/tools/nodetool/ReloadSslCertificates.java similarity index 86% rename from src/java/org/apache/cassandra/tools/ReloadSslCertificates.java rename to src/java/org/apache/cassandra/tools/nodetool/ReloadSslCertificates.java index a572648c24..ae48c56c33 100644 --- a/src/java/org/apache/cassandra/tools/ReloadSslCertificates.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ReloadSslCertificates.java @@ -15,14 +15,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.tools; +package org.apache.cassandra.tools.nodetool; import java.io.IOException; -import io.airlift.airline.Command; +import picocli.CommandLine.Command; +import org.apache.cassandra.tools.NodeProbe; @Command(name = "reloadssl", description = "Signals Cassandra to reload SSL certificates") -public class ReloadSslCertificates extends NodeTool.NodeToolCmd +public class ReloadSslCertificates extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ReloadTriggers.java b/src/java/org/apache/cassandra/tools/nodetool/ReloadTriggers.java index 8727a61083..5c296e90f9 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ReloadTriggers.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ReloadTriggers.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 = "reloadtriggers", description = "Reload trigger classes") -public class ReloadTriggers extends NodeToolCmd +public class ReloadTriggers extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/RelocateSSTables.java b/src/java/org/apache/cassandra/tools/nodetool/RelocateSSTables.java index 853b1d3dc8..da4da2b411 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RelocateSSTables.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RelocateSSTables.java @@ -20,26 +20,38 @@ 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; +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.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "relocatesstables", description = "Relocates sstables to the correct disk") -public class RelocateSSTables extends NodeTool.NodeToolCmd +public class RelocateSSTables extends AbstractCommand { - @Arguments(usage = "
", description = "The keyspace and table name") + @CassandraUsage(usage = "
", description = "The keyspace and table name") private List args = new ArrayList<>(); - @Option(title = "jobs", - name = {"-j", "--jobs"}, + @Parameters(index = "0", description = "The keyspace name", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "The table name", arity = "0..*") + private List tables; + + @Option(paramLabel = "jobs", + names = { "-j", "--jobs" }, description = "Number of sstables to relocate simultanously, set to 0 to use all available compaction threads") private int jobs = 2; @Override public void execute(NodeProbe probe) { + args = concatArgs(keyspace, tables); + List keyspaces = parseOptionalKeyspace(args, probe); String[] cfnames = parseOptionalTables(args); try diff --git a/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java b/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java index 94b3262058..67ab782f60 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java @@ -17,47 +17,59 @@ */ package org.apache.cassandra.tools.nodetool; -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 picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.ParentCommand; -@Command(name = "removenode", description = "Show status of current node removal, abort removal or remove provided ID") -public class RemoveNode extends NodeToolCmd +import static com.google.common.base.Preconditions.checkArgument; + +@Command(name = "removenode", + description = "Show status of current node removal, abort removal or remove provided ID", + subcommands = { RemoveNode.Status.class }) +public class RemoveNode extends AbstractCommand { - @Arguments(title = "remove_operation", usage = "|| --force", description = "Show status of current node removal, or remove provided ID", required = true) - private List removeOperation = null; + @ParentCommand + public NodetoolCommand parent; + + @Parameters(paramLabel = "nodeId", description = "The ID of the node to remove", arity = "0..1") + private String nodeId; + + @Option(names = { "--force" }, description = "Force node removal") + private boolean force = false; @Override public void execute(NodeProbe probe) { - switch (removeOperation.get(0)) - { - case "status": - probe.output().out.println("RemovalStatus: " + probe.getRemovalStatus(printPort)); - break; - case "force": - throw new IllegalArgumentException("Can't force a nodetool removenode. Instead abort the ongoing removenode and retry."); - default: - boolean force = removeOperation.size() > 1 && removeOperation.get(1).equals("--force"); - probe.removeNode(removeOperation.get(0), force); - break; - } + // In order the picocli to parse the subcommand correctly, we need to check the nodeId here, or use @ArgGroup + checkArgument(nodeId != null, "nodeId is required"); + probe.removeNode(nodeId, force); } @Command(name = "abortremovenode", description = "Abort a removenode command") - public static class Abort extends NodeToolCmd + public static class Abort extends AbstractCommand { - @Option(title = "node id", name="--node", description = "The node being removed", required = true) + @Option(paramLabel = "nodeId", names = { "--node" }, description = "The node being removed") private String nodeId; public void execute(NodeProbe probe) { + checkArgument(nodeId != null, "nodeId is required"); probe.abortRemoveNode(nodeId); } } + + @Command(name = "status", description = "Show status of the current node removal operation") + public static class Status extends AbstractCommand + { + @ParentCommand + private RemoveNode parent; + + @Override + public void execute(NodeProbe probe) + { + probe.output().out.println("RemovalStatus: " + probe.getRemovalStatus(parent.parent.printPort)); + } + } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/Repair.java b/src/java/org/apache/cassandra/tools/nodetool/Repair.java index ad7342cf67..8836f00582 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Repair.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Repair.java @@ -27,96 +27,104 @@ import java.util.function.Supplier; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; -import io.airlift.airline.Arguments; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.repair.RepairParallelism; import org.apache.cassandra.repair.messages.RepairOption; import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.streaming.PreviewKind; 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 com.google.common.collect.Lists.newArrayList; +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; import static org.apache.commons.lang3.StringUtils.EMPTY; @Command(name = "repair", description = "Repair one or more tables") -public class Repair extends NodeToolCmd +public class Repair extends AbstractCommand { public final static Set ONLY_EXPLICITLY_REPAIRED = Sets.newHashSet(SchemaConstants.DISTRIBUTED_KEYSPACE_NAME); - @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 = "seqential", name = {"-seq", "--sequential"}, description = "Use -seq to carry out a sequential repair") + @Parameters(index = "0", description = "The keyspace followed by one or many tables", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "Tables to repair", arity = "0..*") + public String[] tables; + + @Option(paramLabel = "seqential", names = { "-seq", "--sequential" }, description = "Use -seq to carry out a sequential repair") private boolean sequential = false; - @Option(title = "dc parallel", name = {"-dcpar", "--dc-parallel"}, description = "Use -dcpar to repair data centers in parallel.") + @Option(paramLabel = "dc_parallel", names = { "-dcpar", "--dc-parallel" }, description = "Use -dcpar to repair data centers in parallel.") private boolean dcParallel = false; - @Option(title = "local_dc", name = {"-local", "--in-local-dc"}, description = "Use -local to only repair against nodes in the same datacenter") + @Option(paramLabel = "local_dc", names = { "-local", "--in-local-dc" }, description = "Use -local to only repair against nodes in the same datacenter") private boolean localDC = false; - @Option(title = "specific_dc", name = {"-dc", "--in-dc"}, description = "Use -dc to repair specific datacenters") - private List specificDataCenters = new ArrayList<>();; + @Option(paramLabel = "specific_dc", names = { "-dc", "--in-dc" }, description = "Use -dc to repair specific datacenters") + private List specificDataCenters = new ArrayList<>(); - @Option(title = "specific_host", name = {"-hosts", "--in-hosts"}, description = "Use -hosts to repair specific hosts") + @Option(paramLabel = "specific_host", names = { "-hosts", "--in-hosts" }, description = "Use -hosts to repair specific hosts") private List specificHosts = new ArrayList<>(); - @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 = EMPTY; - @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 = EMPTY; - @Option(title = "primary_range", name = {"-pr", "--partitioner-range"}, description = "Use -pr to repair only the first range returned by the partitioner") + @Option(paramLabel = "primary_range", names = { "-pr", "--partitioner-range" }, description = "Use -pr to repair only the first range returned by the partitioner") private boolean primaryRange = false; - @Option(title = "full", name = {"-full", "--full"}, description = "Use -full to issue a full repair.") + @Option(paramLabel = "full", names = { "-full", "--full" }, description = "Use -full to issue a full repair.") private boolean fullRepair = false; - @Option(title = "force", name = {"-force", "--force"}, description = "Use -force to filter out down endpoints") + @Option(paramLabel = "force", names = { "-force", "--force" }, description = "Use -force to filter out down endpoints") private boolean force = false; - @Option(title = "preview", name = {"-prv", "--preview"}, description = "Determine ranges and amount of data to be streamed, but don't actually perform repair") + @Option(paramLabel = "preview", names = { "-prv", "--preview" }, description = "Determine ranges and amount of data to be streamed, but don't actually perform repair") private boolean preview = false; - @Option(title = "validate", name = {"-vd", "--validate"}, description = "Checks that repaired data is in sync between nodes. Out of sync repaired data indicates a full repair should be run.") + @Option(paramLabel = "validate", names = { "-vd", "--validate" }, description = "Checks that repaired data is in sync between nodes. Out of sync repaired data indicates a full repair should be run.") private boolean validate = false; - @Option(title = "job_threads", name = {"-j", "--job-threads"}, description = "Number of threads to run repair jobs. " + - "Usually this means number of CFs to repair concurrently. " + - "WARNING: increasing this puts more load on repairing nodes, so be careful. (default: 1, max: 4)") + @Option(paramLabel = "job_threads", names = { "-j", "--job-threads" }, description = "Number of threads to run repair jobs. " + + "Usually this means number of CFs to repair concurrently. " + + "WARNING: increasing this puts more load on repairing nodes, so be careful. (default: 1, max: 4)") private int numJobThreads = 1; - @Option(title = "trace_repair", name = {"-tr", "--trace"}, description = "Use -tr to trace the repair. Traces are logged to system_traces.events.") + @Option(paramLabel = "trace_repair", names = { "-tr", "--trace" }, description = "Use -tr to trace the repair. Traces are logged to system_traces.events.") private boolean trace = false; - @Option(title = "pull_repair", name = {"-pl", "--pull"}, description = "Use --pull to perform a one way repair where data is only streamed from a remote node to this node.") + @Option(paramLabel = "pull_repair", names = { "-pl", "--pull" }, description = "Use --pull to perform a one way repair where data is only streamed from a remote node to this node.") private boolean pullRepair = false; - @Option(title = "optimise_streams", name = {"-os", "--optimise-streams"}, description = "Use --optimise-streams to try to reduce the number of streams we do (EXPERIMENTAL, see CASSANDRA-3200).") + @Option(paramLabel = "optimise_streams", names = { "-os", "--optimise-streams" }, description = "Use --optimise-streams to try to reduce the number of streams we do (EXPERIMENTAL, see CASSANDRA-3200).") private boolean optimiseStreams = false; - @Option(title = "skip-paxos", name = {"-skip-paxos", "--skip-paxos"}, description = "If the --skip-paxos flag is included, the paxos repair step is skipped. Paxos repair is also skipped for preview repairs.") + @Option(paramLabel = "skip-paxos", names = { "-skip-paxos", "--skip-paxos" }, description = "If the --skip-paxos flag is included, the paxos repair step is skipped. Paxos repair is also skipped for preview repairs.") private boolean skipPaxos = false; - @Option(title = "paxos-only", name = {"-paxos-only", "--paxos-only"}, description = "If the --paxos-only flag is included, no table data is repaired, only paxos operations..") + @Option(paramLabel = "paxos-only", names = { "-paxos-only", "--paxos-only" }, description = "If the --paxos-only flag is included, no table data is repaired, only paxos operations..") private boolean paxosOnly = false; - @Option(title = "accord-only", name = {"-accord-only", "--accord-only"}, description = "If the --accord-only flag is included, no table data is repaired, only accord operations..") + @Option(paramLabel = "accord-only", names = { "-accord-only", "--accord-only" }, description = "If the --accord-only flag is included, no table data is repaired, only accord operations..") private boolean accordOnly = false; - @Option(title = "skip-accord", name = {"-skip-accord", "--skip-accord"}, description = "If the --skip-accord flag is included, the Accord repair step is skipped. Accord repair is also skipped for preview repairs.") + @Option(paramLabel = "skip-accord", names = { "-skip-accord", "--skip-accord" }, description = "If the --skip-accord flag is included, the Accord repair step is skipped. Accord repair is also skipped for preview repairs.") private boolean skipAccord = false; - @Option(title = "ignore_unreplicated_keyspaces", name = {"-iuk","--ignore-unreplicated-keyspaces"}, description = "Use --ignore-unreplicated-keyspaces to ignore keyspaces which are not replicated, otherwise the repair will fail") + @Option(paramLabel = "ignore_unreplicated_keyspaces", names = { "-iuk", "--ignore-unreplicated-keyspaces" }, description = "Use --ignore-unreplicated-keyspaces to ignore keyspaces which are not replicated, otherwise the repair will fail") private boolean ignoreUnreplicatedKeyspaces = false; - @Option(title = "no_purge", name = {"--include-gcgs-expired-tombstones"}, description = "Do not apply gc grace seconds to purge any tombstones. Only useful in rare recovery scenarios, never regular operations.") + @Option(paramLabel = "no_purge", names = { "--include-gcgs-expired-tombstones" }, description = "Do not apply gc grace seconds to purge any tombstones. Only useful in rare recovery scenarios, never regular operations.") private boolean dontPurgeTombstones = false; private PreviewKind getPreviewKind() @@ -142,8 +150,9 @@ public class Repair extends NodeToolCmd @Override public void execute(NodeProbe probe) { - KeyspaceSet keyspaceSet = KeyspaceSet.NON_LOCAL_STRATEGY; - List keyspaces = parseOptionalKeyspace(args, probe, keyspaceSet); + args = concatArgs(keyspace, tables); + + List keyspaces = parseOptionalKeyspaceNonLocal(args, probe); String[] cfnames = parseOptionalTables(args); if (primaryRange && (!specificDataCenters.isEmpty() || !specificHosts.isEmpty())) @@ -166,18 +175,7 @@ public class Repair extends NodeToolCmd } } - public static Map parseOptionMap(Supplier localDCOption, List args) - { - List realArgs = new ArrayList<>(args.size() + 1); - realArgs.add("repair"); - realArgs.addAll(args); - Cli parser = Cli.builder("fortesting").withCommand(Repair.class).build(); - Repair repair = (Repair) parser.parse(realArgs); - String[] cfnames = repair.parseOptionalTables(repair.args); - return repair.createOptions(localDCOption, cfnames); - } - - private Map createOptions(Supplier localDCOption, String[] cfnames) + public Map createOptions(Supplier localDCOption, String[] cfnames) { Map options = new HashMap<>(); RepairParallelism parallelismDegree = RepairParallelism.PARALLEL; diff --git a/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java b/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java index 6b30ed12bd..22b0aaf91c 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java @@ -29,32 +29,50 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; 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.repair.consistent.LocalSessionInfo; import org.apache.cassandra.repair.consistent.admin.CleanupSummary; import org.apache.cassandra.repair.consistent.admin.PendingStats; import org.apache.cassandra.repair.consistent.admin.RepairStats; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool; +import org.apache.cassandra.tools.nodetool.layout.CassandraUsage; import org.apache.cassandra.utils.FBUtilities; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs; /** * Supports listing and failing incremental repair sessions */ -public abstract class RepairAdmin extends NodeTool.NodeToolCmd +@Command(name = "repair_admin", + description = "list and fail incremental repair sessions", + subcommands = {RepairAdmin.ListCmd.class, + RepairAdmin.SummarizePendingCmd.class, + RepairAdmin.SummarizeRepairedCmd.class, + RepairAdmin.CleanupDataCmd.class, + RepairAdmin.CancelCmd.class}) +public class RepairAdmin extends AbstractCommand { - @Command(name = "list", description = "list repair sessions") - public static class ListCmd extends RepairAdmin + @Override + protected void execute(NodeProbe probe) { - @Option(title = "all", name = {"-a", "--all"}, description = "include completed and failed sessions") + AbstractCommand cmd = new ListCmd(); + cmd.probe(probe); + cmd.logger(output); + cmd.run(); + } + + @Command(name = "list", description = "list repair sessions") + public static class ListCmd extends AbstractCommand + { + @Option(paramLabel = "all", names = { "-a", "--all" }, description = "include completed and failed sessions") private boolean all; - @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 = StringUtils.EMPTY; - @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 = StringUtils.EMPTY; protected void execute(NodeProbe probe) @@ -93,22 +111,29 @@ public abstract class RepairAdmin extends NodeTool.NodeToolCmd } @Command(name = "summarize-pending", description = "report the amount of data marked pending repair for the given token " + "range (or all replicated range if no tokens are provided") - public static class SummarizePendingCmd extends RepairAdmin + public static class SummarizePendingCmd extends AbstractCommand { - @Option(title = "verbose", name = {"-v", "--verbose"}, description = "print additional info ") + @Option(paramLabel = "verbose", names = { "-v", "--verbose" }, description = "print additional info ") private boolean verbose; - @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 = StringUtils.EMPTY; - @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 = StringUtils.EMPTY; - @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables") + @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables") private List schemaArgs = new ArrayList<>(); + @Parameters(index = "0", description = "The keyspace followed by one or many tables", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "The tables", arity = "0..*") + private List tables; + protected void execute(NodeProbe probe) { + schemaArgs = concatArgs(keyspace, tables); List cds = probe.getRepairServiceProxy().getPendingStats(schemaArgs, getRangeString(startToken, endToken)); List stats = new ArrayList<>(cds.size()); cds.forEach(cd -> stats.add(PendingStats.fromComposite(cd))); @@ -152,22 +177,29 @@ public abstract class RepairAdmin extends NodeTool.NodeToolCmd @Command(name = "summarize-repaired", description = "return the most recent repairedAt timestamp for the given token range " + "(or all replicated ranges if no tokens are provided)") - public static class SummarizeRepairedCmd extends RepairAdmin + public static class SummarizeRepairedCmd extends AbstractCommand { - @Option(title = "verbose", name = {"-v", "--verbose"}, description = "print additional info ") + @Option(paramLabel = "verbose", names = {"-v", "--verbose"}, description = "print additional info ") private boolean verbose = false; - @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 = StringUtils.EMPTY; - @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 = StringUtils.EMPTY; - @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables") + @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables") private List schemaArgs = new ArrayList<>(); + @Parameters(index = "0", description = "The keyspace followed by one or many tables", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "The tables", arity = "0..*") + private List tables; + protected void execute(NodeProbe probe) { + schemaArgs = concatArgs(keyspace, tables); PrintStream out = probe.output().out; List compositeData = probe.getRepairServiceProxy().getRepairStats(schemaArgs, getRangeString(startToken, endToken)); @@ -216,22 +248,29 @@ public abstract class RepairAdmin extends NodeTool.NodeToolCmd "This happens automatically, but the command is provided " + "for situations where it needs to be expedited." + " Use --force to cancel compactions that are preventing promotion") - public static class CleanupDataCmd extends RepairAdmin + public static class CleanupDataCmd extends AbstractCommand { - @Option(title = "force", name = {"-f", "--force"}, description = "Force a cleanup.") + @Option(paramLabel = "force", names = { "-f", "--force" }, description = "Force a cleanup.") private boolean force = false; - @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 = StringUtils.EMPTY; - @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 = StringUtils.EMPTY; - @Arguments(usage = "[ ...]", description = "The keyspace followed by one or many tables") + @CassandraUsage(usage = "[ ...]", description = "The keyspace followed by one or many tables") private List schemaArgs = new ArrayList<>(); + @Parameters(index = "0", description = "The keyspace followed by one or many tables", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "The tables", arity = "0..*") + private List tables; + protected void execute(NodeProbe probe) { + schemaArgs = concatArgs(keyspace, tables); PrintStream out = probe.output().out; out.println("Cleaning up data from completed sessions..."); List compositeData = probe.getRepairServiceProxy().cleanupPending(schemaArgs, getRangeString(startToken, endToken), force); @@ -273,12 +312,12 @@ public abstract class RepairAdmin extends NodeTool.NodeToolCmd @Command(name = "cancel", description = "cancel an incremental repair session." + " Use --force to cancel from a node other than the repair coordinator" + " Attempting to cancel FINALIZED or FAILED sessions is an error.") - public static class CancelCmd extends RepairAdmin + public static class CancelCmd extends AbstractCommand { - @Option(title = "force", name = {"-f", "--force"}, description = "Force a cancellation.") + @Option(paramLabel = "force", names = { "-f", "--force" }, description = "Force a cancellation.") private boolean force = false; - @Option(title = "session", name = {"-s", "--session"}, description = "The session to cancel", required = true) + @Option(paramLabel = "session", names = { "-s", "--session" }, description = "The session to cancel", required = true) private String sessionToCancel; protected void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ReplayBatchlog.java b/src/java/org/apache/cassandra/tools/nodetool/ReplayBatchlog.java index 23c31f7e13..91b6e12e5b 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ReplayBatchlog.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ReplayBatchlog.java @@ -21,12 +21,11 @@ package org.apache.cassandra.tools.nodetool; import java.io.IOError; import java.io.IOException; -import io.airlift.airline.Command; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool; +import picocli.CommandLine.Command; @Command(name = "replaybatchlog", description = "Kick off batchlog replay and wait for finish") -public class ReplayBatchlog extends NodeTool.NodeToolCmd +public class ReplayBatchlog extends AbstractCommand { protected void execute(NodeProbe probe) { diff --git a/src/java/org/apache/cassandra/tools/nodetool/ResetFullQueryLog.java b/src/java/org/apache/cassandra/tools/nodetool/ResetFullQueryLog.java index d7ac30107b..b3df3fffd0 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ResetFullQueryLog.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ResetFullQueryLog.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 = "resetfullquerylog", description = "Stop the full query log and clean files in the configured full query log directory from cassandra.yaml as well as JMX") -public class ResetFullQueryLog extends NodeToolCmd +public class ResetFullQueryLog extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ResetLocalSchema.java b/src/java/org/apache/cassandra/tools/nodetool/ResetLocalSchema.java index 62775a481d..f6d8c647f1 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ResetLocalSchema.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ResetLocalSchema.java @@ -17,15 +17,13 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.airline.Command; - import java.io.IOException; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; @Command(name = "resetlocalschema", description = "Reset node's local schema and resync") -public class ResetLocalSchema extends NodeToolCmd +public class ResetLocalSchema extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/ResumeHandoff.java b/src/java/org/apache/cassandra/tools/nodetool/ResumeHandoff.java index bda98aaec0..0da92beca5 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ResumeHandoff.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ResumeHandoff.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 = "resumehandoff", description = "Resume hints delivery process") -public class ResumeHandoff extends NodeToolCmd +public class ResumeHandoff extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/Ring.java b/src/java/org/apache/cassandra/tools/nodetool/Ring.java index eb7645d6df..331bc8de5b 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Ring.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Ring.java @@ -17,11 +17,6 @@ */ package org.apache.cassandra.tools.nodetool; -import static java.lang.String.format; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; - import java.io.PrintStream; import java.net.UnknownHostException; import java.text.DecimalFormat; @@ -33,20 +28,23 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.apache.cassandra.locator.EndpointSnitchInfoMBean; -import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; - import com.google.common.collect.LinkedHashMultimap; +import org.apache.cassandra.locator.EndpointSnitchInfoMBean; +import org.apache.cassandra.tools.NodeProbe; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +import static java.lang.String.format; + @Command(name = "ring", description = "Print information about the token ring") -public class Ring extends NodeToolCmd +public class Ring extends WithPortDisplayAbstractCommand { - @Arguments(description = "Specify a keyspace for accurate ownership information (topology awareness)") + @Parameters(description = "Specify a keyspace for accurate ownership information (topology awareness)", index = "0", arity = "0..1") private String keyspace = null; - @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; private PrintStream out; @@ -111,7 +109,7 @@ public class Ring extends NodeToolCmd } out.println(); - for (Entry entry : NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) + for (Entry entry : CommandUtils.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) printDc(format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership); if (haveVnodes) diff --git a/src/java/org/apache/cassandra/tools/nodetool/SSTableRepairedSet.java b/src/java/org/apache/cassandra/tools/nodetool/SSTableRepairedSet.java index 2a7b56732a..ac1f8f0509 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SSTableRepairedSet.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SSTableRepairedSet.java @@ -23,41 +23,48 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; - -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalKeyspaceNonLocal; +import static org.apache.cassandra.tools.nodetool.CommandUtils.parseOptionalTables; /** * Provides a way to set the repaired state of SSTables without any downtime through nodetool. */ @Command(name = "sstablerepairedset", description = "Set the repaired state of SSTables for given keyspace/tables") -public class SSTableRepairedSet extends NodeTool.NodeToolCmd +public class SSTableRepairedSet extends AbstractCommand { - @Arguments(usage = "[ ]", description = "Optional keyspace followed by zero or more tables") protected List args = new ArrayList<>(); - @Option(title = "really-set", - name = { "--really-set" }, - description = "Really set the repaired state of SSTables. If not set, only print SSTables that would be affected.") + @Parameters(index = "0", description = "Keyspace to set the repaired state of SSTables. If not provided, all keyspaces are used.") + public String keyspace; + + @Parameters(index = "1..*", description = "Tables to set the repaired state of SSTables. If not provided, all tables in the keyspace are used.") + public String[] tables; + + @Option(paramLabel = "really_set", + names = { "--really-set" }, + description = "Really set the repaired state of SSTables. If not set, only print SSTables that would be affected.") protected boolean reallySet = false; - @Option(title = "is-repaired", - name = { "--is-repaired" }, - description = "Set SSTables to repaired state.") + @Option(paramLabel = "is_repaired", + names = { "--is-repaired" }, + description = "Set SSTables to repaired state.") protected boolean isRepaired = false; - @Option(title = "is-unrepaired", - name = { "--is-unrepaired" }, - description = "Set SSTables to unrepaired state.") + @Option(paramLabel = "is_unrepaired", + names = { "--is-unrepaired" }, + description = "Set SSTables to unrepaired state.") protected boolean isUnrepaired = false; @Override public void execute(NodeProbe probe) { + args = args.isEmpty() ? CommandUtils.concatArgs(keyspace, tables) : args; PrintStream out = probe.output().out; if (isRepaired == isUnrepaired) @@ -72,7 +79,7 @@ public class SSTableRepairedSet extends NodeTool.NodeToolCmd else message = "Previewing repaired state mutation of SSTables for"; - List keyspaces = parseOptionalKeyspace(args, probe, KeyspaceSet.NON_LOCAL_STRATEGY); + List keyspaces = parseOptionalKeyspaceNonLocal(args, probe); List tables = new ArrayList<>(Arrays.asList(parseOptionalTables(args))); if (args.isEmpty()) diff --git a/src/java/org/apache/cassandra/tools/nodetool/Scrub.java b/src/java/org/apache/cassandra/tools/nodetool/Scrub.java index 049d1d01ac..5de96ab1d4 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Scrub.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Scrub.java @@ -17,53 +17,61 @@ */ 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.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; import org.apache.cassandra.tools.StandaloneScrubber; +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; @Command(name = "scrub", description = "Scrub (rebuild sstables for) one or more tables") -public class Scrub extends NodeToolCmd +public class Scrub 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 = "disable_snapshot", - name = {"-ns", "--no-snapshot"}, + @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 scrub", arity = "0..*") + private List tables; + + @Option(paramLabel = "disable_snapshot", + names = { "-ns", "--no-snapshot" }, description = "Scrubbed CFs will be snapshotted first, if disableSnapshot is false. (default false)") private boolean disableSnapshot = false; - @Option(title = "skip_corrupted", - name = {"-s", "--skip-corrupted"}, + @Option(paramLabel = "skip_corrupted", + names = { "-s", "--skip-corrupted" }, description = "Skip corrupted partitions even when scrubbing counter tables. (default false)") private boolean skipCorrupted = false; - @Option(title = "no_validate", - name = {"-n", "--no-validate"}, - description = "Do not validate columns using column validator") + @Option(paramLabel = "no_validate", + names = { "-n", "--no-validate" }, + description = "Do not validate columns using column validator") private boolean noValidation = false; - @Option(title = "reinsert_overflowed_ttl", - name = {"-r", "--reinsert-overflowed-ttl"}, - description = StandaloneScrubber.REINSERT_OVERFLOWED_TTL_OPTION_DESCRIPTION) + @Option(paramLabel = "reinsert_overflowed_ttl", + names = { "-r", "--reinsert-overflowed-ttl" }, + description = StandaloneScrubber.REINSERT_OVERFLOWED_TTL_OPTION_DESCRIPTION) private boolean reinsertOverflowedTTL = false; - @Option(title = "jobs", - name = {"-j", "--jobs"}, + @Option(paramLabel = "jobs", + names = { "-j", "--jobs" }, description = "Number of sstables to scrub 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); - String[] tableNames = parseOptionalTables(args); + args = concatArgs(keyspace, tables); + List keyspaces = CommandUtils.parseOptionalKeyspace(args, probe); + String[] tableNames = CommandUtils.parseOptionalTables(args); for (String keyspace : keyspaces) { diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetAuthCacheConfig.java b/src/java/org/apache/cassandra/tools/nodetool/SetAuthCacheConfig.java index 8adad65876..83cbaa3293 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetAuthCacheConfig.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetAuthCacheConfig.java @@ -17,51 +17,44 @@ */ 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.NodeToolCmd; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; import static com.google.common.base.Preconditions.checkArgument; @Command(name = "setauthcacheconfig", description = "Set configuration for Auth cache") -public class SetAuthCacheConfig extends NodeToolCmd +public class SetAuthCacheConfig 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; - @SuppressWarnings("unused") - @Option(title = "validity-period", - name = {"--validity-period"}, + @Option(paramLabel = "validity-period", + names = { "--validity-period" }, description = "Validity period in milliseconds") private Integer validityPeriod; - @SuppressWarnings("unused") - @Option(title = "update-interval", - name = {"--update-interval"}, + @Option(paramLabel = "update-interval", + names = { "--update-interval" }, description = "Update interval in milliseconds") private Integer updateInterval; - @SuppressWarnings("unused") - @Option(title = "max-entries", - name = {"--max-entries"}, + @Option(paramLabel = "max-entries", + names = { "--max-entries" }, description = "Max entries") private Integer maxEntries; - @SuppressWarnings("unused") - @Option(title = "enable-active-update", - name = {"--enable-active-update"}, + @Option(paramLabel = "enable-active-update", + names = { "--enable-active-update" }, description = "Enable active update") private Boolean enableActiveUpdate; - @SuppressWarnings("unused") - @Option(title = "disable-active-update", - name = {"--disable-active-update"}, + @Option(paramLabel = "disable-active-update", + names = { "--disable-active-update" }, description = "Disable active update") private Boolean disableActiveUpdate; diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetAutoRepairConfig.java b/src/java/org/apache/cassandra/tools/nodetool/SetAutoRepairConfig.java index cc00cabd06..e3343d83e3 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetAutoRepairConfig.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetAutoRepairConfig.java @@ -20,11 +20,6 @@ package org.apache.cassandra.tools.nodetool; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; -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 java.io.PrintStream; import java.util.ArrayList; @@ -33,31 +28,40 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import org.apache.cassandra.tools.NodeProbe; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + import static com.google.common.base.Preconditions.checkArgument; /** * Allows to set AutoRepair configuration through nodetool. */ @Command(name = "setautorepairconfig", description = "sets the autorepair configuration") -public class SetAutoRepairConfig extends NodeToolCmd +public class SetAutoRepairConfig extends AbstractCommand { @VisibleForTesting - @Arguments(title = " ", usage = " ", - description = "autorepair param and value.\nPossible autorepair parameters are as following: " + - "[start_scheduler|number_of_repair_threads|min_repair_interval|sstable_upper_threshold" + - "|enabled|table_max_repair_time|priority_hosts|forcerepair_hosts|ignore_dcs" + - "|history_clear_delete_hosts_buffer_interval|repair_primary_token_range_only" + - "|parallel_repair_count|parallel_repair_percentage" + - "|allow_parallel_replica_repair|allow_parallel_repair_across_schedules" + - "|materialized_view_repair_enabled|repair_max_retries" + - "|repair_retry_backoff|repair_session_timeout|min_repair_task_duration" + - "|repair_by_keyspace|token_range_splitter.]", - required = true) protected List args = new ArrayList<>(); + @Parameters(index = "0", arity = "0..1", description = { "Autorepair param type.", + "Possible autorepair parameters are as following: " + + "[start_scheduler|number_of_repair_threads|min_repair_interval|sstable_upper_threshold" + + "|enabled|table_max_repair_time|priority_hosts|forcerepair_hosts|ignore_dcs" + + "|history_clear_delete_hosts_buffer_interval|repair_primary_token_range_only" + + "|parallel_repair_count|parallel_repair_percentage" + + "|allow_parallel_replica_repair|allow_parallel_repair_across_schedules" + + "|materialized_view_repair_enabled|repair_max_retries" + + "|repair_retry_backoff|repair_session_timeout|min_repair_task_duration" + + "|repair_by_keyspace|token_range_splitter.]" }) + public String autorepairParamType; + + @Parameters(index = "1", description = "Autorepair param value", arity = "0..1") + public String autorepairParamValue; + @VisibleForTesting - @Option(title = "repair type", name = { "-t", "--repair-type" }, description = "Repair type") - protected String repairTypeStr; + @Option(paramLabel = "repairType", names = { "-t", "--repair-type" }, description = "Repair type") + public String repairTypeStr; @VisibleForTesting protected PrintStream out = System.out; @@ -67,6 +71,7 @@ public class SetAutoRepairConfig extends NodeToolCmd @Override public void execute(NodeProbe probe) { + args = args.isEmpty() ? CommandUtils.concatArgs(autorepairParamType, autorepairParamValue) : args; checkArgument(args.size() == 2, "setautorepairconfig requires param-type, and value args."); String paramType = args.get(0); String paramVal = args.get(1); diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetBatchlogReplayThrottle.java b/src/java/org/apache/cassandra/tools/nodetool/SetBatchlogReplayThrottle.java index b96146c5be..aa02bf87c9 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetBatchlogReplayThrottle.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetBatchlogReplayThrottle.java @@ -17,16 +17,15 @@ */ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "setbatchlogreplaythrottle", description = "Set batchlog replay throttle in KB per second, or 0 to disable throttling. " + "This will be reduced proportionally to the number of nodes in the cluster.") -public class SetBatchlogReplayThrottle extends NodeToolCmd +public class SetBatchlogReplayThrottle extends AbstractCommand { - @Arguments(title = "batchlog_replay_throttle", usage = "", description = "Value in KiB per second, 0 to disable throttling", required = true) + @Parameters(paramLabel = "batchlog_replay_throttle", description = "Value in KiB per second, 0 to disable throttling", arity = "1") private Integer batchlogReplayThrottle = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetCacheCapacity.java b/src/java/org/apache/cassandra/tools/nodetool/SetCacheCapacity.java index b07eb9e008..3ff55f5cfe 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetCacheCapacity.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetCacheCapacity.java @@ -17,28 +17,40 @@ */ 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.Optional; 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; @Command(name = "setcachecapacity", description = "Set global key, row, and counter cache capacities (in MB units)") -public class SetCacheCapacity extends NodeToolCmd +public class SetCacheCapacity extends AbstractCommand { - @Arguments(title = " ", - usage = " ", - description = "Key cache, row cache, and counter cache (in MB)", - required = true) + @CassandraUsage(usage = " ", + description = "Key cache, row cache, and counter cache (in MB)") private List args = new ArrayList<>(); + @Parameters(paramLabel = "key-cache-capacity", description = "Key cache capacity in MB", arity = "0..1", index = "0") + private Integer keyCacheCapacity = null; + + @Parameters(paramLabel = "row-cache-capacity", description = "Row cache capacity in MB", arity = "0..1", index = "1") + private Integer rowCacheCapacity = null; + + @Parameters(paramLabel = "counter-cache-capacity", description = "Counter cache capacity in MB", arity = "0..1", index = "2") + private Integer counterCacheCapacity = null; + @Override public void execute(NodeProbe probe) { + Optional.ofNullable(keyCacheCapacity).ifPresent(args::add); + Optional.ofNullable(rowCacheCapacity).ifPresent(args::add); + Optional.ofNullable(counterCacheCapacity).ifPresent(args::add); + checkArgument(args.size() == 3, "setcachecapacity requires key-cache-capacity, row-cache-capacity, and counter-cache-capacity args."); probe.setCacheCapacities(args.get(0), args.get(1), args.get(2)); } diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetCacheKeysToSave.java b/src/java/org/apache/cassandra/tools/nodetool/SetCacheKeysToSave.java index de9bab58c8..9b4f1d8b64 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetCacheKeysToSave.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetCacheKeysToSave.java @@ -17,28 +17,40 @@ */ 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.Optional; 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; @Command(name = "setcachekeystosave", description = "Set number of keys saved by each cache for faster post-restart warmup. 0 to disable") -public class SetCacheKeysToSave extends NodeToolCmd +public class SetCacheKeysToSave extends AbstractCommand { - @Arguments(title = " ", - usage = " ", - description = "The number of keys saved by each cache. 0 to disable", - required = true) + @CassandraUsage(usage = " ", + description = "The number of keys saved by each cache. 0 to disable") private List args = new ArrayList<>(); + @Parameters(paramLabel = "key-cache-keys-to-save", description = "Key cache keys to save", arity = "0..1", index = "0") + private Integer keyCacheKeysToSave = null; + + @Parameters(paramLabel = "row-cache-keys-to-save", description = "Row cache keys to save", arity = "0..1", index = "1") + private Integer rowCacheKeysToSave = null; + + @Parameters(paramLabel = "counter-cache-keys-to-save", description = "Counter cache keys to save", arity = "0..1", index = "2") + private Integer counterCacheKeysToSave = null; + @Override public void execute(NodeProbe probe) { + Optional.ofNullable(keyCacheKeysToSave).ifPresent(args::add); + Optional.ofNullable(rowCacheKeysToSave).ifPresent(args::add); + Optional.ofNullable(counterCacheKeysToSave).ifPresent(args::add); + checkArgument(args.size() == 3, "setcachekeystosave requires key-cache-keys-to-save, row-cache-keys-to-save, and counter-cache-keys-to-save args."); probe.setCacheKeysToSave(args.get(0), args.get(1), args.get(2)); } diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java b/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java index fe5d8b3d98..de9673196d 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java @@ -18,16 +18,14 @@ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "setcolumnindexsize", description = "Set the granularity of the collation index of rows within a partition in KiB") -public class SetColumnIndexSize extends NodeToolCmd +public class SetColumnIndexSize extends AbstractCommand { - @SuppressWarnings("UnusedDeclaration") - @Arguments(title = "column_index_size", usage = "", description = "Value in KiB", required = true) + @Parameters(paramLabel = "column_index_size", description = "Value in KiB", arity = "1") private int columnIndexSizeInKiB; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThreshold.java b/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThreshold.java index 52bb5bc290..29ec91d405 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThreshold.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThreshold.java @@ -17,26 +17,41 @@ */ package org.apache.cassandra.tools.nodetool; -import static com.google.common.base.Preconditions.checkArgument; -import static java.lang.Integer.parseInt; -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 java.lang.Integer.parseInt; +import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "setcompactionthreshold", description = "Set min and max compaction thresholds for a given table") -public class SetCompactionThreshold extends NodeToolCmd +public class SetCompactionThreshold extends AbstractCommand { - @Arguments(title = "
", usage = "
", description = "The keyspace, the table, min and max threshold", required = true) + @CassandraUsage(usage = "
", description = "The keyspace, the table, min and max threshold") private List args = new ArrayList<>(); + @Parameters(paramLabel = "keyspace", description = "The keyspace name", arity = "0..1", index = "0") + private String keyspace; + + @Parameters(paramLabel = "table", description = "The table name", arity = "0..1", index = "1") + private String table; + + @Parameters(paramLabel = "minthreshold", description = "The min threshold", arity = "0..1", index = "2") + private String minthreshold; + + @Parameters(paramLabel = "maxthreshold", description = "The max threshold", arity = "0..1", index = "3") + private String maxthreshold; + @Override public void execute(NodeProbe probe) { + args = concatArgs(keyspace, table, minthreshold, maxthreshold); + checkArgument(args.size() == 4, "setcompactionthreshold requires ks, cf, min, and max threshold args."); int minthreshold = parseInt(args.get(2)); diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThroughput.java b/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThroughput.java index a75aa13250..dc61e93f00 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThroughput.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetCompactionThroughput.java @@ -17,16 +17,14 @@ */ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "setcompactionthroughput", description = "Set the MiB/s throughput cap for compaction in the system, or 0 to disable throttling") -public class SetCompactionThroughput extends NodeToolCmd +public class SetCompactionThroughput extends AbstractCommand { - @Arguments(title = "compaction_throughput", usage = "", description = "Value in MiB, 0 to disable throttling", required = true) + @Parameters(paramLabel = "compaction_throughput", description = "Value in MiB, 0 to disable throttling", arity = "1") private Integer compactionThroughput = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetConcurrency.java b/src/java/org/apache/cassandra/tools/nodetool/SetConcurrency.java index 6705c92094..2e24372e83 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetConcurrency.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetConcurrency.java @@ -20,20 +20,21 @@ 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; import static com.google.common.base.Preconditions.checkArgument; +// TODO CASSANDRA-20791 Types of input aguments shouldn't depend on the number of arguments. Commands should be split into separate commands. @Command(name = "setconcurrency", description = "Set maximum concurrency for processing stage") -public class SetConcurrency extends NodeToolCmd +public class SetConcurrency extends AbstractCommand { - @Arguments(title = " | ", - usage = " | ", - description = "Set concurrency for processing stage", - required = true) + @CassandraUsage(usage = " | ", + description = "Set concurrency for processing stage") + @Parameters(paramLabel = " | ", + arity = "2..3", description = "Set concurrency for processing stage") private List args = new ArrayList<>(); @Override @@ -55,7 +56,7 @@ public class SetConcurrency extends NodeToolCmd { String message = e.getMessage() != null ? e.getMessage() : "invalid pool size"; probe.output().out.println("Unable to set concurrency: " + message); - System.exit(1); + throw e; } } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentCompactors.java b/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentCompactors.java index 5980e99ef8..265890830a 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentCompactors.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentCompactors.java @@ -18,16 +18,16 @@ 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 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 = "setconcurrentcompactors", description = "Set number of concurrent compactors in the system.") -public class SetConcurrentCompactors extends NodeTool.NodeToolCmd +public class SetConcurrentCompactors extends AbstractCommand { - @Arguments(title = "concurrent_compactors", usage = "", description = "Number of concurrent compactors, greater than 0.", required = true) + @Parameters(paramLabel = "concurrent_compactors", description = "Number of concurrent compactors, greater than 0.", arity = "1") private Integer concurrentCompactors = null; protected void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentViewBuilders.java b/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentViewBuilders.java index 96adf2c9b3..4ee03b8c5a 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentViewBuilders.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetConcurrentViewBuilders.java @@ -18,17 +18,16 @@ package org.apache.cassandra.tools.nodetool; -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 = "setconcurrentviewbuilders", description = "Set the number of concurrent view builders in the system") -public class SetConcurrentViewBuilders extends NodeTool.NodeToolCmd +public class SetConcurrentViewBuilders extends AbstractCommand { - @Arguments(title = "concurrent_view_builders", usage = "", description = "Number of concurrent view builders, greater than 0.", required = true) + @Parameters(paramLabel = "concurrent_view_builders", description = "Number of concurrent view builders, greater than 0.", arity = "1") private Integer concurrentViewBuilders = null; protected void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRF.java b/src/java/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRF.java index 6126bac4a4..4bf2ccc9a6 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRF.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRF.java @@ -18,15 +18,14 @@ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "setdefaultrf", description = "Sets default keyspace replication factor.") -public class SetDefaultKeyspaceRF extends NodeTool.NodeToolCmd +public class SetDefaultKeyspaceRF extends AbstractCommand { - @Arguments(title = "default_rf", usage = "", description = "Default replication factor", required = true) + @Parameters(paramLabel = "default_rf", description = "Default replication factor", arity = "1") private Integer defaultRF = null; protected void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetHintedHandoffThrottleInKB.java b/src/java/org/apache/cassandra/tools/nodetool/SetHintedHandoffThrottleInKB.java index b7b7b61e44..21c2a171da 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetHintedHandoffThrottleInKB.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetHintedHandoffThrottleInKB.java @@ -17,16 +17,14 @@ */ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "sethintedhandoffthrottlekb", description = "Set hinted handoff throttle in KiB per second, per delivery thread.") -public class SetHintedHandoffThrottleInKB extends NodeToolCmd +public class SetHintedHandoffThrottleInKB extends AbstractCommand { - @Arguments(title = "throttle_in_kb", usage = "", description = "Value in KiB per second", required = true) + @Parameters(paramLabel = "throttle_in_kb", description = "Value in KiB per second", arity = "1") private Integer throttleInKB = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetInterDCStreamThroughput.java b/src/java/org/apache/cassandra/tools/nodetool/SetInterDCStreamThroughput.java index 29465c6e56..464b226070 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetInterDCStreamThroughput.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetInterDCStreamThroughput.java @@ -17,25 +17,21 @@ */ package org.apache.cassandra.tools.nodetool; -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 picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; @Command(name = "setinterdcstreamthroughput", description = "Set the throughput cap for inter-datacenter streaming and entire SSTable inter-datacenter streaming in the system, or 0 to disable throttling") -public class SetInterDCStreamThroughput extends NodeToolCmd +public class SetInterDCStreamThroughput extends AbstractCommand { - @SuppressWarnings("UnusedDeclaration") - @Arguments(title = "inter_dc_stream_throughput", usage = "", description = "Value in megabits, 0 to disable throttling", required = true) + @Parameters(paramLabel = "inter_dc_stream_throughput", description = "Value in megabits, 0 to disable throttling", arity = "1") private int interDCStreamThroughput; - @SuppressWarnings("UnusedDeclaration") - @Option(name = { "-e", "--entire-sstable-throughput" }, description = "Set entire SSTable streaming throughput in MiB/s") + @Option(names = { "-e", "--entire-sstable-throughput" }, description = "Set entire SSTable streaming throughput in MiB/s") private boolean setEntireSSTableThroughput; - @SuppressWarnings("UnusedDeclaration") - @Option(name = { "-m", "--mib" }, description = "Set streaming throughput in MiB/s") + @Option(names = { "-m", "--mib" }, description = "Set streaming throughput in MiB/s") private boolean interDCStreamThroughputInMebibytes; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetLoggingLevel.java b/src/java/org/apache/cassandra/tools/nodetool/SetLoggingLevel.java index 66d6283cc7..dcde61f6a4 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetLoggingLevel.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetLoggingLevel.java @@ -17,10 +17,6 @@ */ 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.util.ArrayList; import java.util.Collections; import java.util.List; @@ -28,18 +24,32 @@ 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.Parameters; + +import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs; +import static org.apache.commons.lang3.StringUtils.EMPTY; @Command(name = "setlogginglevel", description = "Set the log level threshold for a given component or class. Will reset to the initial configuration if called with no parameters.") -public class SetLoggingLevel extends NodeToolCmd +public class SetLoggingLevel extends AbstractCommand { - @Arguments(usage = " ", description = "The component or class to change the level for and the log level threshold to set. Will reset to initial level if omitted. " - + "Available components: bootstrap, compaction, repair, streaming, cql, ring") + @CassandraUsage(usage = " ", description = "The component or class to change the level for and the log level threshold to set. Will reset to initial level if omitted. " + + "Available components: bootstrap, compaction, repair, streaming, cql, ring") private List args = new ArrayList<>(); + @Parameters(paramLabel = "component", description = "The component or class to change the level for. " + + "Available components: bootstrap, compaction, repair, streaming, cql, ring", arity = "0..1", index = "0") + private String component; + + @Parameters(paramLabel = "level", description = "The log level threshold to set. Will reset to initial level if omitted.", arity = "0..1", index = "1") + private String level; + @Override public void execute(NodeProbe probe) { + args = concatArgs(component, level); + String target = args.size() >= 1 ? args.get(0) : EMPTY; String level = args.size() == 2 ? args.get(1) : EMPTY; diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetMaxHintWindow.java b/src/java/org/apache/cassandra/tools/nodetool/SetMaxHintWindow.java index ace1a493c5..f73e68b8a2 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetMaxHintWindow.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetMaxHintWindow.java @@ -18,15 +18,14 @@ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "setmaxhintwindow", description = "Set the specified max hint window in ms") -public class SetMaxHintWindow extends NodeTool.NodeToolCmd +public class SetMaxHintWindow extends AbstractCommand { - @Arguments(title = "max_hint_window", usage = "", description = "Value of maxhintwindow in ms", required = true) + @Parameters(paramLabel = "max_hint_window", description = "Value of maxhintwindow in ms", arity = "1") private Integer maxHintWindow = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetSnapshotThrottle.java b/src/java/org/apache/cassandra/tools/nodetool/SetSnapshotThrottle.java index 045ccc1713..cbb5557e47 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetSnapshotThrottle.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetSnapshotThrottle.java @@ -17,15 +17,14 @@ */ package org.apache.cassandra.tools.nodetool; -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; @Command(name = "setsnapshotthrottle", description = "Set the snapshot_links_per_second cap for snapshot and clearsnapshot throttling") -public class SetSnapshotThrottle extends NodeToolCmd +public class SetSnapshotThrottle extends AbstractCommand { - @Arguments(title = "setsnapshotthrottle", usage = "", description = "Value represents hardlinks per second ( snapshot_links_per_second ) , 0 to disable throttling", required = true) + @Parameters(paramLabel = "throttle", description = "Value represents hardlinks per second ( snapshot_links_per_second ) , 0 to disable throttling", arity = "1") private Long snapshotThrottle = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetStreamThroughput.java b/src/java/org/apache/cassandra/tools/nodetool/SetStreamThroughput.java index 210a7364cc..d781a1a6ae 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetStreamThroughput.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetStreamThroughput.java @@ -17,25 +17,21 @@ */ package org.apache.cassandra.tools.nodetool; -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 picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; @Command(name = "setstreamthroughput", description = "Set throughput cap for streaming and entire SSTable streaming in the system, or 0 to disable throttling") -public class SetStreamThroughput extends NodeToolCmd +public class SetStreamThroughput extends AbstractCommand { - @SuppressWarnings("UnusedDeclaration") - @Arguments(title = "stream_throughput", usage = "", description = "Value in megabits, 0 to disable throttling", required = true) + @Parameters(paramLabel = "stream_throughput", description = "Value in megabits, 0 to disable throttling", arity = "1") private int streamThroughput; - @SuppressWarnings("UnusedDeclaration") - @Option(name = { "-e", "--entire-sstable-throughput" }, description = "Set entire SSTable streaming throughput in MiB/s") + @Option(names = { "-e", "--entire-sstable-throughput" }, description = "Set entire SSTable streaming throughput in MiB/s") private boolean setEntireSSTableThroughput; - @SuppressWarnings("UnusedDeclaration") - @Option(name = { "-m", "--mib" }, description = "Set streaming throughput in MiB/s") + @Option(names = { "-m", "--mib" }, description = "Set streaming throughput in MiB/s") private boolean streamThroughputInMebibytes; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetTimeout.java b/src/java/org/apache/cassandra/tools/nodetool/SetTimeout.java index 06b859beec..e386a09f9d 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetTimeout.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetTimeout.java @@ -17,28 +17,36 @@ */ 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.concatArgs; @Command(name = "settimeout", description = "Set the specified timeout in ms, or 0 to disable timeout") -public class SetTimeout extends NodeToolCmd +public class SetTimeout extends AbstractCommand { - @Arguments(usage = " ", description = "Timeout type followed by value in ms " + - "(0 disables socket streaming timeout). Type should be one of (" + GetTimeout.TIMEOUT_TYPES + ")", - required = true) + @CassandraUsage(usage = " ", description = "Timeout type followed by value in ms " + + "(0 disables socket streaming timeout). Type should be one of (" + GetTimeout.TIMEOUT_TYPES + ")") private List args = new ArrayList<>(); + @Parameters(paramLabel = "timeout_type", description = "Timeout type", index = "0", arity = "0..1") + private String timeoutType; + + @Parameters(paramLabel = "timeout_in_ms", description = "Timeout in ms", index = "1", arity = "0..1") + private String timeoutInMs; + @Override public void execute(NodeProbe probe) { + args = concatArgs(timeoutType, timeoutInMs); + checkArgument(args.size() == 2, "Timeout type followed by value in ms (0 disables socket streaming timeout)." + " Type should be one of (" + GetTimeout.TIMEOUT_TYPES + ")"); diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetTraceProbability.java b/src/java/org/apache/cassandra/tools/nodetool/SetTraceProbability.java index ef9f4980ba..013a7f3548 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/SetTraceProbability.java +++ b/src/java/org/apache/cassandra/tools/nodetool/SetTraceProbability.java @@ -17,17 +17,16 @@ */ 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 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 = "settraceprobability", description = "Sets the probability for tracing any given request to value. 0 disables, 1 enables for all requests, 0 is the default") -public class SetTraceProbability extends NodeToolCmd +public class SetTraceProbability extends AbstractCommand { - @Arguments(title = "trace_probability", usage = "", description = "Trace probability between 0 and 1 (ex: 0.2)", required = true) + @Parameters(paramLabel = "trace_probability", description = "Trace probability between 0 and 1 (ex: 0.2)", arity = "1") private Double traceProbability = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/Sjk.java b/src/java/org/apache/cassandra/tools/nodetool/Sjk.java index d7f7a043f6..793063ca10 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Sjk.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Sjk.java @@ -25,81 +25,69 @@ import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.Enumeration; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Stack; import java.util.jar.JarEntry; import java.util.jar.JarFile; -/* - * 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. - */ +import javax.annotation.Nullable; import javax.management.MBeanServerConnection; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterDescription; import com.beust.jcommander.Parameterized; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; import org.apache.cassandra.io.util.File; +import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.Output; import org.gridkit.jvmtool.JmxConnectionInfo; import org.gridkit.jvmtool.cli.CommandLauncher; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExecutionException; +import picocli.CommandLine.IParameterPreprocessor; +import picocli.CommandLine.Parameters; -import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; - -@Command(name = "sjk", description = "Run commands of 'Swiss Java Knife'. Run 'nodetool sjk --help' for more information.") -public class Sjk extends NodeToolCmd +@Command(name = "sjk", description = "Run commands of 'Swiss Java Knife'. Run 'nodetool sjk --help' for more information.", + preprocessor = Sjk.ParameterPreprocessor.class) +public class Sjk extends AbstractCommand { - @Arguments(description = "Arguments passed as is to 'Swiss Java Knife'.") - private List args; + @Parameters(description = "Arguments passed as is to 'Swiss Java Knife'.", arity = "0..*", index = "0..*") + private List args = new ArrayList<>(); private final Wrapper wrapper = new Wrapper(); @Override - public void runInternal() + protected boolean shouldConnect() throws ExecutionException { - wrapper.prepare(args != null ? args.toArray(new String[0]) : new String[]{"help"}, output.out, output.err); - - if (!wrapper.requiresMbeanServerConn()) - { - // SJK command does not require an MBeanServerConnection, so just invoke it - wrapper.run(null, output); - } - else - { - // invoke common nodetool handling to establish MBeanServerConnection - super.runInternal(); - } + // We want to parse the given arguments in advance to determine if the SJK command requires an MBeanServerConnection or not. + wrapper.prepare(args.isEmpty() ? new String[]{ "--help" } : args.toArray(new String[0]), output.out, output.err); + // {@code true} then the connection will be established and an {@code NodeProbe} instance will be passed to the execute method. + return wrapper.requiresMbeanServerConn(); } - public void sequenceRun(NodeProbe probe) + @Override + protected void execute(@Nullable NodeProbe probe) { - wrapper.prepare(args != null ? args.toArray(new String[0]) : new String[]{"help"}, probe.output().out, probe.output().err); - if (!wrapper.run(probe, probe.output())) - probe.failed(); + wrapper.run(probe, output); } - protected void execute(NodeProbe probe) + /** Argumets preprocessor for SJK command to pass all arguments to SJK 'as is'. */ + public static class ParameterPreprocessor implements IParameterPreprocessor { - if (!wrapper.run(probe, probe.output())) - probe.failed(); + @Override + public boolean preprocess(Stack args, CommandLine.Model.CommandSpec commandSpec, CommandLine.Model.ArgSpec argSpec, Map info) + { + // Consume all arguments and pass them to SJK. + assert commandSpec.userObject() instanceof Sjk; + if (args.isEmpty()) + return true; + + ((Sjk) commandSpec.userObject()).args.add(args.pop()); + return true; + } } /** @@ -193,7 +181,7 @@ public class Sjk extends NodeToolCmd } catch (Throwable e) { - e.printStackTrace(err); + throw new RuntimeException(e); } } @@ -204,10 +192,10 @@ public class Sjk extends NodeToolCmd parser.usage(sb, optionalCommand); else parser.usage(sb); - out.println(sb.toString()); + out.println(sb); } - public boolean run(final NodeProbe probe, final Output output) + public void run(final NodeProbe probe, final Output output) { PrintStream out = output.out; PrintStream err = output.err; @@ -218,7 +206,6 @@ public class Sjk extends NodeToolCmd if (cmd != null) cmd.run(); - return true; } catch (CommandAbortedError error) { @@ -234,15 +221,11 @@ public class Sjk extends NodeToolCmd { printUsage(parser, out, parser.getParsedCommand()); } - return true; } catch (Throwable e) { - e.printStackTrace(err); + throw new RuntimeException(e); } - - // abnormal termination - return false; } private void setJmxConnInfo(final NodeProbe probe) throws IllegalAccessException diff --git a/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java b/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java index c3392bbcf5..e789057784 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java @@ -24,38 +24,37 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.config.DurationSpec; import org.apache.cassandra.io.util.File; import org.apache.cassandra.service.snapshot.SnapshotOptions; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; import static com.google.common.collect.Iterables.toArray; import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; import static org.apache.commons.lang3.StringUtils.join; @Command(name = "snapshot", description = "Take a snapshot of specified keyspaces or a snapshot of the specified table") -public class Snapshot extends NodeToolCmd +public class Snapshot extends AbstractCommand { - @Arguments(usage = "[]", description = "List of keyspaces. By default, all keyspaces") + @Parameters(description = "List of keyspaces. By default, all keyspaces", arity = "0..*") private List keyspaces = new ArrayList<>(); - @Option(title = "table", name = {"-cf", "--column-family", "--table"}, description = "The table name (you must specify one and only one keyspace for using this option)") + @Option(paramLabel = "table", names = { "-cf", "--column-family", "--table" }, description = "The table name (you must specify one and only one keyspace for using this option)") private String table = null; - @Option(title = "tag", name = {"-t", "--tag"}, description = "The name of the snapshot") + @Option(paramLabel = "tag", names = { "-t", "--tag" }, description = "The name of the snapshot") private String snapshotName = Long.toString(currentTimeMillis()); - @Option(title = "ktlist", name = { "-kt", "--kt-list", "-kc", "--kc.list" }, description = "The list of Keyspace.table to take snapshot.(you must not specify only keyspace)") + @Option(paramLabel = "ktlist", names = { "-kt", "--kt-list", "-kc", "--kc.list" }, description = "The list of Keyspace.table to take snapshot.(you must not specify only keyspace)") private String ktList = null; - @Option(title = "skip-flush", name = {"-sf", "--skip-flush"}, description = "Do not flush memtables before snapshotting (snapshot will not contain unflushed data)") + @Option(paramLabel = "skip-flush", names = { "-sf", "--skip-flush" }, description = "Do not flush memtables before snapshotting (snapshot will not contain unflushed data)") private boolean skipFlush = false; - @Option(title = "ttl", name = {"--ttl"}, description = "Specify a TTL of created snapshot") + @Option(paramLabel = "ttl", names = { "--ttl" }, description = "Specify a TTL of created snapshot") private String ttl = null; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/Status.java b/src/java/org/apache/cassandra/tools/nodetool/Status.java index 4961757721..2f10b2ef0b 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Status.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Status.java @@ -36,44 +36,39 @@ import java.util.stream.Collectors; import com.google.common.collect.ArrayListMultimap; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.locator.EndpointSnitchInfoMBean; import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; import org.apache.cassandra.utils.LocalizeString; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; import static java.util.stream.Collectors.toMap; -@SuppressWarnings("UseOfSystemOutOrSystemErr") @Command(name = "status", description = "Print cluster information (state, load, IDs, ...)") -public class Status extends NodeToolCmd +public class Status extends WithPortDisplayAbstractCommand { - @Arguments(usage = "[]", description = "The keyspace name") + @Parameters(description = "The keyspace name", arity = "0..1") private String keyspace = null; - @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; - @Option(title = "sort", - name = { "-s", "--sort" }, - description = "Sort by one of 'ip', 'host', 'load', 'owns', 'id', 'rack', 'state' or 'token'. " + - "Default ordering is ascending for 'ip', 'host', 'id', 'token', 'rack' and descending for 'load', 'owns', 'state'. " + - "Sorting by token is possible only when cluster does not use vnodes. When using vnodes, default " + - "sorting is by id otherwise by token.", - allowedValues = { "ip", "host", "load", "owns", "id", "rack", "state", "token" }) - private String sortBy = null; + @Option(paramLabel = "sort", + names = { "-s", "--sort" }, + description = "Sort by one of 'ip', 'host', 'load', 'owns', 'id', 'rack', 'state' or 'token'. " + + "Default ordering is ascending for 'ip', 'host', 'id', 'token', 'rack' and descending for 'load', 'owns', 'state'. " + + "Sorting by token is possible only when cluster does not use vnodes. When using vnodes, default " + + "sorting is by id otherwise by token.") + private SortBy sortBy = null; - @Option(title = "sort_order", - name = { "-o", "--order" }, - description = "Sorting order: 'asc' for ascending, 'desc' for descending.", - allowedValues = { "asc", "desc" }) - private String sortOrder = null; + @Option(paramLabel = "sort_order", + names = { "-o", "--order" }, + description = "Sorting order: 'asc' for ascending, 'desc' for descending.") + private SortOrder sortOrder = null; private boolean isTokenPerNode = true; private Collection joiningNodes, leavingNodes, movingNodes, liveNodes, unreachableNodes; @@ -86,8 +81,8 @@ public class Status extends NodeToolCmd PrintStream out = probe.output().out; PrintStream errOut = probe.output().err; - SortBy sortBy = parseSortBy(this.sortBy, errOut); - SortOrder sortOrder = parseSortOrder(this.sortOrder, errOut); + SortBy sortBy = parseSortBy(this.sortBy == null ? null : this.sortBy.toString(), errOut); + SortOrder sortOrder = parseSortOrder(this.sortOrder == null ? null : this.sortOrder.toString(), errOut); joiningNodes = probe.getJoiningNodes(true); leavingNodes = probe.getLeavingNodes(true); @@ -119,16 +114,16 @@ public class Status extends NodeToolCmd catch (Exception ex) { errOut.printf("%nError: %s%n", ex.getMessage()); - System.exit(1); + throw ex; } } catch (IllegalArgumentException ex) { errOut.printf("%nError: %s%n", ex.getMessage()); - System.exit(1); + throw ex; } - SortedMap dcs = NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships); + SortedMap dcs = CommandUtils.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships); int nodesOfTokens = tokensToEndpoints.values().size(); @@ -146,12 +141,12 @@ public class Status extends NodeToolCmd else if (!isTokenPerNode && sortBy == SortBy.token) { errOut.printf("%nError: Can not sort by token when there is not token per node.%n"); - System.exit(1); + throw new IllegalArgumentException("Error: Can not sort by token when there is not token per node."); } else if (!resolveIp && sortBy == SortBy.host) { errOut.printf("%nError: Can not sort by host when there is not -r/--resolve-ip flag used.%n"); - System.exit(1); + throw new IllegalArgumentException("Error: Can not sort by host when there is not -r/--resolve-ip flag used."); } // Datacenters @@ -505,8 +500,7 @@ public class Status extends NodeToolCmd .collect(Collectors.joining(", ")); out.printf("%nError: Illegal value for -s/--sort used: '" + setSortBy + "'. Supported values are " + enabledValues + ".%n"); - System.exit(1); - return null; + throw ex; } } @@ -526,8 +520,7 @@ public class Status extends NodeToolCmd .collect(Collectors.joining(", ")); out.printf("%nError: Illegal value for -o/--order used: '" + setSortOrder + "'. Supported values are " + enabledValues + ".%n"); - System.exit(1); - return null; + throw ex; } } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/StatusAutoCompaction.java b/src/java/org/apache/cassandra/tools/nodetool/StatusAutoCompaction.java index a82f8e854f..ab12d7ef9a 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/StatusAutoCompaction.java +++ b/src/java/org/apache/cassandra/tools/nodetool/StatusAutoCompaction.java @@ -22,25 +22,36 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -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.formatter.TableBuilder; +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.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "statusautocompaction", description = "status of autocompaction of the given keyspace and table") -public class StatusAutoCompaction extends NodeToolCmd +public class StatusAutoCompaction 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 = "show_all", name = { "-a", "--all" }, description = "Show auto compaction status for each keyspace/table") + @Parameters(index = "0", description = "The keyspace followed by one or many tables", arity = "0..1") + private String keyspace; + + @Parameters(index = "1..*", description = "Tables to get autocomaction status to", arity = "0..*") + private List tables; + + @Option(paramLabel = "show_all", names = { "-a", "--all" }, description = "Show auto compaction status for each keyspace/table") private boolean showAll = false; @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/StatusBackup.java b/src/java/org/apache/cassandra/tools/nodetool/StatusBackup.java index 0d5b5271a5..675b11af79 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/StatusBackup.java +++ b/src/java/org/apache/cassandra/tools/nodetool/StatusBackup.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 = "statusbackup", description = "Status of incremental backup") -public class StatusBackup extends NodeToolCmd +public class StatusBackup extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/StatusBinary.java b/src/java/org/apache/cassandra/tools/nodetool/StatusBinary.java index 12bbdf78f5..f4c62dc331 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/StatusBinary.java +++ b/src/java/org/apache/cassandra/tools/nodetool/StatusBinary.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 = "statusbinary", description = "Status of native transport (binary protocol)") -public class StatusBinary extends NodeToolCmd +public class StatusBinary extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/StatusGossip.java b/src/java/org/apache/cassandra/tools/nodetool/StatusGossip.java index 1c4e66a7a2..a93160df01 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/StatusGossip.java +++ b/src/java/org/apache/cassandra/tools/nodetool/StatusGossip.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 = "statusgossip", description = "Status of gossip") -public class StatusGossip extends NodeToolCmd +public class StatusGossip extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/StatusHandoff.java b/src/java/org/apache/cassandra/tools/nodetool/StatusHandoff.java index 09788618ce..4bec42752f 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/StatusHandoff.java +++ b/src/java/org/apache/cassandra/tools/nodetool/StatusHandoff.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 = "statushandoff", description = "Status of storing future hints on the current node") -public class StatusHandoff extends NodeToolCmd +public class StatusHandoff extends AbstractCommand { @Override public void execute(NodeProbe probe) diff --git a/src/java/org/apache/cassandra/tools/nodetool/Stop.java b/src/java/org/apache/cassandra/tools/nodetool/Stop.java index aa05d75212..daa07491db 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Stop.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Stop.java @@ -17,27 +17,24 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; - import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; @Command(name = "stop", description = "Stop compaction") -public class Stop extends NodeToolCmd +public class Stop extends AbstractCommand { - @Arguments(title = "compaction_type", - usage = "", - description = "Supported types are COMPACTION, VALIDATION, CLEANUP, SCRUB, UPGRADE_SSTABLES, INDEX_BUILD, TOMBSTONE_COMPACTION, ANTICOMPACTION, VERIFY, VIEW_BUILD, INDEX_SUMMARY, RELOCATE, GARBAGE_COLLECT", - required = false) + @Parameters(paramLabel = "compaction_type", + description = "Supported types are COMPACTION, VALIDATION, CLEANUP, SCRUB, UPGRADE_SSTABLES, INDEX_BUILD, TOMBSTONE_COMPACTION, ANTICOMPACTION, VERIFY, VIEW_BUILD, INDEX_SUMMARY, RELOCATE, GARBAGE_COLLECT", + arity = "0..1") private OperationType compactionType = OperationType.UNKNOWN; - @Option(title = "compactionId", - name = {"-id", "--compaction-id"}, - description = "Use -id to stop a compaction by the specified id. Ids can be found in the transaction log files whose name starts with compaction_, located in the table transactions folder.", - required = false) + @Option(paramLabel = "compactionId", + names = { "-id", "--compaction-id" }, + description = "Use -id to stop a compaction by the specified id. Ids can be found in the transaction log files whose name starts with compaction_, located in the table transactions folder.", + arity = "0..1") private String compactionId = ""; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/StopDaemon.java b/src/java/org/apache/cassandra/tools/nodetool/StopDaemon.java index 6e453da936..ac44a05ba2 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/StopDaemon.java +++ b/src/java/org/apache/cassandra/tools/nodetool/StopDaemon.java @@ -17,22 +17,21 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.airline.Command; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; import org.apache.cassandra.utils.JVMStabilityInspector; +import picocli.CommandLine.Command; @Command(name = "stopdaemon", description = "Stop cassandra daemon") -public class StopDaemon extends NodeToolCmd +public class StopDaemon extends AbstractCommand { @Override public void execute(NodeProbe probe) { try { - DatabaseDescriptor.toolInitialization(); + DatabaseDescriptor.toolInitialization(false); probe.stopCassandraDaemon(); } catch (Exception e) { diff --git a/src/java/org/apache/cassandra/tools/nodetool/TableHistograms.java b/src/java/org/apache/cassandra/tools/nodetool/TableHistograms.java index 08806a07f5..6db88830c1 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/TableHistograms.java +++ b/src/java/org/apache/cassandra/tools/nodetool/TableHistograms.java @@ -17,11 +17,6 @@ */ package org.apache.cassandra.tools.nodetool; -import static com.google.common.base.Preconditions.checkArgument; -import static java.lang.String.format; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; - import java.io.PrintStream; import java.util.ArrayList; import java.util.Iterator; @@ -30,25 +25,37 @@ import java.util.Map; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.apache.commons.lang3.ArrayUtils; import org.apache.cassandra.db.ColumnFamilyStoreMBean; 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.layout.CassandraUsage; import org.apache.cassandra.utils.EstimatedHistogram; - import org.apache.cassandra.utils.Pair; -import org.apache.commons.lang3.ArrayUtils; +import picocli.CommandLine.Command; +import picocli.CommandLine.Parameters; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; +import static org.apache.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "tablehistograms", description = "Print statistic histograms for a given table") -public class TableHistograms extends NodeToolCmd +public class TableHistograms extends AbstractCommand { - @Arguments(usage = "[
| ]", description = "The keyspace and table name") + @CassandraUsage(usage = "[
| ]", description = "The keyspace and table name") private List args = new ArrayList<>(); + @Parameters(index = "0", description = "The keyspace name or keyspace and table name", arity = "0..1") + private String keyspaceTable = null; + + @Parameters(index = "1", description = "The table name", arity = "0..1") + private String table = null; + @Override public void execute(NodeProbe probe) { + args = concatArgs(keyspaceTable, table); PrintStream out = probe.output().out; Multimap tablesList = HashMultimap.create(); diff --git a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java index 0faaa32aa0..ee98cd722d 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java +++ b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java @@ -17,37 +17,43 @@ */ package org.apache.cassandra.tools.nodetool; -import java.util.*; - -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.apache.cassandra.tools.NodeProbe; -import org.apache.cassandra.tools.NodeTool.NodeToolCmd; -import org.apache.cassandra.tools.nodetool.stats.*; +import org.apache.cassandra.tools.nodetool.layout.CassandraUsage; +import org.apache.cassandra.tools.nodetool.stats.StatsHolder; +import org.apache.cassandra.tools.nodetool.stats.StatsPrinter; +import org.apache.cassandra.tools.nodetool.stats.StatsTableComparator; +import org.apache.cassandra.tools.nodetool.stats.TableStatsHolder; +import org.apache.cassandra.tools.nodetool.stats.TableStatsPrinter; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Command(name = "tablestats", description = "Print statistics on tables") -public class TableStats extends NodeToolCmd +public class TableStats extends AbstractCommand { - @Arguments(usage = "[...]", description = "List of tables (or keyspace) names") + @CassandraUsage(usage = "[...]", description = "List of tables (or keyspace) names") + @CommandLine.Parameters(paramLabel = "keyspace.table", index = "0..*", arity = "0..*", description = "List of tables (or keyspace) names") private List tableNames = new ArrayList<>(); - @Option(name = "-i", description = "Ignore the list of tables and display the remaining tables") + @Option(names = { "-i" }, description = "Ignore the list of tables and display the remaining tables") private boolean ignore = false; - @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 = "format", - name = {"-F", "--format"}, + @Option(paramLabel = "format", + names = { "-F", "--format" }, description = "Output format (json, yaml)") private String outputFormat = ""; - @Option(title = "sort_key", - name = {"-s", "--sort"}, + @Option(paramLabel = "sort_key", + names = { "-s", "--sort" }, description = "Sort tables by specified sort key (average_live_cells_per_slice_last_five_minutes, " + "average_tombstones_per_slice_last_five_minutes, bloom_filter_false_positives, " + "bloom_filter_false_ratio, bloom_filter_off_heap_memory_used, bloom_filter_space_used, " @@ -66,13 +72,13 @@ public class TableStats extends NodeToolCmd + "sai_rows_filtered, sai_total_query_timeouts, sai_total_queryable_index_ratio)") private String sortKey = ""; - @Option(title = "top", - name = {"-t", "--top"}, + @Option(paramLabel = "top", + names = { "-t", "--top" }, description = "Show only the top K tables for the sort key (specify the number K of tables to be shown") private int top = 0; - @Option(title = "sstable_location_check", - name = {"-l", "--sstable-location-check"}, + @Option(paramLabel = "sstable_location_check", + names = { "-l", "--sstable-location-check" }, description = "Check whether or not the SSTables are in the correct location.") private boolean locationCheck = false; diff --git a/src/java/org/apache/cassandra/tools/nodetool/TopPartitions.java b/src/java/org/apache/cassandra/tools/nodetool/TopPartitions.java index 35a50e7eaa..b8379301a4 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/TopPartitions.java +++ b/src/java/org/apache/cassandra/tools/nodetool/TopPartitions.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.tools.nodetool; -import io.airlift.airline.Command; +import picocli.CommandLine.Command; @Command(name = "toppartitions", description = "Sample and print the most active partitions") /** @deprecated See CASSANDRA-14436 */ diff --git a/src/java/org/apache/cassandra/tools/nodetool/TpStats.java b/src/java/org/apache/cassandra/tools/nodetool/TpStats.java index ce35b52e81..5f3b966d76 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/TpStats.java +++ b/src/java/org/apache/cassandra/tools/nodetool/TpStats.java @@ -17,24 +17,24 @@ */ 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.*; - +import org.apache.cassandra.tools.nodetool.stats.StatsHolder; +import org.apache.cassandra.tools.nodetool.stats.StatsPrinter; +import org.apache.cassandra.tools.nodetool.stats.TpStatsHolder; +import org.apache.cassandra.tools.nodetool.stats.TpStatsPrinter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; @Command(name = "tpstats", description = "Print usage statistics of thread pools") -public class TpStats extends NodeToolCmd +public class TpStats extends AbstractCommand { - @Option(title = "format", - name = {"-F", "--format"}, + @Option(paramLabel = "format", + names = { "-F", "--format" }, description = "Output format (json, yaml)") private String outputFormat = ""; - @Option(title = "verbose", - name = {"-v", "--verbose"}, + @Option(paramLabel = "verbose", + names = { "-v", "--verbose" }, description = "Display detailed metrics about thread pool's sizes") private boolean verbose = false; diff --git a/src/java/org/apache/cassandra/tools/nodetool/TruncateHints.java b/src/java/org/apache/cassandra/tools/nodetool/TruncateHints.java index 2a19d3a629..ea42e45b57 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/TruncateHints.java +++ b/src/java/org/apache/cassandra/tools/nodetool/TruncateHints.java @@ -17,17 +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 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.commons.lang3.StringUtils.EMPTY; @Command(name = "truncatehints", description = "Truncate all hints on the local node, or truncate hints for the endpoint(s) specified.") -public class TruncateHints extends NodeToolCmd +public class TruncateHints extends AbstractCommand { - @Arguments(usage = "[endpoint ... ]", description = "Endpoint address(es) to delete hints for, either ip address (\"127.0.0.1\") or hostname") + @CassandraUsage(usage = "[endpoint ... ]", description = "Endpoint address to delete hints for, either ip address (\"127.0.0.1\") or hostname") + @Parameters(index = "0", arity = "0..1", description = "Endpoint address to delete hints for, either ip address ('127.0.0.1') or hostname") private String endpoint = EMPTY; @Override diff --git a/src/java/org/apache/cassandra/tools/nodetool/UpdateCIDRGroup.java b/src/java/org/apache/cassandra/tools/nodetool/UpdateCIDRGroup.java index 36bef4ed8c..27bbb73111 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/UpdateCIDRGroup.java +++ b/src/java/org/apache/cassandra/tools/nodetool/UpdateCIDRGroup.java @@ -20,26 +20,34 @@ 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 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.concatArgs; /** * Nodetool command to insert/update a CIDR group and associated mapping in the table {@link AuthKeyspace#CIDR_GROUPS} */ @Command(name = "updatecidrgroup", description = "Insert/Update a cidr group") -public class UpdateCIDRGroup extends NodeToolCmd +public class UpdateCIDRGroup extends AbstractCommand { - @Arguments(usage = "[ ...]", description = "Requires a cidr group name, followed by one or more CIDRs separated by space") - private List args = new ArrayList<>(); + @CassandraUsage(usage = "[ ...]", description = "Requires a cidr group name, followed by one or more CIDRs separated by space") + public List args = new ArrayList<>(); + + @Parameters(index = "0", description = "Name of the CIDR group", arity = "0..1") + public String cidrGroupName; + + @Parameters(index = "1..*", description = "CIDRs separated by space") + public List cidrs; @Override public void execute(NodeProbe probe) { + args = concatArgs(cidrGroupName, cidrs); checkArgument(args.size() > 1, "updatecidrgroup command requires a cidr group name and atleast one CIDR"); String cidrGroupName = args.get(0); diff --git a/src/java/org/apache/cassandra/tools/nodetool/UpgradeSSTable.java b/src/java/org/apache/cassandra/tools/nodetool/UpgradeSSTable.java index fb881bf106..93638cd820 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/UpgradeSSTable.java +++ b/src/java/org/apache/cassandra/tools/nodetool/UpgradeSSTable.java @@ -17,9 +17,6 @@ */ 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; @@ -28,32 +25,46 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.cassandra.db.compaction.CompactionInterruptedException; 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.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "upgradesstables", description = "Rewrite sstables (for the requested tables) that are not on the current version (thus upgrading them to said current version)") -public class UpgradeSSTable extends NodeToolCmd +public class UpgradeSSTable 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 = "include_all", - name = {"-a", "--include-all-sstables"}, + @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 upgrade", arity = "0..*") + private List tables; + + @Option(paramLabel = "include_all", + names = { "-a", "--include-all-sstables" }, description = "Use -a to include all sstables, even those already on the current version") private boolean includeAll = false; - @Option(title = "max_timestamp", - name = {"-t", "--max-timestamp"}, + @Option(paramLabel = "max_timestamp", + names = { "-t", "--max-timestamp" }, description = "Use -t to compact only SSTables that have local creation time _older_ than the given timestamp") private long maxSSTableTimestamp = Long.MAX_VALUE; - @Option(title = "jobs", - name = {"-j", "--jobs"}, + @Option(paramLabel = "jobs", + names = { "-j", "--jobs" }, description = "Number of sstables to upgrade simultanously, set to 0 to use all available compaction threads") private int jobs = 2; @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/Verify.java b/src/java/org/apache/cassandra/tools/nodetool/Verify.java index 0a610b3266..8c94bc0364 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Verify.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Verify.java @@ -17,61 +17,71 @@ */ package org.apache.cassandra.tools.nodetool; -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.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.cassandra.tools.nodetool.CommandUtils.concatArgs; @Command(name = "verify", description = "Verify (check data checksum for) one or more tables") -public class Verify extends NodeToolCmd +public class Verify 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 = "extended_verify", - name = {"-e", "--extended-verify"}, + @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 upgrade", arity = "0..*") + private List tables; + + @Option(paramLabel = "extended_verify", + names = { "-e", "--extended-verify" }, description = "Verify each cell data, beyond simply checking sstable checksums") private boolean extendedVerify = false; - @Option(title = "check_version", - name = {"-c", "--check-version"}, + @Option(paramLabel = "check_version", + names = { "-c", "--check-version" }, description = "Also check that all sstables are the latest version") private boolean checkVersion = false; - @Option(title = "override-disable", - name = {"-f", "--force"}, - description = "Override disabling of verify tool - see CASSANDRA-9947 for caveats") + @Option(paramLabel = "override-disable", + names = { "-f", "--force" }, + description = "Override disabling of verify tool - see CASSANDRA-9947 for caveats") private boolean overrideDisable = false; - @Option(title = "dfp", - name = {"-d", "--dfp"}, + @Option(paramLabel = "dfp", + names = { "-d", "--dfp" }, description = "Invoke the disk failure policy if a corrupt sstable is found") private boolean diskFailurePolicy = false; - @Option(title = "repair_status_change", - name = {"-r", "--rsc"}, + @Option(paramLabel = "repair_status_change", + names = { "-r", "--rsc" }, description = "Mutate the repair status on corrupt sstables") private boolean mutateRepairStatus = false; - @Option(title = "check_owns_tokens", - name = {"-t", "--check-tokens"}, + @Option(paramLabel = "check_owns_tokens", + names = { "-t", "--check-tokens" }, description = "Verify that all tokens in sstables are owned by this node") private boolean checkOwnsTokens = false; - @Option(title = "quick", - name = {"-q", "--quick"}, - description = "Do a quick check - avoid reading all data to verify checksums") + @Option(paramLabel = "quick", + names = { "-q", "--quick" }, + description = "Do a quick check - avoid reading all data to verify checksums") private boolean quick = false; @Override public void execute(NodeProbe probe) { + args = concatArgs(keyspace, tables); PrintStream out = probe.output().out; if (!overrideDisable) { diff --git a/src/java/org/apache/cassandra/tools/nodetool/Version.java b/src/java/org/apache/cassandra/tools/nodetool/Version.java index 9b92249b3c..c279c4f3cf 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Version.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Version.java @@ -17,19 +17,18 @@ */ 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.cassandra.config.CassandraRelevantProperties.JAVA_VERSION; import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_VM_NAME; @Command(name = "version", description = "Print cassandra version") -public class Version extends NodeToolCmd +public class Version extends AbstractCommand { - @Option(title = "verbose", - name = {"-v", "--verbose"}, + @Option(paramLabel = "verbose", + names = { "-v", "--verbose" }, description = "Include additional information") private boolean verbose = false; diff --git a/src/java/org/apache/cassandra/tools/nodetool/ViewBuildStatus.java b/src/java/org/apache/cassandra/tools/nodetool/ViewBuildStatus.java index 89d35a41a2..560e13258b 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/ViewBuildStatus.java +++ b/src/java/org/apache/cassandra/tools/nodetool/ViewBuildStatus.java @@ -23,24 +23,32 @@ 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; import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; +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.concatArgs; @Command(name = "viewbuildstatus", description = "Show progress of a materialized view build") -public class ViewBuildStatus extends NodeTool.NodeToolCmd +public class ViewBuildStatus extends AbstractCommand { private final static String SUCCESS = "SUCCESS"; - @Arguments(usage = " | ", description = "The keyspace and view name") + @CassandraUsage(usage = " | ", description = "The keyspace and view name") private List args = new ArrayList<>(); + @Parameters(index = "0", description = "Keyspace or keyspace and view in format | ", arity = "0..1") + private String keyspaceView; + + @Parameters(index = "1", description = "View name", arity = "0..1") + private String view; + protected void execute(NodeProbe probe) { + args = concatArgs(keyspaceView, view); PrintStream out = probe.output().out; String keyspace = null, view = null; if (args.size() == 2) @@ -73,14 +81,15 @@ public class ViewBuildStatus extends NodeTool.NodeToolCmd builder.add(status.getKey(), status.getValue()); } - if (failed) { - out.println(String.format("%s.%s has not finished building; node status is below.", keyspace, view)); + if (failed) + { + String message = String.format("%s.%s has not finished building; node status is below.", keyspace, view); + out.println(message); out.println(); builder.printTo(out); - System.exit(1); - } else { - out.println(String.format("%s.%s has finished building", keyspace, view)); - System.exit(0); + throw new RuntimeException(message); } + else + out.println(String.format("%s.%s has finished building", keyspace, view)); } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/WithPortDisplayAbstractCommand.java b/src/java/org/apache/cassandra/tools/nodetool/WithPortDisplayAbstractCommand.java new file mode 100644 index 0000000000..fe67f126f3 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/WithPortDisplayAbstractCommand.java @@ -0,0 +1,40 @@ +/* + * 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.ParentCommand; + +/** + * Abstract class for commands that display endpoints that can be disambiguated by port number. (e.g. gossipinfo, describecluster etc.). + */ +abstract class WithPortDisplayAbstractCommand extends AbstractCommand +{ + @ParentCommand + private NodetoolCommand parent; + + /** See {@link NodetoolCommand#printPort} option. */ + protected boolean printPort; + + @Override + protected boolean shouldConnect() + { + printPort = parent.printPort; + return true; + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/layout/CassandraCliHelpLayout.java b/src/java/org/apache/cassandra/tools/nodetool/layout/CassandraCliHelpLayout.java new file mode 100644 index 0000000000..c12f615496 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/layout/CassandraCliHelpLayout.java @@ -0,0 +1,781 @@ +/* + * 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.layout; + +import org.apache.cassandra.tools.nodetool.CommandUtils; +import org.apache.cassandra.tools.nodetool.JmxConnect; +import org.apache.cassandra.tools.nodetool.NodetoolCommand; +import org.apache.cassandra.utils.Pair; +import org.apache.commons.lang3.StringUtils; +import picocli.CommandLine; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.cassandra.tools.nodetool.CommandUtils.findCassandraBackwardCompatibleArgument; +import static org.apache.cassandra.tools.nodetool.CommandUtils.sortShortestFirst; +import static org.apache.commons.lang3.ArrayUtils.isEmpty; +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_DESCRIPTION; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_DESCRIPTION_HEADING; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_END_OF_OPTIONS; +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_OPTION_LIST; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_OPTION_LIST_HEADING; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_PARAMETER_LIST; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_PARAMETER_LIST_HEADING; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_SYNOPSIS; +import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_SYNOPSIS_HEADING; + +/** + * Help factory for the Cassandra nodetool to generate the help output in the format + * of the airline help output, which is used as the default layout for the Cassandra nodetool. + *

+ * Note, that JMX connect options are always shown in the help output and are not hidden. The + * {@link JmxConnect} class is used to connect to a C* node via JMX, but the opttions are not + * part of the command hierarchy to allow reusage of the commands in other contexts. + */ +public class CassandraCliHelpLayout extends CommandLine.Help +{ + // The default width for the usage help output to match the width of + // the airline help output and minimize the divergence of layouts. + public static final int DEFAULT_USAGE_HELP_WIDTH = 88; + private static final int TOP_LEVEL_USAGE_HELP_WIDTH = 999; + private static final String DESCRIPTION_HEADING = "NAME%n"; + private static final String SYNOPSIS_HEADING = "SYNOPSIS%n"; + private static final String OPTIONS_HEADING = "OPTIONS%n"; + private static final String COMMANDS_HEADING = "COMMANDS%n"; + private static final String FOOTER_HEADING = "%n"; + private static final String SINGLE_SPACE = " "; + private static final int DESCRIPTION_INDENT = 4; + public static final int COLUMN_INDENT = 8; + public static final int SUBCOMMANDS_INDENT = 4; + public static final int SUBCOMMANDS_DESCRIPTION_INDENT_TOP_LEVEL = 3; + private static final CommandLine.Model.OptionSpec CASSANDRA_END_OF_OPTIONS_OPTION = + CommandLine.Model.OptionSpec.builder("--") + .description("This option can be used to separate command-line options from the " + + "list of argument, (useful when arguments might be mistaken for " + + "command-line options") + .arity("0") + .build(); + public static final String TOP_LEVEL_SYNOPSIS_LIST_PREFIX = "usage:"; + public static final String TOP_LEVEL_COMMAND_HEADING = "The most commonly used nodetool commands are:"; + public static final String USAGE_HELP_FOOTER = "See 'nodetool help ' for more information on a specific command."; + public static final String SYNOPSIS_SUBCOMMANDS_LABEL = " []"; + public static final String SUBCOMMAND_OPTION_TEMPLATE = "With %s option, %s"; + public static final String SUBCOMMAND_SUBHEADER = "With no arguments, Display help information"; + private static final String[] EMPTY_FOOTER = new String[0]; + + public CassandraCliHelpLayout(CommandLine.Model.CommandSpec spec, ColorScheme scheme) + { + super(spec, scheme); + } + + @Override + public String descriptionHeading(Object... params) + { + return createHeading(DESCRIPTION_HEADING, params); + } + + /** + * @param params Arguments referenced by the format specifiers in the header strings + * @return the header string. + */ + @Override + public String description(Object... params) + { + CommandLine.Model.CommandSpec spec = commandSpec(); + String fullName = spec.qualifiedName(); + + TextTable table = TextTable.forColumns(colorScheme(), + new Column(spec.usageMessage().width() - COLUMN_INDENT, COLUMN_INDENT, + Column.Overflow.WRAP)); + table.setAdjustLineBreaksForWideCJKCharacters(spec.usageMessage().adjustLineBreaksForWideCJKCharacters()); + table.indentWrappedLines = 0; + + table.addRowValues(colorScheme().commandText(fullName) + .concat(" - ") + .concat(colorScheme().text(String.join(" ", spec.usageMessage().description())))); + table.addRowValues(Ansi.OFF.new Text("", colorScheme())); + return table.toString(new StringBuilder()).toString(); + } + + @Override + public String synopsisHeading(Object... params) + { + return createHeading(SYNOPSIS_HEADING, params); + } + + /** + * This method is overridden to provide a detailed synopsis for the command and its subcommands. + *

+     * {@code
+     *     SYNOPSIS
+     *         nodetool [(-h  | --host )] [(-p  | --port )]
+     *                 [(-pp | --print-port)] [(-pw  | --password )]
+     *                 [(-pwf  | --password-file )]
+     *                 [(-u  | --username )] bootstrap 
+     *                 []
+     * }
+     * 
+ * + * @param synopsisHeadingLength the length of the synopsis heading that will be displayed on the same line + * @return The synopsis string. + */ + @Override + public String synopsis(int synopsisHeadingLength) + { + StringBuilder top = new StringBuilder(printDetailedSynopsis(commandSpec(), "", true)); + for (CommandLine sub : commandSpec().subcommands().values()) + top.append(printDetailedSynopsis(sub.getCommandSpec(), "", true)); + return top.toString(); + } + + private String printDetailedSynopsis(CommandLine.Model.CommandSpec commandSpec, + String synopsisPrefix, + boolean showEndOfOptionsDelimiter) + { + // Cassandra uses end of options delimiter in usage help. + commandSpec.usageMessage().showEndOfOptionsDelimiterInUsageHelp(showEndOfOptionsDelimiter); + + ColorScheme colorScheme = colorScheme(); + + List parentOptions = parentCommandOptionsWithJmxOptions(commandSpec); + List commandOptions = commandSpec.options(); + // Retain only the options that are not part of the command hierarchy (e.g. dynamic options). + Comparator comparator = new OptionSpecByNamesComparator(); + parentOptions.removeIf(parentOpt -> + commandOptions.stream().anyMatch(commandOpt -> comparator.compare(parentOpt, commandOpt) == 0)); + + List parentOptionsList = createCassandraSynopsisOptionsText(parentOptions); + List commandOptionsList = createCassandraSynopsisOptionsText(commandOptions); + + Ansi.Text positionalParamText = createCassandraSynopsisPositionalsText(commandSpec, colorScheme); + Ansi.Text endOfOptionsText = positionalParamText.plainString().isEmpty() ? + colorScheme.text("") : + colorScheme.text("[") + .concat(commandSpec.parser().endOfOptionsDelimiter()) + .concat("]"); + + Ansi.Text commandText = ansi().new Text(0); + if (!commandSpec.subcommands().isEmpty()) + commandText = commandText.concat(SYNOPSIS_SUBCOMMANDS_LABEL); + + int usageHelpWidth = commandSpec.usageMessage().width(); + boolean isEmptyParent = commandSpec.root() == commandSpec; + Ansi.Text rootCommandText = colorScheme.commandText(commandSpec.root().name()); + // If the command is the top-level command, use the command name as the root command text. + // Otherwise, use the fully qualified command name without the root command name. + // Example: "nodetool status" -> "status", "nodetool status thrift" -> "status thrift" + Ansi.Text mainCommandText = isEmptyParent ? colorScheme.commandText(commandSpec.name()) : + colorScheme.commandText(commandSpec.qualifiedName().replace(rootCommandText.plainString(), "").trim()); + TextTable textTable = TextTable.forColumns(colorScheme, new Column(usageHelpWidth, COLUMN_INDENT, Column.Overflow.WRAP)); + textTable.indentWrappedLines = COLUMN_INDENT; + textTable.setAdjustLineBreaksForWideCJKCharacters(commandSpec.usageMessage().adjustLineBreaksForWideCJKCharacters()); + + // Consider the following example: + // SYNOPSIS + // nodetool [(-h | --host )] [(-p | --port )] + // [(-pw | --password )] + // [(-pwf | --password-file )] + // [(-u | --username )] describecluster + // [(-pp | --print-port)] + new LineBreakingLayout(colorScheme, usageHelpWidth, textTable) + .concatItem(synopsisPrefix.isEmpty() ? rootCommandText : colorScheme.text(synopsisPrefix).concat(" ").concat(rootCommandText)) + // Print "[(-h | --host )] [(-p | --port )]" options related to the parent command. + .concatItems(parentOptionsList) + // Print "describecluster" in the same line as the parent options. + .concatItem(isEmptyParent ? colorScheme.text("") : mainCommandText) + // Print "[(-pp | --print-port)]" options related to the command itself. + .concatItems(commandOptionsList) + .concatItem(endOfOptionsText) + // All other fields added to the synopsis are left-adjusted, so we don't need to add them one by one. + .flush(positionalParamText.concat(commandText)); + + textTable.addEmptyRow(); + return textTable.toString(); + } + + private static Ansi.Text createCassandraSynopsisPositionalsText(CommandLine.Model.CommandSpec spec, + ColorScheme colorScheme) + { + List positionals = cassandraPositionals(spec); + + Pair commandArgumensSpec = findCassandraBackwardCompatibleArgument(spec.userObject()); + Ansi.Text text = colorScheme.text(""); + // If the command has a backward compatible @CassandraUsage argument, + // use it to generate the synopsis based on the old format. + if (commandArgumensSpec != null) + return colorScheme.parameterText(commandArgumensSpec.left); + + IParamLabelRenderer parameterLabelRenderer = CassandraStyleParamLabelRender.create(); + for (CommandLine.Model.PositionalParamSpec positionalParam : positionals) + { + Ansi.Text label = parameterLabelRenderer.renderParameterLabel(positionalParam, colorScheme.ansi(), colorScheme.parameterStyles()); + text = text.plainString().isEmpty() ? label : text.concat(" ").concat(label); + } + return text; + } + + private static List parentCommandOptionsWithJmxOptions(final CommandLine.Model.CommandSpec commandSpec) + { + // If the command is the help local command, no need to show the parent options. + if (commandSpec.helpCommand()) + return Collections.emptyList(); + + List hierarhy = new LinkedList<>(); + CommandLine.Model.CommandSpec curr; + CommandLine.Model.CommandSpec command = commandSpec; + while ((curr = command.parent()) != null) + { + hierarhy.add(curr); + command = curr; + } + Collections.reverse(hierarhy); + List options = new ArrayList<>(); + for (CommandLine.Model.CommandSpec spec : hierarhy) + { + for (CommandLine.Model.OptionSpec option : spec.options()) + { + // JmxConnect and NodetoolCommand options are always shown in the help output for backwards compatibility. + if (option.userObject() instanceof Field && + (((Field) option.userObject()).getDeclaringClass().equals(JmxConnect.class) || + ((Field) option.userObject()).getDeclaringClass().equals(NodetoolCommand.class))) + options.add(option); + else + { + if (option.hidden() || option.scopeType() == CommandLine.ScopeType.LOCAL) + continue; + options.add(option); + } + } + } + return options; + } + + private List createCassandraSynopsisOptionsText(List options) + { + // Cassandra uses alphabetical order for options, ordered by short name. + List optionList = new ArrayList<>(options); + optionList.sort(createShortOptionNameComparator()); + List result = new ArrayList<>(); + + ColorScheme colorScheme = colorScheme(); + IParamLabelRenderer parameterLabelRenderer = CassandraStyleParamLabelRender.create(); + + for (CommandLine.Model.OptionSpec option : optionList) + { + if (option.hidden()) + continue; + + Ansi.Text text = ansi().new Text(0); + String[] names = sortShortestFirst(option.names()); + if (names.length == 1) + { + text = text.concat("[").concat(colorScheme.optionText(names[0])) + .concat(spacedParamLabel(option, parameterLabelRenderer, colorScheme)) + .concat("]"); + } + else + { + Ansi.Text shortName = colorScheme.optionText(option.shortestName()); + Ansi.Text fullName = colorScheme.optionText(option.longestName()); + boolean isArrayOrCollection = + option.userObject() instanceof Field + && (((Field) option.userObject()).getType().isArray() || + Collection.class.isAssignableFrom(((Field) option.userObject()).getType())); + text = text.concat("[(") + .concat(shortName) + .concat(spacedParamLabel(option, parameterLabelRenderer, colorScheme)) + .concat(" | ") + .concat(fullName) + .concat(spacedParamLabel(option, parameterLabelRenderer, colorScheme)) + .concat(isArrayOrCollection ? ")...]" : ")]"); + } + + result.add(text); + } + return result; + } + + @Override + public String optionListHeading(Object... params) + { + return createHeading(OPTIONS_HEADING, params); + } + + /** + * Returns the help for the options of the current command. + *
+     * {@code
+     * OPTIONS
+     *         --help
+     *             Show usage help for the help command and exit.
+     *
+     *         --
+     *             This option can be used to separate command-line options from
+     *             the list of argument, (useful when arguments might be mistaken
+     *             for command-line options
+     * }
+     * 
+ * @return The string representation of the options. + */ + @Override + public String optionList() + { + CommandLine.Model.CommandSpec spec = commandSpec(); + List commandOptions = spec.options(); + List parentOptions = parentCommandOptionsWithJmxOptions(spec); + Comparator comparator = new OptionSpecByNamesComparator(); + parentOptions.removeIf(parentOpt -> + commandOptions.stream().anyMatch(commandOpt -> comparator.compare(parentOpt, commandOpt) == 0)); + + List uniqueOptions = Stream.concat(parentOptions.stream(), + commandOptions.stream().filter(o -> !o.hidden())) + .distinct() + .sorted(createShortOptionNameComparator()) + .collect(Collectors.toList()); + + Layout layout = cassandraSingleColumnOptionsParametersLayout(); + layout.addAllOptions(uniqueOptions, CassandraStyleParamLabelRender.create()); + return layout.toString(); + } + + @Override + public String endOfOptionsList() + { + List positionals = cassandraPositionals(commandSpec()); + if (positionals.isEmpty()) + return ""; + Layout layout = cassandraSingleColumnOptionsParametersLayout(); + layout.addOption(CASSANDRA_END_OF_OPTIONS_OPTION, CassandraStyleParamLabelRender.create()); + return layout.toString(); + } + + private Layout cassandraSingleColumnOptionsParametersLayout() + { + return new Layout(colorScheme(), configureLayoutTextTable(), new CassandraStyleOptionRenderer(), new CassandraStyleParameterRenderer()); + } + + private TextTable configureLayoutTextTable() + { + TextTable table = TextTable.forColumns(colorScheme(), new Column(commandSpec().usageMessage().width() - COLUMN_INDENT, + COLUMN_INDENT, Column.Overflow.WRAP)); + table.setAdjustLineBreaksForWideCJKCharacters(commandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters()); + table.indentWrappedLines = DESCRIPTION_INDENT; + return table; + } + + @Override + public String parameterList() + { + Pair cassandraArgument = findCassandraBackwardCompatibleArgument(commandSpec().userObject()); + List positionalParams = cassandraPositionals(commandSpec()); + TextTable table = configureLayoutTextTable(); + Layout layout = cassandraArgument == null ? + cassandraSingleColumnOptionsParametersLayout() : + new Layout(colorScheme(), + table, + new CassandraStyleOptionRenderer(), + new CassandraStyleParameterRenderer()) + { + // If the command has a backward compatible argument, use it to generate the synopsis + // based on the old format. + @Override + public void layout(CommandLine.Model.ArgSpec argSpec, Ansi.Text[][] cellValues) + { + Ansi.Text descPadding = Ansi.OFF.new Text(leadingSpaces(DESCRIPTION_INDENT), colorScheme); + cellValues[0] = new Ansi.Text[]{ colorScheme.parameterText(cassandraArgument.left) }; + cellValues[1] = new Ansi.Text[]{ descPadding.concat(colorScheme.parameterText(cassandraArgument.right)) }; + cellValues[2] = new Ansi.Text[]{ Ansi.OFF.new Text("", colorScheme) }; + for (Ansi.Text[] oneRow : cellValues) + table.addRowValues(oneRow); + } + + @Override + public void addAllPositionalParameters(List params, + IParamLabelRenderer paramLabelRenderer) + { + layout(null, new Ansi.Text[3][]); + } + }; + + layout.addAllPositionalParameters(positionalParams, CassandraStyleParamLabelRender.create()); + table.addEmptyRow(); + return layout.toString(); + } + + @Override + public String commandListHeading(Object... params) + { + if (commandSpec().subcommands().isEmpty()) + return ""; + return createHeading(COMMANDS_HEADING, params); + } + + /** + * Returns the help for the subcommands of the current command. + *
+     * {@code
+     * COMMANDS
+     *         With no arguments, Display help information
+     *
+     *         resume
+     *             Resume bootstrap streaming
+     *
+     *             With --force option, Use --force to resume bootstrap regardless of
+     *             cassandra.reset_bootstrap_progress environment variable. WARNING: This
+     *             is potentially dangerous, see CASSANDRA-17679
+     * }
+     * 
+ * @param subcommands The subcommands of the current command. + * @return The string representation of the subcommands. + */ + @Override + public String commandList(Map subcommands) + { + if (subcommands.isEmpty()) + return ""; + TextTable table = TextTable.forColumns(colorScheme(), new Column(commandSpec().usageMessage().width(), + COLUMN_INDENT, Column.Overflow.WRAP)); + table.indentWrappedLines = SUBCOMMANDS_INDENT; + table.setAdjustLineBreaksForWideCJKCharacters(commandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters()); + table.addRowValues(colorScheme().parameterText(SUBCOMMAND_SUBHEADER)); + table.addEmptyRow(); + + for (Map.Entry entry : subcommands.entrySet()) + { + CommandLine.Help help = entry.getValue(); + CommandLine.Model.UsageMessageSpec usage = help.commandSpec().usageMessage(); + String header = isEmpty(usage.header()) ? (isEmpty(usage.description()) ? "" : usage.description()[0]) : usage.header()[0]; + table.addRowValues(colorScheme().commandText(entry.getKey())); + + Ansi.Text leadingSpaces = Ansi.OFF.new Text(leadingSpaces(SUBCOMMANDS_INDENT), colorScheme()); + table.addRowValues(leadingSpaces.concat(colorScheme().text(header))); + + List optionSpecs = help.commandSpec().options(); + for (CommandLine.Model.OptionSpec optionSpec : optionSpecs) + { + if (optionSpec.hidden()) + continue; + table.addEmptyRow(); + + // Print the option description in multiple lines as it is set in the annotation. + for (int i = 0; i < optionSpec.description().length; i++) + { + if (i == 0) + { + table.addRowValues(leadingSpaces.concat( + colorScheme().optionText(String.format(SUBCOMMAND_OPTION_TEMPLATE, + optionSpec.longestName(), + colorScheme().text(optionSpec.description()[i]))))); + } + else + { + table.addRowValues(leadingSpaces.concat(colorScheme().text(optionSpec.description()[i]))); + } + } + } + } + return table.toString(); + } + + @Override + public String footerHeading(Object... params) + { + return createHeading(FOOTER_HEADING, params); + } + + @Override + public String footer(Object... params) + { + String[] footer; + if (commandSpec().parent() == null) + footer = isEmpty(commandSpec().usageMessage().footer()) ? new String[]{ USAGE_HELP_FOOTER } : + commandSpec().usageMessage().footer(); + else + footer = EMPTY_FOOTER; + + TextTable table = TextTable.forColumns(colorScheme(), new Column(commandSpec().usageMessage().width(), 0, Column.Overflow.WRAP)); + table.setAdjustLineBreaksForWideCJKCharacters(commandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters()); + table.indentWrappedLines = 0; + + for (String summaryLine : footer) + table.addRowValues(String.format(summaryLine, params)); + table.addEmptyRow(); + return table.toString(); + } + + public String topLevelCommandListHeading(Object... params) + { + return createHeading(TOP_LEVEL_COMMAND_HEADING + "%n", params); + } + + public String topLevelSynopsis(CommandLine.Help help) + { + return printDetailedSynopsis(commandSpec(), TOP_LEVEL_SYNOPSIS_LIST_PREFIX, false); + } + + /** + * Returns the help for the top-level command. This differs from the {@link #commandList(Map)} method + * in that it does not include the subcommands. + *
+     * {@code
+     * The most commonly used nodetool commands are:
+     *     abortbootstrap                      Abort a failed bootstrap
+     *     bootstrap                           Monitor/manage node's bootstrap process
+     *     cidrfilteringstats                  Print statistics on CIDR filtering
+     *     clientstats                         Print information about connected clients
+     * }
+     * 
+ * @param help The help object to use for rendering the command list. + * @return The top-level subcommands list. + */ + public String topLevelCommandList(CommandLine.Help help) + { + Map subcommands = new TreeMap<>(commandSpec().commandLine().getHelp().subcommands()); + int width = TOP_LEVEL_USAGE_HELP_WIDTH; + int commandLength = Math.min(CommandUtils.maxLength(subcommands.keySet()), width / 2); + int leadinColumnWidth = commandLength + SUBCOMMANDS_INDENT; + TextTable table = TextTable.forColumns(colorScheme(), + new Column(leadinColumnWidth, SUBCOMMANDS_INDENT, Column.Overflow.SPAN), + new Column(width - leadinColumnWidth, SUBCOMMANDS_DESCRIPTION_INDENT_TOP_LEVEL, Column.Overflow.TRUNCATE)); + table.setAdjustLineBreaksForWideCJKCharacters(commandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters()); + + for (Map.Entry entry : subcommands.entrySet()) + { + CommandLine.Help helpSubcommand = entry.getValue(); + CommandLine.Model.UsageMessageSpec usage = helpSubcommand.commandSpec().usageMessage(); + String header = isEmpty(usage.header()) ? (isEmpty(usage.description()) ? "" : usage.description()[0]) : usage.header()[0]; + Ansi.Text[] lines = colorScheme().text(header).splitLines(); + for (int i = 0; i < lines.length; i++) + table.addRowValues(i == 0 ? colorScheme().commandText(entry.getKey()) : Ansi.OFF.new Text(0), lines[i]); + } + return table.toString(); + } + + private static List cassandraPositionals(CommandLine.Model.CommandSpec commandSpec) + { + List positionals = new ArrayList<>(commandSpec.positionalParameters()); + positionals.removeIf(CommandLine.Model.ArgSpec::hidden); + return positionals; + } + + /** + * Layout for cassandra help CLI output. + * @return List of keys for the help sections. + */ + public static List cassandraHelpSectionKeys() + { + List result = new LinkedList<>(); + result.add(SECTION_KEY_HEADER_HEADING); + result.add(SECTION_KEY_HEADER); + result.add(SECTION_KEY_DESCRIPTION_HEADING); + result.add(SECTION_KEY_DESCRIPTION); + result.add(SECTION_KEY_SYNOPSIS_HEADING); + result.add(SECTION_KEY_SYNOPSIS); + result.add(SECTION_KEY_OPTION_LIST_HEADING); + result.add(SECTION_KEY_OPTION_LIST); + result.add(SECTION_KEY_END_OF_OPTIONS); + result.add(SECTION_KEY_PARAMETER_LIST_HEADING); + result.add(SECTION_KEY_PARAMETER_LIST); + result.add(SECTION_KEY_COMMAND_LIST_HEADING); + result.add(SECTION_KEY_COMMAND_LIST); + result.add(SECTION_KEY_EXIT_CODE_LIST_HEADING); + result.add(SECTION_KEY_EXIT_CODE_LIST); + result.add(SECTION_KEY_FOOTER_HEADING); + result.add(SECTION_KEY_FOOTER); + return result; + } + + /** + * Returns a string with the given number of leading spaces. + * + * @param num the number of leading spaces + * @return the string with the given number of leading spaces + */ + private static String leadingSpaces(int num) + { + return SINGLE_SPACE.repeat(num); + } + + private static Ansi.Text spacedParamLabel(CommandLine.Model.OptionSpec optionSpec, + IParamLabelRenderer parameterLabelRenderer, + ColorScheme scheme) + { + return optionSpec.typeInfo().isBoolean() ? scheme.text("") : + scheme.text(" ").concat(parameterLabelRenderer.renderParameterLabel(optionSpec, scheme.ansi(), scheme.optionParamStyles())); + } + + private static class CassandraStyleParamLabelRender implements IParamLabelRenderer + { + public static IParamLabelRenderer create() + { + return new CassandraStyleParamLabelRender(); + } + + @Override + public Ansi.Text renderParameterLabel(CommandLine.Model.ArgSpec argSpec, Ansi ansi, List styles) + { + ColorScheme colorScheme = CommandLine.Help.defaultColorScheme(ansi); + if (argSpec.equals(CASSANDRA_END_OF_OPTIONS_OPTION)) + return colorScheme.text(""); + if (argSpec instanceof CommandLine.Model.OptionSpec && argSpec.typeInfo().isBoolean()) + return colorScheme.text(""); + + if (argSpec.paramLabel().contains(" ")) + throw new IllegalArgumentException("Spaces are not allowed in paramLabel: " + argSpec.paramLabel()); + return argSpec.isOption() ? colorScheme.optionText(renderParamLabel(argSpec)) : + colorScheme.parameterText(renderParamLabel(argSpec)); + } + + private static String renderParamLabel(CommandLine.Model.ArgSpec argSpec) + { + if (argSpec.userObject() instanceof Field) + { + Field field = (Field) argSpec.userObject(); + if (StringUtils.isEmpty(argSpec.paramLabel())) + return field.getName(); + String label = argSpec.paramLabel().replace("<", "").replace(">", ""); + return '<' + label + '>'; + } + return argSpec.paramLabel(); + } + + @Override + public String separator() { return ""; } + } + + private static class CassandraStyleOptionRenderer implements IOptionRenderer + { + @Override + public Ansi.Text[][] render(CommandLine.Model.OptionSpec option, IParamLabelRenderer parameterLabelRenderer, ColorScheme scheme) + { + Ansi.Text optionText = scheme.optionText(""); + for (int i = 0; i < option.names().length; i++) + { + String name = option.names()[i]; + optionText = optionText.concat(scheme.optionText(name)) + .concat(spacedParamLabel(option, parameterLabelRenderer, scheme)) + .concat(i == option.names().length - 1 ? "" : ", "); + } + + Ansi.Text descPadding = Ansi.OFF.new Text(leadingSpaces(DESCRIPTION_INDENT), scheme); + String[] description = option.description().length == 0 ? new String[]{ "" } : option.description(); + // header, descriptions [0..*], empty line + int height = 2 + description.length; + Ansi.Text[][] result = new Ansi.Text[height][]; + result[0] = new Ansi.Text[]{ optionText }; + for (int i = 0; i < description.length; i++) + result[i + 1] = new Ansi.Text[]{ descPadding.concat(scheme.text(description[i])) }; + result[height - 1] = new Ansi.Text[]{ scheme.text("") }; + return result; + } + } + + private static class CassandraStyleParameterRenderer implements IParameterRenderer + { + @Override + public Ansi.Text[][] render(CommandLine.Model.PositionalParamSpec param, IParamLabelRenderer parameterLabelRenderer, ColorScheme scheme) + { + String descriptionString = param.description()[0]; + Ansi.Text descPadding = Ansi.OFF.new Text(leadingSpaces(DESCRIPTION_INDENT), scheme); + Ansi.Text[][] result = new Ansi.Text[3][]; + result[0] = new Ansi.Text[]{ parameterLabelRenderer.renderParameterLabel(param, scheme.ansi(), scheme.parameterStyles()) }; + result[1] = new Ansi.Text[]{ descPadding.concat(scheme.parameterText(descriptionString)) }; + result[2] = new Ansi.Text[]{ Ansi.OFF.new Text("", scheme) }; + return result; + } + } + + private static class LineBreakingLayout + { + private static final int spaceWidth = 1; + private final int width; + private final TextTable textTable; + private final Ansi.Text padding; + /** Current line being built, always less than width. */ + private Ansi.Text current; + + public LineBreakingLayout(ColorScheme colorScheme, int width, TextTable textTable) + { + this.width = width - textTable.columns()[0].indent; + this.padding = colorScheme.text(leadingSpaces(textTable.indentWrappedLines)); + this.textTable = textTable; + current = colorScheme.text(""); + } + + public LineBreakingLayout concatItems(List items) + { + for (Ansi.Text item : items) + concatItem(item); + return this; + } + + public LineBreakingLayout concatItem(Ansi.Text item) + { + if (item.plainString().isEmpty()) + return this; + + if (current.plainString().length() + spaceWidth + item.plainString().length() >= width) + { + textTable.addRowValues(current); + current = padding.concat(item); + } + else + current = current == padding || current.plainString().isEmpty() ? + current.concat(item) : + current.concat(" ").concat(item); + return this; + } + + public void flush(Ansi.Text end) + { + textTable.addRowValues(current == padding ? end : current.concat(" ").concat(end)); + } + } + + private static class OptionSpecByNamesComparator implements Comparator + { + private final Comparator comparator = createShortOptionNameComparator(); + @Override + public int compare(CommandLine.Model.OptionSpec o1, CommandLine.Model.OptionSpec o2) + { + if (Objects.deepEquals(o1.names(), o2.names())) + return 0; + return comparator.compare(o1, o2); + } + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/layout/CassandraUsage.java b/src/java/org/apache/cassandra/tools/nodetool/layout/CassandraUsage.java new file mode 100644 index 0000000000..3e9a24e4d2 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/layout/CassandraUsage.java @@ -0,0 +1,38 @@ +/* + * 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.layout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Argument annotation for Cassandra commands, used to provide a message + * for command-line arguments for backward compatibility when help is requested. + *

+ * Used only once in the command class hierarchy. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD }) +public @interface CassandraUsage +{ + String description() default ""; + String usage() default ""; +} diff --git a/test/distributed/org/apache/cassandra/distributed/impl/Instance.java b/test/distributed/org/apache/cassandra/distributed/impl/Instance.java index a74e9eb596..589cec2cd0 100644 --- a/test/distributed/org/apache/cassandra/distributed/impl/Instance.java +++ b/test/distributed/org/apache/cassandra/distributed/impl/Instance.java @@ -1208,7 +1208,7 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance public DTestNodeTool(boolean withNotifications, Output output) { - super(new InternalNodeProbeFactory(withNotifications), output); + super(new InternalNodeProbeFactory(withNotifications, output), output); internalNodeProbe = new InternalNodeProbe(withNotifications); storageProxy = internalNodeProbe.getStorageService(); storageProxy.addNotificationListener(notifications, null, null); diff --git a/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbeFactory.java b/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbeFactory.java index 1904aa7202..67b0a89a67 100644 --- a/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbeFactory.java +++ b/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbeFactory.java @@ -20,23 +20,32 @@ package org.apache.cassandra.distributed.mock.nodetool; import java.io.IOException; -import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.INodeProbeFactory; +import org.apache.cassandra.tools.NodeProbe; +import org.apache.cassandra.tools.Output; public class InternalNodeProbeFactory implements INodeProbeFactory { private final boolean withNotifications; + private final Output output; - public InternalNodeProbeFactory(boolean withNotifications) + public InternalNodeProbeFactory(boolean withNotifications, Output output) { this.withNotifications = withNotifications; + this.output = output; } public NodeProbe create(String host, int port) throws IOException { - return new InternalNodeProbe(withNotifications); + return createInternalNodeProbe(withNotifications, output); } public NodeProbe create(String host, int port, String username, String password) throws IOException { - return new InternalNodeProbe(withNotifications); + return createInternalNodeProbe(withNotifications, output); + } + + private static NodeProbe createInternalNodeProbe(boolean withNotifications, Output output) { + NodeProbe probe = new InternalNodeProbe(withNotifications); + probe.setOutput(output); + return probe; } } diff --git a/test/distributed/org/apache/cassandra/distributed/test/AbstractNodetoolStatusTest.java b/test/distributed/org/apache/cassandra/distributed/test/AbstractNodetoolStatusTest.java index 2d60a93552..f259dc10cf 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/AbstractNodetoolStatusTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/AbstractNodetoolStatusTest.java @@ -108,11 +108,11 @@ public abstract class AbstractNodetoolStatusTest extends TestBaseImpl // test illegal sort by or order NodeToolResult invalidSortBy = NODE_1.nodetoolResult("status", "--sort", "not_an_option"); invalidSortBy.asserts().failure(); - Assert.assertTrue(invalidSortBy.getStderr().contains("Illegal value for -s/--sort used: 'not_an_option'. Supported values are state, ip, host, load, owns, id, token, rack.")); + Assert.assertTrue(invalidSortBy.getStdout().contains("Invalid value for option '--sort': expected one of [state, ip, host, load, owns, id, token, rack] (case-sensitive) but was 'not_an_option'")); NodeToolResult invalidSortOrder = NODE_1.nodetoolResult("status", "--sort", "ip", "-o", "not_an_order"); invalidSortOrder.asserts().failure(); - Assert.assertTrue(invalidSortOrder.getStderr().contains("Illegal value for -o/--order used: 'not_an_order'. Supported values are asc, desc.")); + Assert.assertTrue(invalidSortOrder.getStdout().contains("Invalid value for option '--order': expected one of [asc, desc] (case-sensitive) but was 'not_an_order'")); // test order alone assertEquals(0, NODE_1.nodetool("status", "-o", "asc")); diff --git a/test/distributed/org/apache/cassandra/distributed/test/BooleanTest.java b/test/distributed/org/apache/cassandra/distributed/test/BooleanTest.java index fa6722d9a9..0b8f28279e 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/BooleanTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/BooleanTest.java @@ -25,6 +25,9 @@ import org.apache.cassandra.distributed.Cluster; import org.apache.cassandra.distributed.api.ConsistencyLevel; import org.apache.cassandra.serializers.BooleanSerializer; +/** + * @see org.apache.cassandra.tools.nodetool.GetSSTables + */ public class BooleanTest extends TestBaseImpl { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/test/GetEndpointsTest.java b/test/distributed/org/apache/cassandra/distributed/test/GetEndpointsTest.java index c2fa538335..c9c00719e5 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/GetEndpointsTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/GetEndpointsTest.java @@ -20,6 +20,7 @@ package org.apache.cassandra.distributed.test; import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; import org.junit.Test; @@ -29,6 +30,7 @@ import org.apache.cassandra.distributed.api.IInvokableInstance; import org.apache.cassandra.locator.Replicas; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.tcm.ClusterMetadata; +import org.apache.cassandra.tools.ToolRunner; import org.apache.cassandra.utils.FBUtilities; import static org.junit.Assert.assertEquals; @@ -43,11 +45,11 @@ public class GetEndpointsTest extends TestBaseImpl { for (IInvokableInstance i : cluster) { - i.runOnInstance(() -> { - List endpoints = StorageService.instance.getNaturalEndpointsWithPort("system", "compaction_history", "7d431310-43c9-11ef-bd50-53ff742309a9"); - assertEquals(1, endpoints.size()); - assertEquals(FBUtilities.getBroadcastAddressAndPort().getHostAddressAndPort(), endpoints.get(0)); - }); + ToolRunner.ToolResult tool = ToolRunner.invokeNodetoolJvmDtestIsolated(i, "-pp", "getendpoints", "system", "compaction_history", "7d431310-43c9-11ef-bd50-53ff742309a9"); + tool.asserts().success(); + List endpoints = tool.getStdout().trim().lines().collect(Collectors.toList()); + assertEquals(1, endpoints.size()); + i.runOnInstance(() -> assertEquals(FBUtilities.getBroadcastAddressAndPort().getHostAddressAndPort(), endpoints.get(0))); } } } @@ -59,19 +61,20 @@ public class GetEndpointsTest extends TestBaseImpl .withConfig(c -> c.with(Feature.NETWORK)) .start())) { + ToolRunner.ToolResult tool; for (IInvokableInstance i : cluster) { - i.runOnInstance(() -> { - List endpoints = StorageService.instance.getNaturalEndpointsWithPort("system", "local_metadata_log", "1"); - assertEquals(1, endpoints.size()); - assertEquals(FBUtilities.getBroadcastAddressAndPort().getHostAddressAndPort(), endpoints.get(0)); - }); + tool = ToolRunner.invokeNodetoolJvmDtestIsolated(i, "-pp", "getendpoints", "system", "local_metadata_log", "1"); + tool.asserts().success(); + List systemKsEndpoints = tool.getStdout().trim().lines().collect(Collectors.toList()); + assertEquals(1, systemKsEndpoints.size()); + i.runOnInstance(() -> assertEquals(FBUtilities.getBroadcastAddressAndPort().getHostAddressAndPort(), systemKsEndpoints.get(0))); - i.runOnInstance(() -> { - List endpoints = StorageService.instance.getNaturalEndpointsWithPort("system_cluster_metadata", "distributed_metadata_log", "1"); - assertEquals(1, endpoints.size()); - assertEquals(Replicas.stringify(ClusterMetadata.current().fullCMSMembersAsReplicas(), true), endpoints); - }); + tool = ToolRunner.invokeNodetoolJvmDtestIsolated(i, "-pp", "getendpoints", "system_cluster_metadata", "distributed_metadata_log", "1"); + tool.asserts().success(); + List scmEndpoints = tool.getStdout().trim().lines().collect(Collectors.toList()); + assertEquals(1, scmEndpoints.size()); + i.runOnInstance(() -> assertEquals(Replicas.stringify(ClusterMetadata.current().fullCMSMembersAsReplicas(), true), scmEndpoints)); } cluster.get(1).nodetoolResult("cms", "reconfigure", "3").asserts().success(); for (IInvokableInstance i : cluster) diff --git a/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java b/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java index b7f7c5ffb5..9c52ac92a9 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java @@ -66,6 +66,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +/** + * Test gossip related functionality. + * + * @see org.apache.cassandra.tools.nodetool.EnableGossip + * @see org.apache.cassandra.tools.nodetool.DisableGossip + */ public class GossipTest extends TestBaseImpl { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/test/HintedHandoffNodetoolTest.java b/test/distributed/org/apache/cassandra/distributed/test/HintedHandoffNodetoolTest.java index a401bd01ba..6757138089 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/HintedHandoffNodetoolTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/HintedHandoffNodetoolTest.java @@ -40,6 +40,14 @@ import static org.junit.Assert.assertTrue; /** * These tests replace the nodetool-related Python dtests in hintedhandoff_test.py. + * + * @see org.apache.cassandra.tools.nodetool.DisableHandoff + * @see org.apache.cassandra.tools.nodetool.DisableHintsForDC + * @see org.apache.cassandra.tools.nodetool.EnableHandoff + * @see org.apache.cassandra.tools.nodetool.EnableHintsForDC + * @see org.apache.cassandra.tools.nodetool.PauseHandoff + * @see org.apache.cassandra.tools.nodetool.ResumeHandoff + * @see org.apache.cassandra.tools.nodetool.StatusHandoff */ @RunWith(Parameterized.class) public class HintedHandoffNodetoolTest extends TestBaseImpl diff --git a/test/distributed/org/apache/cassandra/distributed/test/IncRepairAdminTest.java b/test/distributed/org/apache/cassandra/distributed/test/IncRepairAdminTest.java index e0f26d43d8..b94dd483b7 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/IncRepairAdminTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/IncRepairAdminTest.java @@ -50,6 +50,9 @@ import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUID; import static org.junit.Assert.assertTrue; +/** + * @see org.apache.cassandra.tools.nodetool.RepairAdmin + */ public class IncRepairAdminTest extends TestBaseImpl { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java b/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java index 304136ba1f..fd25928703 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java @@ -40,6 +40,9 @@ import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.io.sstable.format.TOCComponent; import org.apache.cassandra.io.util.File; +/** + * @see org.apache.cassandra.tools.nodetool.RelocateSSTables + */ public class MultipleDataDirectoryTest extends TestBaseImpl { private static Cluster CLUSTER; diff --git a/test/distributed/org/apache/cassandra/distributed/test/NodeToolEnableDisableBinaryTest.java b/test/distributed/org/apache/cassandra/distributed/test/NodeToolEnableDisableBinaryTest.java index 36803fbdfd..2417ad6ef5 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/NodeToolEnableDisableBinaryTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/NodeToolEnableDisableBinaryTest.java @@ -37,6 +37,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +/** + * Test for enabling and disabling binary protocol + * + * @see org.apache.cassandra.tools.nodetool.EnableBinary + * @see org.apache.cassandra.tools.nodetool.DisableBinary + */ public class NodeToolEnableDisableBinaryTest extends TestBaseImpl { private static ICluster cluster; @@ -59,78 +65,6 @@ public class NodeToolEnableDisableBinaryTest extends TestBaseImpl cluster.close(); } - @Test - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolResult tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "help", "disablebinary"); - String help = "NAME\n" + - " nodetool disablebinary - Disable native transport (binary protocol)\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] disablebinary [(-f | --force)]\n" + - "\n" + - "OPTIONS\n" + - " -f, --force\n" + - " Use -f to interrupt client requests that have already started\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - Assertions.assertThat(tool.getStdout()).isEqualTo(help); - - tool = ToolRunner.invokeNodetoolJvmDtest(cluster.get(1), "help", "enablebinary"); - help = "NAME\n" + - " nodetool enablebinary - Reenable native transport (binary protocol)\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] enablebinary\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - Assertions.assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testEnableDisableBinary() throws Throwable { diff --git a/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java b/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java index 24a65e3d4e..f85a0efe4f 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java @@ -33,6 +33,13 @@ import org.apache.cassandra.distributed.api.NodeToolResult; import static org.junit.Assert.assertEquals; +/** + * Tests for nodetool commands, including capturing console output and verifying exit codes. + * + * @see org.apache.cassandra.tools.nodetool.Flush + * @see org.apache.cassandra.tools.nodetool.Info + * @see org.apache.cassandra.tools.nodetool.SetCacheCapacity + */ public class NodeToolTest extends TestBaseImpl { private static Cluster CLUSTER; diff --git a/test/distributed/org/apache/cassandra/distributed/test/RemoveNodeTest.java b/test/distributed/org/apache/cassandra/distributed/test/RemoveNodeTest.java index cb1514b2c8..ade42b7415 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/RemoveNodeTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/RemoveNodeTest.java @@ -34,6 +34,7 @@ import org.apache.cassandra.tcm.membership.NodeId; import org.apache.cassandra.tcm.membership.NodeState; import org.apache.cassandra.tcm.sequences.SingleNodeSequences; import org.apache.cassandra.tcm.transformations.PrepareLeave; +import org.apache.cassandra.tools.nodetool.RemoveNode; import static org.apache.cassandra.distributed.shared.ClusterUtils.pauseBeforeCommit; import static org.apache.cassandra.distributed.shared.ClusterUtils.pauseBeforeEnacting; @@ -42,6 +43,11 @@ import static org.apache.cassandra.distributed.shared.ClusterUtils.unpauseEnactm import static org.apache.cassandra.distributed.shared.ClusterUtils.waitForCMSToQuiesce; import static org.junit.Assert.assertTrue; +/** + * @see org.apache.cassandra.tools.nodetool.RemoveNode + * @see org.apache.cassandra.tools.nodetool.RemoveNode.Abort + * @see RemoveNode.Status + */ public class RemoveNodeTest extends TestBaseImpl { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/test/TableEstimatesTest.java b/test/distributed/org/apache/cassandra/distributed/test/TableEstimatesTest.java index 5a660ffc24..7d01e4304e 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/TableEstimatesTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/TableEstimatesTest.java @@ -31,6 +31,9 @@ import org.apache.cassandra.distributed.api.IInstance; import org.apache.cassandra.distributed.api.QueryResult; import org.assertj.core.api.Assertions; +/** + * @see org.apache.cassandra.tools.nodetool.RefreshSizeEstimates + */ public class TableEstimatesTest extends TestBaseImpl { private static Cluster CLUSTER; diff --git a/test/distributed/org/apache/cassandra/distributed/test/UpgradeSSTablesTest.java b/test/distributed/org/apache/cassandra/distributed/test/UpgradeSSTablesTest.java index 3ec8952221..0e25d5d0e6 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/UpgradeSSTablesTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/UpgradeSSTablesTest.java @@ -48,6 +48,9 @@ import org.apache.cassandra.utils.concurrent.CountDownLatch; import static net.bytebuddy.matcher.ElementMatchers.named; import static org.apache.cassandra.utils.concurrent.CountDownLatch.newCountDownLatch; +/** + * @see org.apache.cassandra.tools.nodetool.UpgradeSSTable + */ public class UpgradeSSTablesTest extends TestBaseImpl { diff --git a/test/distributed/org/apache/cassandra/distributed/test/fql/FqlReplayDDLExclusionTest.java b/test/distributed/org/apache/cassandra/distributed/test/fql/FqlReplayDDLExclusionTest.java index daa1ae25d6..f3aecd82cc 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/fql/FqlReplayDDLExclusionTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/fql/FqlReplayDDLExclusionTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.distributed.test.fql; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -44,7 +43,6 @@ public class FqlReplayDDLExclusionTest extends TestBaseImpl @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - @Ignore @Test public void test() throws Throwable { diff --git a/test/distributed/org/apache/cassandra/distributed/test/gossip/GossipShutdownTest.java b/test/distributed/org/apache/cassandra/distributed/test/gossip/GossipShutdownTest.java index 59ac4feeeb..b6be7379e4 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/gossip/GossipShutdownTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/gossip/GossipShutdownTest.java @@ -31,6 +31,7 @@ import org.apache.cassandra.distributed.shared.ClusterUtils; import org.apache.cassandra.distributed.test.TestBaseImpl; import org.apache.cassandra.net.Verb; +/** Tests that we can shutdown gossip with the {@link org.apache.cassandra.tools.nodetool.DisableGossip} command. */ public class GossipShutdownTest extends TestBaseImpl { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/test/log/ReconfigureCMSTest.java b/test/distributed/org/apache/cassandra/distributed/test/log/ReconfigureCMSTest.java index 0f95735e5a..d0e1f358fb 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/log/ReconfigureCMSTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/log/ReconfigureCMSTest.java @@ -61,6 +61,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.psjava.util.AssertStatus.assertTrue; +/** + * @see org.apache.cassandra.tools.nodetool.CMSAdmin.ReconfigureCMS + */ public class ReconfigureCMSTest extends FuzzTestBase { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/test/ring/BootstrapTest.java b/test/distributed/org/apache/cassandra/distributed/test/ring/BootstrapTest.java index f43c459f8f..f9e92c21ef 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/ring/BootstrapTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/ring/BootstrapTest.java @@ -59,7 +59,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; - +/** + * @see org.apache.cassandra.tools.nodetool.Join + */ public class BootstrapTest extends TestBaseImpl { @Test diff --git a/test/distributed/org/apache/cassandra/distributed/upgrade/ClusterMetadataSingleNodeUpgradeTest.java b/test/distributed/org/apache/cassandra/distributed/upgrade/ClusterMetadataSingleNodeUpgradeTest.java index 6750825d43..10d6c02dcc 100644 --- a/test/distributed/org/apache/cassandra/distributed/upgrade/ClusterMetadataSingleNodeUpgradeTest.java +++ b/test/distributed/org/apache/cassandra/distributed/upgrade/ClusterMetadataSingleNodeUpgradeTest.java @@ -23,6 +23,9 @@ import org.junit.Test; import org.apache.cassandra.distributed.Constants; import org.apache.cassandra.distributed.api.Feature; +/** + * @see org.apache.cassandra.tools.nodetool.CMSAdmin.InitializeCMS + */ public class ClusterMetadataSingleNodeUpgradeTest extends UpgradeTestBase { @Test diff --git a/test/resources/nodetool/help/abortbootstrap b/test/resources/nodetool/help/abortbootstrap new file mode 100644 index 0000000000..d8ff26cb8c --- /dev/null +++ b/test/resources/nodetool/help/abortbootstrap @@ -0,0 +1,34 @@ +NAME + nodetool abortbootstrap - Abort a failed bootstrap + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] abortbootstrap [--ip ] + [--node ] + +OPTIONS + -h , --host + Node hostname or ip address + + --ip + IP of the node that failed bootstrap + + --node + Node ID of the node that failed bootstrap + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/abortdecommission b/test/resources/nodetool/help/abortdecommission new file mode 100644 index 0000000000..abbdddcf0d --- /dev/null +++ b/test/resources/nodetool/help/abortdecommission @@ -0,0 +1,31 @@ +NAME + nodetool abortdecommission - Abort an ongoing, failed decommission + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] abortdecommission + [--node ] + +OPTIONS + -h , --host + Node hostname or ip address + + --node + + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/abortmove b/test/resources/nodetool/help/abortmove new file mode 100644 index 0000000000..685864b839 --- /dev/null +++ b/test/resources/nodetool/help/abortmove @@ -0,0 +1,31 @@ +NAME + nodetool abortmove - Abort a failed move operation for this or a remote + node + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] abortmove [--node ] + +OPTIONS + -h , --host + Node hostname or ip address + + --node + + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/abortremovenode b/test/resources/nodetool/help/abortremovenode new file mode 100644 index 0000000000..bb295a09ba --- /dev/null +++ b/test/resources/nodetool/help/abortremovenode @@ -0,0 +1,31 @@ +NAME + nodetool abortremovenode - Abort a removenode command + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] abortremovenode + [--node ] + +OPTIONS + -h , --host + Node hostname or ip address + + --node + The node being removed + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/accord b/test/resources/nodetool/help/accord new file mode 100644 index 0000000000..49045d6206 --- /dev/null +++ b/test/resources/nodetool/help/accord @@ -0,0 +1,56 @@ +NAME + nodetool accord - Manage the operation of Accord + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord [] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord describe + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord mark_stale [--] + + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord mark_rejoining [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + +COMMANDS + With no arguments, Display help information + + describe + Describe current cluster metadata relating to Accord + mark_stale + Mark a replica as being stale and no longer able to participate in + durability status coordination + mark_rejoining + Mark a stale replica as being allowed to participate in durability status + coordination again diff --git a/test/resources/nodetool/help/accord$describe b/test/resources/nodetool/help/accord$describe new file mode 100644 index 0000000000..d46c1ee0e3 --- /dev/null +++ b/test/resources/nodetool/help/accord$describe @@ -0,0 +1,28 @@ +NAME + nodetool accord describe - Describe current cluster metadata relating + to Accord + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord describe + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/accord$mark_rejoining b/test/resources/nodetool/help/accord$mark_rejoining new file mode 100644 index 0000000000..e595b7d761 --- /dev/null +++ b/test/resources/nodetool/help/accord$mark_rejoining @@ -0,0 +1,37 @@ +NAME + nodetool accord mark_rejoining - Mark a stale replica as being allowed + to participate in durability status coordination again + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord mark_rejoining [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + One or more node IDs to mark no longer stale diff --git a/test/resources/nodetool/help/accord$mark_stale b/test/resources/nodetool/help/accord$mark_stale new file mode 100644 index 0000000000..67747662f8 --- /dev/null +++ b/test/resources/nodetool/help/accord$mark_stale @@ -0,0 +1,37 @@ +NAME + nodetool accord mark_stale - Mark a replica as being stale and no + longer able to participate in durability status coordination + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] accord mark_stale [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + One or more node IDs to mark stale diff --git a/test/resources/nodetool/help/altertopology b/test/resources/nodetool/help/altertopology new file mode 100644 index 0000000000..db81f300d6 --- /dev/null +++ b/test/resources/nodetool/help/altertopology @@ -0,0 +1,37 @@ +NAME + nodetool altertopology - Modify the datacenter and/or rack of one or + more nodes + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] altertopology [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + One or more node identifiers, which may be either a node id, host + id or broadcast address, each with a target dc:rack diff --git a/test/resources/nodetool/help/assassinate b/test/resources/nodetool/help/assassinate new file mode 100644 index 0000000000..1f0f0c2f48 --- /dev/null +++ b/test/resources/nodetool/help/assassinate @@ -0,0 +1,36 @@ +NAME + nodetool assassinate - Forcefully remove a dead node without + re-replicating any data. Use as a last resort if you cannot removenode + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] assassinate [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + IP address of the endpoint to assassinate diff --git a/test/resources/nodetool/help/autorepairstatus b/test/resources/nodetool/help/autorepairstatus new file mode 100644 index 0000000000..796d0986b0 --- /dev/null +++ b/test/resources/nodetool/help/autorepairstatus @@ -0,0 +1,31 @@ +NAME + nodetool autorepairstatus - Print autorepair status + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] autorepairstatus + [(-t | --repair-type )] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -t , --repair-type + Repair type + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/bootstrap b/test/resources/nodetool/help/bootstrap new file mode 100644 index 0000000000..8494acae71 --- /dev/null +++ b/test/resources/nodetool/help/bootstrap @@ -0,0 +1,43 @@ +NAME + nodetool bootstrap - Monitor/manage node's bootstrap process + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] bootstrap [] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] bootstrap resume + [(-f | --force)] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + +COMMANDS + With no arguments, Display help information + + resume + Resume bootstrap streaming + + With --force option, Use --force to resume bootstrap regardless of + cassandra.reset_bootstrap_progress environment variable. WARNING: + This is potentially dangerous, see CASSANDRA-17679 diff --git a/test/resources/nodetool/help/bootstrap$resume b/test/resources/nodetool/help/bootstrap$resume new file mode 100644 index 0000000000..b71617143d --- /dev/null +++ b/test/resources/nodetool/help/bootstrap$resume @@ -0,0 +1,33 @@ +NAME + nodetool bootstrap resume - Resume bootstrap streaming + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] bootstrap resume + [(-f | --force)] + +OPTIONS + -f, --force + Use --force to resume bootstrap regardless of + cassandra.reset_bootstrap_progress environment variable. WARNING: + This is potentially dangerous, see CASSANDRA-17679 + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cidrfilteringstats b/test/resources/nodetool/help/cidrfilteringstats new file mode 100644 index 0000000000..f66234b01b --- /dev/null +++ b/test/resources/nodetool/help/cidrfilteringstats @@ -0,0 +1,27 @@ +NAME + nodetool cidrfilteringstats - Print statistics on CIDR filtering + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cidrfilteringstats + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cleanup b/test/resources/nodetool/help/cleanup new file mode 100644 index 0000000000..965e474c69 --- /dev/null +++ b/test/resources/nodetool/help/cleanup @@ -0,0 +1,41 @@ +NAME + nodetool cleanup - Triggers the immediate cleanup of keys no longer + belonging to a node. By default, clean all keyspaces + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cleanup + [(-j | --jobs )] [--] [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -j , --jobs + Number of sstables to cleanup simultanously, set to 0 to use all + available compaction threads + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/clearsnapshot b/test/resources/nodetool/help/clearsnapshot new file mode 100644 index 0000000000..03c462ccb7 --- /dev/null +++ b/test/resources/nodetool/help/clearsnapshot @@ -0,0 +1,52 @@ +NAME + nodetool clearsnapshot - Remove the snapshot with the given name from + the given keyspaces + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] clearsnapshot [--all] + [--older-than ] + [--older-than-timestamp ] [-t ] + [--] [...] + +OPTIONS + --all + Removes all snapshots + + -h , --host + Node hostname or ip address + + --older-than + Clear snapshots older than specified time period. + + --older-than-timestamp + Clear snapshots older than specified timestamp. It has to be a + string in ISO format, for example '2022-12-03T10:15:30Z' + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -t + Remove the snapshot with a given name + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + Remove snapshots from the given keyspaces diff --git a/test/resources/nodetool/help/clientstats b/test/resources/nodetool/help/clientstats new file mode 100644 index 0000000000..d65c4208a8 --- /dev/null +++ b/test/resources/nodetool/help/clientstats @@ -0,0 +1,44 @@ +NAME + nodetool clientstats - Print information about connected clients + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] clientstats [--all] + [--by-protocol] [--clear-history] [--client-options] [--verbose] + +OPTIONS + --all + Lists all connections + + --by-protocol + Lists most recent client connections by protocol version + + --clear-history + Clear the history of connected clients + + --client-options + Lists all connections and the client options + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + --verbose + Lists all connections with additional details (client options, + authenticator-specific metadata and more) diff --git a/test/resources/nodetool/help/cms b/test/resources/nodetool/help/cms new file mode 100644 index 0000000000..d81b8f3570 --- /dev/null +++ b/test/resources/nodetool/help/cms @@ -0,0 +1,119 @@ +NAME + nodetool cms - Manage cluster metadata + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms [] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms describe + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms initialize + [(-i | --ignore )...] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms reconfigure + [(-c | --cancel)] [(-r | --resume)] [--status] [--] [] or : ... + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms snapshot + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms unregister [--] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms abortinitialization + [--initiator ] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms dumpdirectory [--tokens] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms dumplog + [--end ] [--start ] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms resumedropaccordtable + [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + +COMMANDS + With no arguments, Display help information + + describe + Describe the current Cluster Metadata Service + initialize + Upgrade from gossip and initialize CMS + + With --ignore option, Hosts to ignore due to them being down + reconfigure + Reconfigure replication factor of CMS + + With --status option, Poll status of the reconfigure command. All other + flags and arguments are ignored when this one is used. + + With --resume option, Whether or not a previously interrupted sequence + should be resumed + + With --cancel option, Cancels any in progress CMS reconfiguration + snapshot + Request a checkpointing snapshot of cluster metadata + unregister + Unregister nodes in LEFT state + abortinitialization + Abort an incomplete initialization + + With --initiator option, The address of the node where `cms initialize` was + run. + dumpdirectory + Dump the directory from the current ClusterMetadata + + With --tokens option, Include tokens in output + dumplog + Dump the metadata log + + With --start option, Start epoch + + With --end option, End epoch + resumedropaccordtable + Resume a drop accord table operation which has stalled diff --git a/test/resources/nodetool/help/cms$abortinitialization b/test/resources/nodetool/help/cms$abortinitialization new file mode 100644 index 0000000000..57c4a22ea3 --- /dev/null +++ b/test/resources/nodetool/help/cms$abortinitialization @@ -0,0 +1,31 @@ +NAME + nodetool cms abortinitialization - Abort an incomplete initialization + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms abortinitialization + [--initiator ] + +OPTIONS + -h , --host + Node hostname or ip address + + --initiator + The address of the node where `cms initialize` was run. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cms$describe b/test/resources/nodetool/help/cms$describe new file mode 100644 index 0000000000..877c48c294 --- /dev/null +++ b/test/resources/nodetool/help/cms$describe @@ -0,0 +1,27 @@ +NAME + nodetool cms describe - Describe the current Cluster Metadata Service + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms describe + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cms$dumpdirectory b/test/resources/nodetool/help/cms$dumpdirectory new file mode 100644 index 0000000000..8038cc5ed0 --- /dev/null +++ b/test/resources/nodetool/help/cms$dumpdirectory @@ -0,0 +1,31 @@ +NAME + nodetool cms dumpdirectory - Dump the directory from the current + ClusterMetadata + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms dumpdirectory [--tokens] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --tokens + Include tokens in output + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cms$dumplog b/test/resources/nodetool/help/cms$dumplog new file mode 100644 index 0000000000..9ef81da039 --- /dev/null +++ b/test/resources/nodetool/help/cms$dumplog @@ -0,0 +1,34 @@ +NAME + nodetool cms dumplog - Dump the metadata log + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms dumplog + [--end ] [--start ] + +OPTIONS + --end + End epoch + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --start + Start epoch + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cms$initialize b/test/resources/nodetool/help/cms$initialize new file mode 100644 index 0000000000..76e193fa29 --- /dev/null +++ b/test/resources/nodetool/help/cms$initialize @@ -0,0 +1,31 @@ +NAME + nodetool cms initialize - Upgrade from gossip and initialize CMS + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms initialize + [(-i | --ignore )...] + +OPTIONS + -h , --host + Node hostname or ip address + + -i , --ignore + Hosts to ignore due to them being down + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cms$reconfigure b/test/resources/nodetool/help/cms$reconfigure new file mode 100644 index 0000000000..437ec20bc8 --- /dev/null +++ b/test/resources/nodetool/help/cms$reconfigure @@ -0,0 +1,47 @@ +NAME + nodetool cms reconfigure - Reconfigure replication factor of CMS + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms reconfigure + [(-c | --cancel)] [(-r | --resume)] [--status] [--] [] or : ... + +OPTIONS + -c, --cancel + Cancels any in progress CMS reconfiguration + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -r, --resume + Whether or not a previously interrupted sequence should be resumed + + --status + Poll status of the reconfigure command. All other flags and + arguments are ignored when this one is used. + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [] or : ... + Replication factor of new CMS diff --git a/test/resources/nodetool/help/cms$resumedropaccordtable b/test/resources/nodetool/help/cms$resumedropaccordtable new file mode 100644 index 0000000000..b65601e59a --- /dev/null +++ b/test/resources/nodetool/help/cms$resumedropaccordtable @@ -0,0 +1,37 @@ +NAME + nodetool cms resumedropaccordtable - Resume a drop accord table + operation which has stalled + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms resumedropaccordtable + [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Table id of the table being dropped diff --git a/test/resources/nodetool/help/cms$snapshot b/test/resources/nodetool/help/cms$snapshot new file mode 100644 index 0000000000..ca43b6ade8 --- /dev/null +++ b/test/resources/nodetool/help/cms$snapshot @@ -0,0 +1,28 @@ +NAME + nodetool cms snapshot - Request a checkpointing snapshot of cluster + metadata + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms snapshot + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/cms$unregister b/test/resources/nodetool/help/cms$unregister new file mode 100644 index 0000000000..75dbb65323 --- /dev/null +++ b/test/resources/nodetool/help/cms$unregister @@ -0,0 +1,35 @@ +NAME + nodetool cms unregister - Unregister nodes in LEFT state + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] cms unregister [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + One or more nodeIds to unregister, they all need to be in LEFT state diff --git a/test/resources/nodetool/help/compact b/test/resources/nodetool/help/compact new file mode 100644 index 0000000000..897e758e6b --- /dev/null +++ b/test/resources/nodetool/help/compact @@ -0,0 +1,67 @@ +NAME + nodetool compact - Force a (major) compaction on one or more tables or + user-defined compaction on given SSTables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] compact + [(-et | --end-token )] + [(-j | --jobs )] [--partition ] + [(-s | --split-output)] + [(-st | --start-token )] [--user-defined] + [--] [ ...] or ... + +OPTIONS + -et , --end-token + Use -et to specify a token at which compaction range ends + (inclusive) + + -h , --host + Node hostname or ip address + + -j , --jobs + 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). + + -p , --port + Remote jmx agent port number + + --partition + String representation of the partition key + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -s, --split-output + Use -s to not create a single big file + + -st , --start-token + Use -st to specify a token at which the compaction range starts + (inclusive) + + -u , --username + Remote jmx agent username + + --user-defined + Use --user-defined to submit listed files for user-defined + compaction + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] or ... + The keyspace followed by one or many tables or list of SSTable data + files when using --user-defined diff --git a/test/resources/nodetool/help/compactionhistory b/test/resources/nodetool/help/compactionhistory new file mode 100644 index 0000000000..fab1212232 --- /dev/null +++ b/test/resources/nodetool/help/compactionhistory @@ -0,0 +1,34 @@ +NAME + nodetool compactionhistory - Print history of compaction + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] compactionhistory + [(-F | --format )] [(-H | --human-readable)] + +OPTIONS + -F , --format + Output format (json, yaml) + + -h , --host + Node hostname or ip address + + -H, --human-readable + Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/compactionstats b/test/resources/nodetool/help/compactionstats new file mode 100644 index 0000000000..6716af175a --- /dev/null +++ b/test/resources/nodetool/help/compactionstats @@ -0,0 +1,34 @@ +NAME + nodetool compactionstats - Print statistics on compactions + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] compactionstats + [(-H | --human-readable)] [(-V | --vtable)] + +OPTIONS + -h , --host + Node hostname or ip address + + -H, --human-readable + Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -V, --vtable + Display fields matching vtable output diff --git a/test/resources/nodetool/help/consensus_admin b/test/resources/nodetool/help/consensus_admin new file mode 100644 index 0000000000..749675119a --- /dev/null +++ b/test/resources/nodetool/help/consensus_admin @@ -0,0 +1,78 @@ +NAME + nodetool consensus_admin - List and mark ranges as migrating between + consensus protocols + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] consensus_admin + [] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + consensus_admin begin-migration + [(-et | --end-token )] + [(-st | --start-token )] [--] + + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + consensus_admin finish-migration + [(-et | --end-token )] + [(-st | --start-token )] [--] + + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] consensus_admin list + [(-f | --format )] [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + +COMMANDS + With no arguments, Display help information + + begin-migration + Mark the range as migrating for the specified token range and tables + + With --start-token option, Use -st to specify a token at which the repair + range starts + + With --end-token option, Use -et to specify a token at which repair range + ends + finish-migration + Complete the migration for a range that has already begun migration + + With --start-token option, Use -st to specify a token at which the repair + range starts (exclusive) + + With --end-token option, Use -et to specify a token at which repair range + ends (inclusive) + list + List migrating tables and ranges + + With --format option, Output format, YAML and JSON are the only supported + formats, default YAML, prefix with `minified-` to turn off pretty printing diff --git a/test/resources/nodetool/help/consensus_admin$begin-migration b/test/resources/nodetool/help/consensus_admin$begin-migration new file mode 100644 index 0000000000..11d7529cc0 --- /dev/null +++ b/test/resources/nodetool/help/consensus_admin$begin-migration @@ -0,0 +1,49 @@ +NAME + nodetool consensus_admin begin-migration - Mark the range as migrating + for the specified token range and tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + consensus_admin begin-migration + [(-et | --end-token )] + [(-st | --start-token )] [--] + + +OPTIONS + -et , --end-token + Use -et to specify a token at which repair range ends + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -st , --start-token + Use -st to specify a token at which the repair range starts + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace followed by one or many tables + + + The tables diff --git a/test/resources/nodetool/help/consensus_admin$finish-migration b/test/resources/nodetool/help/consensus_admin$finish-migration new file mode 100644 index 0000000000..ef0b9fbec8 --- /dev/null +++ b/test/resources/nodetool/help/consensus_admin$finish-migration @@ -0,0 +1,50 @@ +NAME + nodetool consensus_admin finish-migration - Complete the migration for + a range that has already begun migration + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + consensus_admin finish-migration + [(-et | --end-token )] + [(-st | --start-token )] [--] + + +OPTIONS + -et , --end-token + Use -et to specify a token at which repair range ends (inclusive) + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -st , --start-token + Use -st to specify a token at which the repair range starts + (exclusive) + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace followed by one or many tables + + + The tables diff --git a/test/resources/nodetool/help/consensus_admin$list b/test/resources/nodetool/help/consensus_admin$list new file mode 100644 index 0000000000..aef67a2ac5 --- /dev/null +++ b/test/resources/nodetool/help/consensus_admin$list @@ -0,0 +1,43 @@ +NAME + nodetool consensus_admin list - List migrating tables and ranges + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] consensus_admin list + [(-f | --format )] [--] + +OPTIONS + -f , --format + Output format, YAML and JSON are the only supported formats, + default YAML, prefix with `minified-` to turn off pretty printing + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace followed by one or many tables + + + The tables diff --git a/test/resources/nodetool/help/datapaths b/test/resources/nodetool/help/datapaths new file mode 100644 index 0000000000..5917977861 --- /dev/null +++ b/test/resources/nodetool/help/datapaths @@ -0,0 +1,40 @@ +NAME + nodetool datapaths - Print all directories where data of tables are + stored + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] datapaths + [(-F | --format )] [--] [...] + +OPTIONS + -F , --format + Output format (json, yaml) + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of table (or keyspace) names diff --git a/test/resources/nodetool/help/decommission b/test/resources/nodetool/help/decommission new file mode 100644 index 0000000000..d4933b11e9 --- /dev/null +++ b/test/resources/nodetool/help/decommission @@ -0,0 +1,31 @@ +NAME + nodetool decommission - Decommission the *node I am connecting to* + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] decommission [(-f | --force)] + +OPTIONS + -f, --force + Force decommission of this node even when it reduces the number of + replicas to below configured RF + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/describecluster b/test/resources/nodetool/help/describecluster new file mode 100644 index 0000000000..37cb1269b3 --- /dev/null +++ b/test/resources/nodetool/help/describecluster @@ -0,0 +1,28 @@ +NAME + nodetool describecluster - Print the name, snitch, partitioner and + schema version of a cluster + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] describecluster + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/describering b/test/resources/nodetool/help/describering new file mode 100644 index 0000000000..afc505f10b --- /dev/null +++ b/test/resources/nodetool/help/describering @@ -0,0 +1,35 @@ +NAME + nodetool describering - Shows the token ranges info of a given keyspace + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] describering [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace name diff --git a/test/resources/nodetool/help/disableauditlog b/test/resources/nodetool/help/disableauditlog new file mode 100644 index 0000000000..e1c1410ac6 --- /dev/null +++ b/test/resources/nodetool/help/disableauditlog @@ -0,0 +1,27 @@ +NAME + nodetool disableauditlog - Disable the audit log + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disableauditlog + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/disableautocompaction b/test/resources/nodetool/help/disableautocompaction new file mode 100644 index 0000000000..0d6f7ea426 --- /dev/null +++ b/test/resources/nodetool/help/disableautocompaction @@ -0,0 +1,37 @@ +NAME + nodetool disableautocompaction - Disable autocompaction for the given + keyspace and table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disableautocompaction [--] + [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/disablebackup b/test/resources/nodetool/help/disablebackup new file mode 100644 index 0000000000..8fbd12176a --- /dev/null +++ b/test/resources/nodetool/help/disablebackup @@ -0,0 +1,27 @@ +NAME + nodetool disablebackup - Disable incremental backup + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disablebackup + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/disablebinary b/test/resources/nodetool/help/disablebinary new file mode 100644 index 0000000000..bb4997e6f8 --- /dev/null +++ b/test/resources/nodetool/help/disablebinary @@ -0,0 +1,31 @@ +NAME + nodetool disablebinary - Disable native transport (binary protocol) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disablebinary + [(-f | --force)] + +OPTIONS + -f, --force + Use -f to interrupt client requests that have already started + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/disablefullquerylog b/test/resources/nodetool/help/disablefullquerylog new file mode 100644 index 0000000000..6d46dff1d8 --- /dev/null +++ b/test/resources/nodetool/help/disablefullquerylog @@ -0,0 +1,27 @@ +NAME + nodetool disablefullquerylog - Disable the full query log + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disablefullquerylog + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/disablegossip b/test/resources/nodetool/help/disablegossip new file mode 100644 index 0000000000..dc39174516 --- /dev/null +++ b/test/resources/nodetool/help/disablegossip @@ -0,0 +1,28 @@ +NAME + nodetool disablegossip - Disable gossip (effectively marking the node + down) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disablegossip + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/disablehandoff b/test/resources/nodetool/help/disablehandoff new file mode 100644 index 0000000000..c51b5fa2c8 --- /dev/null +++ b/test/resources/nodetool/help/disablehandoff @@ -0,0 +1,27 @@ +NAME + nodetool disablehandoff - Disable storing hinted handoffs + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disablehandoff + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/disablehintsfordc b/test/resources/nodetool/help/disablehintsfordc new file mode 100644 index 0000000000..be2561631d --- /dev/null +++ b/test/resources/nodetool/help/disablehintsfordc @@ -0,0 +1,36 @@ +NAME + nodetool disablehintsfordc - Disable hints for a data center + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disablehintsfordc [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The data center to disable diff --git a/test/resources/nodetool/help/disableoldprotocolversions b/test/resources/nodetool/help/disableoldprotocolversions new file mode 100644 index 0000000000..2e37bf2d0e --- /dev/null +++ b/test/resources/nodetool/help/disableoldprotocolversions @@ -0,0 +1,27 @@ +NAME + nodetool disableoldprotocolversions - Disable old protocol versions + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] disableoldprotocolversions + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/drain b/test/resources/nodetool/help/drain new file mode 100644 index 0000000000..6c062e74c4 --- /dev/null +++ b/test/resources/nodetool/help/drain @@ -0,0 +1,28 @@ +NAME + nodetool drain - Drain the node (stop accepting writes and flush all + tables) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] drain + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/dropcidrgroup b/test/resources/nodetool/help/dropcidrgroup new file mode 100644 index 0000000000..abc83659dc --- /dev/null +++ b/test/resources/nodetool/help/dropcidrgroup @@ -0,0 +1,35 @@ +NAME + nodetool dropcidrgroup - Drop an existing cidr group + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] dropcidrgroup [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Requires a cidr group name diff --git a/test/resources/nodetool/help/enableauditlog b/test/resources/nodetool/help/enableauditlog new file mode 100644 index 0000000000..86cb7a0f4c --- /dev/null +++ b/test/resources/nodetool/help/enableauditlog @@ -0,0 +1,89 @@ +NAME + nodetool enableauditlog - Enable the audit log + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enableauditlog + [--archive-command ] [--blocking ] + [--excluded-categories ] + [--excluded-keyspaces ] + [--excluded-users ] + [--included-categories ] + [--included-keyspaces ] + [--included-users ] [--logger ] + [--max-archive-retries ] + [--max-log-size ] [--max-queue-weight ] + [--roll-cycle ] + +OPTIONS + --archive-command + 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. + + --blocking + If the queue is full whether to block producers or drop samples + [true|false]. + + --excluded-categories + Comma separated list of Audit Log Categories to be excluded for + audit log. If not set the value from cassandra.yaml will be used + + --excluded-keyspaces + Comma separated list of keyspaces to be excluded for audit log. If + not set the value from cassandra.yaml will be used + + --excluded-users + Comma separated list of users to be excluded for audit log. If not + set the value from cassandra.yaml will be used + + -h , --host + Node hostname or ip address + + --included-categories + Comma separated list of Audit Log Categories to be included for + audit log. If not set the value from cassandra.yaml will be used + + --included-keyspaces + Comma separated list of keyspaces to be included for audit log. If + not set the value from cassandra.yaml will be used + + --included-users + Comma separated list of users to be included for audit log. If not + set the value from cassandra.yaml will be used + + --logger + Logger name to be used for AuditLogging. Default BinAuditLogger. If + not set the value from cassandra.yaml will be used + + --max-archive-retries + Max number of archive retries. + + --max-log-size + 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. + + --max-queue-weight + Maximum number of bytes of query data to queue to disk before + blocking or dropping samples. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --roll-cycle + How often to roll the log file (MINUTELY, HOURLY, DAILY). + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/enableautocompaction b/test/resources/nodetool/help/enableautocompaction new file mode 100644 index 0000000000..647c10f57a --- /dev/null +++ b/test/resources/nodetool/help/enableautocompaction @@ -0,0 +1,37 @@ +NAME + nodetool enableautocompaction - Enable autocompaction for the given + keyspace and table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enableautocompaction [--] + [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/enablebackup b/test/resources/nodetool/help/enablebackup new file mode 100644 index 0000000000..1a36e2710c --- /dev/null +++ b/test/resources/nodetool/help/enablebackup @@ -0,0 +1,27 @@ +NAME + nodetool enablebackup - Enable incremental backup + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enablebackup + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/enablebinary b/test/resources/nodetool/help/enablebinary new file mode 100644 index 0000000000..1485e5783d --- /dev/null +++ b/test/resources/nodetool/help/enablebinary @@ -0,0 +1,27 @@ +NAME + nodetool enablebinary - Reenable native transport (binary protocol) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enablebinary + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/enablefullquerylog b/test/resources/nodetool/help/enablefullquerylog new file mode 100644 index 0000000000..08ff8bee1d --- /dev/null +++ b/test/resources/nodetool/help/enablefullquerylog @@ -0,0 +1,61 @@ +NAME + nodetool enablefullquerylog - Enable full query logging, defaults for + the options are configured in cassandra.yaml + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enablefullquerylog + [--archive-command ] [--blocking ] + [--max-archive-retries ] + [--max-log-size ] [--max-queue-weight ] + [--path ] [--roll-cycle ] + +OPTIONS + --archive-command + 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. + + --blocking + If the queue is full whether to block producers or drop samples + [true|false]. + + -h , --host + Node hostname or ip address + + --max-archive-retries + Max number of archive retries. + + --max-log-size + 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. + + --max-queue-weight + Maximum number of bytes of query data to queue to disk before + blocking or dropping samples. + + -p , --port + Remote jmx agent port number + + --path + Path to store the full query log at. Will have it's contents + recursively deleted. + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --roll-cycle + How often to roll the log file (MINUTELY, HOURLY, DAILY). + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/enablegossip b/test/resources/nodetool/help/enablegossip new file mode 100644 index 0000000000..b2d1deef0a --- /dev/null +++ b/test/resources/nodetool/help/enablegossip @@ -0,0 +1,27 @@ +NAME + nodetool enablegossip - Reenable gossip + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enablegossip + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/enablehandoff b/test/resources/nodetool/help/enablehandoff new file mode 100644 index 0000000000..78f78aa2fc --- /dev/null +++ b/test/resources/nodetool/help/enablehandoff @@ -0,0 +1,28 @@ +NAME + nodetool enablehandoff - Reenable future hints storing on the current + node + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enablehandoff + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/enablehintsfordc b/test/resources/nodetool/help/enablehintsfordc new file mode 100644 index 0000000000..2728882a59 --- /dev/null +++ b/test/resources/nodetool/help/enablehintsfordc @@ -0,0 +1,37 @@ +NAME + nodetool enablehintsfordc - Enable hints for a data center that was + previsouly disabled + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enablehintsfordc [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The data center to enable diff --git a/test/resources/nodetool/help/enableoldprotocolversions b/test/resources/nodetool/help/enableoldprotocolversions new file mode 100644 index 0000000000..bbb43ae147 --- /dev/null +++ b/test/resources/nodetool/help/enableoldprotocolversions @@ -0,0 +1,27 @@ +NAME + nodetool enableoldprotocolversions - Enable old protocol versions + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] enableoldprotocolversions + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/failuredetector b/test/resources/nodetool/help/failuredetector new file mode 100644 index 0000000000..cc5a2286b6 --- /dev/null +++ b/test/resources/nodetool/help/failuredetector @@ -0,0 +1,28 @@ +NAME + nodetool failuredetector - Shows the failure detector information for + the cluster + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] failuredetector + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/flush b/test/resources/nodetool/help/flush new file mode 100644 index 0000000000..3d467a907e --- /dev/null +++ b/test/resources/nodetool/help/flush @@ -0,0 +1,36 @@ +NAME + nodetool flush - Flush one or more tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] flush [--] [ + ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/forcecompact b/test/resources/nodetool/help/forcecompact new file mode 100644 index 0000000000..9f0de059c4 --- /dev/null +++ b/test/resources/nodetool/help/forcecompact @@ -0,0 +1,37 @@ +NAME + nodetool forcecompact - Force a (major) compaction on a table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] forcecompact [--] [ +

] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [
] + The keyspace, table, and a list of partition keys ignoring the + gc_grace_seconds diff --git a/test/resources/nodetool/help/garbagecollect b/test/resources/nodetool/help/garbagecollect new file mode 100644 index 0000000000..1eec8a03ce --- /dev/null +++ b/test/resources/nodetool/help/garbagecollect @@ -0,0 +1,46 @@ +NAME + nodetool garbagecollect - Remove deleted data from one or more tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] garbagecollect + [(-g | --granularity )] + [(-j | --jobs )] [--] [ ...] + +OPTIONS + -g , --granularity + Granularity of garbage removal. ROW (default) removes deleted + partitions and rows, CELL also removes overwritten or deleted cells. + + -h , --host + Node hostname or ip address + + -j , --jobs + 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. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/gcstats b/test/resources/nodetool/help/gcstats new file mode 100644 index 0000000000..a0040c4bf1 --- /dev/null +++ b/test/resources/nodetool/help/gcstats @@ -0,0 +1,34 @@ +NAME + nodetool gcstats - Print GC Statistics + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] gcstats + [(-F | --format )] [(-H | --human-readable)] + +OPTIONS + -F , --format + Output format (json, yaml, table) + + -h , --host + Node hostname or ip address + + -H, --human-readable + Display gcstats with human-readable units + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getauditlog b/test/resources/nodetool/help/getauditlog new file mode 100644 index 0000000000..d69ce5bec6 --- /dev/null +++ b/test/resources/nodetool/help/getauditlog @@ -0,0 +1,28 @@ +NAME + nodetool getauditlog - Print configuration of audit log if enabled, + otherwise the configuration reflected in cassandra.yaml + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getauditlog + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getauthcacheconfig b/test/resources/nodetool/help/getauthcacheconfig new file mode 100644 index 0000000000..c2a9e5a43e --- /dev/null +++ b/test/resources/nodetool/help/getauthcacheconfig @@ -0,0 +1,31 @@ +NAME + nodetool getauthcacheconfig - Get configuration of Auth cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getauthcacheconfig + [--cache-name ] + +OPTIONS + --cache-name + Name of Auth cache (required) + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getautorepairconfig b/test/resources/nodetool/help/getautorepairconfig new file mode 100644 index 0000000000..b853ea4ab7 --- /dev/null +++ b/test/resources/nodetool/help/getautorepairconfig @@ -0,0 +1,27 @@ +NAME + nodetool getautorepairconfig - Print autorepair configurations + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getautorepairconfig + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getbatchlogreplaythrottle b/test/resources/nodetool/help/getbatchlogreplaythrottle new file mode 100644 index 0000000000..27543bb5ba --- /dev/null +++ b/test/resources/nodetool/help/getbatchlogreplaythrottle @@ -0,0 +1,29 @@ +NAME + nodetool getbatchlogreplaythrottle - Print batchlog replay throttle in + KB/s. This is reduced proportionally to the number of nodes in the + cluster. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getbatchlogreplaythrottle + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getcidrgroupsofip b/test/resources/nodetool/help/getcidrgroupsofip new file mode 100644 index 0000000000..49d6a4142b --- /dev/null +++ b/test/resources/nodetool/help/getcidrgroupsofip @@ -0,0 +1,36 @@ +NAME + nodetool getcidrgroupsofip - Print CIDR groups associated with given IP + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getcidrgroupsofip [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Requires IP address as a string diff --git a/test/resources/nodetool/help/getcolumnindexsize b/test/resources/nodetool/help/getcolumnindexsize new file mode 100644 index 0000000000..637b6318b2 --- /dev/null +++ b/test/resources/nodetool/help/getcolumnindexsize @@ -0,0 +1,28 @@ +NAME + nodetool getcolumnindexsize - Print the granularity of the collation + index of rows within a partition in KiB + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getcolumnindexsize + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getcompactionthreshold b/test/resources/nodetool/help/getcompactionthreshold new file mode 100644 index 0000000000..913eee8ae0 --- /dev/null +++ b/test/resources/nodetool/help/getcompactionthreshold @@ -0,0 +1,37 @@ +NAME + nodetool getcompactionthreshold - Print min and max compaction + thresholds for a given table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getcompactionthreshold [--] +
+ +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
+ The keyspace with a table diff --git a/test/resources/nodetool/help/getcompactionthroughput b/test/resources/nodetool/help/getcompactionthroughput new file mode 100644 index 0000000000..6e46a6a89a --- /dev/null +++ b/test/resources/nodetool/help/getcompactionthroughput @@ -0,0 +1,33 @@ +NAME + nodetool getcompactionthroughput - Print the MiB/s throughput cap for + compaction in the system as a rounded number + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getcompactionthroughput + [(-d | --precise-mib)] + +OPTIONS + -d, --precise-mib + Print the MiB/s throughput cap for compaction in the system as a + precise number (double) + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getconcurrency b/test/resources/nodetool/help/getconcurrency new file mode 100644 index 0000000000..69a1389c5e --- /dev/null +++ b/test/resources/nodetool/help/getconcurrency @@ -0,0 +1,36 @@ +NAME + nodetool getconcurrency - Get maximum concurrency for processing stages + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getconcurrency [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Optional list of stage names, otherwise display all stages diff --git a/test/resources/nodetool/help/getconcurrentcompactors b/test/resources/nodetool/help/getconcurrentcompactors new file mode 100644 index 0000000000..887d845ec1 --- /dev/null +++ b/test/resources/nodetool/help/getconcurrentcompactors @@ -0,0 +1,28 @@ +NAME + nodetool getconcurrentcompactors - Get the number of concurrent + compactors in the system. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getconcurrentcompactors + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getconcurrentviewbuilders b/test/resources/nodetool/help/getconcurrentviewbuilders new file mode 100644 index 0000000000..852f59465e --- /dev/null +++ b/test/resources/nodetool/help/getconcurrentviewbuilders @@ -0,0 +1,28 @@ +NAME + nodetool getconcurrentviewbuilders - Get the number of concurrent view + builders in the system + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getconcurrentviewbuilders + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getdefaultrf b/test/resources/nodetool/help/getdefaultrf new file mode 100644 index 0000000000..97c6df2539 --- /dev/null +++ b/test/resources/nodetool/help/getdefaultrf @@ -0,0 +1,27 @@ +NAME + nodetool getdefaultrf - Gets default keyspace replication factor. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getdefaultrf + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getendpoints b/test/resources/nodetool/help/getendpoints new file mode 100644 index 0000000000..eada8ad571 --- /dev/null +++ b/test/resources/nodetool/help/getendpoints @@ -0,0 +1,37 @@ +NAME + nodetool getendpoints - Print the end points that owns the key + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getendpoints [--] +
+ +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
+ The keyspace, the table, and the partition key for which we need to + find the endpoint diff --git a/test/resources/nodetool/help/getfullquerylog b/test/resources/nodetool/help/getfullquerylog new file mode 100644 index 0000000000..74a75b2396 --- /dev/null +++ b/test/resources/nodetool/help/getfullquerylog @@ -0,0 +1,28 @@ +NAME + nodetool getfullquerylog - Print configuration of fql if enabled, + otherwise the configuration reflected in cassandra.yaml + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getfullquerylog + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getguardrailsconfig b/test/resources/nodetool/help/getguardrailsconfig new file mode 100644 index 0000000000..8b1841b6fa --- /dev/null +++ b/test/resources/nodetool/help/getguardrailsconfig @@ -0,0 +1,47 @@ +NAME + nodetool getguardrailsconfig - Print runtime configuration of + guardrails. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getguardrailsconfig + [(-c | --category )] [--expand] + [--] + +OPTIONS + --category , -c + Category of guardrails to filter, can be one of 'values', + 'thresholds', 'flags', 'others'. + + --expand + Expand all guardrail names so they reflect their counterparts in + cassandra.yaml + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Specific name of a guardrail to get configuration of or all + guardrails if not specified. diff --git a/test/resources/nodetool/help/getinterdcstreamthroughput b/test/resources/nodetool/help/getinterdcstreamthroughput new file mode 100644 index 0000000000..dcfe3b106a --- /dev/null +++ b/test/resources/nodetool/help/getinterdcstreamthroughput @@ -0,0 +1,42 @@ +NAME + nodetool getinterdcstreamthroughput - Print the throughput cap for + inter-datacenter streaming and entire SSTable inter-datacenter + streaming in the systemin rounded megabits. For precise number, please, + use option -d + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getinterdcstreamthroughput + [(-d | --precise-mbit)] [(-e | --entire-sstable-throughput)] + [(-m | --mib)] + +OPTIONS + -d, --precise-mbit + Print the throughput cap for inter-datacenter streaming in precise + Mbits (double) + + -e, --entire-sstable-throughput + Print entire SSTable streaming throughput in MiB/s + + -h , --host + Node hostname or ip address + + -m, --mib + Print the throughput cap for inter-datacenter streaming in MiB/s + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getlogginglevels b/test/resources/nodetool/help/getlogginglevels new file mode 100644 index 0000000000..2cbf788f06 --- /dev/null +++ b/test/resources/nodetool/help/getlogginglevels @@ -0,0 +1,27 @@ +NAME + nodetool getlogginglevels - Get the runtime logging levels + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getlogginglevels + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getmaxhintwindow b/test/resources/nodetool/help/getmaxhintwindow new file mode 100644 index 0000000000..c3e36094c9 --- /dev/null +++ b/test/resources/nodetool/help/getmaxhintwindow @@ -0,0 +1,27 @@ +NAME + nodetool getmaxhintwindow - Print the max hint window in ms + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getmaxhintwindow + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getseeds b/test/resources/nodetool/help/getseeds new file mode 100644 index 0000000000..dbe6dcc828 --- /dev/null +++ b/test/resources/nodetool/help/getseeds @@ -0,0 +1,28 @@ +NAME + nodetool getseeds - Get the currently in use seed node IP list + excluding the node IP + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getseeds + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getsnapshotthrottle b/test/resources/nodetool/help/getsnapshotthrottle new file mode 100644 index 0000000000..91611e9b1e --- /dev/null +++ b/test/resources/nodetool/help/getsnapshotthrottle @@ -0,0 +1,28 @@ +NAME + nodetool getsnapshotthrottle - Print the snapshot_links_per_second + throttle for snapshot/clearsnapshot + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getsnapshotthrottle + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/getsstables b/test/resources/nodetool/help/getsstables new file mode 100644 index 0000000000..d5cf795af9 --- /dev/null +++ b/test/resources/nodetool/help/getsstables @@ -0,0 +1,44 @@ +NAME + nodetool getsstables - Print the sstable filenames that own the key + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getsstables + [(-hf | --hex-format)] [(-l | --show-levels)] [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -hf, --hex-format + Specify the key in hexadecimal string format + + -l, --show-levels + If the table is using leveled compaction the level of each sstable + will be included in the output (Default: false) + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace, the column family, and the key diff --git a/test/resources/nodetool/help/getstreamthroughput b/test/resources/nodetool/help/getstreamthroughput new file mode 100644 index 0000000000..c44e58ab9e --- /dev/null +++ b/test/resources/nodetool/help/getstreamthroughput @@ -0,0 +1,40 @@ +NAME + nodetool getstreamthroughput - Print the throughput cap for streaming + and entire SSTable streaming in the system in rounded megabits. For + precise number, please, use option -d + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] getstreamthroughput + [(-d | --precise-mbit)] [(-e | --entire-sstable-throughput)] + [(-m | --mib)] + +OPTIONS + -d, --precise-mbit + Print the throughput cap for streaming in precise Mbits (double) + + -e, --entire-sstable-throughput + Print entire SSTable streaming throughput in MiB/s + + -h , --host + Node hostname or ip address + + -m, --mib + Print the throughput cap for streaming in MiB/s + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/gettimeout b/test/resources/nodetool/help/gettimeout new file mode 100644 index 0000000000..303bb66cd5 --- /dev/null +++ b/test/resources/nodetool/help/gettimeout @@ -0,0 +1,37 @@ +NAME + nodetool gettimeout - Print the timeout of the given type in ms + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] gettimeout [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The timeout type, one of (read, range, write, counterwrite, + cascontention, truncate, internodeconnect, internodeuser, + internodestreaminguser, misc (general rpc_timeout_in_ms)) diff --git a/test/resources/nodetool/help/gettraceprobability b/test/resources/nodetool/help/gettraceprobability new file mode 100644 index 0000000000..56b8b91212 --- /dev/null +++ b/test/resources/nodetool/help/gettraceprobability @@ -0,0 +1,27 @@ +NAME + nodetool gettraceprobability - Print the current trace probability value + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] gettraceprobability + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/gossipinfo b/test/resources/nodetool/help/gossipinfo new file mode 100644 index 0000000000..adb3e9ccda --- /dev/null +++ b/test/resources/nodetool/help/gossipinfo @@ -0,0 +1,31 @@ +NAME + nodetool gossipinfo - Shows the gossip information for the cluster + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] gossipinfo + [(-r | --resolve-ip)] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -r, --resolve-ip + Show node domain names instead of IPs + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/help b/test/resources/nodetool/help/help new file mode 100644 index 0000000000..5f52ee392b --- /dev/null +++ b/test/resources/nodetool/help/help @@ -0,0 +1,14 @@ +NAME + nodetool help - Display help information + +SYNOPSIS + nodetool help [--] + +OPTIONS + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The COMMAND to display the usage help message for. diff --git a/test/resources/nodetool/help/import b/test/resources/nodetool/help/import new file mode 100644 index 0000000000..672d839a5c --- /dev/null +++ b/test/resources/nodetool/help/import @@ -0,0 +1,73 @@ +NAME + nodetool import - Import new SSTables to the system + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] import + [(-c | --no-invalidate-caches)] [(-cd | --copy-data)] + [(-e | --extended-verify)] [(-l | --keep-level)] + [(-niv | --no-index-validation)] [(-q | --quick)] + [(-r | --keep-repaired)] [(-ri | --require-index-components)] + [(-t | --no-tokens)] [(-v | --no-verify)] [--]
+ ... + +OPTIONS + -c, --no-invalidate-caches + Don't invalidate the row cache when importing + + -cd, --copy-data + Copy data from source directories instead of moving them + + -e, --extended-verify + Run an extended verify, verifying all values in the new sstables + + -h , --host + Node hostname or ip address + + -l, --keep-level + Keep the level on the new sstables + + -niv, --no-index-validation + Skip SSTable-attached index checksum validation + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -q, --quick + Do a quick import without verifying sstables, clearing row cache or + checking in which data directory to put the file + + -r, --keep-repaired + Keep any repaired information from the sstables + + -ri, --require-index-components + Require existing index components for SSTables with attached indexes + + -t, --no-tokens + Don't verify that all tokens in the new sstable are owned by the + current node + + -u , --username + Remote jmx agent username + + -v, --no-verify + Don't verify new sstables + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
... + The keyspace, table name and directories to import sstables from diff --git a/test/resources/nodetool/help/info b/test/resources/nodetool/help/info new file mode 100644 index 0000000000..4d4699dedb --- /dev/null +++ b/test/resources/nodetool/help/info @@ -0,0 +1,30 @@ +NAME + nodetool info - Print node information (uptime, load, ...) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] info [(-T | --tokens)] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -T, --tokens + Display all tokens + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/invalidatecidrpermissionscache b/test/resources/nodetool/help/invalidatecidrpermissionscache new file mode 100644 index 0000000000..ba53df15d8 --- /dev/null +++ b/test/resources/nodetool/help/invalidatecidrpermissionscache @@ -0,0 +1,37 @@ +NAME + nodetool invalidatecidrpermissionscache - Invalidate the cidr + permissions cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + invalidatecidrpermissionscache [--] [...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of roles to invalidate. By default, all roles diff --git a/test/resources/nodetool/help/invalidatecountercache b/test/resources/nodetool/help/invalidatecountercache new file mode 100644 index 0000000000..9229ca6c7d --- /dev/null +++ b/test/resources/nodetool/help/invalidatecountercache @@ -0,0 +1,27 @@ +NAME + nodetool invalidatecountercache - Invalidate the counter cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidatecountercache + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/invalidatecredentialscache b/test/resources/nodetool/help/invalidatecredentialscache new file mode 100644 index 0000000000..ed7ba1f2dc --- /dev/null +++ b/test/resources/nodetool/help/invalidatecredentialscache @@ -0,0 +1,36 @@ +NAME + nodetool invalidatecredentialscache - Invalidate the credentials cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidatecredentialscache + [--] [...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of roles to invalidate. By default, all roles diff --git a/test/resources/nodetool/help/invalidatejmxpermissionscache b/test/resources/nodetool/help/invalidatejmxpermissionscache new file mode 100644 index 0000000000..22b1a70819 --- /dev/null +++ b/test/resources/nodetool/help/invalidatejmxpermissionscache @@ -0,0 +1,37 @@ +NAME + nodetool invalidatejmxpermissionscache - Invalidate the JMX permissions + cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidatejmxpermissionscache + [--] [...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of roles to invalidate. By default, all roles diff --git a/test/resources/nodetool/help/invalidatekeycache b/test/resources/nodetool/help/invalidatekeycache new file mode 100644 index 0000000000..6ec5300e08 --- /dev/null +++ b/test/resources/nodetool/help/invalidatekeycache @@ -0,0 +1,27 @@ +NAME + nodetool invalidatekeycache - Invalidate the key cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidatekeycache + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/invalidatenetworkpermissionscache b/test/resources/nodetool/help/invalidatenetworkpermissionscache new file mode 100644 index 0000000000..9c77fb0fda --- /dev/null +++ b/test/resources/nodetool/help/invalidatenetworkpermissionscache @@ -0,0 +1,37 @@ +NAME + nodetool invalidatenetworkpermissionscache - Invalidate the network + permissions cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + invalidatenetworkpermissionscache [--] [...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of roles to invalidate. By default, all roles diff --git a/test/resources/nodetool/help/invalidatepermissionscache b/test/resources/nodetool/help/invalidatepermissionscache new file mode 100644 index 0000000000..ed9a3e7211 --- /dev/null +++ b/test/resources/nodetool/help/invalidatepermissionscache @@ -0,0 +1,77 @@ +NAME + nodetool invalidatepermissionscache - Invalidate the permissions cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidatepermissionscache + [--all-functions] [--all-keyspaces] [--all-mbeans] [--all-roles] + [--all-tables] [--function ] + [--functions-in-keyspace ] + [--keyspace ] [--mbean ] [--role ] + [--table
] [--] + +OPTIONS + --all-functions + Invalidate permissions for 'ALL FUNCTIONS' + + --all-keyspaces + Invalidate permissions for 'ALL KEYSPACES' + + --all-mbeans + Invalidate permissions for 'ALL MBEANS' + + --all-roles + Invalidate permissions for 'ALL ROLES' + + --all-tables + Invalidate permissions for 'ALL TABLES' + + --function + 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]) + + --functions-in-keyspace + Keyspace to invalidate permissions for + + -h , --host + Node hostname or ip address + + --keyspace + Keyspace to invalidate permissions for + + --mbean + MBean to invalidate permissions for + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --role + Role to invalidate permissions for + + --table
+ Table to invalidate permissions for (you must specify --keyspace + for using this option) + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + A role for which permissions to specified resources need to be + invalidated diff --git a/test/resources/nodetool/help/invalidaterolescache b/test/resources/nodetool/help/invalidaterolescache new file mode 100644 index 0000000000..fa02644439 --- /dev/null +++ b/test/resources/nodetool/help/invalidaterolescache @@ -0,0 +1,36 @@ +NAME + nodetool invalidaterolescache - Invalidate the roles cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidaterolescache [--] + [...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of roles to invalidate. By default, all roles diff --git a/test/resources/nodetool/help/invalidaterowcache b/test/resources/nodetool/help/invalidaterowcache new file mode 100644 index 0000000000..fbe3db99ae --- /dev/null +++ b/test/resources/nodetool/help/invalidaterowcache @@ -0,0 +1,27 @@ +NAME + nodetool invalidaterowcache - Invalidate the row cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] invalidaterowcache + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/join b/test/resources/nodetool/help/join new file mode 100644 index 0000000000..e2fc38c0a4 --- /dev/null +++ b/test/resources/nodetool/help/join @@ -0,0 +1,27 @@ +NAME + nodetool join - Join the ring + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] join + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/listcidrgroups b/test/resources/nodetool/help/listcidrgroups new file mode 100644 index 0000000000..99292b8e5b --- /dev/null +++ b/test/resources/nodetool/help/listcidrgroups @@ -0,0 +1,36 @@ +NAME + nodetool listcidrgroups - List existing cidr groups + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] listcidrgroups [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + LIST operation can be invoked with or without cidr group name diff --git a/test/resources/nodetool/help/listpendinghints b/test/resources/nodetool/help/listpendinghints new file mode 100644 index 0000000000..638511085e --- /dev/null +++ b/test/resources/nodetool/help/listpendinghints @@ -0,0 +1,27 @@ +NAME + nodetool listpendinghints - Print all pending hints that this node has + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] listpendinghints + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/listsnapshots b/test/resources/nodetool/help/listsnapshots new file mode 100644 index 0000000000..7dbcd04121 --- /dev/null +++ b/test/resources/nodetool/help/listsnapshots @@ -0,0 +1,49 @@ +NAME + nodetool listsnapshots - Lists all the snapshots along with the size on + disk and true size. True size is the total size of all SSTables which + are not backed up to disk. Size on disk is total size of the snapshot + on disk. Total TrueDiskSpaceUsed does not make any SSTable + deduplication. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] listsnapshots + [(-e | --ephemeral)] [(-k | --keyspace )] + [(-n | --snapshot )] [(-nt | --no-ttl)] + [(-t
| --table
)] + +OPTIONS + -e, --ephemeral + Include ephememeral snapshots + + -h , --host + Node hostname or ip address + + -k , --keyspace + Include snapshots of specified keyspace name + + -n , --snapshot + Include snapshots of specified name + + -nt, --no-ttl + Skip snapshots with TTL + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -t
, --table
+ Include snapshots of specified table name + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/move b/test/resources/nodetool/help/move new file mode 100644 index 0000000000..85df33ca93 --- /dev/null +++ b/test/resources/nodetool/help/move @@ -0,0 +1,39 @@ +NAME + nodetool move - Move node on the token ring to a new token + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] move [--resume] [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --resume + Resume an ongoing move operation + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The new token. diff --git a/test/resources/nodetool/help/netstats b/test/resources/nodetool/help/netstats new file mode 100644 index 0000000000..07354daf6b --- /dev/null +++ b/test/resources/nodetool/help/netstats @@ -0,0 +1,32 @@ +NAME + nodetool netstats - Print network information on provided host + (connecting node by default) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] netstats + [(-H | --human-readable)] + +OPTIONS + -h , --host + Node hostname or ip address + + -H, --human-readable + Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/nodetool b/test/resources/nodetool/help/nodetool new file mode 100644 index 0000000000..3162f4e3cc --- /dev/null +++ b/test/resources/nodetool/help/nodetool @@ -0,0 +1,164 @@ +usage: nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] [] + +The most commonly used nodetool commands are: + abortbootstrap Abort a failed bootstrap + abortdecommission Abort an ongoing, failed decommission + abortmove Abort a failed move operation for this or a remote node + abortremovenode Abort a removenode command + accord Manage the operation of Accord + altertopology Modify the datacenter and/or rack of one or more nodes + assassinate Forcefully remove a dead node without re-replicating any data. Use as a last resort if you cannot removenode + autorepairstatus Print autorepair status + bootstrap Monitor/manage node's bootstrap process + cidrfilteringstats Print statistics on CIDR filtering + cleanup Triggers the immediate cleanup of keys no longer belonging to a node. By default, clean all keyspaces + clearsnapshot Remove the snapshot with the given name from the given keyspaces + clientstats Print information about connected clients + cms Manage cluster metadata + compact Force a (major) compaction on one or more tables or user-defined compaction on given SSTables + compactionhistory Print history of compaction + compactionstats Print statistics on compactions + consensus_admin List and mark ranges as migrating between consensus protocols + datapaths Print all directories where data of tables are stored + decommission Decommission the *node I am connecting to* + describecluster Print the name, snitch, partitioner and schema version of a cluster + describering Shows the token ranges info of a given keyspace + disableauditlog Disable the audit log + disableautocompaction Disable autocompaction for the given keyspace and table + disablebackup Disable incremental backup + disablebinary Disable native transport (binary protocol) + disablefullquerylog Disable the full query log + disablegossip Disable gossip (effectively marking the node down) + disablehandoff Disable storing hinted handoffs + disablehintsfordc Disable hints for a data center + disableoldprotocolversions Disable old protocol versions + drain Drain the node (stop accepting writes and flush all tables) + dropcidrgroup Drop an existing cidr group + enableauditlog Enable the audit log + enableautocompaction Enable autocompaction for the given keyspace and table + enablebackup Enable incremental backup + enablebinary Reenable native transport (binary protocol) + enablefullquerylog Enable full query logging, defaults for the options are configured in cassandra.yaml + enablegossip Reenable gossip + enablehandoff Reenable future hints storing on the current node + enablehintsfordc Enable hints for a data center that was previsouly disabled + enableoldprotocolversions Enable old protocol versions + failuredetector Shows the failure detector information for the cluster + flush Flush one or more tables + forcecompact Force a (major) compaction on a table + garbagecollect Remove deleted data from one or more tables + gcstats Print GC Statistics + getauditlog Print configuration of audit log if enabled, otherwise the configuration reflected in cassandra.yaml + getauthcacheconfig Get configuration of Auth cache + getautorepairconfig Print autorepair configurations + getbatchlogreplaythrottle Print batchlog replay throttle in KB/s. This is reduced proportionally to the number of nodes in the cluster. + getcidrgroupsofip Print CIDR groups associated with given IP + getcolumnindexsize Print the granularity of the collation index of rows within a partition in KiB + getcompactionthreshold Print min and max compaction thresholds for a given table + getcompactionthroughput Print the MiB/s throughput cap for compaction in the system as a rounded number + getconcurrency Get maximum concurrency for processing stages + getconcurrentcompactors Get the number of concurrent compactors in the system. + getconcurrentviewbuilders Get the number of concurrent view builders in the system + getdefaultrf Gets default keyspace replication factor. + getendpoints Print the end points that owns the key + getfullquerylog Print configuration of fql if enabled, otherwise the configuration reflected in cassandra.yaml + getguardrailsconfig Print runtime configuration of guardrails. + getinterdcstreamthroughput Print the throughput cap for inter-datacenter streaming and entire SSTable inter-datacenter streaming in the systemin rounded megabits. For precise number, please, use option -d + getlogginglevels Get the runtime logging levels + getmaxhintwindow Print the max hint window in ms + getseeds Get the currently in use seed node IP list excluding the node IP + getsnapshotthrottle Print the snapshot_links_per_second throttle for snapshot/clearsnapshot + getsstables Print the sstable filenames that own the key + getstreamthroughput Print the throughput cap for streaming and entire SSTable streaming in the system in rounded megabits. For precise number, please, use option -d + gettimeout Print the timeout of the given type in ms + gettraceprobability Print the current trace probability value + gossipinfo Shows the gossip information for the cluster + help Display help information + import Import new SSTables to the system + info Print node information (uptime, load, ...) + invalidatecidrpermissionscache Invalidate the cidr permissions cache + invalidatecountercache Invalidate the counter cache + invalidatecredentialscache Invalidate the credentials cache + invalidatejmxpermissionscache Invalidate the JMX permissions cache + invalidatekeycache Invalidate the key cache + invalidatenetworkpermissionscache Invalidate the network permissions cache + invalidatepermissionscache Invalidate the permissions cache + invalidaterolescache Invalidate the roles cache + invalidaterowcache Invalidate the row cache + join Join the ring + listcidrgroups List existing cidr groups + listpendinghints Print all pending hints that this node has + listsnapshots Lists all the snapshots along with the size on disk and true size. True size is the total size of all SSTables which are not backed up to disk. Size on disk is total size of the snapshot on disk. Total TrueDiskSpaceUsed does not make any SSTable deduplication. + move Move node on the token ring to a new token + netstats Print network information on provided host (connecting node by default) + pausehandoff Pause hints delivery process + profileload Low footprint profiling of activity for a period of time + proxyhistograms Print statistic histograms for network operations + rangekeysample Shows the sampled keys held across all keyspaces + rebuild Rebuild data by streaming from other nodes (similarly to bootstrap) + rebuild_index A full rebuild of native secondary indexes for a given table + recompress_sstables Rewrite sstables (for the requested tables) that have compression configuration different from the current + refresh Load newly placed SSTables to the system without restart + refreshsizeestimates Refresh system.size_estimates + reloadcidrgroupscache Reload CIDR groups cache with latest entries in cidr_groups table, when CIDR authorizer is enabled + reloadlocalschema Reload local node schema from system tables + reloadseeds Reload the seed node list from the seed node provider + reloadssl Signals Cassandra to reload SSL certificates + reloadtriggers Reload trigger classes + relocatesstables Relocates sstables to the correct disk + removenode Show status of current node removal, abort removal or remove provided ID + repair Repair one or more tables + repair_admin list and fail incremental repair sessions + replaybatchlog Kick off batchlog replay and wait for finish + resetfullquerylog Stop the full query log and clean files in the configured full query log directory from cassandra.yaml as well as JMX + resetlocalschema Reset node's local schema and resync + resumehandoff Resume hints delivery process + ring Print information about the token ring + scrub Scrub (rebuild sstables for) one or more tables + setauthcacheconfig Set configuration for Auth cache + setautorepairconfig sets the autorepair configuration + setbatchlogreplaythrottle Set batchlog replay throttle in KB per second, or 0 to disable throttling. This will be reduced proportionally to the number of nodes in the cluster. + setcachecapacity Set global key, row, and counter cache capacities (in MB units) + setcachekeystosave Set number of keys saved by each cache for faster post-restart warmup. 0 to disable + setcolumnindexsize Set the granularity of the collation index of rows within a partition in KiB + setcompactionthreshold Set min and max compaction thresholds for a given table + setcompactionthroughput Set the MiB/s throughput cap for compaction in the system, or 0 to disable throttling + setconcurrency Set maximum concurrency for processing stage + setconcurrentcompactors Set number of concurrent compactors in the system. + setconcurrentviewbuilders Set the number of concurrent view builders in the system + setdefaultrf Sets default keyspace replication factor. + setguardrailsconfig Modify runtime configuration of guardrails. + sethintedhandoffthrottlekb Set hinted handoff throttle in KiB per second, per delivery thread. + setinterdcstreamthroughput Set the throughput cap for inter-datacenter streaming and entire SSTable inter-datacenter streaming in the system, or 0 to disable throttling + setlogginglevel Set the log level threshold for a given component or class. Will reset to the initial configuration if called with no parameters. + setmaxhintwindow Set the specified max hint window in ms + setsnapshotthrottle Set the snapshot_links_per_second cap for snapshot and clearsnapshot throttling + setstreamthroughput Set throughput cap for streaming and entire SSTable streaming in the system, or 0 to disable throttling + settimeout Set the specified timeout in ms, or 0 to disable timeout + settraceprobability Sets the probability for tracing any given request to value. 0 disables, 1 enables for all requests, 0 is the default + sjk Run commands of 'Swiss Java Knife'. Run 'nodetool sjk --help' for more information. + snapshot Take a snapshot of specified keyspaces or a snapshot of the specified table + sstablerepairedset Set the repaired state of SSTables for given keyspace/tables + status Print cluster information (state, load, IDs, ...) + statusautocompaction status of autocompaction of the given keyspace and table + statusbackup Status of incremental backup + statusbinary Status of native transport (binary protocol) + statusgossip Status of gossip + statushandoff Status of storing future hints on the current node + stop Stop compaction + stopdaemon Stop cassandra daemon + tablehistograms Print statistic histograms for a given table + tablestats Print statistics on tables + toppartitions Sample and print the most active partitions + tpstats Print usage statistics of thread pools + truncatehints Truncate all hints on the local node, or truncate hints for the endpoint(s) specified. + updatecidrgroup Insert/Update a cidr group + upgradesstables Rewrite sstables (for the requested tables) that are not on the current version (thus upgrading them to said current version) + verify Verify (check data checksum for) one or more tables + version Print cassandra version + viewbuildstatus Show progress of a materialized view build + +See 'nodetool help ' for more information on a specific command. diff --git a/test/resources/nodetool/help/pausehandoff b/test/resources/nodetool/help/pausehandoff new file mode 100644 index 0000000000..892f5c9e9b --- /dev/null +++ b/test/resources/nodetool/help/pausehandoff @@ -0,0 +1,27 @@ +NAME + nodetool pausehandoff - Pause hints delivery process + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] pausehandoff + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/profileload b/test/resources/nodetool/help/profileload new file mode 100644 index 0000000000..636592490c --- /dev/null +++ b/test/resources/nodetool/help/profileload @@ -0,0 +1,60 @@ +NAME + nodetool profileload - Low footprint profiling of activity for a period + of time + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] profileload [-a ] + [(-i | --interval )] [-k ] + [(-l | --list)] [-s ] [(-t | --stop)] [--] + + +OPTIONS + -a + Comma separated list of samplers to use (Default: all) + + -h , --host + Node hostname or ip address + + -i , --interval + Schedule a new job that samples every interval milliseconds + (Default: disabled) in the background + + -k + Number of the top samples to list (Default: 10) + + -l, --list + List the scheduled sampling jobs + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -s + Capacity of the sampler, higher for more accuracy (Default: 256) + + -t, --stop + Stop the scheduled sampling job identified by and + . Jobs are stopped until the last schedules complete. + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace, column family name, and duration in milliseconds + (Default: 10000) diff --git a/test/resources/nodetool/help/proxyhistograms b/test/resources/nodetool/help/proxyhistograms new file mode 100644 index 0000000000..5aa2079069 --- /dev/null +++ b/test/resources/nodetool/help/proxyhistograms @@ -0,0 +1,28 @@ +NAME + nodetool proxyhistograms - Print statistic histograms for network + operations + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] proxyhistograms + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/rangekeysample b/test/resources/nodetool/help/rangekeysample new file mode 100644 index 0000000000..dd2d7a26c4 --- /dev/null +++ b/test/resources/nodetool/help/rangekeysample @@ -0,0 +1,28 @@ +NAME + nodetool rangekeysample - Shows the sampled keys held across all + keyspaces + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] rangekeysample + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/rebuild b/test/resources/nodetool/help/rebuild new file mode 100644 index 0000000000..bdfdae5bfd --- /dev/null +++ b/test/resources/nodetool/help/rebuild @@ -0,0 +1,58 @@ +NAME + nodetool rebuild - Rebuild data by streaming from other nodes + (similarly to bootstrap) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] rebuild [--exclude-local-dc] + [(-ks | --keyspace )] + [(-s | --sources )] + [(-ts | --tokens )] [--] + + +OPTIONS + --exclude-local-dc + Use --exclude-local-dc to exclude nodes in local data center as + source for streaming. + + -h , --host + Node hostname or ip address + + -ks , --keyspace + Use -ks to rebuild specific keyspace. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -s , --sources + Use -s to specify hosts that this node should stream from when -ts + is used. Multiple hosts should be separated using commas (e.g. + 127.0.0.1,127.0.0.2,...) + + -ts , --tokens + Use -ts to rebuild specific token ranges, in the format of + "(start_token_1,end_token_1],(start_token_2,end_token_2],... + (start_token_n,end_token_n]". + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Name of DC from which to select sources for streaming. By default, + pick any DC (except local DC when --exclude-local-dc is set) diff --git a/test/resources/nodetool/help/rebuild_index b/test/resources/nodetool/help/rebuild_index new file mode 100644 index 0000000000..020973fdce --- /dev/null +++ b/test/resources/nodetool/help/rebuild_index @@ -0,0 +1,37 @@ +NAME + nodetool rebuild_index - A full rebuild of native secondary indexes for + a given table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] rebuild_index [--] +
+ +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
+ The keyspace and table name followed by a list of index names diff --git a/test/resources/nodetool/help/recompress_sstables b/test/resources/nodetool/help/recompress_sstables new file mode 100644 index 0000000000..1835f7ea72 --- /dev/null +++ b/test/resources/nodetool/help/recompress_sstables @@ -0,0 +1,41 @@ +NAME + nodetool recompress_sstables - Rewrite sstables (for the requested + tables) that have compression configuration different from the current + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] recompress_sstables + [(-j | --jobs )] [--] [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -j , --jobs + Number of sstables to upgrade simultanously, set to 0 to use all + available compaction threads + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/refresh b/test/resources/nodetool/help/refresh new file mode 100644 index 0000000000..0e4117dd83 --- /dev/null +++ b/test/resources/nodetool/help/refresh @@ -0,0 +1,37 @@ +NAME + nodetool refresh - Load newly placed SSTables to the system without + restart + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] refresh [--] +
+ +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
+ The keyspace and table name diff --git a/test/resources/nodetool/help/refreshsizeestimates b/test/resources/nodetool/help/refreshsizeestimates new file mode 100644 index 0000000000..48f1690e9e --- /dev/null +++ b/test/resources/nodetool/help/refreshsizeestimates @@ -0,0 +1,27 @@ +NAME + nodetool refreshsizeestimates - Refresh system.size_estimates + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] refreshsizeestimates + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/reloadcidrgroupscache b/test/resources/nodetool/help/reloadcidrgroupscache new file mode 100644 index 0000000000..3012157b08 --- /dev/null +++ b/test/resources/nodetool/help/reloadcidrgroupscache @@ -0,0 +1,28 @@ +NAME + nodetool reloadcidrgroupscache - Reload CIDR groups cache with latest + entries in cidr_groups table, when CIDR authorizer is enabled + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] reloadcidrgroupscache + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/reloadlocalschema b/test/resources/nodetool/help/reloadlocalschema new file mode 100644 index 0000000000..df937eaa9c --- /dev/null +++ b/test/resources/nodetool/help/reloadlocalschema @@ -0,0 +1,27 @@ +NAME + nodetool reloadlocalschema - Reload local node schema from system tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] reloadlocalschema + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/reloadseeds b/test/resources/nodetool/help/reloadseeds new file mode 100644 index 0000000000..2cdd324fdf --- /dev/null +++ b/test/resources/nodetool/help/reloadseeds @@ -0,0 +1,28 @@ +NAME + nodetool reloadseeds - Reload the seed node list from the seed node + provider + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] reloadseeds + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/reloadssl b/test/resources/nodetool/help/reloadssl new file mode 100644 index 0000000000..e620723d9b --- /dev/null +++ b/test/resources/nodetool/help/reloadssl @@ -0,0 +1,27 @@ +NAME + nodetool reloadssl - Signals Cassandra to reload SSL certificates + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] reloadssl + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/reloadtriggers b/test/resources/nodetool/help/reloadtriggers new file mode 100644 index 0000000000..0160fd39e1 --- /dev/null +++ b/test/resources/nodetool/help/reloadtriggers @@ -0,0 +1,27 @@ +NAME + nodetool reloadtriggers - Reload trigger classes + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] reloadtriggers + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/relocatesstables b/test/resources/nodetool/help/relocatesstables new file mode 100644 index 0000000000..a57d661029 --- /dev/null +++ b/test/resources/nodetool/help/relocatesstables @@ -0,0 +1,40 @@ +NAME + nodetool relocatesstables - Relocates sstables to the correct disk + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] relocatesstables + [(-j | --jobs )] [--]
+ +OPTIONS + -h , --host + Node hostname or ip address + + -j , --jobs + Number of sstables to relocate simultanously, set to 0 to use all + available compaction threads + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
+ The keyspace and table name diff --git a/test/resources/nodetool/help/removenode b/test/resources/nodetool/help/removenode new file mode 100644 index 0000000000..a08d765e7d --- /dev/null +++ b/test/resources/nodetool/help/removenode @@ -0,0 +1,51 @@ +NAME + nodetool removenode - Show status of current node removal, abort + removal or remove provided ID + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] removenode [--force] [--] + [] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] removenode status + +OPTIONS + --force + Force node removal + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The ID of the node to remove + +COMMANDS + With no arguments, Display help information + + status + Show status of the current node removal operation diff --git a/test/resources/nodetool/help/removenode$status b/test/resources/nodetool/help/removenode$status new file mode 100644 index 0000000000..70e79d3f56 --- /dev/null +++ b/test/resources/nodetool/help/removenode$status @@ -0,0 +1,28 @@ +NAME + nodetool removenode status - Show status of the current node removal + operation + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] removenode status + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/repair b/test/resources/nodetool/help/repair new file mode 100644 index 0000000000..7710ab4091 --- /dev/null +++ b/test/resources/nodetool/help/repair @@ -0,0 +1,131 @@ +NAME + nodetool repair - Repair one or more tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair + [(-accord-only | --accord-only)] + [(-dc | --in-dc )...] + [(-dcpar | --dc-parallel)] + [(-et | --end-token )] [(-force | --force)] + [(-full | --full)] + [(-hosts | --in-hosts )...] + [--include-gcgs-expired-tombstones] + [(-iuk | --ignore-unreplicated-keyspaces)] + [(-j | --job-threads )] + [(-local | --in-local-dc)] [(-os | --optimise-streams)] + [(-paxos-only | --paxos-only)] [(-pl | --pull)] + [(-pr | --partitioner-range)] [(-prv | --preview)] + [(-seq | --sequential)] [(-skip-accord | --skip-accord)] + [(-skip-paxos | --skip-paxos)] + [(-st | --start-token )] [(-tr | --trace)] + [(-vd | --validate)] [--] [ ...] + +OPTIONS + -accord-only, --accord-only + If the --accord-only flag is included, no table data is repaired, + only accord operations.. + + -dc , --in-dc + Use -dc to repair specific datacenters + + -dcpar, --dc-parallel + Use -dcpar to repair data centers in parallel. + + -et , --end-token + Use -et to specify a token at which repair range ends (inclusive) + + -force, --force + Use -force to filter out down endpoints + + -full, --full + Use -full to issue a full repair. + + -h , --host + Node hostname or ip address + + -hosts , --in-hosts + Use -hosts to repair specific hosts + + --include-gcgs-expired-tombstones + Do not apply gc grace seconds to purge any tombstones. Only useful + in rare recovery scenarios, never regular operations. + + -iuk, --ignore-unreplicated-keyspaces + Use --ignore-unreplicated-keyspaces to ignore keyspaces which are + not replicated, otherwise the repair will fail + + -j , --job-threads + Number of threads to run repair jobs. Usually this means number of + CFs to repair concurrently. WARNING: increasing this puts more load + on repairing nodes, so be careful. (default: 1, max: 4) + + -local, --in-local-dc + Use -local to only repair against nodes in the same datacenter + + -os, --optimise-streams + Use --optimise-streams to try to reduce the number of streams we do + (EXPERIMENTAL, see CASSANDRA-3200). + + -p , --port + Remote jmx agent port number + + -paxos-only, --paxos-only + If the --paxos-only flag is included, no table data is repaired, + only paxos operations.. + + -pl, --pull + Use --pull to perform a one way repair where data is only streamed + from a remote node to this node. + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pr, --partitioner-range + Use -pr to repair only the first range returned by the partitioner + + -prv, --preview + Determine ranges and amount of data to be streamed, but don't + actually perform repair + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -seq, --sequential + Use -seq to carry out a sequential repair + + -skip-accord, --skip-accord + If the --skip-accord flag is included, the Accord repair step is + skipped. Accord repair is also skipped for preview repairs. + + -skip-paxos, --skip-paxos + If the --skip-paxos flag is included, the paxos repair step is + skipped. Paxos repair is also skipped for preview repairs. + + -st , --start-token + Use -st to specify a token at which the repair range starts + (exclusive) + + -tr, --trace + Use -tr to trace the repair. Traces are logged to system_traces. + events. + + -u , --username + Remote jmx agent username + + -vd, --validate + Checks that repaired data is in sync between nodes. Out of sync + repaired data indicates a full repair should be run. + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/repair_admin b/test/resources/nodetool/help/repair_admin new file mode 100644 index 0000000000..ffa401f015 --- /dev/null +++ b/test/resources/nodetool/help/repair_admin @@ -0,0 +1,123 @@ +NAME + nodetool repair_admin - list and fail incremental repair sessions + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin + [] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin list + [(-a | --all)] [(-et | --end-token )] + [(-st | --start-token )] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + repair_admin summarize-pending + [(-et | --end-token )] + [(-st | --start-token )] [(-v | --verbose)] + [--] [ ...] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + repair_admin summarize-repaired + [(-et | --end-token )] + [(-st | --start-token )] [(-v | --verbose)] + [--] [ ...] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin cleanup + [(-et | --end-token )] [(-f | --force)] + [(-st | --start-token )] [--] [ + ...] + + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin cancel + [(-f | --force)] [(-s | --session )] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + +COMMANDS + With no arguments, Display help information + + list + list repair sessions + + With --all option, include completed and failed sessions + + With --start-token option, Use -st to specify a token at which the repair + range starts + + With --end-token option, Use -et to specify a token at which repair range + ends + summarize-pending + report the amount of data marked pending repair for the given token range + (or all replicated range if no tokens are provided + + With --verbose option, print additional info + + With --start-token option, Use -st to specify a token at which the repair + range starts + + With --end-token option, Use -et to specify a token at which repair range + ends + summarize-repaired + return the most recent repairedAt timestamp for the given token range (or + all replicated ranges if no tokens are provided) + + With --verbose option, print additional info + + With --start-token option, Use -st to specify a token at which the repair + range starts + + With --end-token option, Use -et to specify a token at which repair range + ends + cleanup + cleans up pending data from completed sessions. This happens automatically, + but the command is provided for situations where it needs to be expedited. + Use --force to cancel compactions that are preventing promotion + + With --force option, Force a cleanup. + + With --start-token option, Use -st to specify a token at which the repair + range starts + + With --end-token option, Use -et to specify a token at which repair range + ends + cancel + cancel an incremental repair session. Use --force to cancel from a node + other than the repair coordinator Attempting to cancel FINALIZED or FAILED + sessions is an error. + + With --force option, Force a cancellation. + + With --session option, The session to cancel diff --git a/test/resources/nodetool/help/repair_admin$cancel b/test/resources/nodetool/help/repair_admin$cancel new file mode 100644 index 0000000000..a0c705f563 --- /dev/null +++ b/test/resources/nodetool/help/repair_admin$cancel @@ -0,0 +1,36 @@ +NAME + nodetool repair_admin cancel - cancel an incremental repair session. + Use --force to cancel from a node other than the repair coordinator + Attempting to cancel FINALIZED or FAILED sessions is an error. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin cancel + [(-f | --force)] [(-s | --session )] + +OPTIONS + -f, --force + Force a cancellation. + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -s , --session + The session to cancel + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/repair_admin$cleanup b/test/resources/nodetool/help/repair_admin$cleanup new file mode 100644 index 0000000000..0dbd8d5fe1 --- /dev/null +++ b/test/resources/nodetool/help/repair_admin$cleanup @@ -0,0 +1,50 @@ +NAME + nodetool repair_admin cleanup - cleans up pending data from completed + sessions. This happens automatically, but the command is provided for + situations where it needs to be expedited. Use --force to cancel + compactions that are preventing promotion + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin cleanup + [(-et | --end-token )] [(-f | --force)] + [(-st | --start-token )] [--] [ + ...] + +OPTIONS + -et , --end-token + Use -et to specify a token at which repair range ends + + -f, --force + Force a cleanup. + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -st , --start-token + Use -st to specify a token at which the repair range starts + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/repair_admin$list b/test/resources/nodetool/help/repair_admin$list new file mode 100644 index 0000000000..7912d5658d --- /dev/null +++ b/test/resources/nodetool/help/repair_admin$list @@ -0,0 +1,38 @@ +NAME + nodetool repair_admin list - list repair sessions + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] repair_admin list + [(-a | --all)] [(-et | --end-token )] + [(-st | --start-token )] + +OPTIONS + -a, --all + include completed and failed sessions + + -et , --end-token + Use -et to specify a token at which repair range ends + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -st , --start-token + Use -st to specify a token at which the repair range starts + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/repair_admin$summarize-pending b/test/resources/nodetool/help/repair_admin$summarize-pending new file mode 100644 index 0000000000..763f67361c --- /dev/null +++ b/test/resources/nodetool/help/repair_admin$summarize-pending @@ -0,0 +1,50 @@ +NAME + nodetool repair_admin summarize-pending - report the amount of data + marked pending repair for the given token range (or all replicated + range if no tokens are provided + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + repair_admin summarize-pending + [(-et | --end-token )] + [(-st | --start-token )] [(-v | --verbose)] + [--] [ ...] + +OPTIONS + -et , --end-token + Use -et to specify a token at which repair range ends + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -st , --start-token + Use -st to specify a token at which the repair range starts + + -u , --username + Remote jmx agent username + + -v, --verbose + print additional info + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/repair_admin$summarize-repaired b/test/resources/nodetool/help/repair_admin$summarize-repaired new file mode 100644 index 0000000000..8490781848 --- /dev/null +++ b/test/resources/nodetool/help/repair_admin$summarize-repaired @@ -0,0 +1,50 @@ +NAME + nodetool repair_admin summarize-repaired - return the most recent + repairedAt timestamp for the given token range (or all replicated + ranges if no tokens are provided) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] + repair_admin summarize-repaired + [(-et | --end-token )] + [(-st | --start-token )] [(-v | --verbose)] + [--] [ ...] + +OPTIONS + -et , --end-token + Use -et to specify a token at which repair range ends + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -st , --start-token + Use -st to specify a token at which the repair range starts + + -u , --username + Remote jmx agent username + + -v, --verbose + print additional info + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/replaybatchlog b/test/resources/nodetool/help/replaybatchlog new file mode 100644 index 0000000000..2a681b6dd6 --- /dev/null +++ b/test/resources/nodetool/help/replaybatchlog @@ -0,0 +1,27 @@ +NAME + nodetool replaybatchlog - Kick off batchlog replay and wait for finish + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] replaybatchlog + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/resetfullquerylog b/test/resources/nodetool/help/resetfullquerylog new file mode 100644 index 0000000000..74cd5ae0e6 --- /dev/null +++ b/test/resources/nodetool/help/resetfullquerylog @@ -0,0 +1,29 @@ +NAME + nodetool resetfullquerylog - Stop the full query log and clean files in + the configured full query log directory from cassandra.yaml as well as + JMX + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] resetfullquerylog + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/resetlocalschema b/test/resources/nodetool/help/resetlocalschema new file mode 100644 index 0000000000..36208b4f07 --- /dev/null +++ b/test/resources/nodetool/help/resetlocalschema @@ -0,0 +1,27 @@ +NAME + nodetool resetlocalschema - Reset node's local schema and resync + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] resetlocalschema + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/resumehandoff b/test/resources/nodetool/help/resumehandoff new file mode 100644 index 0000000000..46a833467d --- /dev/null +++ b/test/resources/nodetool/help/resumehandoff @@ -0,0 +1,27 @@ +NAME + nodetool resumehandoff - Resume hints delivery process + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] resumehandoff + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/ring b/test/resources/nodetool/help/ring new file mode 100644 index 0000000000..ea5a31c5fb --- /dev/null +++ b/test/resources/nodetool/help/ring @@ -0,0 +1,40 @@ +NAME + nodetool ring - Print information about the token ring + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] ring [(-r | --resolve-ip)] + [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -r, --resolve-ip + Show node domain names instead of IPs + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Specify a keyspace for accurate ownership information (topology + awareness) diff --git a/test/resources/nodetool/help/scrub b/test/resources/nodetool/help/scrub new file mode 100644 index 0000000000..289ec3206f --- /dev/null +++ b/test/resources/nodetool/help/scrub @@ -0,0 +1,61 @@ +NAME + nodetool scrub - Scrub (rebuild sstables for) one or more tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] scrub + [(-j | --jobs )] [(-n | --no-validate)] + [(-ns | --no-snapshot)] [(-r | --reinsert-overflowed-ttl)] + [(-s | --skip-corrupted)] [--] [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -j , --jobs + Number of sstables to scrub simultanously, set to 0 to use all + available compaction threads + + -n, --no-validate + Do not validate columns using column validator + + -ns, --no-snapshot + Scrubbed CFs will be snapshotted first, if disableSnapshot is + false. (default false) + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -r, --reinsert-overflowed-ttl + Rewrites rows with overflowed expiration date affected by + CASSANDRA-14092 with the maximum supported expiration date of + 2038-01-19T03:14:06+00:00. The rows are rewritten with the original + timestamp incremented by one millisecond to override/supersede any + potential tombstone that may have been generated during compaction + of the affected rows. + + -s, --skip-corrupted + Skip corrupted partitions even when scrubbing counter tables. + (default false) + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/setauthcacheconfig b/test/resources/nodetool/help/setauthcacheconfig new file mode 100644 index 0000000000..5e97efc020 --- /dev/null +++ b/test/resources/nodetool/help/setauthcacheconfig @@ -0,0 +1,49 @@ +NAME + nodetool setauthcacheconfig - Set configuration for Auth cache + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setauthcacheconfig + [--cache-name ] [--disable-active-update] + [--enable-active-update] [--max-entries ] + [--update-interval ] + [--validity-period ] + +OPTIONS + --cache-name + Name of Auth cache (required) + + --disable-active-update + Disable active update + + --enable-active-update + Enable active update + + -h , --host + Node hostname or ip address + + --max-entries + Max entries + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + --update-interval + Update interval in milliseconds + + --validity-period + Validity period in milliseconds diff --git a/test/resources/nodetool/help/setautorepairconfig b/test/resources/nodetool/help/setautorepairconfig new file mode 100644 index 0000000000..aa034097b8 --- /dev/null +++ b/test/resources/nodetool/help/setautorepairconfig @@ -0,0 +1,43 @@ +NAME + nodetool setautorepairconfig - sets the autorepair configuration + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setautorepairconfig + [(-t | --repair-type )] [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -t , --repair-type + Repair type + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Autorepair param type. + + + Autorepair param value diff --git a/test/resources/nodetool/help/setbatchlogreplaythrottle b/test/resources/nodetool/help/setbatchlogreplaythrottle new file mode 100644 index 0000000000..f56bf4faf4 --- /dev/null +++ b/test/resources/nodetool/help/setbatchlogreplaythrottle @@ -0,0 +1,38 @@ +NAME + nodetool setbatchlogreplaythrottle - Set batchlog replay throttle in KB + per second, or 0 to disable throttling. This will be reduced + proportionally to the number of nodes in the cluster. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setbatchlogreplaythrottle + [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value in KiB per second, 0 to disable throttling diff --git a/test/resources/nodetool/help/setcachecapacity b/test/resources/nodetool/help/setcachecapacity new file mode 100644 index 0000000000..0faed96219 --- /dev/null +++ b/test/resources/nodetool/help/setcachecapacity @@ -0,0 +1,37 @@ +NAME + nodetool setcachecapacity - Set global key, row, and counter cache + capacities (in MB units) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setcachecapacity [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Key cache, row cache, and counter cache (in MB) diff --git a/test/resources/nodetool/help/setcachekeystosave b/test/resources/nodetool/help/setcachekeystosave new file mode 100644 index 0000000000..ec394a94a4 --- /dev/null +++ b/test/resources/nodetool/help/setcachekeystosave @@ -0,0 +1,39 @@ +NAME + nodetool setcachekeystosave - Set number of keys saved by each cache + for faster post-restart warmup. 0 to disable + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setcachekeystosave [--] + + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + + The number of keys saved by each cache. 0 to disable diff --git a/test/resources/nodetool/help/setcolumnindexsize b/test/resources/nodetool/help/setcolumnindexsize new file mode 100644 index 0000000000..916848acaf --- /dev/null +++ b/test/resources/nodetool/help/setcolumnindexsize @@ -0,0 +1,37 @@ +NAME + nodetool setcolumnindexsize - Set the granularity of the collation + index of rows within a partition in KiB + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setcolumnindexsize [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value in KiB diff --git a/test/resources/nodetool/help/setcompactionthreshold b/test/resources/nodetool/help/setcompactionthreshold new file mode 100644 index 0000000000..3a2ab45fdb --- /dev/null +++ b/test/resources/nodetool/help/setcompactionthreshold @@ -0,0 +1,37 @@ +NAME + nodetool setcompactionthreshold - Set min and max compaction thresholds + for a given table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setcompactionthreshold [--] +
+ +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + +
+ The keyspace, the table, min and max threshold diff --git a/test/resources/nodetool/help/setcompactionthroughput b/test/resources/nodetool/help/setcompactionthroughput new file mode 100644 index 0000000000..a619d45083 --- /dev/null +++ b/test/resources/nodetool/help/setcompactionthroughput @@ -0,0 +1,37 @@ +NAME + nodetool setcompactionthroughput - Set the MiB/s throughput cap for + compaction in the system, or 0 to disable throttling + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setcompactionthroughput [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value in MiB, 0 to disable throttling diff --git a/test/resources/nodetool/help/setconcurrency b/test/resources/nodetool/help/setconcurrency new file mode 100644 index 0000000000..c736f7f313 --- /dev/null +++ b/test/resources/nodetool/help/setconcurrency @@ -0,0 +1,38 @@ +NAME + nodetool setconcurrency - Set maximum concurrency for processing stage + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setconcurrency [--] + | + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + | + + Set concurrency for processing stage diff --git a/test/resources/nodetool/help/setconcurrentcompactors b/test/resources/nodetool/help/setconcurrentcompactors new file mode 100644 index 0000000000..52aeb589a4 --- /dev/null +++ b/test/resources/nodetool/help/setconcurrentcompactors @@ -0,0 +1,37 @@ +NAME + nodetool setconcurrentcompactors - Set number of concurrent compactors + in the system. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setconcurrentcompactors [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Number of concurrent compactors, greater than 0. diff --git a/test/resources/nodetool/help/setconcurrentviewbuilders b/test/resources/nodetool/help/setconcurrentviewbuilders new file mode 100644 index 0000000000..2428ff1476 --- /dev/null +++ b/test/resources/nodetool/help/setconcurrentviewbuilders @@ -0,0 +1,37 @@ +NAME + nodetool setconcurrentviewbuilders - Set the number of concurrent view + builders in the system + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setconcurrentviewbuilders + [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Number of concurrent view builders, greater than 0. diff --git a/test/resources/nodetool/help/setdefaultrf b/test/resources/nodetool/help/setdefaultrf new file mode 100644 index 0000000000..0a8662bc56 --- /dev/null +++ b/test/resources/nodetool/help/setdefaultrf @@ -0,0 +1,35 @@ +NAME + nodetool setdefaultrf - Sets default keyspace replication factor. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setdefaultrf [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Default replication factor diff --git a/test/resources/nodetool/help/setguardrailsconfig b/test/resources/nodetool/help/setguardrailsconfig new file mode 100644 index 0000000000..4804c42829 --- /dev/null +++ b/test/resources/nodetool/help/setguardrailsconfig @@ -0,0 +1,42 @@ +NAME + nodetool setguardrailsconfig - Modify runtime configuration of + guardrails. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setguardrailsconfig [--] + [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + 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. diff --git a/test/resources/nodetool/help/sethintedhandoffthrottlekb b/test/resources/nodetool/help/sethintedhandoffthrottlekb new file mode 100644 index 0000000000..99a595adb7 --- /dev/null +++ b/test/resources/nodetool/help/sethintedhandoffthrottlekb @@ -0,0 +1,37 @@ +NAME + nodetool sethintedhandoffthrottlekb - Set hinted handoff throttle in + KiB per second, per delivery thread. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] sethintedhandoffthrottlekb + [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value in KiB per second diff --git a/test/resources/nodetool/help/setinterdcstreamthroughput b/test/resources/nodetool/help/setinterdcstreamthroughput new file mode 100644 index 0000000000..8f41a06481 --- /dev/null +++ b/test/resources/nodetool/help/setinterdcstreamthroughput @@ -0,0 +1,45 @@ +NAME + nodetool setinterdcstreamthroughput - Set the throughput cap for + inter-datacenter streaming and entire SSTable inter-datacenter + streaming in the system, or 0 to disable throttling + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setinterdcstreamthroughput + [(-e | --entire-sstable-throughput)] [(-m | --mib)] [--] + + +OPTIONS + -e, --entire-sstable-throughput + Set entire SSTable streaming throughput in MiB/s + + -h , --host + Node hostname or ip address + + -m, --mib + Set streaming throughput in MiB/s + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value in megabits, 0 to disable throttling diff --git a/test/resources/nodetool/help/setlogginglevel b/test/resources/nodetool/help/setlogginglevel new file mode 100644 index 0000000000..a63c60ff7a --- /dev/null +++ b/test/resources/nodetool/help/setlogginglevel @@ -0,0 +1,40 @@ +NAME + nodetool setlogginglevel - Set the log level threshold for a given + component or class. Will reset to the initial configuration if called + with no parameters. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setlogginglevel [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The component or class to change the level for and the log level + threshold to set. Will reset to initial level if omitted. Available + components: bootstrap, compaction, repair, streaming, cql, ring diff --git a/test/resources/nodetool/help/setmaxhintwindow b/test/resources/nodetool/help/setmaxhintwindow new file mode 100644 index 0000000000..68615cc51c --- /dev/null +++ b/test/resources/nodetool/help/setmaxhintwindow @@ -0,0 +1,36 @@ +NAME + nodetool setmaxhintwindow - Set the specified max hint window in ms + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setmaxhintwindow [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value of maxhintwindow in ms diff --git a/test/resources/nodetool/help/setsnapshotthrottle b/test/resources/nodetool/help/setsnapshotthrottle new file mode 100644 index 0000000000..e7bae46934 --- /dev/null +++ b/test/resources/nodetool/help/setsnapshotthrottle @@ -0,0 +1,38 @@ +NAME + nodetool setsnapshotthrottle - Set the snapshot_links_per_second cap + for snapshot and clearsnapshot throttling + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setsnapshotthrottle [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value represents hardlinks per second ( snapshot_links_per_second ) + , 0 to disable throttling diff --git a/test/resources/nodetool/help/setstreamthroughput b/test/resources/nodetool/help/setstreamthroughput new file mode 100644 index 0000000000..60a8cbeda9 --- /dev/null +++ b/test/resources/nodetool/help/setstreamthroughput @@ -0,0 +1,44 @@ +NAME + nodetool setstreamthroughput - Set throughput cap for streaming and + entire SSTable streaming in the system, or 0 to disable throttling + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] setstreamthroughput + [(-e | --entire-sstable-throughput)] [(-m | --mib)] [--] + + +OPTIONS + -e, --entire-sstable-throughput + Set entire SSTable streaming throughput in MiB/s + + -h , --host + Node hostname or ip address + + -m, --mib + Set streaming throughput in MiB/s + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Value in megabits, 0 to disable throttling diff --git a/test/resources/nodetool/help/settimeout b/test/resources/nodetool/help/settimeout new file mode 100644 index 0000000000..c1c87c5b1c --- /dev/null +++ b/test/resources/nodetool/help/settimeout @@ -0,0 +1,40 @@ +NAME + nodetool settimeout - Set the specified timeout in ms, or 0 to disable + timeout + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] settimeout [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Timeout type followed by value in ms (0 disables socket streaming + timeout). Type should be one of (read, range, write, counterwrite, + cascontention, truncate, internodeconnect, internodeuser, + internodestreaminguser, misc (general rpc_timeout_in_ms)) diff --git a/test/resources/nodetool/help/settraceprobability b/test/resources/nodetool/help/settraceprobability new file mode 100644 index 0000000000..0ae7e29aef --- /dev/null +++ b/test/resources/nodetool/help/settraceprobability @@ -0,0 +1,38 @@ +NAME + nodetool settraceprobability - Sets the probability for tracing any + given request to value. 0 disables, 1 enables for all requests, 0 is + the default + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] settraceprobability [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Trace probability between 0 and 1 (ex: 0.2) diff --git a/test/resources/nodetool/help/sjk b/test/resources/nodetool/help/sjk new file mode 100644 index 0000000000..fa0665bd4d --- /dev/null +++ b/test/resources/nodetool/help/sjk @@ -0,0 +1,36 @@ +NAME + nodetool sjk - Run commands of 'Swiss Java Knife'. Run 'nodetool sjk + --help' for more information. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] sjk [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Arguments passed as is to 'Swiss Java Knife'. diff --git a/test/resources/nodetool/help/snapshot b/test/resources/nodetool/help/snapshot new file mode 100644 index 0000000000..d6a5b03c88 --- /dev/null +++ b/test/resources/nodetool/help/snapshot @@ -0,0 +1,57 @@ +NAME + nodetool snapshot - Take a snapshot of specified keyspaces or a + snapshot of the specified table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] snapshot + [(-cf
| --column-family
)] + [(-kt | --kt-list )] [(-sf | --skip-flush)] + [(-t | --tag )] [--ttl ] [--] + +OPTIONS + -cf
, --column-family
, --table
+ The table name (you must specify one and only one keyspace for + using this option) + + -h , --host + Node hostname or ip address + + -kt , --kt-list , -kc , --kc.list + The list of Keyspace.table to take snapshot.(you must not specify + only keyspace) + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -sf, --skip-flush + Do not flush memtables before snapshotting (snapshot will not + contain unflushed data) + + -t , --tag + The name of the snapshot + + --ttl + Specify a TTL of created snapshot + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + List of keyspaces. By default, all keyspaces diff --git a/test/resources/nodetool/help/sstablerepairedset b/test/resources/nodetool/help/sstablerepairedset new file mode 100644 index 0000000000..792afdab84 --- /dev/null +++ b/test/resources/nodetool/help/sstablerepairedset @@ -0,0 +1,53 @@ +NAME + nodetool sstablerepairedset - Set the repaired state of SSTables for + given keyspace/tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] sstablerepairedset + [--is-repaired] [--is-unrepaired] [--really-set] [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + --is-repaired + Set SSTables to repaired state. + + --is-unrepaired + Set SSTables to unrepaired state. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + --really-set + Really set the repaired state of SSTables. If not set, only print + SSTables that would be affected. + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Keyspace to set the repaired state of SSTables. If not provided, + all keyspaces are used. + + + Tables to set the repaired state of SSTables. If not provided, all + tables in the keyspace are used. diff --git a/test/resources/nodetool/help/status b/test/resources/nodetool/help/status new file mode 100644 index 0000000000..a1955a417a --- /dev/null +++ b/test/resources/nodetool/help/status @@ -0,0 +1,50 @@ +NAME + nodetool status - Print cluster information (state, load, IDs, ...) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] status + [(-o | --order )] [(-r | --resolve-ip)] + [(-s | --sort )] [--] + +OPTIONS + -h , --host + Node hostname or ip address + + -o , --order + Sorting order: 'asc' for ascending, 'desc' for descending. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -r, --resolve-ip + Show node domain names instead of IPs + + -s , --sort + Sort by one of 'ip', 'host', 'load', 'owns', 'id', 'rack', 'state' + or 'token'. Default ordering is ascending for 'ip', 'host', 'id', + 'token', 'rack' and descending for 'load', 'owns', 'state'. Sorting + by token is possible only when cluster does not use vnodes. When + using vnodes, default sorting is by id otherwise by token. + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace name diff --git a/test/resources/nodetool/help/statusautocompaction b/test/resources/nodetool/help/statusautocompaction new file mode 100644 index 0000000000..8afa2ef8eb --- /dev/null +++ b/test/resources/nodetool/help/statusautocompaction @@ -0,0 +1,40 @@ +NAME + nodetool statusautocompaction - status of autocompaction of the given + keyspace and table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] statusautocompaction + [(-a | --all)] [--] [ ...] + +OPTIONS + -a, --all + Show auto compaction status for each keyspace/table + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/statusbackup b/test/resources/nodetool/help/statusbackup new file mode 100644 index 0000000000..0160ecfcaf --- /dev/null +++ b/test/resources/nodetool/help/statusbackup @@ -0,0 +1,27 @@ +NAME + nodetool statusbackup - Status of incremental backup + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] statusbackup + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/statusbinary b/test/resources/nodetool/help/statusbinary new file mode 100644 index 0000000000..54662f1faf --- /dev/null +++ b/test/resources/nodetool/help/statusbinary @@ -0,0 +1,27 @@ +NAME + nodetool statusbinary - Status of native transport (binary protocol) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] statusbinary + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/statusgossip b/test/resources/nodetool/help/statusgossip new file mode 100644 index 0000000000..25c3e78e2a --- /dev/null +++ b/test/resources/nodetool/help/statusgossip @@ -0,0 +1,27 @@ +NAME + nodetool statusgossip - Status of gossip + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] statusgossip + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/statushandoff b/test/resources/nodetool/help/statushandoff new file mode 100644 index 0000000000..d9f756ff92 --- /dev/null +++ b/test/resources/nodetool/help/statushandoff @@ -0,0 +1,28 @@ +NAME + nodetool statushandoff - Status of storing future hints on the current + node + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] statushandoff + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/stop b/test/resources/nodetool/help/stop new file mode 100644 index 0000000000..f3bd55741b --- /dev/null +++ b/test/resources/nodetool/help/stop @@ -0,0 +1,45 @@ +NAME + nodetool stop - Stop compaction + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] stop + [(-id | --compaction-id )] [--] + + +OPTIONS + -h , --host + Node hostname or ip address + + -id , --compaction-id + Use -id to stop a compaction by the specified id. Ids can be found + in the transaction log files whose name starts with compaction_, + located in the table transactions folder. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Supported types are COMPACTION, VALIDATION, CLEANUP, SCRUB, + UPGRADE_SSTABLES, INDEX_BUILD, TOMBSTONE_COMPACTION, + ANTICOMPACTION, VERIFY, VIEW_BUILD, INDEX_SUMMARY, RELOCATE, + GARBAGE_COLLECT diff --git a/test/resources/nodetool/help/stopdaemon b/test/resources/nodetool/help/stopdaemon new file mode 100644 index 0000000000..bbfdeb0b41 --- /dev/null +++ b/test/resources/nodetool/help/stopdaemon @@ -0,0 +1,27 @@ +NAME + nodetool stopdaemon - Stop cassandra daemon + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] stopdaemon + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username diff --git a/test/resources/nodetool/help/tablehistograms b/test/resources/nodetool/help/tablehistograms new file mode 100644 index 0000000000..69790ed21b --- /dev/null +++ b/test/resources/nodetool/help/tablehistograms @@ -0,0 +1,36 @@ +NAME + nodetool tablehistograms - Print statistic histograms for a given table + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] tablehistograms [--] + [
| ] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [
| ] + The keyspace and table name diff --git a/test/resources/nodetool/help/tablestats b/test/resources/nodetool/help/tablestats new file mode 100644 index 0000000000..33f043c5b7 --- /dev/null +++ b/test/resources/nodetool/help/tablestats @@ -0,0 +1,79 @@ +NAME + nodetool tablestats - Print statistics on tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] tablestats + [(-F | --format )] [(-H | --human-readable)] [-i] + [(-l | --sstable-location-check)] [(-s | --sort )] + [(-t | --top )] [--] [...] + +OPTIONS + -F , --format + Output format (json, yaml) + + -h , --host + Node hostname or ip address + + -H, --human-readable + Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB + + -i + Ignore the list of tables and display the remaining tables + + -l, --sstable-location-check + Check whether or not the SSTables are in the correct location. + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -s , --sort + Sort tables by specified sort key + (average_live_cells_per_slice_last_five_minutes, + average_tombstones_per_slice_last_five_minutes, + bloom_filter_false_positives, bloom_filter_false_ratio, + bloom_filter_off_heap_memory_used, bloom_filter_space_used, + compacted_partition_maximum_bytes, compacted_partition_mean_bytes, + compacted_partition_minimum_bytes, + compression_metadata_off_heap_memory_used, full_name, + index_summary_off_heap_memory_used, local_read_count, + local_read_latency_ms, local_write_latency_ms, + maximum_live_cells_per_slice_last_five_minutes, + maximum_tombstones_per_slice_last_five_minutes, + memtable_cell_count, memtable_data_size, + memtable_off_heap_memory_used, memtable_switch_count, + number_of_partitions_estimate, off_heap_memory_used_total, + pending_flushes, percent_repaired, read_latency, reads, + space_used_by_snapshots_total, space_used_live, space_used_total, + sstable_compression_ratio, sstable_count, table_name, + write_latency, writes, max_sstable_size, local_read_write_ratio, + twcs_max_duration, sai_local_query_latency_ms, + sai_post_filtering_read_latency, sai_disk_used_bytes, + sai_sstable_indexes_hit, sai_index_segments_hit sai_rows_filtered, + sai_total_query_timeouts, sai_total_queryable_index_ratio) + + -t , --top + Show only the top K tables for the sort key (specify the number K + of tables to be shown + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [...] + List of tables (or keyspace) names diff --git a/test/resources/nodetool/help/toppartitions b/test/resources/nodetool/help/toppartitions new file mode 100644 index 0000000000..e98eb4a04c --- /dev/null +++ b/test/resources/nodetool/help/toppartitions @@ -0,0 +1,59 @@ +NAME + nodetool toppartitions - Sample and print the most active partitions + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] toppartitions [-a ] + [(-i | --interval )] [-k ] + [(-l | --list)] [-s ] [(-t | --stop)] [--] + + +OPTIONS + -a + Comma separated list of samplers to use (Default: all) + + -h , --host + Node hostname or ip address + + -i , --interval + Schedule a new job that samples every interval milliseconds + (Default: disabled) in the background + + -k + Number of the top samples to list (Default: 10) + + -l, --list + List the scheduled sampling jobs + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -s + Capacity of the sampler, higher for more accuracy (Default: 256) + + -t, --stop + Stop the scheduled sampling job identified by and + . Jobs are stopped until the last schedules complete. + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + The keyspace, column family name, and duration in milliseconds + (Default: 10000) diff --git a/test/resources/nodetool/help/tpstats b/test/resources/nodetool/help/tpstats new file mode 100644 index 0000000000..d18e31c4a7 --- /dev/null +++ b/test/resources/nodetool/help/tpstats @@ -0,0 +1,34 @@ +NAME + nodetool tpstats - Print usage statistics of thread pools + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] tpstats + [(-F | --format )] [(-v | --verbose)] + +OPTIONS + -F , --format + Output format (json, yaml) + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -v, --verbose + Display detailed metrics about thread pool's sizes diff --git a/test/resources/nodetool/help/truncatehints b/test/resources/nodetool/help/truncatehints new file mode 100644 index 0000000000..62adeee8d4 --- /dev/null +++ b/test/resources/nodetool/help/truncatehints @@ -0,0 +1,38 @@ +NAME + nodetool truncatehints - Truncate all hints on the local node, or + truncate hints for the endpoint(s) specified. + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] truncatehints [--] [endpoint + ... ] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [endpoint ... ] + Endpoint address to delete hints for, either ip address + ("127.0.0.1") or hostname diff --git a/test/resources/nodetool/help/updatecidrgroup b/test/resources/nodetool/help/updatecidrgroup new file mode 100644 index 0000000000..ea807b2423 --- /dev/null +++ b/test/resources/nodetool/help/updatecidrgroup @@ -0,0 +1,37 @@ +NAME + nodetool updatecidrgroup - Insert/Update a cidr group + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] updatecidrgroup [--] + [ ...] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + Requires a cidr group name, followed by one or more CIDRs separated + by space diff --git a/test/resources/nodetool/help/upgradesstables b/test/resources/nodetool/help/upgradesstables new file mode 100644 index 0000000000..0d7fcd0576 --- /dev/null +++ b/test/resources/nodetool/help/upgradesstables @@ -0,0 +1,52 @@ +NAME + nodetool upgradesstables - Rewrite sstables (for the requested tables) + that are not on the current version (thus upgrading them to said + current version) + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] upgradesstables + [(-a | --include-all-sstables)] [(-j | --jobs )] + [(-t | --max-timestamp )] [--] + [ ...] + +OPTIONS + -a, --include-all-sstables + Use -a to include all sstables, even those already on the current + version + + -h , --host + Node hostname or ip address + + -j , --jobs + Number of sstables to upgrade simultanously, set to 0 to use all + available compaction threads + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -t , --max-timestamp + Use -t to compact only SSTables that have local creation time + _older_ than the given timestamp + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/verify b/test/resources/nodetool/help/verify new file mode 100644 index 0000000000..b763dc1fb2 --- /dev/null +++ b/test/resources/nodetool/help/verify @@ -0,0 +1,59 @@ +NAME + nodetool verify - Verify (check data checksum for) one or more tables + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] verify + [(-c | --check-version)] [(-d | --dfp)] [(-e | --extended-verify)] + [(-f | --force)] [(-q | --quick)] [(-r | --rsc)] + [(-t | --check-tokens)] [--] [ ...] + +OPTIONS + -c, --check-version + Also check that all sstables are the latest version + + -d, --dfp + Invoke the disk failure policy if a corrupt sstable is found + + -e, --extended-verify + Verify each cell data, beyond simply checking sstable checksums + + -f, --force + Override disabling of verify tool - see CASSANDRA-9947 for caveats + + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -q, --quick + Do a quick check - avoid reading all data to verify checksums + + -r, --rsc + Mutate the repair status on corrupt sstables + + -t, --check-tokens + Verify that all tokens in sstables are owned by this node + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + [ ...] + The keyspace followed by one or many tables diff --git a/test/resources/nodetool/help/version b/test/resources/nodetool/help/version new file mode 100644 index 0000000000..63a45bcc02 --- /dev/null +++ b/test/resources/nodetool/help/version @@ -0,0 +1,30 @@ +NAME + nodetool version - Print cassandra version + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] version [(-v | --verbose)] + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -v, --verbose + Include additional information diff --git a/test/resources/nodetool/help/viewbuildstatus b/test/resources/nodetool/help/viewbuildstatus new file mode 100644 index 0000000000..19395ca64b --- /dev/null +++ b/test/resources/nodetool/help/viewbuildstatus @@ -0,0 +1,36 @@ +NAME + nodetool viewbuildstatus - Show progress of a materialized view build + +SYNOPSIS + nodetool [(-h | --host )] [(-p | --port )] + [(-pp | --print-port)] [(-pw | --password )] + [(-pwf | --password-file )] + [(-u | --username )] viewbuildstatus [--] + | + +OPTIONS + -h , --host + Node hostname or ip address + + -p , --port + Remote jmx agent port number + + -pp, --print-port + Operate in 4.0 mode with hosts disambiguated by port number + + -pw , --password + Remote jmx agent password + + -pwf , --password-file + Path to the JMX password file + + -u , --username + Remote jmx agent username + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + | + The keyspace and view name diff --git a/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java b/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java index 1e75aa01f2..d1d8a6c8b6 100644 --- a/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java +++ b/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java @@ -30,14 +30,12 @@ import java.util.function.ToDoubleFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.inject.Inject; import org.junit.BeforeClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.airlift.airline.Command; -import io.airlift.airline.Help; -import io.airlift.airline.Option; import io.netty.util.concurrent.FastThreadLocal; import org.apache.cassandra.config.CassandraRelevantProperties; import org.apache.cassandra.simulator.Debug.Info; @@ -55,6 +53,8 @@ import org.apache.cassandra.utils.Clock; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Hex; import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; import static java.util.Arrays.stream; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -149,132 +149,135 @@ public class SimulationRunner ThreadLocalRandom.current(); } - protected interface ICommand> + protected abstract static class BasicCommand> implements Runnable { - public void run(B builder) throws IOException; - } + @Inject + protected B builder; - protected abstract static class BasicCommand> implements ICommand - { - @Option(name = { "--seed" } , title = "0x", description = "Specify the first seed to test (each simulation will increment the seed by 1)") + @Option(names = { "--seed" }, description = { "Specify the first seed to test (each simulation will increment the seed by 1)", "0x" }) protected String seed; - @Option(name = { "--simulations"} , title = "int", description = "The number of simulations to run") + @Option(names = { "--simulations" }, description = { "The number of simulations to run", "int" }) protected int simulationCount = 1; - @Option(name = { "-t", "--threads" }, title = "int", description = "The number of threads to split between node thread pools. Each ongoing action also requires its own thread.") + @Option(names = { "-t", "--threads" }, description = { "The number of threads to split between node thread pools. Each ongoing action also requires its own thread.", "int" }) protected int threadCount = 1000; - @Option(name = { "-n", "--nodes" } , title = "int...int", description = "Cluster size range, lb..ub (default 4..16)") + @Option(names = { "-n", "--nodes" }, description = { "Cluster size range, lb..ub (default 4..16)", "int...int" }) protected String nodeCount = "4..16"; - @Option(name = { "--dcs" }, title = "int...int", description = "Cluster DC count, lb..ub (default 1..2)") + @Option(names = { "--dcs" }, description = { "Cluster DC count, lb..ub (default 1..2)", "int...int" }) protected String dcCount = "1..2"; - @Option(name = { "-o", "--within-key-concurrency" }, title = "int..int", description = "Number of simultaneous paxos operations per key, lb..ub (default 2..5)") + @Option(names = { "-o", "--within-key-concurrency" }, description = { "Number of simultaneous paxos operations per key, lb..ub (default 2..5)", "int...int" }) protected String withinKeyConcurrency = "2..5"; - @Option(name = { "-c", "--concurrency" }, title = "int", description = "Number of keys to operate on simultaneously (default 10)") + @Option(names = { "-c", "--concurrency" }, description = { "Number of keys to operate on simultaneously (default 10)", "int" }) protected int concurrency = 10; - @Option(name = { "-k", "--keys" }, title = "int", description = "Number of unique partition keys to operate over (default to 2* concurrency)") + @Option(names = { "-k", "--keys" }, description = { "Number of unique partition keys to operate over (default to 2* concurrency)", "int" }) protected int primaryKeyCount = -1; - @Option(name = { "--key-seconds" }, title = "int...int", description = "Number of seconds to simulate a partition key for before selecting another (default 5..30)") + @Option(names = { "--key-seconds" }, description = { "Number of seconds to simulate a partition key for before selecting another (default 5..30)", "int...int" }) protected String primaryKeySeconds = "5..30"; - @Option(name = { "--cluster-actions" }, title = "JOIN,LEAVE,REPLACE,CHANGE_RF", description = "Cluster actions to select from, comma delimited (JOIN, LEAVE, REPLACE, CHANGE_RF)") + @Option(names = { "--cluster-actions" }, description = { "Cluster actions to select from, comma delimited (JOIN, LEAVE, REPLACE, CHANGE_RF)", "JOIN,LEAVE,REPLACE,CHANGE_RF" }) protected String topologyChanges = stream(TopologyChange.values()).map(Object::toString).collect(Collectors.joining(",")); - @Option(name = { "--cluster-action-interval" }, title = "int...int(s|ms|us|ns)", description = "The period of time between two cluster actions (default 5..15s)") + @Option(names = { "--cluster-action-interval" }, description = { "The period of time between two cluster actions (default 5..15s)", "int...int(s|ms|us|ns)" }) protected String topologyChangeInterval = "5..15s"; - @Option(name = { "--cluster-action-limit" }, title = "int", description = "The maximum number of topology change events to perform (default 0 disabled, -1 unlimited)") + @Option(names = { "--cluster-action-limit" }, description = { "The maximum number of topology change events to perform (default 0 disabled, -1 unlimited)", "int" }) protected String topologyChangeLimit = "0"; - @Option(name = { "--consensus-actions" }, title = "ACCORD_MIGRATE", description = "Consensus migration actions to select from, comma delimited (ACCORD_MIGRATE)") + @Option(names = { "--consensus-actions" }, description = { "Consensus migration actions to select from, comma delimited (ACCORD_MIGRATE)", "ACCORD_MIGRATE" }) protected String consensusChanges = stream(ConsensusChange.values()).map(Object::toString).collect(Collectors.joining(",")); - @Option(name = { "--consensus-action-interval" }, title = "int...int(s|ms|us|ns)", description = "The period of time between two consensus actions (default 5..15s)") + @Option(names = { "--consensus-action-interval" }, description = { "The period of time between two consensus actions (default 5..15s)", "int...int(s|ms|us|ns)" }) protected String consensusChangeInterval = "1..5s"; - @Option(name = { "--consensus-action-limit" }, title = "int", description = "The maximum number of consensus change events to perform (default 0 disabled, -1 unlimited)") + @Option(names = { "--consensus-action-limit" }, description = { "The maximum number of consensus change events to perform (default 0 disabled, -1 unlimited)", "int" }) protected String consensusChangeLimit = "0"; - @Option(name = { "-s", "--run-time" }, title = "int", description = "Length of simulated time to run in seconds (default -1)") + @Option(names = { "-s", "--run-time" }, description = { "Length of simulated time to run in seconds (default -1)", "int" }) protected int secondsToSimulate = -1; - @Option(name = { "--reads" }, title = "[distribution:]float...float", description = "Proportion of actions that are reads (default: 0.05..0.95)") + @Option(names = { "--reads" }, description = { "Proportion of actions that are reads (default: 0.05..0.95)", "[distribution:]float...float" }) protected String readChance; - @Option(name = { "--nemesis" }, title = "[distribution:]float...float", description = "Proportion of nemesis points that are intercepted (default: 0..0.01)") + @Option(names = { "--nemesis" }, description = { "Proportion of nemesis points that are intercepted (default: 0..0.01)", "[distribution:]float...float" }) protected String nemesisChance; - @Option(name = { "--priority" }, title = "uniform|randomwalk|seq", description = "Priority assignment for actions that may overlap their execution", allowedValues = { "uniform", "randomwalk", "seq" }) + @Option(names = { "--priority" }, description = { "Priority assignment for actions that may overlap their execution", "uniform|randomwalk|seq" }) protected String priority; // TODO (feature): simulate GC pauses - @Option(name = { "--network-flaky-chance" }, title = "[distribution:]float...float", description = "Chance of some minority of nodes experiencing flaky connections (default: qlog:0.01..0.1)") + @Option(names = { "--network-flaky-chance" }, description = { "Chance of some minority of nodes experiencing flaky connections (default: qlog:0.01..0.1)", "[distribution:]float...float" }) protected String networkFlakyChance = "qlog:0.01..0.1"; - @Option(name = { "--network-partition-chance" }, title = "[distribution:]float...float", description = "Chance of some minority of nodes being isolated (default: qlog:0.01..0.1)") + @Option(names = { "--network-partition-chance" }, description = { "Chance of some minority of nodes being isolated (default: qlog:0.01..0.1)", "[distribution:]float...float" }) protected String networkPartitionChance = "qlog:0.01..0.1"; - @Option(name = { "--network-reconfigure-interval" }, title = "int...int(s|ms|us|ns)", description = "Period of time for which a flaky or catastrophic network partition may be in force") + @Option(names = { "--network-reconfigure-interval" }, description = { "Period of time for which a flaky or catastrophic network partition may be in force", "int...int(s|ms|us|ns)" }) protected String networkReconfigureInterval = "1..10s"; - @Option(name = { "--network-drop-chance" }, title = "[distribution:]float...float", description = "Chance of dropping a message under normal circumstances (default: qlog:0..0.001)") + @Option(names = { "--network-drop-chance" }, description = { "Chance of dropping a message under normal circumstances (default: qlog:0..0.001)", "[[distribution:]float...float]" }) protected String networkDropChance = "qlog:0..0.001"; // TODO (feature): TDP vs UDP simulation (right now we have no head of line blocking so we deliver in a UDP fashion which is not how the cluster operates) - @Option(name = { "--network-delay-chance" }, title = "[distribution:]float...float", description = "Chance of delaying a message under normal circumstances (default: qlog:0..0.1)") + @Option(names = { "--network-delay-chance" }, description = { "Chance of delaying a message under normal circumstances (default: qlog:0..0.1)", "[[distribution:]float...float]" }) protected String networkDelayChance = "qlog:0..0.01"; - @Option(name = { "--network-latency" }, title = "int...int(s|ms|us|ns)", description = "Range of possible latencies messages can be simulated to experience (default 1..2ms)") + @Option(names = { "--network-latency" }, description = { "Range of possible latencies messages can be simulated to experience (default 1..2ms)", "[int...int(s|ms|us|ns)]" }) protected String networkLatency = "1..2ms"; - @Option(name = { "--network-delay" }, title = "int...int(s|ms|us|ns)", description = "Range of possible latencies messages can be simulated to experience when delayed (default 2..20ms)") + @Option(names = { "--network-delay" }, description = { "Range of possible latencies messages can be simulated to experience when delayed (default 2..20ms)", "[int...int(s|ms|us|ns)]" }) protected String networkDelay = "2..20ms"; - @Option(name = { "--network-flaky-drop-chance" }, title = "[distribution:]float...float", description = "Chance of dropping a message on a flaky connection (default: qlog:0.01..0.1)") + @Option(names = { "--network-flaky-drop-chance" }, description = { "Chance of dropping a message on a flaky connection (default: qlog:0.01..0.1)", "[[distribution:]float...float]" }) protected String flakyNetworkDropChance = "qlog:0.01..0.1"; - @Option(name = { "--network-flaky-delay-chance" }, title = "[distribution:]float...float", description = "Chance of delaying a message on a flaky connection (default: qlog:0.01..0.2)") + @Option(names = { "--network-flaky-delay-chance" }, description = { "Chance of delaying a message on a flaky connection (default: qlog:0.01..0.2)", "[[distribution:]float...float]" }) protected String flakyNetworkDelayChance = "qlog:0.01..0.2"; - @Option(name = { "--network-flaky-latency" }, title = "int...int(s|ms|us|ns)", description = "Range of possible latencies messages can be simulated to experience on a flaky connection (default 2..4ms)") + @Option(names = { "--network-flaky-latency" }, description = { "Range of possible latencies messages can be simulated to experience on a flaky connection (default 2..4ms)", "[int...int(s|ms|us|ns)]" }) protected String flakyNetworkLatency = "2..4ms"; - @Option(name = { "--network-flaky-delay" }, title = "int...int(s|ms|us|ns)", description = "Range of possible latencies messages can be simulated to experience when delayed on a flaky connection (default 4..100ms)") + @Option(names = { "--network-flaky-delay" }, description = { "Range of possible latencies messages can be simulated to experience when delayed on a flaky connection (default 4..100ms)", "[int...int(s|ms|us|ns)]" }) protected String flakyNetworkDelay = "4..100ms"; - @Option(name = { "--clock-drift" }, title = "int...int(s|ms|us|ns)", description = "The range of clock skews to experience (default 10..1000ms)") + @Option(names = { "--clock-drift" }, description = { "The range of clock skews to experience (default 10..1000ms)", "[int...int(s|ms|us|ns)]" }) protected String clockDrift = "10..1000ms"; - @Option(name = { "--clock-discontinuity-interval" }, title = "int...int(s|ms|us|ns)", description = "The period of clock continuity (a discontinuity is a large jump of the global clock to introduce additional chaos for event scheduling) (default 10..60s)") + @Option(names = { "--clock-discontinuity-interval" }, description = { "The period of clock continuity (a discontinuity is a large jump of the global clock to introduce additional chaos for event scheduling) (default 10..60s)", "[int...int(s|ms|us|ns)]" }) protected String clockDiscontinuityInterval = "10..60s"; - @Option(name = { "--scheduler-jitter" }, title = "int...int(s|ms|us|ns)", description = "The scheduler will randomly prioritise all tasks scheduled to run within this interval (default 10..1500us)") + @Option(names = { "--scheduler-jitter" }, description = { "The scheduler will randomly prioritise all tasks scheduled to run within this interval (default 10..1500us)", "[int...int(s|ms|us|ns)]" }) protected String schedulerJitter = "10..1500us"; - @Option(name = { "--scheduler-delay-chance" }, title = "[distribution:]float...float", description = "Chance of delaying the consequence of an action (default: 0..0.1)") + @Option(names = { "--scheduler-delay-chance" }, description = { "Chance of delaying the consequence of an action (default: 0..0.1)", "[[distribution:]float...float]" }) protected String schedulerDelayChance = "qlog:0..0.1"; - @Option(name = { "--scheduler-delay" }, title = "int...int(s|ms|us|ns)", description = "Range of possible additional latencies thread execution can be simulated to experience when delayed (default 1..10000us)") + @Option(names = { "--scheduler-delay" }, description = { "Range of possible additional latencies thread execution can be simulated to experience when delayed (default 1..10000us)", "[int...int(s|ms|us|ns)]" }) protected String schedulerDelayMicros = "1..10000us"; - @Option(name = { "--scheduler-long-delay" }, title = "int...int(s|ms|us|ns)", description = "Range of possible additional latencies thread execution can be simulated to experience when delayed (default 1..10000us)") + @Option(names = { "--scheduler-long-delay" }, description = { "Range of possible additional latencies thread execution can be simulated to experience when delayed (default 1..10000us)", "[int...int(s|ms|us|ns)]" }) protected String schedulerLongDelayMicros = "1..10000us"; - @Option(name = { "--log" }, title = "level", description = " level events, between 0 and 2", arity = 2) + @Option(names = { "--log" }, description = " level events, between 0 and 2", arity = "2") protected List log; - @Option(name = { "--debug-keys" }, title = "level", description = "Print debug info only for these keys (comma delimited)") + @Option(names = { "--debug-keys" }, description = "Print debug info only for these keys (comma delimited)") protected String debugKeys; - @Option(name = { "--debug-rf" }, title = "level", description = "Print RF on events; level 0 to 2", arity = 2, allowedValues = { "0", "1", "2" }) + @Option(names = { "--debug-rf" }, description = { "Print RF on events; level 0 to 2", "allowed values 0|1|2" }, arity = "2") protected List debugRf; - @Option(name = { "--debug-ownership" }, title = "level", description = "Print ownership on events; level 0 to 2", arity = 2, allowedValues = { "0", "1", "2" }) + @Option(names = { "--debug-ownership" }, description = { "Print ownership on events; level 0 to 2", "allowed values 0|1|2" }, arity = "2") protected List debugOwnership; - @Option(name = { "--debug-ring" }, title = "level", description = "Print ring state on events; level 0 to 2", arity = 2, allowedValues = { "0", "1", "2" }) + @Option(names = { "--debug-ring" }, description = { "Print ring state on events; level 0 to 2", "allowed values 0|1|2" }, arity = "2") protected List debugRing; - @Option(name = { "--debug-gossip" }, title = "level", description = "Debug gossip at events; level 0 to 2", arity = 2, allowedValues = { "0", "1", "2" }) + @Option(names = { "--debug-gossip" }, description = { "Debug gossip at events; level 0 to 2", "allowed values 0|1|2" }, arity = "2") protected List debugGossip; - @Option(name = { "--debug-paxos" }, title = "level", description = "Print paxos state on events; level 0 to 2", arity = 2, allowedValues = { "0", "1", "2" }) + @Option(names = { "--debug-paxos" }, description = { "Print paxos state on events; level 0 to 2", "allowed values 0|1|2" }, arity = "2") protected List debugPaxos; - @Option(name = { "--capture" }, title = "wait,wake,now", description = "Capture thread stack traces alongside events, choose from (wait,wake,now)") + @Option(names = { "--capture" }, description = "Capture thread stack traces alongside events, choose from (wait,wake,now)") protected String capture; - @Option(name = { "--transactional-mode" }, title = "off|mixed_reads|full]", description = "Starting transactional mode for the created table") + @Option(names = { "--transactional-mode" }, description = { "Starting transactional mode for the created table", "[off|mixed_reads|full]" }) protected String transactionalMode; protected void propagate(B builder) { + checkArgumentAllowed(debugRf, 0, 1, 2); + checkArgumentAllowed(debugOwnership, 0, 1, 2); + checkArgumentAllowed(debugRing, 0, 1, 2); + checkArgumentAllowed(debugGossip, 0, 1, 2); + checkArgumentAllowed(debugPaxos, 0, 1, 2); builder.threadCount(threadCount); builder.concurrency(concurrency); if (primaryKeyCount >= 0) builder.primaryKeyCount(primaryKeyCount); @@ -346,6 +349,21 @@ public class SimulationRunner Optional.ofNullable(transactionalMode).ifPresent(builder::transactionalMode); } + @Override + public void run() + { + try + { + if (builder == null) + throw new IllegalStateException("No builder provided"); + run(builder); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + public void run(B builder) throws IOException { long seed = parseHex(Optional.ofNullable(this.seed)).orElse(new Random(System.nanoTime()).nextLong()); @@ -412,18 +430,20 @@ public class SimulationRunner @Command(name = "record") protected static class Record> extends BasicCommand { - @Option(name = {"--to"}, description = "Directory of recordings to reconcile with for the seed", required = true) + @Option(names = { "--to" }, description = "Directory of recordings to reconcile with for the seed", required = true) private String dir; - @Option(name = {"--with-rng"}, title = "0|1", description = "Record RNG values (with or without call sites)", allowedValues = {"0", "1"}) + @Option(names = { "--with-rng" }, description = { "Record RNG values (with or without call sites)", "allowed values 0|1" }) private int rng = -1; - @Option(name = {"--with-time"}, title = "0|1", description = "Record time values (with or without call sites)", allowedValues = {"0", "1"}) + @Option(names = { "--with-time" }, description = { "Record time values (with or without call sites)", "allowed values 0|1" }) private int time = -1; @Override protected void run(long seed, B builder) throws IOException { + checkArgumentAllowed(rng, 0, 1); + checkArgumentAllowed(time, 0, 1); record(dir, seed, RecordOption.values()[rng + 1], RecordOption.values()[time + 1], builder); } } @@ -431,24 +451,26 @@ public class SimulationRunner @Command(name = "reconcile") protected static class Reconcile> extends BasicCommand { - @Option(name = {"--with"}, description = "Directory of recordings to reconcile with for the seed") + @Option(names = { "--with" }, description = "Directory of recordings to reconcile with for the seed") private String dir; - @Option(name = {"--with-rng"}, title = "0|1", description = "Reconcile RNG values (if present in source)", allowedValues = {"0", "1"}) + @Option(names = { "--with-rng" }, description = { "Reconcile RNG values (if present in source)", "allowed values 0|1" }) private int rng = -1; - @Option(name = {"--with-time"}, title = "0|1", description = "Reconcile time values (if present in source)", allowedValues = {"0", "1"}) + @Option(names = { "--with-time" }, description = { "Reconcile time values (if present in source)", "allowed values 0|1" }) private int time = -1; - @Option(name = {"--with-allocations"}, description = "Reconcile memtable allocations (only with --with-self)", arity = 0) + @Option(names = { "--with-allocations" }, description = "Reconcile memtable allocations (only with --with-self)", arity = "0..1") private boolean allocations; - @Option(name = {"--with-self"}, description = "Reconcile with self", arity = 0) + @Option(names = { "--with-self" }, description = "Reconcile with self", arity = "0..1") private boolean withSelf; @Override protected void run(long seed, B builder) throws IOException { + checkArgumentAllowed(rng, 0, 1); + checkArgumentAllowed(time, 0, 1); RecordOption withRng = RecordOption.values()[rng + 1]; RecordOption withTime = RecordOption.values()[time + 1]; if (withSelf) reconcileWithSelf(seed, withRng, withTime, allocations, builder); @@ -457,26 +479,28 @@ public class SimulationRunner } } - protected static class HelpCommand> extends Help implements ICommand + public static void checkArgumentAllowed(int value, int... allowed) { - @Override - public void run(B builder) throws IOException - { - super.run(); - } + if (Arrays.stream(allowed).noneMatch(v -> v == value)) + throw new IllegalArgumentException("Value " + value + " not allowed, must be one of " + Arrays.toString(allowed)); } @Command(name = "version", description = "Display version information") - protected static class VersionCommand> implements ICommand + public static class VersionCommand implements Runnable { @Override - public void run(B builder) throws IOException + public void run() { System.out.println(FBUtilities.getReleaseVersionString()); System.out.println(FBUtilities.getGitSHA()); } } + public static void checkArgumentAllowed(List values, int... allowed) + { + values.forEach(v -> checkArgumentAllowed(v, allowed)); + } + public static Optional parseHex(Optional value) { return value.map(SimulationRunner::parseHex); diff --git a/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordClusterSimulation.java b/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordClusterSimulation.java index 4506c2de40..be6939ffd9 100644 --- a/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordClusterSimulation.java +++ b/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordClusterSimulation.java @@ -27,10 +27,10 @@ import org.apache.cassandra.simulator.utils.KindOfSequence; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.cassandra.distributed.api.ConsistencyLevel.SERIAL; -class AccordClusterSimulation extends ClusterSimulation implements AutoCloseable +public class AccordClusterSimulation extends ClusterSimulation implements AutoCloseable { @SuppressWarnings("UnusedReturnValue") - static class Builder extends ClusterSimulation.Builder + public static class Builder extends ClusterSimulation.Builder { public AccordClusterSimulation create(long seed) throws IOException { diff --git a/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordSimulationRunner.java b/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordSimulationRunner.java index 2f11720f57..5fed781d18 100644 --- a/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordSimulationRunner.java +++ b/test/simulator/main/org/apache/cassandra/simulator/paxos/AccordSimulationRunner.java @@ -23,14 +23,21 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.BeforeClass; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; import org.apache.cassandra.config.CassandraRelevantProperties; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.simulator.SimulationRunner; import org.apache.cassandra.simulator.SimulatorUtils; import org.apache.cassandra.utils.StorageCompatibilityMode; +import picocli.CommandLine; +import picocli.CommandLine.Command; +@Command(name = "accord", + description = "Run an Accord simulation", + helpCommand = true, + subcommands = { CommandLine.HelpCommand.class, + AccordSimulationRunner.Run.class, + AccordSimulationRunner.Record.class, + AccordSimulationRunner.Reconcile.class}) public class AccordSimulationRunner extends SimulationRunner { @BeforeClass @@ -79,8 +86,6 @@ public class AccordSimulationRunner extends SimulationRunner } } - public static class Help extends HelpCommand {} - // for simple unit tests so we can simply invoke main() private static final AtomicInteger uniqueNum = new AtomicInteger(); @@ -93,14 +98,7 @@ public class AccordSimulationRunner extends SimulationRunner AccordClusterSimulation.Builder builder = new AccordClusterSimulation.Builder(); builder.unique(uniqueNum.getAndIncrement()); - Cli.>builder("accord") - .withCommand(Run.class) - .withCommand(Reconcile.class) - .withCommand(Record.class) - .withCommand(Help.class) - .withDefaultCommand(Help.class) - .build() - .parse(args) - .run(builder); + CommandLine commandLine = new CommandLine(AccordSimulationRunner.class, new PaxosSimulationRunner.InjectPaxosClusterSimulationFactory(builder)); + commandLine.execute(args); } } diff --git a/test/simulator/main/org/apache/cassandra/simulator/paxos/PaxosSimulationRunner.java b/test/simulator/main/org/apache/cassandra/simulator/paxos/PaxosSimulationRunner.java index 4a9389ebca..83f33a5a56 100644 --- a/test/simulator/main/org/apache/cassandra/simulator/paxos/PaxosSimulationRunner.java +++ b/test/simulator/main/org/apache/cassandra/simulator/paxos/PaxosSimulationRunner.java @@ -19,35 +19,48 @@ package org.apache.cassandra.simulator.paxos; import java.io.IOException; +import java.lang.reflect.Field; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; -import io.airlift.airline.Option; +import javax.inject.Inject; + import org.apache.cassandra.config.Config; import org.apache.cassandra.distributed.api.ConsistencyLevel; +import org.apache.cassandra.simulator.ClusterSimulation; import org.apache.cassandra.simulator.SimulationRunner; import org.apache.cassandra.simulator.SimulatorUtils; -public class PaxosSimulationRunner extends SimulationRunner +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "paxos", + description = "Run a paxos simulation", + helpCommand = true, + subcommands = { CommandLine.HelpCommand.class, + PaxosSimulationRunner.Run.class, + PaxosSimulationRunner.VersionCommand.class, + PaxosSimulationRunner.Record.class, + PaxosSimulationRunner.Reconcile.class}) +public class PaxosSimulationRunner extends SimulationRunner implements Runnable { @Command(name = "run") public static class Run extends SimulationRunner.Run { - @Option(name = "--consistency", description = "Specify the consistency level to perform paxos operations at") + @Option(names = "--consistency", description = "Specify the consistency level to perform paxos operations at") String consistency; - @Option(name = "--with-paxos-state-cache", arity = 0, description = "Run with the paxos state cache always enabled") + @Option(names = "--with-paxos-state-cache", description = "Run with the paxos state cache always enabled") boolean withStateCache; - @Option(name = "--without-paxos-state-cache", arity = 0, description = "Run with the paxos state cache always disabled") + @Option(names = "--without-paxos-state-cache", description = "Run with the paxos state cache always disabled") boolean withoutStateCache; - @Option(name = "--variant", title = "paxos variant", description = "Specify the initial paxos variant to use") + @Option(names = "--variant", paramLabel = "from_paxos_variant", description = "Specify the initial paxos variant to use") String variant; - @Option(name = "--to-variant", title = "paxos variant", description = "Specify the paxos variant to change to at some point during the simulation") + @Option(names = "--to-variant", paramLabel = "to_paxos_variant", description = "Specify the paxos variant to change to at some point during the simulation") String toVariant; public Run() {} @@ -69,19 +82,19 @@ public class PaxosSimulationRunner extends SimulationRunner @Command(name = "record") public static class Record extends SimulationRunner.Record { - @Option(name = "--consistency") + @Option(names = "--consistency") String consistency; - @Option(name = "--with-paxos-state-cache", arity = 0) + @Option(names = "--with-paxos-state-cache") boolean withStateCache; - @Option(name = "--without-paxos-state-cache", arity = 0) + @Option(names = "--without-paxos-state-cache") boolean withoutStateCache; - @Option(name = "--variant", description = "paxos variant to use") + @Option(names = "--variant", description = "paxos variant to use") String variant; - @Option(name = "--to-variant", description = "paxos variant to change to during the simulation") + @Option(names = "--to-variant", description = "paxos variant to change to during the simulation") String toVariant; public Record() {} @@ -98,19 +111,19 @@ public class PaxosSimulationRunner extends SimulationRunner public static class Reconcile extends SimulationRunner.Reconcile { - @Option(name = "--consistency") + @Option(names = "--consistency") String consistency; - @Option(name = "--with-paxos-state-cache", arity = 0) + @Option(names = "--with-paxos-state-cache") boolean withStateCache; - @Option(name = "--without-paxos-state-cache", arity = 0) + @Option(names = "--without-paxos-state-cache") boolean withoutStateCache; - @Option(name = "--variant", description = "paxos variant to use") + @Option(names = "--variant", description = "paxos variant to use") String variant; - @Option(name = "--to-variant", description = "paxos variant to change to during the simulation") + @Option(names = "--to-variant", description = "paxos variant to change to during the simulation") String toVariant; public Reconcile() {} @@ -123,9 +136,6 @@ public class PaxosSimulationRunner extends SimulationRunner } } - public static class Help extends HelpCommand {} - public static class Version extends VersionCommand {} - static void propagateTo(String consistency, boolean withStateCache, boolean withoutStateCache, String variant, String toVariant, PaxosClusterSimulation.Builder builder) { Optional.ofNullable(consistency).map(ConsistencyLevel::valueOf).ifPresent(builder::consistency); @@ -138,6 +148,59 @@ public class PaxosSimulationRunner extends SimulationRunner // for simple unit tests so we can simply invoke main() private static final AtomicInteger uniqueNum = new AtomicInteger(); + @Override + public void run() + { + CommandLine.usage(this, System.out); + } + + public static class InjectPaxosClusterSimulationFactory implements CommandLine.IFactory + { + private final CommandLine.IFactory defaultFactory = CommandLine.defaultFactory(); + private final ClusterSimulation.Builder builder; + + public InjectPaxosClusterSimulationFactory(ClusterSimulation.Builder builder) + { + this.builder = builder; + } + + @Override + public K create(Class cls) throws Exception + { + try + { + K bean = this.defaultFactory.create(cls); + Class beanClass = bean.getClass(); + do + { + Field[] fields = beanClass.getDeclaredFields(); + for (Field field : fields) + { + if (!field.isAnnotationPresent(Inject.class)) + continue; + if (field.getType().equals(ClusterSimulation.Builder.class)) + { + field.setAccessible(true); + field.set(bean, builder); + } + else + { + throw new RuntimeException("Unsupported injectable field type: " + field.getType() + + " in class " + beanClass.getName() + ". " + + "Only INodeProbeFactory and Output are supported."); + } + } + } + while ((beanClass = beanClass.getSuperclass()) != null); + return bean; + } + catch (Exception e) + { + throw new CommandLine.InitializationException("Failed to create instance of " + cls, e); + } + } + } + /** * See {@link org.apache.cassandra.simulator} package info for execution tips */ @@ -147,15 +210,7 @@ public class PaxosSimulationRunner extends SimulationRunner PaxosClusterSimulation.Builder builder = new PaxosClusterSimulation.Builder(); builder.unique(uniqueNum.getAndIncrement()); - Cli.>builder("paxos") - .withCommand(Run.class) - .withCommand(Reconcile.class) - .withCommand(Record.class) - .withCommand(Version.class) - .withCommand(Help.class) - .withDefaultCommand(Help.class) - .build() - .parse(args) - .run(builder); + CommandLine commandLine = new CommandLine(PaxosSimulationRunner.class, new InjectPaxosClusterSimulationFactory(builder)); + commandLine.execute(args); } } diff --git a/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java b/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java index 3682d28cba..ba4d0d9caf 100644 --- a/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java +++ b/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java @@ -35,17 +35,12 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import javax.inject.Inject; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import accord.utils.Invariants; -import io.airlift.airline.Command; -import io.airlift.airline.HelpOption; -import io.airlift.airline.Option; -import io.airlift.airline.SingleCommand; import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.distributed.Cluster; import org.apache.cassandra.distributed.api.ConsistencyLevel; @@ -107,6 +102,9 @@ import org.apache.cassandra.tcm.sequences.SingleNodeSequences; import org.apache.cassandra.tcm.transformations.PrepareJoin; import org.apache.cassandra.utils.CloseableIterator; import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.cassandra.distributed.api.ConsistencyLevel.ALL; @@ -183,27 +181,36 @@ import static org.apache.cassandra.simulator.cluster.ClusterActions.Options.noAc --add-opens jdk.management.jfr/jdk.management.jfr=ALL-UNNAMED --add-opens java.desktop/com.sun.beans.introspect=ALL-UNNAMED */ -@Command(name = "harry", description = "Harry simulation test") -public class HarrySimulatorTest +@Command(name = "harry", description = "Harry simulation test", mixinStandardHelpOptions = true) +public class HarrySimulatorTest implements Runnable { private static final Logger logger = LoggerFactory.getLogger(HarrySimulatorTest.class); - @Inject - public HelpOption helpOption; - @Option(name = { "-r", "--rows-per-phase"}, description = "Number of rows to check at each phase of the test") + @Option(names = { "-r", "--rows-per-phase" }, description = "Number of rows to check at each phase of the test") public int rowsPerPhase = 10; - @Option(name = {"--nodes-per-dc"}, description = "How many nodes per dc for replication") + @Option(names = { "--nodes-per-dc" }, description = "How many nodes per dc for replication") public int nodesPerDc = 3; - @Option(name = {"-s", "--seed"}, title = "0x", description = "What seed to run with; in hex format... example: 0x190e6ff01d6") + @Option(names = { "-s", "--seed" }, description = "What seed to run with; in hex format... example: 0x190e6ff01d6") public String seed = null; public static void main(String... args) throws Throwable { - HarrySimulatorTest test = SingleCommand.singleCommand(HarrySimulatorTest.class).parse(args); - if (test.helpOption.showHelpIfRequested()) - return; - test.testInternal(); - System.exit(1); + CommandLine commandLine = new CommandLine(HarrySimulatorTest.class); + commandLine.execute(args); + } + + @Override + public void run() + { + try + { + testInternal(); + } + catch (Throwable t) + { + logger.error("Error running simulation", t); + throw new RuntimeException(t); + } } @Test diff --git a/test/unit/org/apache/cassandra/db/CounterCacheTest.java b/test/unit/org/apache/cassandra/db/CounterCacheTest.java index 094e7a45f7..23e91e3793 100644 --- a/test/unit/org/apache/cassandra/db/CounterCacheTest.java +++ b/test/unit/org/apache/cassandra/db/CounterCacheTest.java @@ -25,11 +25,11 @@ import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.SchemaLoader; +import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.db.marshal.CounterColumnType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.dht.Bounds; import org.apache.cassandra.dht.Token; -import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.WriteTimeoutException; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.KeyspaceMetadata; @@ -38,22 +38,26 @@ import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.SchemaTestUtil; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.service.CacheService; +import org.apache.cassandra.tools.ToolRunner; import org.apache.cassandra.utils.ByteBufferUtil; import static org.apache.cassandra.utils.ByteBufferUtil.bytes; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +/** + * @see org.apache.cassandra.tools.nodetool.InvalidateCounterCache + */ public class CounterCacheTest { private static final String KEYSPACE1 = "CounterCacheTest"; private static final String COUNTER1 = "Counter1"; @BeforeClass - public static void defineSchema() throws ConfigurationException + public static void defineSchema() throws Exception { SchemaLoader.prepareServer(); - + CQLTester.startJMXServer(); TableMetadata.Builder counterTable = TableMetadata.builder(KEYSPACE1, COUNTER1) .isCounter(true) @@ -68,6 +72,7 @@ public class CounterCacheTest public static void cleanup() { SchemaLoader.cleanupSavedCaches(); + CQLTester.tearDownClass(); } @Test @@ -75,7 +80,7 @@ public class CounterCacheTest { ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(COUNTER1); cfs.truncateBlocking(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); Clustering c1 = CBuilder.create(cfs.metadata().comparator).add(ByteBufferUtil.bytes(1)).build(); Clustering c2 = CBuilder.create(cfs.metadata().comparator).add(ByteBufferUtil.bytes(2)).build(); @@ -103,7 +108,7 @@ public class CounterCacheTest { ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(COUNTER1); cfs.truncateBlocking(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); Clustering c1 = CBuilder.create(cfs.metadata().comparator).add(ByteBufferUtil.bytes(1)).build(); Clustering c2 = CBuilder.create(cfs.metadata().comparator).add(ByteBufferUtil.bytes(2)).build(); @@ -148,7 +153,7 @@ public class CounterCacheTest { ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(COUNTER1); cfs.truncateBlocking(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); new CounterMutation(new RowUpdateBuilder(cfs.metadata(), 0, bytes(1)).clustering(1).add("c", 1L).build(), ConsistencyLevel.ONE).apply(); new CounterMutation(new RowUpdateBuilder(cfs.metadata(), 0, bytes(1)).clustering(2).add("c", 2L).build(), ConsistencyLevel.ONE).apply(); @@ -159,7 +164,7 @@ public class CounterCacheTest // flush the counter cache and invalidate CacheService.instance.counterCache.submitWrite(Integer.MAX_VALUE).get(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); assertEquals(0, CacheService.instance.counterCache.size()); // load from cache and validate @@ -181,7 +186,7 @@ public class CounterCacheTest { ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(COUNTER1); cfs.truncateBlocking(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); new CounterMutation(new RowUpdateBuilder(cfs.metadata(), 0, bytes(1)).clustering(1).add("c", 1L).build(), ConsistencyLevel.ONE).apply(); new CounterMutation(new RowUpdateBuilder(cfs.metadata(), 0, bytes(1)).clustering(2).add("c", 2L).build(), ConsistencyLevel.ONE).apply(); @@ -190,7 +195,7 @@ public class CounterCacheTest // flush the counter cache and invalidate CacheService.instance.counterCache.submitWrite(Integer.MAX_VALUE).get(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); assertEquals(0, CacheService.instance.counterCache.size()); KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(KEYSPACE1); @@ -213,7 +218,7 @@ public class CounterCacheTest { ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(COUNTER1); cfs.truncateBlocking(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); new CounterMutation(new RowUpdateBuilder(cfs.metadata(), 0, bytes(1)).clustering(1).add("c", 1L).build(), ConsistencyLevel.ONE).apply(); new CounterMutation(new RowUpdateBuilder(cfs.metadata(), 0, bytes(1)).clustering(2).add("c", 2L).build(), ConsistencyLevel.ONE).apply(); @@ -222,7 +227,7 @@ public class CounterCacheTest // flush the counter cache and invalidate CacheService.instance.counterCache.submitWrite(Integer.MAX_VALUE).get(); - CacheService.instance.invalidateCounterCache(); + ToolRunner.invokeNodetoolInJvm("invalidatecountercache").assertOnCleanExit(); assertEquals(0, CacheService.instance.counterCache.size()); diff --git a/test/unit/org/apache/cassandra/db/ImportTest.java b/test/unit/org/apache/cassandra/db/ImportTest.java index 4d7a9efe31..8b0faf598e 100644 --- a/test/unit/org/apache/cassandra/db/ImportTest.java +++ b/test/unit/org/apache/cassandra/db/ImportTest.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -32,6 +33,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.Util; @@ -60,17 +62,28 @@ import org.apache.cassandra.tcm.membership.NodeAddresses; import org.apache.cassandra.tcm.membership.NodeState; import org.apache.cassandra.tcm.transformations.Register; import org.apache.cassandra.tcm.transformations.UnsafeJoin; +import org.apache.cassandra.tools.ToolRunner; import org.apache.cassandra.utils.ByteBufferUtil; import static org.apache.lucene.codecs.CodecUtil.FOOTER_MAGIC; import static org.apache.lucene.codecs.CodecUtil.writeBEInt; import static org.apache.lucene.codecs.CodecUtil.writeBELong; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +/** + * @see org.apache.cassandra.tools.nodetool.Import + */ public class ImportTest extends CQLTester { + @BeforeClass + public static void setup() throws Throwable + { + startJMXServer(); + } @Override public void afterTest() throws Throwable @@ -491,9 +504,7 @@ public class ImportTest extends CQLTester .verifySSTables(true) .verifyTokens(true) .extendedVerify(true).build(); - SSTableImporter importer = new SSTableImporter(getCurrentColumnFamilyStore()); - List failedDirectories = importer.importNewSSTables(options); - assertEquals(Collections.singletonList(backupdir.toString()), failedDirectories); + assertThat(assertImportFailed(getCurrentColumnFamilyStore(), options).getStderr()).contains(backupdir.toString()); } @@ -669,17 +680,7 @@ public class ImportTest extends CQLTester assertEquals(0, execute("select * from %s").size()); SSTableImporter.Options options = SSTableImporter.Options.options(Sets.newHashSet(backupdir.toString(), backupdir2.toString(), "/tmp/DOESNTEXIST")).build(); - SSTableImporter importer = new SSTableImporter(getCurrentColumnFamilyStore()); - boolean gotException = false; - try - { - importer.importNewSSTables(options); - } - catch (Throwable t) - { - gotException = true; - } - assertTrue(gotException); + assertThat(assertImportFailed(getCurrentColumnFamilyStore(), options).getStderr()).contains("error: Directory /tmp/DOESNTEXIST does not exist"); assertEquals(0, execute("select * from %s").size()); assertEquals(0, getCurrentColumnFamilyStore().getLiveSSTables().size()); } @@ -710,10 +711,8 @@ public class ImportTest extends CQLTester // copy is true - so importing will be done by copying - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()).copyData(true).build(); - List failedDirectories = importer.importNewSSTables(options); - assertTrue(failedDirectories.isEmpty()); + assertImportSuccessfull(cfs, options); assertEquals(10, execute(String.format("select * from %s.%s", KEYSPACE, table)).size()); // files are left there as they were just copied @@ -754,10 +753,8 @@ public class ImportTest extends CQLTester assertEquals(0, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, table)).size()); - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()).copyData(true).build(); - List failedDirectories = importer.importNewSSTables(options); - assertTrue(failedDirectories.isEmpty()); + assertImportSuccessfull(cfs, options); assertEquals(10, execute(String.format("select * from %s.%s", KEYSPACE, table)).size()); // files are left there as they were just copied @@ -790,12 +787,11 @@ public class ImportTest extends CQLTester assertEquals(0, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_test")).size()); - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()) .copyData(true) .failOnMissingIndex(true) .build(); - assertTrue(importer.importNewSSTables(options).isEmpty()); + assertImportSuccessfull(cfs, options); assertEquals(10, execute(String.format("SELECT * FROM %s.%s WHERE d >= 0", KEYSPACE, "sai_test")).size()); } finally @@ -824,14 +820,13 @@ public class ImportTest extends CQLTester assertEquals(0, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_less_test")).size()); - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()) .copyData(true) // this does not mean anything // because our table does not have any SAI index .failOnMissingIndex(true) .build(); - assertTrue(importer.importNewSSTables(options).isEmpty()); + assertImportSuccessfull(cfs, options); assertEquals(10, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_less_test")).size()); } finally @@ -865,12 +860,11 @@ public class ImportTest extends CQLTester // data when index was not created yet) schemaChange(String.format("CREATE INDEX idx1 ON %s.%s (d) USING 'sai'", KEYSPACE, "sai_test")); - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()) .copyData(true) .failOnMissingIndex(true) .build(); - assertFalse(importer.importNewSSTables(options).isEmpty()); + assertThat(assertImportFailed(cfs, options).getStderr()).contains("Some directories failed to import, check server logs for details"); assertEquals(0, execute(String.format("SELECT * FROM %s.%s WHERE d >= 0", KEYSPACE, "sai_test")).size()); } finally @@ -924,7 +918,6 @@ public class ImportTest extends CQLTester assertEquals(0, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_test")).size()); - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()) .copyData(true) .failOnMissingIndex(true) @@ -932,7 +925,7 @@ public class ImportTest extends CQLTester .build(); // even with corrupted column completion marker (wrong checksum), it will import - assertTrue(importer.importNewSSTables(options).isEmpty()); + assertImportSuccessfull(cfs, options); assertEquals(10, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_test")).size()); } finally @@ -962,14 +955,12 @@ public class ImportTest extends CQLTester File backupDir = moveToBackupDir(sstables); assertEquals(0, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_test")).size()); - - SSTableImporter importer = new SSTableImporter(cfs); SSTableImporter.Options options = SSTableImporter.Options.options(backupDir.toString()) .copyData(true) .failOnMissingIndex(true) .validateIndexChecksum(true) .build(); - assertTrue(importer.importNewSSTables(options).isEmpty()); + assertImportSuccessfull(cfs, options); assertEquals(10, execute(String.format("SELECT * FROM %s.%s", KEYSPACE, "sai_test")).size()); } finally @@ -978,6 +969,50 @@ public class ImportTest extends CQLTester } } + private static void assertImportSuccessfull(ColumnFamilyStore cfs, SSTableImporter.Options options) + { + ToolRunner.ToolResult result = doImportWithNodetool(cfs, options); + result.assertOnCleanExit(); + assertThat(result.getStderr()).doesNotContain("Some directories failed to import, check server logs for details"); + } + + private static ToolRunner.ToolResult assertImportFailed(ColumnFamilyStore cfs, SSTableImporter.Options options) + { + ToolRunner.ToolResult result = doImportWithNodetool(cfs, options); + assertNotEquals(0, result.getExitCode()); + return result; + } + + private static ToolRunner.ToolResult doImportWithNodetool(ColumnFamilyStore cfs, SSTableImporter.Options options) + { + List args = new ArrayList<>(); + args.add("import"); + if (!options.resetLevel) + args.add("--keep-level"); + if (!options.clearRepaired) + args.add("--keep-repaired"); + if (!options.verifySSTables) + args.add("--no-verify"); + if (!options.verifyTokens) + args.add("--no-tokens"); + if (!options.invalidateCaches) + args.add("--no-invalidate-caches"); + if (options.extendedVerify) + args.add("--extended-verify"); + if (options.copyData) + args.add("--copy-data"); + if (options.failOnMissingIndex) + args.add("--require-index-components"); + if (!options.validateIndexChecksum) + args.add("--no-index-validation"); + + args.add(cfs.keyspace.getName()); + args.add(cfs.getTableName()); + args.addAll(options.srcPaths); + + return ToolRunner.invokeNodetoolInJvm(args.toArray(new String[0])); + } + private static class MockCFS extends ColumnFamilyStore { public MockCFS(ColumnFamilyStore cfs, Directories dirs) diff --git a/test/unit/org/apache/cassandra/hints/HintsMaker.java b/test/unit/org/apache/cassandra/hints/HintsMaker.java index a89baa6a02..e68ca8754a 100644 --- a/test/unit/org/apache/cassandra/hints/HintsMaker.java +++ b/test/unit/org/apache/cassandra/hints/HintsMaker.java @@ -32,9 +32,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.UpdateBuilder; import org.apache.cassandra.Util; @@ -50,7 +47,14 @@ import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.memory.MemoryUtil; -public class HintsMaker +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "hintsmaker", + description = "Generate hints file", + subcommands = { CommandLine.HelpCommand.class, HintsMaker.MakeHint.class }) +public class HintsMaker implements Runnable { public static void main(String[] args) { @@ -59,21 +63,18 @@ public class HintsMaker public int execute(String[] args) { - Cli.CliBuilder builder = Cli.builder("hintsmaker") - .withCommands(MakeHint.class) - .withDefaultCommand(MakeHint.class); + CommandLine commandLine = new CommandLine(HintsMaker.class); + commandLine.setExecutionExceptionHandler((ex, c, arg) -> { + ex.printStackTrace(System.err); + return 1; + }); + return commandLine.execute(args); + } - try - { - builder.build().parse(args).run(); - return 0; - } - catch (Throwable t) - { - t.printStackTrace(System.err); - } - - return 1; + @Override + public void run() + { + CommandLine.usage(this, System.out); } @Command(name = "make", description = "make file of hints") @@ -93,25 +94,25 @@ public class HintsMaker private static ByteBuffer dataSource; - @Option(name = "dir") + @Option(names = "dir") private String dir = DATA_DIR; - @Option(name = HOST_ID_PROPERTY) + @Option(names = HOST_ID_PROPERTY) private UUID hostId = UUID.randomUUID(); - @Option(name = "maxLength") // 1MB by default + @Option(names = "maxLength") // 1MB by default private long maxLength = 1024 * 1024; - @Option(name = "randomSize") + @Option(names = "randomSize") private boolean randomSize; - @Option(name = "cellSize") + @Option(names = "cellSize") private int cellSize = 256; - @Option(name = "numCells") + @Option(names = "numCells") private int numCells = 1; - @Option(name = DESCRIPTOR_TIMESTAMP_PROPERTY) + @Option(names = DESCRIPTOR_TIMESTAMP_PROPERTY) private long descriptorTimestamp = System.currentTimeMillis(); public void run() diff --git a/test/unit/org/apache/cassandra/repair/FuzzTestBase.java b/test/unit/org/apache/cassandra/repair/FuzzTestBase.java index 72148701de..7e4254750c 100644 --- a/test/unit/org/apache/cassandra/repair/FuzzTestBase.java +++ b/test/unit/org/apache/cassandra/repair/FuzzTestBase.java @@ -51,7 +51,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; - import org.junit.BeforeClass; import accord.utils.DefaultRandom; @@ -129,8 +128,8 @@ import org.apache.cassandra.service.paxos.cleanup.PaxosCleanupComplete; import org.apache.cassandra.service.paxos.cleanup.PaxosCleanupHistory; import org.apache.cassandra.service.paxos.cleanup.PaxosCleanupRequest; import org.apache.cassandra.service.paxos.cleanup.PaxosCleanupResponse; -import org.apache.cassandra.service.paxos.cleanup.PaxosRepairState; import org.apache.cassandra.service.paxos.cleanup.PaxosFinishPrepareCleanup; +import org.apache.cassandra.service.paxos.cleanup.PaxosRepairState; import org.apache.cassandra.service.paxos.cleanup.PaxosStartPrepareCleanup; import org.apache.cassandra.streaming.StreamEventHandler; import org.apache.cassandra.streaming.StreamReceiveException; @@ -157,6 +156,7 @@ import org.apache.cassandra.utils.progress.ProgressEventType; import org.assertj.core.api.Assertions; import org.mockito.Mockito; import org.quicktheories.impl.JavaRandom; +import picocli.CommandLine; import static org.apache.cassandra.config.CassandraRelevantProperties.ACCORD_REPAIR_RANGE_STEP_UPDATE_INTERVAL; import static org.apache.cassandra.config.CassandraRelevantProperties.CLOCK_GLOBAL; @@ -554,14 +554,17 @@ public abstract class FuzzTestBase extends CQLTester.InMemory private static RepairOption repairOption(RandomSource rs, Cluster.Node coordinator, String ks, Gen> tablesGen, Gen repairTypeGen, Gen previewTypeGen, Gen repairParallelismGen) { + Repair repair = new Repair(); List args = new RepairGenerators.Builder(tablesGen.map(l -> ImmutableList.builderWithExpectedSize(l.size() + 1).add(ks).addAll(l).build())) - .withType(repairTypeGen) - .withPreviewType(previewTypeGen) - .withParallelism(repairParallelismGen) - .withRanges(i -> RepairGenerators.PRIMARY_RANGE) - .build() - .next(rs); - RepairOption options = RepairOption.parse(Repair.parseOptionMap(() -> "test", args), DatabaseDescriptor.getPartitioner()); + .withType(repairTypeGen) + .withPreviewType(previewTypeGen) + .withParallelism(repairParallelismGen) + .withRanges(i -> RepairGenerators.PRIMARY_RANGE) + .build() + .next(rs); + new CommandLine(repair).parseArgs(args.toArray(new String[0])); + RepairOption options = RepairOption.parse(repair.createOptions(() -> "test", repair.tables), DatabaseDescriptor.getPartitioner()); + if (options.getRanges().isEmpty()) { if (options.isPrimaryRange()) diff --git a/test/unit/org/apache/cassandra/service/StorageServiceDrainTest.java b/test/unit/org/apache/cassandra/service/StorageServiceDrainTest.java index ca1e62e9ad..8f10205487 100644 --- a/test/unit/org/apache/cassandra/service/StorageServiceDrainTest.java +++ b/test/unit/org/apache/cassandra/service/StorageServiceDrainTest.java @@ -18,10 +18,12 @@ package org.apache.cassandra.service; -import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; @@ -29,13 +31,16 @@ import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.ServerTestUtils; import org.apache.cassandra.Util; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.RowUpdateBuilder; import org.apache.cassandra.db.commitlog.CommitLog; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.schema.KeyspaceParams; +import org.apache.cassandra.tools.ToolRunner; import org.apache.cassandra.utils.ByteBufferUtil; +import org.awaitility.Awaitility; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertTrue; @@ -48,7 +53,7 @@ public class StorageServiceDrainTest private static final int ROWS = 1000; @Before - public void before() throws UnknownHostException + public void before() throws Exception { ServerTestUtils.prepareServerNoRegister(); DatabaseDescriptor.setTransientReplicationEnabledUnsafe(true); @@ -58,9 +63,12 @@ public class StorageServiceDrainTest CompactionManager.instance.disableAutoCompaction(); SchemaLoader.prepareServer(); + CQLTester.startJMXServer(); + StorageService.instance.registerMBeans(); SchemaLoader.createKeyspace(KEYSPACE, KeyspaceParams.simple(1), SchemaLoader.standardCFMD(KEYSPACE, TABLE)); StorageService.instance.unsafeSetInitialized(); + final ColumnFamilyStore table = Keyspace.open(KEYSPACE).getColumnFamilyStore(TABLE); for (int row = 0; row < ROWS; row++) { @@ -74,6 +82,12 @@ public class StorageServiceDrainTest Util.flush(table); } + @AfterClass + public static void afterClass() + { + CQLTester.tearDownClass(); + } + @Test public void testSSTablesImportAbort() { @@ -86,15 +100,16 @@ public class StorageServiceDrainTest Executors.newSingleThreadExecutor().execute(() -> { try { - StorageService.instance.drain(); + ToolRunner.invokeNodetoolInJvm("drain").assertOnCleanExit(); } catch (final Exception exception) { throw new RuntimeException(exception); }}); - while (!StorageService.instance.isDraining()) - Thread.yield(); + Awaitility.await() + .atMost(30, TimeUnit.SECONDS) + .until(StorageService.instance::isDraining); assertThatThrownBy(() -> table .importNewSSTables(Collections.emptySet(), false, false, false, false, false, false, false)) diff --git a/test/unit/org/apache/cassandra/tools/JMXToolTest.java b/test/unit/org/apache/cassandra/tools/JMXToolTest.java index dbd18f3b9d..5af7942438 100644 --- a/test/unit/org/apache/cassandra/tools/JMXToolTest.java +++ b/test/unit/org/apache/cassandra/tools/JMXToolTest.java @@ -58,15 +58,14 @@ public class JMXToolTest result.assertOnCleanExit(); Assertions.assertThat(result.getStdout()) - .isEqualTo("usage: jmxtool []\n" + - "\n" + - "The most commonly used jmxtool commands are:\n" + - " diff Diff two jmx dump files and report their differences\n" + - " dump Dump the Apache Cassandra JMX objects and metadata.\n" + - " help Display help information\n" + - "\n" + - "See 'jmxtool help ' for more information on a specific command.\n" + - "\n"); + .isEqualTo("Usage: jmxtool [-hV] [COMMAND]\n" + + "JMX tool for Apache Cassandra\n" + + " -h, --help Show this help message and exit.\n" + + " -V, --version Print version information and exit.\n" + + "Commands:\n" + + " help Display help information about the specified command.\n" + + " dump Dump the Apache Cassandra JMX objects and metadata.\n" + + " diff Diff two jmx dump files and report their differences\n"); } @Test @@ -76,51 +75,33 @@ public class JMXToolTest result.assertOnCleanExit(); Assertions.assertThat(result.getStdout()) - .isEqualTo("NAME\n" + - " jmxtool diff - Diff two jmx dump files and report their differences\n" + - "\n" + - "SYNOPSIS\n" + - " jmxtool diff [--exclude-attribute ...]\n" + - " [--exclude-object ...]\n" + - " [--exclude-operation ...]\n" + - " [(-f | --format )] [(-h | --help)]\n" + - " [--ignore-missing-on-left] [--ignore-missing-on-right] [--] \n" + - " \n" + - "\n" + - "OPTIONS\n" + - " --exclude-attribute \n" + - " Ignores processing specific attributes. Each usage should take a\n" + - " single attribute, but can use this flag multiple times.\n" + - "\n" + - " --exclude-object \n" + - " Ignores processing specific objects. Each usage should take a single\n" + - " object, but can use this flag multiple times.\n" + - "\n" + - " --exclude-operation \n" + - " Ignores processing specific operations. Each usage should take a\n" + - " single operation, but can use this flag multiple times.\n" + - "\n" + - " -f , --format \n" + - " What format the files are in; only support json and yaml as format\n" + - "\n" + - " -h, --help\n" + - " Display help information\n" + - "\n" + - " --ignore-missing-on-left\n" + - " Ignore results missing on the left\n" + - "\n" + - " --ignore-missing-on-right\n" + - " Ignore results missing on the right\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " \n" + - " Files to diff\n" + - "\n" + - "\n"); + .isEqualTo("Usage: jmxtool diff [-hV] [--ignore-missing-on-left]\n" + + " [--ignore-missing-on-right] [-f=format]\n" + + " [--exclude-attribute=exclude_attributes]...\n" + + " [--exclude-object=exclude_objects]...\n" + + " [--exclude-operation=exclud_operations]... [...]\n" + + "Diff two jmx dump files and report their differences\n" + + " [...] Files to diff\n" + + " --exclude-attribute=exclude_attributes\n" + + " Ignores processing specific attributes. Each usage\n" + + " should take a single attribute, but can use this flag\n" + + " multiple times.\n" + + " --exclude-object=exclude_objects\n" + + " Ignores processing specific objects. Each usage should\n" + + " take a single object, but can use this flag multiple\n" + + " times.\n" + + " --exclude-operation=exclud_operations\n" + + " Ignores processing specific operations. Each usage\n" + + " should take a single operation, but can use this flag\n" + + " multiple times.\n" + + " -f, --format=format What format the files are in; only support json and\n" + + " yaml as format\n" + + " -h, --help Show this help message and exit.\n" + + " --ignore-missing-on-left\n" + + " Ignore results missing on the left\n" + + " --ignore-missing-on-right\n" + + " Ignore results missing on the right\n" + + " -V, --version Print version information and exit.\n"); } @Test @@ -130,25 +111,13 @@ public class JMXToolTest result.assertOnCleanExit(); Assertions.assertThat(result.getStdout()) - .isEqualTo("NAME\n" + - " jmxtool dump - Dump the Apache Cassandra JMX objects and metadata.\n" + - "\n" + - "SYNOPSIS\n" + - " jmxtool dump [(-f | --format )] [(-h | --help)]\n" + - " [(-u | --url )]\n" + - "\n" + - "OPTIONS\n" + - " -f , --format \n" + - " What format to dump content as; supported values are console\n" + - " (default), json, and yaml\n" + - "\n" + - " -h, --help\n" + - " Display help information\n" + - "\n" + - " -u , --url \n" + - " JMX url to target\n" + - "\n" + - "\n"); + .isEqualTo("Usage: jmxtool dump [-hV] [-f=format] [-u=url]\n" + + "Dump the Apache Cassandra JMX objects and metadata.\n" + + " -f, --format=format What format to dump content as; supported values are\n" + + " console (default), json, and yaml\n" + + " -h, --help Show this help message and exit.\n" + + " -u, --url=url JMX url to target\n" + + " -V, --version Print version information and exit.\n"); } private static ToolResult jmxtool(String... args) diff --git a/test/unit/org/apache/cassandra/tools/ToolRunner.java b/test/unit/org/apache/cassandra/tools/ToolRunner.java index 666cc6f91b..cb2e5b06bd 100644 --- a/test/unit/org/apache/cassandra/tools/ToolRunner.java +++ b/test/unit/org/apache/cassandra/tools/ToolRunner.java @@ -32,13 +32,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,6 +50,7 @@ import org.apache.cassandra.distributed.api.NodeToolResult; import org.apache.cassandra.utils.Pair; import org.assertj.core.util.Lists; +import static com.github.jknack.handlebars.internal.lang3.ArrayUtils.isEmpty; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -192,6 +195,21 @@ public class ToolRunner return invoke(CQLTester.buildNodetoolArgs(args)); } + public static ToolResult invokeNodetool(Map env, String... args) + { + return invokeNodetool(env, Arrays.asList(args)); + } + + public static ToolResult invokeNodetool(Map env, List args) + { + return invoke(env, CQLTester.buildNodetoolArgs(args)); + } + + public static ToolRunner.ToolResult invokeNodetoolInJvm(String... args) + { + return ToolRunner.invokeNodetoolInJvm(NodeTool::new, args); + } + public static ToolResult invoke(List args) { return invoke(args.toArray(new String[args.size()])); @@ -284,8 +302,24 @@ public class ToolRunner res.right.getException()); } - + + public static ToolResult invokeNodetoolJvmDtestIsolated(IInstance node, String... args) + { + return invokeNodetoolJvmDtest(node, o -> o.left.getStdout(), e -> e.left.getStderr(), args); + } + public static ToolResult invokeNodetoolJvmDtest(IInstance node, String... args) + { + return invokeNodetoolJvmDtest(node, + res -> res.right.getStdout() + res.left.getStdout(), + res -> res.right.getStderr() + res.left.getStderr(), + args); + } + + private static ToolResult invokeNodetoolJvmDtest(IInstance node, + Function, String> stdout, + Function, String> stderr, + String... args) { Supplier runMe = new Supplier() { @@ -301,11 +335,64 @@ public class ToolRunner return new ToolResult(Arrays.asList(args), res.left, res.right.getExitCode() == -1 ? -1 : res.left.getRc(), - res.right.getStdout() + res.left.getStdout(), - res.right.getStderr() + res.left.getStderr(), + stdout.apply(res), + stderr.apply(res), res.right.getException()); } + public static ToolRunner.ToolResult invokeNodetoolInJvm(BiFunction nodeTool, String... args) + { + PrintStream originalSysOut = System.out; + PrintStream originalSysErr = System.err; + LinesOutputStream out = new LinesOutputStream(logger::info); + LinesOutputStream err = new LinesOutputStream(logger::error); + PrintStream printOut = new PrintStream(out); + PrintStream printErr = new PrintStream(err); + Output output = new Output(printOut, printErr); + List clearedArgs = CQLTester.buildNodetoolArgs(isEmpty(args) ? new ArrayList<>() : List.of(args)); + clearedArgs.remove("bin/nodetool"); + try + { + Object runner = nodeTool.apply(new INodeProbeFactory() + { + private final INodeProbeFactory delegate = new NodeProbeFactory(); + + @Override + public NodeProbe create(String host, int port) throws IOException + { + NodeProbe probe = delegate.create(host, port); + probe.setOutput(output); + return probe; + } + + @Override + public NodeProbe create(String host, int port, String username, String password) throws IOException + { + NodeProbe probe = delegate.create(host, port, username, password); + probe.setOutput(output); + return probe; + } + }, output); + + System.setOut(printOut); + System.setErr(printErr); + Object result = runner.getClass().getMethod("execute", String[].class) + .invoke(runner, new Object[] { clearedArgs.toArray(new String[0]) }); + assertTrue(result instanceof Integer); + return new ToolResult(clearedArgs, (Integer) result, out.getOutput(), err.getOutput(), null); + } + catch (Exception e) + { + return new ToolResult(clearedArgs, -1, out.getOutput(), + err.getOutput() + '\n' + Throwables.getStackTraceAsString(e), e); + } + finally + { + System.setOut(originalSysOut); + System.setErr(originalSysErr); + } + } + public static Pair invokeSupplier(Supplier runMe) { return invokeSupplier(runMe, null); @@ -740,4 +827,47 @@ public class ToolRunner return ToolRunner.invoke(env, stdin, args); } } + + private static class LinesOutputStream extends OutputStream + { + private final List outputLines = new ArrayList<>(); + private final StringBuilder buffer = new StringBuilder(); + private final Consumer logger; + + public LinesOutputStream(Consumer logger) + { + this.logger = logger; + } + + @Override + public void write(int b) + { + char c = (char) b; + if (c == '\n') + { + // Add the buffer to the list if it's a new line + outputLines.add(buffer.toString()); + logger.accept(buffer.toString()); + buffer.setLength(0); // Clear the buffer + } + else + buffer.append(c); + } + + public void flush() + { + if (buffer.length() > 0) + { + outputLines.add(buffer.toString()); + logger.accept(buffer.toString()); + buffer.setLength(0); + } + } + + public String getOutput() + { + flush(); + return String.join("\n", outputLines); + } + } } diff --git a/test/unit/org/apache/cassandra/tools/nodetool/CIDRFilteringStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/CIDRFilteringStatsTest.java index 091a439a8d..dc0abc3bd2 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/CIDRFilteringStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/CIDRFilteringStatsTest.java @@ -64,46 +64,6 @@ public class CIDRFilteringStatsTest extends CQLTester CIDR.getInstance("10.20.30.5/24")))); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "cidrfilteringstats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool cidrfilteringstats - Print statistics on CIDR filtering\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] cidrfilteringstats\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testCIDRFilteringStats() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/ClientStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/ClientStatsTest.java index ff1b16d6db..1f599763ea 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/ClientStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/ClientStatsTest.java @@ -141,61 +141,6 @@ public class ClientStatsTest cassandra.stop(); } - @Test - public void testClientStatsHelp() - { - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "clientstats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool clientstats - Print information about connected clients\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] clientstats [--all]\n" + - " [--by-protocol] [--clear-history] [--client-options] [--verbose]\n" + - "\n" + - "OPTIONS\n" + - " --all\n" + - " Lists all connections\n" + - "\n" + - " --by-protocol\n" + - " Lists most recent client connections by protocol version\n" + - "\n" + - " --clear-history\n" + - " Clear the history of connected clients\n" + - "\n" + - " --client-options\n" + - " Lists all connections and the client options\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --verbose\n" + - " Lists all connections with additional details (client options,\n" + - " authenticator-specific metadata and more)\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testClientStats() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/CompactTest.java b/test/unit/org/apache/cassandra/tools/nodetool/CompactTest.java index 928f8851e8..1763147547 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/CompactTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/CompactTest.java @@ -19,14 +19,13 @@ package org.apache.cassandra.tools.nodetool; import java.util.Arrays; +import org.assertj.core.api.Assertions; import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; -import org.apache.cassandra.dht.Murmur3Partitioner; -import org.assertj.core.api.Assertions; import static org.apache.cassandra.tools.ToolRunner.invokeNodetool; @@ -42,8 +41,7 @@ public class CompactTest extends CQLTester @Test public void keyPresent() throws Throwable { - long token = 42; - long key = Murmur3Partitioner.LongToken.keyForToken(token).getLong(); + long key = 42; createTable("CREATE TABLE %s (id bigint, value text, PRIMARY KEY ((id)))"); ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable()); cfs.disableAutoCompaction(); @@ -63,8 +61,7 @@ public class CompactTest extends CQLTester @Test public void keyNotPresent() throws Throwable { - long token = 42; - long key = Murmur3Partitioner.LongToken.keyForToken(token).getLong(); + long key = 42; createTable("CREATE TABLE %s (id bigint, value text, PRIMARY KEY ((id)))"); ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable()); cfs.disableAutoCompaction(); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/CompactionHistoryTest.java b/test/unit/org/apache/cassandra/tools/nodetool/CompactionHistoryTest.java index 7dbda77684..c2ee30f9bc 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/CompactionHistoryTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/CompactionHistoryTest.java @@ -47,6 +47,12 @@ import static org.apache.cassandra.tools.ToolRunner.invokeNodetool; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertTrue; +/** + * Tests for compaction history. + * + * @see Compact + * @see GarbageCollect + */ @RunWith(Parameterized.class) public class CompactionHistoryTest extends CQLTester { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/CompactionStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/CompactionStatsTest.java index 65cc638531..64ab8fe34b 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/CompactionStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/CompactionStatsTest.java @@ -49,53 +49,6 @@ public class CompactionStatsTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "compactionstats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool compactionstats - Print statistics on compactions\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] compactionstats\n" + - " [(-H | --human-readable)] [(-V | --vtable)]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -H, --human-readable\n" + - " Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " -V, --vtable\n" + - " Display fields matching vtable output\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testCompactionStats() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/DataPathsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/DataPathsTest.java index d09aceb3af..cdab8772c5 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/DataPathsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/DataPathsTest.java @@ -37,57 +37,6 @@ public class DataPathsTest extends CQLTester startJMXServer(); } - @Test - public void testHelp() - { - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", SUBCOMMAND); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool datapaths - Print all directories where data of tables are\n" + - " stored\n" + - '\n' + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] datapaths\n" + - " [(-F | --format )] [--] [...]\n" + - '\n' + - "OPTIONS\n" + - " -F , --format \n" + - " Output format (json, yaml)\n" + - '\n' + - " -h , --host \n" + - " Node hostname or ip address\n" + - '\n' + - " -p , --port \n" + - " Remote jmx agent port number\n" + - '\n' + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - '\n' + - " -pw , --password \n" + - " Remote jmx agent password\n" + - '\n' + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - '\n' + - " -u , --username \n" + - " Remote jmx agent username\n" + - '\n' + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - '\n' + - " [...]\n" + - " List of table (or keyspace) names\n" + - '\n' + - '\n'; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testAllOutput() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/DropCIDRGroupTest.java b/test/unit/org/apache/cassandra/tools/nodetool/DropCIDRGroupTest.java index d8e2a5d2c7..38d27ab598 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/DropCIDRGroupTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/DropCIDRGroupTest.java @@ -55,54 +55,6 @@ public class DropCIDRGroupTest extends CQLTester AuthTestUtils.insertCidrsMappings(cidrsMapping); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testDropCidrGroupDoc() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "dropcidrgroup"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool dropcidrgroup - Drop an existing cidr group\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] dropcidrgroup [--] \n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " \n" + - " Requires a cidr group name\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testDropCidrGroup() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/ForceCompactionTest.java b/test/unit/org/apache/cassandra/tools/nodetool/ForceCompactionTest.java index 878433d702..a78b80584a 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/ForceCompactionTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/ForceCompactionTest.java @@ -24,13 +24,11 @@ import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; -import org.apache.cassandra.Util; - import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.*; - +import org.apache.cassandra.Util; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; @@ -40,12 +38,24 @@ import org.apache.cassandra.db.rows.Unfiltered; import org.apache.cassandra.db.rows.UnfilteredRowIterator; import org.apache.cassandra.io.sstable.ISSTableScanner; import org.apache.cassandra.io.sstable.format.SSTableReader; +import org.apache.cassandra.tools.ToolRunner; + +import static org.apache.commons.lang3.ArrayUtils.addAll; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class ForceCompactionTest extends CQLTester { private final static int NUM_PARTITIONS = 10; private final static int NUM_ROWS = 100; + @BeforeClass + public static void setupClass() throws Exception + { + requireNetwork(); + startJMXServer(); + } + @Before public void setup() throws Throwable { @@ -241,7 +251,8 @@ public class ForceCompactionTest extends CQLTester if (cfs != null) { cfs.forceMajorCompaction(); - cfs.forceCompactionKeysIgnoringGcGrace(partitionKeysIgnoreGcGrace); + ToolRunner.invokeNodetool(addAll(new String[]{ "forcecompact", cfs.keyspace.getName(), cfs.getTableName() }, + partitionKeysIgnoreGcGrace)).assertOnCleanExit(); } } diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GcStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GcStatsTest.java index 56ad5d2611..43906ba471 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GcStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GcStatsTest.java @@ -48,53 +48,6 @@ public class GcStatsTest extends CQLTester GCInspector.register(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolResult tool = ToolRunner.invokeNodetool("help", "gcstats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool gcstats - Print GC Statistics\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] gcstats\n" + - " [(-F | --format )] [(-H | --human-readable)]\n" + - "\n" + - "OPTIONS\n" + - " -F , --format \n" + - " Output format (json, yaml, table)\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -H, --human-readable\n" + - " Display gcstats with human-readable units\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n"; - - assertThat(tool.getStdout().trim()).isEqualTo(help.trim()); - } - @Test public void testDefaultGcStatsOutput() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GetAuditLogTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GetAuditLogTest.java index e23c7f8c3d..816c7031b4 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GetAuditLogTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GetAuditLogTest.java @@ -33,6 +33,10 @@ import org.apache.cassandra.tools.ToolRunner; import static org.assertj.core.api.Assertions.assertThat; +/** + * Tests for the {@code GetAuditLog} nodetool command. + * @see GetAuditLog + */ public class GetAuditLogTest extends CQLTester { @BeforeClass diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GetAuthCacheConfigTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GetAuthCacheConfigTest.java index ae82602b4e..02fd75e827 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GetAuthCacheConfigTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GetAuthCacheConfigTest.java @@ -45,61 +45,17 @@ public class GetAuthCacheConfigTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "getauthcacheconfig"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool getauthcacheconfig - Get configuration of Auth cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] getauthcacheconfig\n" + - " --cache-name \n" + - "\n" + - "OPTIONS\n" + - " --cache-name \n" + - " Name of Auth cache (required)\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidCacheName() { ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("getauthcacheconfig"); assertThat(tool.getExitCode()).isEqualTo(1); - assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required option '--cache-name' is missing")); + assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Missing required option: '--cache-name=cache-name'")); assertThat(tool.getCleanedStderr()).isEmpty(); tool = ToolRunner.invokeNodetool("getauthcacheconfig", "--cache-name"); assertThat(tool.getExitCode()).isEqualTo(1); - assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required values for option 'cache-name' not provided")); + assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Missing required parameter for option '--cache-name' (cache-name)")); assertThat(tool.getCleanedStderr()).isEmpty(); tool = ToolRunner.invokeNodetool("getauthcacheconfig", "--cache-name", "wrong"); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIPTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIPTest.java index 988418ca9c..c274e0a4ab 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIPTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GetCIDRGroupsOfIPTest.java @@ -55,55 +55,6 @@ public class GetCIDRGroupsOfIPTest extends CQLTester AuthTestUtils.insertCidrsMappings(cidrsMapping); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testGetCidrGroupsOfIpDoc() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "getcidrgroupsofip"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool getcidrgroupsofip - Print CIDR groups associated with given IP\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] getcidrgroupsofip [--] \n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " \n" + - " Requires IP address as a string\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testGetCidrGroupsOfIp() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRFTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRFTest.java index 7db6f10ad2..32ab151772 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRFTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GetDefaultKeyspaceRFTest.java @@ -36,46 +36,6 @@ public class GetDefaultKeyspaceRFTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "getdefaultrf"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool getdefaultrf - Gets default keyspace replication factor.\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] getdefaultrf\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testGetDefaultRF() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GetFullQueryLogTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GetFullQueryLogTest.java index dfc1099a8e..d4877ffa29 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GetFullQueryLogTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GetFullQueryLogTest.java @@ -30,6 +30,14 @@ import org.apache.cassandra.tools.ToolRunner; import static org.assertj.core.api.Assertions.assertThat; +/** + * Tests for the {@code GetFullQueryLog} nodetool command. + * + * @see GetFullQueryLog + * @see EnableFullQueryLog + * @see ResetFullQueryLog + * @see DisableFullQueryLog + */ public class GetFullQueryLogTest extends CQLTester { @ClassRule diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GossipInfoTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GossipInfoTest.java index 57846060b9..f50e2552c5 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GossipInfoTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GossipInfoTest.java @@ -34,6 +34,9 @@ import org.assertj.core.api.Assertions; import static org.apache.cassandra.net.Verb.ECHO_REQ; import static org.assertj.core.api.Assertions.assertThat; +/** + * @see GossipInfo + */ public class GossipInfoTest extends CQLTester { private static String token; @@ -46,50 +49,6 @@ public class GossipInfoTest extends CQLTester token = StorageService.instance.getTokens().get(0); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "gossipinfo"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool gossipinfo - Shows the gossip information for the cluster\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] gossipinfo\n" + - " [(-r | --resolve-ip)]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -r, --resolve-ip\n" + - " Show node domain names instead of IPs\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testGossipInfo() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java index 60965b41d9..0095b492fa 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java @@ -76,7 +76,7 @@ public class GuardrailsConfigCommandsTest extends CQLTester ToolResult wrongCategory = invokeNodetool("getguardrailsconfig", "-c", "nonsense"); wrongCategory.asserts().failure(); - assertTrue(getOutput(wrongCategory).contains("Error: Illegal value for -c/--category used: 'nonsense'. Supported values are values,thresholds,flags,others.")); + assertTrue(getOutput(wrongCategory).contains("Invalid value for option '--category': expected one of [values, thresholds, flags, others] (case-sensitive) but was 'nonsense'")); // individual guardrail ToolResult individualResult = invokeNodetool("getguardrailsconfig", "group_by_enabled"); @@ -86,7 +86,7 @@ public class GuardrailsConfigCommandsTest extends CQLTester // more than one guardrail ToolResult multipleResult = invokeNodetool("getguardrailsconfig", "group_by_enabled", "keyspaces_fail_threshold"); multipleResult.asserts().failure(); - assertTrue(getOutput(multipleResult).contains("Specify only one guardrail name to get the configuration of or no name to get the configuration of all of them.")); + assertTrue(getOutput(multipleResult).contains("Unmatched argument at index 6: 'keyspaces_fail_threshold'")); // category with individual ToolResult categoryWithIndividualResult = invokeNodetool("getguardrailsconfig", "-c", "values", "group_by_enabled"); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCacheTest.java index 3b43146185..081a27b474 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCIDRPermissionsCacheTest.java @@ -55,56 +55,6 @@ public class InvalidateCIDRPermissionsCacheTest extends CQLTester ipAddr = new InetSocketAddress("127.0.0.0", 0); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "invalidatecidrpermissionscache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool invalidatecidrpermissionscache - Invalidate the cidr\n" + - " permissions cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] invalidatecidrpermissionscache\n" + - " [--] [...]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [...]\n" + - " List of roles to invalidate. By default, all roles\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidateSingleCidrPermission() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java index a5ea607b63..6b4e6bea8b 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java @@ -37,6 +37,9 @@ import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B; import static org.apache.cassandra.auth.AuthTestUtils.getRolesReadCount; import static org.assertj.core.api.Assertions.assertThat; +/** + * @see InvalidateCredentialsCache + */ public class InvalidateCredentialsCacheTest extends CQLTester { private static IAuthenticator.SaslNegotiator roleANegotiator; @@ -62,55 +65,6 @@ public class InvalidateCredentialsCacheTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "invalidatecredentialscache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool invalidatecredentialscache - Invalidate the credentials cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] invalidatecredentialscache\n" + - " [--] [...]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [...]\n" + - " List of roles to invalidate. By default, all roles\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidateSingleCredential() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java index 63aaf1e51b..085970aeea 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java @@ -43,6 +43,9 @@ import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B; import static org.apache.cassandra.auth.AuthTestUtils.getRolePermissionsReadCount; import static org.assertj.core.api.Assertions.assertThat; +/** + * @see InvalidateJmxPermissionsCache + */ public class InvalidateJmxPermissionsCacheTest extends CQLTester { private static final AuthorizationProxy authorizationProxy = new AuthTestUtils.NoAuthSetupAuthorizationProxy(); @@ -72,56 +75,6 @@ public class InvalidateJmxPermissionsCacheTest extends CQLTester authorizer.grant(AuthenticatedUser.SYSTEM_USER, jmxPermissions, rootJmxResource, ROLE_B); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "invalidatejmxpermissionscache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool invalidatejmxpermissionscache - Invalidate the JMX permissions\n" + - " cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] invalidatejmxpermissionscache\n" + - " [--] [...]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [...]\n" + - " List of roles to invalidate. By default, all roles\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidateSingleJMXPermission() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java index 7ef5dbeebb..538e97d685 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java @@ -48,56 +48,6 @@ public class InvalidateNetworkPermissionsCacheTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "invalidatenetworkpermissionscache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool invalidatenetworkpermissionscache - Invalidate the network\n" + - " permissions cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )]\n" + - " invalidatenetworkpermissionscache [--] [...]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [...]\n" + - " List of roles to invalidate. By default, all roles\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidateSingleNetworkPermission() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java index 895fe1da56..de6064d877 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java @@ -94,96 +94,6 @@ public class InvalidatePermissionsCacheTest extends CQLTester } } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "invalidatepermissionscache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool invalidatepermissionscache - Invalidate the permissions cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] invalidatepermissionscache\n" + - " [--all-functions] [--all-keyspaces] [--all-mbeans] [--all-roles]\n" + - " [--all-tables] [--function ]\n" + - " [--functions-in-keyspace ]\n" + - " [--keyspace ] [--mbean ] [--role ]\n" + - " [--table
] [--] []\n" + - "\n" + - "OPTIONS\n" + - " --all-functions\n" + - " Invalidate permissions for 'ALL FUNCTIONS'\n" + - "\n" + - " --all-keyspaces\n" + - " Invalidate permissions for 'ALL KEYSPACES'\n" + - "\n" + - " --all-mbeans\n" + - " Invalidate permissions for 'ALL MBEANS'\n" + - "\n" + - " --all-roles\n" + - " Invalidate permissions for 'ALL ROLES'\n" + - "\n" + - " --all-tables\n" + - " Invalidate permissions for 'ALL TABLES'\n" + - "\n" + - " --function \n" + - " Function to invalidate permissions for (you must specify\n" + - " --functions-in-keyspace for using this option; function format:\n" + - " name[arg1^..^agrN], for example: foo[Int32Type^DoubleType])\n" + - "\n" + - " --functions-in-keyspace \n" + - " Keyspace to invalidate permissions for\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " --keyspace \n" + - " Keyspace to invalidate permissions for\n" + - "\n" + - " --mbean \n" + - " MBean to invalidate permissions for\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " --role \n" + - " Role to invalidate permissions for\n" + - "\n" + - " --table
\n" + - " Table to invalidate permissions for (you must specify --keyspace for\n" + - " using this option)\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " []\n" + - " A role for which permissions to specified resources need to be\n" + - " invalidated\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidatePermissionsWithIncorrectParameters() { @@ -202,7 +112,7 @@ public class InvalidatePermissionsCacheTest extends CQLTester tool = ToolRunner.invokeNodetool("invalidatepermissionscache", "role1", "--invalid-option"); assertThat(tool.getExitCode()).isEqualTo(1); assertThat(tool.getStdout()) - .isEqualTo(wrapByDefaultNodetoolMessage("A single is only supported / you have a typo in the resource options spelling")); + .isEqualTo(wrapByDefaultNodetoolMessage("Unknown option: '--invalid-option'")); assertThat(tool.getCleanedStderr()).isEmpty(); tool = ToolRunner.invokeNodetool("invalidatepermissionscache", "role1", "--all-tables"); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java index f78c393732..0dacba9cef 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java @@ -48,55 +48,6 @@ public class InvalidateRolesCacheTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "invalidaterolescache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool invalidaterolescache - Invalidate the roles cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] invalidaterolescache [--]\n" + - " [...]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [...]\n" + - " List of roles to invalidate. By default, all roles\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidateSingleRole() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/ListCIDRGroupTest.java b/test/unit/org/apache/cassandra/tools/nodetool/ListCIDRGroupTest.java index f64d3a0509..071dd80e9e 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/ListCIDRGroupTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/ListCIDRGroupTest.java @@ -56,55 +56,6 @@ public class ListCIDRGroupTest extends CQLTester insertCidrsMappings(cidrsMapping); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testListCidrGroupDoc() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "listcidrgroups"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool listcidrgroups - List existing cidr groups\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] listcidrgroups [--]\n" + - " []\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " []\n" + - " LIST operation can be invoked with or without cidr group name\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testListCidrGroup() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/NetStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/NetStatsTest.java index 9664c90714..d033859477 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/NetStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/NetStatsTest.java @@ -53,51 +53,6 @@ public class NetStatsTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "netstats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool netstats - Print network information on provided host\n" + - " (connecting node by default)\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] netstats\n" + - " [(-H | --human-readable)]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -H, --human-readable\n" + - " Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testNetStats() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/NodetoolClassHierarchyTest.java b/test/unit/org/apache/cassandra/tools/nodetool/NodetoolClassHierarchyTest.java new file mode 100644 index 0000000000..77faf006f0 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/NodetoolClassHierarchyTest.java @@ -0,0 +1,73 @@ +/* + * 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.lang.reflect.Field; +import javax.inject.Inject; + +import org.junit.Test; + +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.tools.INodeProbeFactory; +import org.apache.cassandra.tools.NodeTool; +import org.apache.cassandra.tools.Output; +import picocli.CommandLine; + +public class NodetoolClassHierarchyTest extends CQLTester +{ + @Test + public void testNoDuplicatesForInjectableFields() throws Exception + { + checkInjectableDuplicates(NodeTool.createCommandLine(CommandLine.defaultFactory())); + } + + private void checkInjectableDuplicates(CommandLine command) + { + for (CommandLine sub : command.getSubcommands().values()) + checkInjectableDuplicates(sub); + + if (command.getCommandSpec().userObject() instanceof AbstractCommand) + { + AbstractCommand userObject = (AbstractCommand) command.getCommandSpec().userObject(); + int nodeProbeFactoryCount = 0; + int outputCount = 0; + Class beanClass = userObject.getClass(); + do + { + Field[] fields = beanClass.getDeclaredFields(); + for (Field field : fields) + { + if (!field.isAnnotationPresent(Inject.class)) + continue; + if (field.getType().equals(INodeProbeFactory.class)) + nodeProbeFactoryCount++; + else if (field.getType().equals(Output.class)) + outputCount++; + else + throw new AssertionError("Unexpected injectable field type: " + field.getType()); + } + } + while ((beanClass = beanClass.getSuperclass()) != null); + + if (nodeProbeFactoryCount > 1 || outputCount > 1) + throw new AssertionError("Multiple injectable fields in the command class hierarchy (should be exactly 1 for each type): " + + userObject.getClass().getCanonicalName()); + } + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpCommandsOutputTest.java b/test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpCommandsOutputTest.java new file mode 100644 index 0000000000..3916b03dc5 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpCommandsOutputTest.java @@ -0,0 +1,183 @@ +/* + * 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.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import com.github.difflib.DiffUtils; +import com.github.difflib.patch.AbstractDelta; +import com.github.difflib.patch.Patch; +import com.google.common.collect.Streams; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.tools.NodeTool; +import org.apache.cassandra.tools.ToolRunner; + +import static org.junit.Assert.assertTrue; + +@RunWith(Parameterized.class) +public class NodetoolHelpCommandsOutputTest extends CQLTester +{ + private static final Map runnersMap = Map.of( + "shell", ToolRunner::invokeNodetool, + "injvm", ToolRunner::invokeNodetoolInJvm); + + public static final String COMMAND_FULL_NAME_SEPARATOR = "$"; + private static final List COMMANDS = NodeTool.getCommandsWithoutRoot(COMMAND_FULL_NAME_SEPARATOR); + private static final String NODETOOL_COMMAND_HELP_FILE_PATTERN = "nodetool/help/%s"; + + private static final Pattern SPLIT_PATTERN = Pattern.compile('\\' + COMMAND_FULL_NAME_SEPARATOR); + private static final String[] EMPTY_ARGS = new String[0]; + private static final String COMMAND_WITHOUT_ARGS = "nodetool"; + + @Parameterized.Parameter + public String runner; + + @Parameterized.Parameter(1) + public String command; + + @Parameterized.Parameters(name = "runner={0}, command={1}") + public static Collection data() + { + List res = new ArrayList<>(); + for (String tool : runnersMap.keySet()) + { + for (String command : COMMANDS) + res.add(new Object[]{ tool, command }); + // add a special case for the help command with no arguments + res.add(new Object[]{ tool, COMMAND_WITHOUT_ARGS }); + } + return res; + } + + @Test + public void testCompareCommandHelpOutputBetweenTools() throws Exception + { + compareCommandHelpOutput(command); + } + + private void compareCommandHelpOutput(String commandName) throws Exception + { + Assume.assumeFalse("Skipping nodetool-injvmv2 nodetool during the migration period", + COMMAND_WITHOUT_ARGS.equals(commandName) && runner.equals("injvmv2")); + + List targetLines = sliceStdout(invokeNodetool(commandName.equals(COMMAND_WITHOUT_ARGS) ? EMPTY_ARGS : + Streams.concat(Stream.of("help"), Stream.of(SPLIT_PATTERN.split(commandName))) + .toArray(String[]::new))); + List origLines = readCommandLines(String.format(NODETOOL_COMMAND_HELP_FILE_PATTERN, commandName)); + + String diff = computeDiff(targetLines, origLines); + assertTrue(printFormattedDiffsMessage(origLines, targetLines, commandName, diff), + StringUtils.isBlank(diff)); + } + + public ToolRunner.ToolResult invokeNodetool(String... args) + { + return runnersMap.get(runner).execute(args); + } + + public static List sliceStdout(ToolRunner.ToolResult result) + { + result.assertOnCleanExit(); + return Arrays.asList(result.getStdout().trim().split("\\R")); + } + + protected static String printFormattedDiffsMessage(List stdoutOrig, + List stdoutNew, + String commandName, + String diff) + { + return '\n' + ">> file_content <<" + '\n' + + printFormattedNodeToolOutput(stdoutOrig) + + '\n' + ">> command_output <<" + + '\n' + printFormattedNodeToolOutput(stdoutNew) + + '\n' + " difference for \"" + commandName + "\":" + diff + '\n' + + "The difference between the original and the new output is shown above. Make sure the " + + "changes are expected and update the test data if necessary. Use NodetoolHelpGenerator class."; + } + + protected static String printFormattedNodeToolOutput(List output) + { + StringBuilder sb = new StringBuilder(); + DecimalFormat df = new DecimalFormat("000"); + for(int i = 0; i < output.size(); i++) + { + sb.append(df.format(i)).append(':').append(output.get(i)); + if(i < output.size() - 1) + sb.append('\n'); + } + return sb.toString(); + } + + protected static String computeDiff(List original, List revised) { + Patch patch = DiffUtils.diff(original, revised); + List diffLines = new ArrayList<>(); + + for (AbstractDelta delta : patch.getDeltas()) { + for (String line : delta.getSource().getLines()) { + diffLines.add(delta.getType().toString().toLowerCase() + " command: " + line); + } + for (String line : delta.getTarget().getLines()) { + diffLines.add(delta.getType().toString().toLowerCase() + " srcfile: " + line); + } + } + + return '\n' + String.join("\n", diffLines); + } + + protected static List readCommandLines(String resource) throws Exception + { + List lines = new ArrayList<>(); + URL url = NodetoolHelpCommandsOutputTest.class.getClassLoader().getResource(resource); + if (url == null) + { + logger.error("Command test output not found: {}", resource); + return Collections.singletonList("Command test output not found: " + resource); + } + + try (Stream stream = Files.lines(Paths.get(url.toURI()))) + { + stream.forEach(lines::add); + } + return lines; + } + + public interface ToolHandler + { + ToolRunner.ToolResult execute(String... args); + default ToolRunner.ToolResult execute(List args) { return execute(args.toArray(new String[0])); } + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpGenerator.java b/test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpGenerator.java new file mode 100644 index 0000000000..01b11fe333 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/NodetoolHelpGenerator.java @@ -0,0 +1,166 @@ +/* + * 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.File; //checkstyle: permit this import +import java.io.FileWriter; //checkstyle: permit this import +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.cassandra.config.CassandraRelevantProperties; +import org.apache.cassandra.tools.ToolRunner; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * Generates files with commands help output for all available nodetool commands. The {@code $} character is used as + * a separator between command hierarchy levels in the file names (e.g. {@code "info$threads"}) due to the fact that + * a command name can contain special characters like {@code -} or {@code _}. + *

+ * The generator calls the {@code ./nodetool help} command to get the list of available commands and their descriptions, + * in order to generate the latest help output for each command be sure to run the generator after the jars are built + * (e.g. {@code ant jar}). + */ +public class NodetoolHelpGenerator +{ + private static final Logger logger = LoggerFactory.getLogger(NodetoolHelpGenerator.class); + private static final Map ENV = ImmutableMap.of("JAVA_HOME", CassandraRelevantProperties.JAVA_HOME.getString()); + private static final String NODETOOL_COMMAND_HELP_WRITE_DIR = "test/resources/nodetool/help/"; + private static final String IGNORE_LINE = " With no arguments,"; + private static final String NODETOOL_COMMAND_LIST_START_AFTER = "The most commonly used nodetool commands are:"; + private static final String NODETOOL_SUBCOMMAND_LIST_START_AFTER = "COMMANDS"; + private static final Pattern NODETOOL_COMMAND_DESCRIPTION_SPACES = Pattern.compile("^ {4}(\\S+)"); + private static final Pattern NODETOOL_SUBCOMMAND_DESCRIPTION_SPACES = Pattern.compile("^ {8}(\\S+)"); + private static final String COMMAND_FULL_NAME_SEPARATOR = "$"; + + /** + * Main method to generate help files for all nodetool commands to the {@code test/resources/nodetool/help/}. + *

+ * For example, the {@code nodetool help bootstrap resume} help output results in a file + * {@code test/resources/nodetool/help/bootstrap$resume}, where the {@code $} character + * is used as a separator for the subcommand. The arguments are passed as a list of commands + * to generate help files for. For example, {@code bootstrap resume} is passed. + *

+ * By default, the files are written to {@code test/resources/nodetool/help/}. + */ + public static void main(String[] args) + { + List commands = new ArrayList<>(List.of(args)); +// commands.add("assassinate"); + + if (commands.isEmpty()) + new NodetoolHelpGenerator().writeCommandsHelpOutput(); + else + new NodetoolHelpGenerator().writer(commands); + } + + public void writeCommandsHelpOutput() + { + List roots = find(() -> ToolRunner.invoke(ENV, newArrayList("bin/nodetool", "help")), + NODETOOL_COMMAND_LIST_START_AFTER, NODETOOL_COMMAND_DESCRIPTION_SPACES); + + for (String command : roots) + writeToFileRecursively(newArrayList(command), this::writer); + } + + private void writeToFileRecursively(List hierarchy, Consumer> writer) + { + List subcommands = find(() -> ToolRunner.invoke(ENV, Lists.asList("bin/nodetool", + "help", + hierarchy.toArray(new String[0]))), + NODETOOL_SUBCOMMAND_LIST_START_AFTER, NODETOOL_SUBCOMMAND_DESCRIPTION_SPACES); + for (String subcommand : subcommands) + { + List subhierarchy = new ArrayList<>(hierarchy); + subhierarchy.add(subcommand); + writeToFileRecursively(subhierarchy, writer); + } + + writer.accept(hierarchy); + } + + public void writer(List fullCommand) + { + ToolRunner.ToolResult result = ToolRunner.invoke(ENV, Lists.asList("bin/nodetool", "help", + fullCommand.toArray(new String[0]))); + result.assertOnCleanExit(); + + try + { + File commandHelpOut = new File(NODETOOL_COMMAND_HELP_WRITE_DIR, String.join(COMMAND_FULL_NAME_SEPARATOR, fullCommand)); //checkstyle: permit this instantiation + boolean created = commandHelpOut.getParentFile().mkdirs(); + if (created) + logger.debug("Created directory: {}", commandHelpOut.getParentFile().getAbsolutePath()); + + boolean success = commandHelpOut.createNewFile(); + if (success) + logger.debug("Created file: {}", commandHelpOut.getAbsolutePath()); + + try (FileWriter fw = new FileWriter(commandHelpOut)) + { + fw.write(result.getStdout().trim()); + fw.write("\n"); + } + logger.info("The help is written for '{}' to '{}'", fullCommand, commandHelpOut.getAbsolutePath()); + } + catch (IOException e) + { + throw new RuntimeException("Error creating file", e); + } + } + + private static List find(Supplier cmdResult, String afterLine, Pattern commandPattern) + { + ToolRunner.ToolResult result = cmdResult.get(); + result.assertOnCleanExit(); + String[] lines = result.getStdout().split("\n"); + List commands = new ArrayList<>(); + boolean start = false; + for (String line : lines) + { + if (line.contains(IGNORE_LINE)) + continue; + + if (line.contains(afterLine)) + { + start = true; + continue; + } + + if (start) + { + Matcher matcher = commandPattern.matcher(line); + if (matcher.find()) + commands.add(matcher.group(1)); + } + } + return commands; + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCacheTest.java b/test/unit/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCacheTest.java index f07c1ba122..8c50083189 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCacheTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/ReloadCIDRGroupsCacheTest.java @@ -63,47 +63,6 @@ public class ReloadCIDRGroupsCacheTest extends CQLTester AUTH_KEYSPACE_NAME, CIDR_GROUPS)); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "reloadcidrgroupscache"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool reloadcidrgroupscache - Reload CIDR groups cache with latest\n" + - " entries in cidr_groups table, when CIDR authorizer is enabled\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] reloadcidrgroupscache\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - private long getCidrGroupsReadCount() { return DatabaseDescriptor.getCIDRAuthorizer().getCidrAuthorizerMetrics().cacheReloadCount.getCount(); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/RingTest.java b/test/unit/org/apache/cassandra/tools/nodetool/RingTest.java index f29e61ce82..3707a1b37f 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/RingTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/RingTest.java @@ -32,6 +32,9 @@ import org.apache.cassandra.utils.FBUtilities; import static org.assertj.core.api.Assertions.assertThat; +/** + * @see Ring + */ public class RingTest extends CQLTester { private static String token; @@ -53,12 +56,12 @@ public class RingTest extends CQLTester final HostStatWithPort host = new HostStatWithPort(null, FBUtilities.getBroadcastAddressAndPort(), false, null); validateRingOutput(host.ipOrDns(false), "ring"); - Arrays.asList("-pp", "--print-port").forEach(arg -> validateRingOutput(host.ipOrDns(true), "-pp", "ring")); + Arrays.asList("-pp", "--print-port").forEach(arg -> validateRingOutput(host.ipOrDns(true), arg, "ring")); final HostStatWithPort hostResolved = new HostStatWithPort(null, FBUtilities.getBroadcastAddressAndPort(), true, null); Arrays.asList("-r", "--resolve-ip").forEach(arg -> - validateRingOutput(hostResolved.ipOrDns(false), "ring", "-r")); + validateRingOutput(hostResolved.ipOrDns(false), "ring", arg)); validateRingOutput(hostResolved.ipOrDns(true), "-pp", "ring", "-r"); } @@ -98,59 +101,6 @@ public class RingTest extends CQLTester assertThat(tool.getStdout()).contains("nodetool help"); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "ring"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + " nodetool ring - Print information about the token ring\n" - + "\n" - + "SYNOPSIS\n" - + " nodetool [(-h | --host )] [(-p | --port )]\n" - + " [(-pp | --print-port)] [(-pw | --password )]\n" - + " [(-pwf | --password-file )]\n" - + " [(-u | --username )] ring [(-r | --resolve-ip)]\n" - + " [--] []\n" - + "\n" - + "OPTIONS\n" - + " -h , --host \n" - + " Node hostname or ip address\n" - + "\n" - + " -p , --port \n" - + " Remote jmx agent port number\n" - + "\n" - + " -pp, --print-port\n" - + " Operate in 4.0 mode with hosts disambiguated by port number\n" - + "\n" - + " -pw , --password \n" - + " Remote jmx agent password\n" - + "\n" - + " -pwf , --password-file \n" - + " Path to the JMX password file\n" - + "\n" - + " -r, --resolve-ip\n" - + " Show node domain names instead of IPs\n" - + "\n" - + " -u , --username \n" - + " Remote jmx agent username\n" - + "\n" - + " --\n" - + " This option can be used to separate command-line options from the\n" - + " list of argument, (useful when arguments might be mistaken for\n" - + " command-line options\n" - + "\n" - + " \n" - + " Specify a keyspace for accurate ownership information (topology\n" - + " awareness)\n" - + "\n" - + "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testRingKeyspace() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SSTableRepairedSetTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SSTableRepairedSetTest.java index 5d23d22253..e76b82985a 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SSTableRepairedSetTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SSTableRepairedSetTest.java @@ -40,7 +40,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * Unit tests for {@link org.apache.cassandra.tools.nodetool.SSTableRepairedSetTest} + * Unit tests for {@link org.apache.cassandra.tools.nodetool.SSTableRepairedSet} */ public class SSTableRepairedSetTest { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetAuthCacheConfigTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetAuthCacheConfigTest.java index 5405995751..4bcd6e1984 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetAuthCacheConfigTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetAuthCacheConfigTest.java @@ -45,79 +45,17 @@ public class SetAuthCacheConfigTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "setauthcacheconfig"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool setauthcacheconfig - Set configuration for Auth cache\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] setauthcacheconfig\n" + - " --cache-name [--disable-active-update]\n" + - " [--enable-active-update] [--max-entries ]\n" + - " [--update-interval ]\n" + - " [--validity-period ]\n" + - "\n" + - "OPTIONS\n" + - " --cache-name \n" + - " Name of Auth cache (required)\n" + - "\n" + - " --disable-active-update\n" + - " Disable active update\n" + - "\n" + - " --enable-active-update\n" + - " Enable active update\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " --max-entries \n" + - " Max entries\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --update-interval \n" + - " Update interval in milliseconds\n" + - "\n" + - " --validity-period \n" + - " Validity period in milliseconds\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testInvalidCacheName() { ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("setauthcacheconfig"); assertThat(tool.getExitCode()).isEqualTo(1); - assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required option '--cache-name' is missing")); + assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Missing required option: '--cache-name=cache-name'")); assertThat(tool.getCleanedStderr()).isEmpty(); tool = ToolRunner.invokeNodetool("setauthcacheconfig", "--cache-name"); assertThat(tool.getExitCode()).isEqualTo(1); - assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required values for option 'cache-name' not provided")); + assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Missing required parameter for option '--cache-name' (cache-name)")); assertThat(tool.getCleanedStderr()).isEmpty(); tool = ToolRunner.invokeNodetool("setauthcacheconfig", "--cache-name", "wrong", "--validity-period", "1"); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRFTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRFTest.java index 92250a37bd..d7b0985f07 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRFTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetDefaultKeyspaceRFTest.java @@ -36,54 +36,6 @@ public class SetDefaultKeyspaceRFTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "setdefaultrf"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool setdefaultrf - Sets default keyspace replication factor.\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] setdefaultrf [--] \n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " \n" + - " Default replication factor\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testSetDefaultRF() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java index 1a19f09305..9b37d0ede4 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java @@ -43,7 +43,7 @@ public class SetGetColumnIndexSizeTest extends CQLTester @Test public void testNull() { - assertSetInvalidColumnIndexSize(null, "Required parameters are missing: column_index_size", 1); + assertSetInvalidColumnIndexSize(null, "Missing required parameter: 'column_index_size'", 1); } @Test @@ -79,8 +79,8 @@ public class SetGetColumnIndexSizeTest extends CQLTester @Test public void testUnparseable() { - assertSetInvalidColumnIndexSize("1.2", "column_index_size: can not convert \"1.2\" to a int", 1); - assertSetInvalidColumnIndexSize("value", "column_index_size: can not convert \"value\" to a int", 1); + assertSetInvalidColumnIndexSize("1.2", "Invalid value for positional parameter at index 0 (column_index_size): '1.2' is not an int", 1); + assertSetInvalidColumnIndexSize("value", "Invalid value for positional parameter at index 0 (column_index_size): 'value' is not an int", 1); } private static void assertSetGetValidColumnIndexSize(int columnIndexSizeInKB) diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetCompactionThroughputTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetCompactionThroughputTest.java index 2771881dc2..5e24fb0d4a 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetGetCompactionThroughputTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetCompactionThroughputTest.java @@ -45,7 +45,7 @@ public class SetGetCompactionThroughputTest extends CQLTester @Test public void testNull() { - assertSetInvalidThroughput(null, "Required parameters are missing: compaction_throughput"); + assertSetInvalidThroughput(null, "Missing required parameter: 'compaction_throughput'"); } @Test @@ -75,8 +75,8 @@ public class SetGetCompactionThroughputTest extends CQLTester @Test public void testUnparseable() { - assertSetInvalidThroughput("1.2", "compaction_throughput: can not convert \"1.2\" to a Integer"); - assertSetInvalidThroughput("value", "compaction_throughput: can not convert \"value\" to a Integer"); + assertSetInvalidThroughput("1.2", "Invalid value for positional parameter at index 0 (compaction_throughput): '1.2' is not an int"); + assertSetInvalidThroughput("value", "Invalid value for positional parameter at index 0 (compaction_throughput): 'value' is not an int"); assertSetInvalidThroughput(); assertPreciseMibFlagNeeded(); } diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableInterDCStreamThroughputTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableInterDCStreamThroughputTest.java index d6d325367d..7cc489aeb1 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableInterDCStreamThroughputTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableInterDCStreamThroughputTest.java @@ -31,6 +31,9 @@ import static org.assertj.core.api.Assertions.withPrecision; /** * Tests for entire SSTable {@code nodetool setinterdcstreamthroughput} and {@code nodetool getinterdcstreamthroughput}. + * + * @see GetInterDCStreamThroughput + * @see SetInterDCStreamThroughput */ public class SetGetEntireSSTableInterDCStreamThroughputTest extends CQLTester { @@ -46,7 +49,7 @@ public class SetGetEntireSSTableInterDCStreamThroughputTest extends CQLTester @Test public void testNull() { - assertSetInvalidThroughput(null, "Required parameters are missing: inter_dc_stream_throughput"); + assertSetInvalidThroughput(null, "Missing required parameter: 'inter_dc_stream_throughput'"); } @Test @@ -76,8 +79,8 @@ public class SetGetEntireSSTableInterDCStreamThroughputTest extends CQLTester @Test public void testUnparseable() { - assertSetInvalidThroughput("1.2", "inter_dc_stream_throughput: can not convert \"1.2\" to a int"); - assertSetInvalidThroughput("value", "inter_dc_stream_throughput: can not convert \"value\" to a int"); + assertSetInvalidThroughput("1.2", "Invalid value for positional parameter at index 0 (inter_dc_stream_throughput): '1.2' is not an int"); + assertSetInvalidThroughput("value", "Invalid value for positional parameter at index 0 (inter_dc_stream_throughput): 'value' is not an int"); } private static void assertSetGetValidThroughput(int throughput, double rateInBytes) diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableStreamThroughputTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableStreamThroughputTest.java index f250699a95..d67a8637d9 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableStreamThroughputTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetEntireSSTableStreamThroughputTest.java @@ -46,7 +46,7 @@ public class SetGetEntireSSTableStreamThroughputTest extends CQLTester @Test public void testNull() { - assertSetInvalidThroughput(null, "Required parameters are missing: stream_throughput"); + assertSetInvalidThroughput(null, "Missing required parameter: 'stream_throughput'"); } @Test @@ -76,8 +76,8 @@ public class SetGetEntireSSTableStreamThroughputTest extends CQLTester @Test public void testUnparseable() { - assertSetInvalidThroughput("1.2", "stream_throughput: can not convert \"1.2\" to a int"); - assertSetInvalidThroughput("value", "stream_throughput: can not convert \"value\" to a int"); + assertSetInvalidThroughput("1.2", "Invalid value for positional parameter at index 0 (stream_throughput): '1.2' is not an int"); + assertSetInvalidThroughput("value", "Invalid value for positional parameter at index 0 (stream_throughput): 'value' is not an int"); } private static void assertSetGetValidThroughput(int throughput) diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetInterDCStreamThroughputTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetInterDCStreamThroughputTest.java index 6cbb9480a9..c250ae2854 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetGetInterDCStreamThroughputTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetInterDCStreamThroughputTest.java @@ -53,7 +53,7 @@ public class SetGetInterDCStreamThroughputTest extends CQLTester @Test public void testNull() { - assertSetInvalidThroughput(null, "Required parameters are missing: inter_dc_stream_throughput"); + assertSetInvalidThroughput(null, "nodetool: Missing required parameter: 'inter_dc_stream_throughput'"); } @Test @@ -101,8 +101,8 @@ public class SetGetInterDCStreamThroughputTest extends CQLTester @Test public void testUnparseable() { - assertSetInvalidThroughput("1.2", "inter_dc_stream_throughput: can not convert \"1.2\" to a int"); - assertSetInvalidThroughput("value", "inter_dc_stream_throughput: can not convert \"value\" to a int"); + assertSetInvalidThroughput("1.2", "Invalid value for positional parameter at index 0 (inter_dc_stream_throughput): '1.2' is not an int"); + assertSetInvalidThroughput("value", "Invalid value for positional parameter at index 0 (inter_dc_stream_throughput): 'value' is not an int"); assertSetGetMoreFlagsIsInvalid(); assertDFlagNeeded(); } @@ -178,9 +178,9 @@ public class SetGetInterDCStreamThroughputTest extends CQLTester private static void assertSetGetMoreFlagsIsInvalid() { - ToolResult tool = invokeNodetool("setinterdcstreamthroughput", "-m", "5", "-e", "5"); + ToolResult tool = invokeNodetool("setinterdcstreamthroughput", "-m", "5", "-e", "6"); assertThat(tool.getExitCode()).isEqualTo(1); - assertThat(tool.getStdout()).contains("You cannot use -e and -m at the same time"); + assertThat(tool.getStdout()).contains("nodetool: Unmatched argument at index 8: '6'"); tool = invokeNodetool("getinterdcstreamthroughput", "-m", "-e"); assertThat(tool.getExitCode()).isEqualTo(1); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetStreamThroughputTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetStreamThroughputTest.java index cf688d62d4..1f1995bf0f 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/SetGetStreamThroughputTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetStreamThroughputTest.java @@ -33,6 +33,9 @@ import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@code nodetool setstreamthroughput} and {@code nodetool getstreamthroughput}. + * + * @see GetStreamThroughput + * @see SetStreamThroughput */ public class SetGetStreamThroughputTest extends CQLTester { @@ -54,7 +57,7 @@ public class SetGetStreamThroughputTest extends CQLTester @Test public void testNull() { - assertSetInvalidThroughput(null, "Required parameters are missing: stream_throughput"); + assertSetInvalidThroughput(null, "Missing required parameter: 'stream_throughput'"); } @Test @@ -102,8 +105,8 @@ public class SetGetStreamThroughputTest extends CQLTester @Test public void testUnparseable() { - assertSetInvalidThroughput("1.2", "stream_throughput: can not convert \"1.2\" to a int"); - assertSetInvalidThroughput("value", "stream_throughput: can not convert \"value\" to a int"); + assertSetInvalidThroughput("1.2", "Invalid value for positional parameter at index 0 (stream_throughput): '1.2' is not an int"); + assertSetInvalidThroughput("value", "Invalid value for positional parameter at index 0 (stream_throughput): 'value' is not an int"); assertSetGetMoreFlagsIsInvalid(); assertDFlagNeeded(); assertSetGetMoreFlagsIsInvalid(); @@ -179,9 +182,9 @@ public class SetGetStreamThroughputTest extends CQLTester private static void assertSetGetMoreFlagsIsInvalid() { - ToolResult tool = invokeNodetool("setstreamthroughput", "-m", "5", "-e", "5"); + ToolResult tool = invokeNodetool("setstreamthroughput", "-m", "5", "-e", "6"); assertThat(tool.getExitCode()).isEqualTo(1); - assertThat(tool.getStdout()).contains("You cannot use -e and -m at the same time"); + assertThat(tool.getStdout()).contains("nodetool: Unmatched argument at index 8: '6'"); tool = invokeNodetool("getstreamthroughput", "-m", "-e"); assertThat(tool.getExitCode()).isEqualTo(1); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/TableHistogramsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/TableHistogramsTest.java index b3c51d196b..95f47332c5 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/TableHistogramsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/TableHistogramsTest.java @@ -33,9 +33,11 @@ import org.apache.cassandra.tracing.TraceKeyspace; import static org.apache.cassandra.tools.ToolRunner.invokeNodetool; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +/** + * @see TableHistograms + */ public class TableHistogramsTest extends CQLTester { private static final String INFO_ROW = "Percentile Read Latency Write Latency SSTables Partition Size Cell Count"; @@ -54,56 +56,6 @@ public class TableHistogramsTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = invokeNodetool("help", "tablehistograms"); - assertEquals(0, tool.getExitCode()); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool tablehistograms - Print statistic histograms for a given table\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] tablehistograms [--]\n" + - " [

| ]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [
| ]\n" + - " The keyspace and table name\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testWithNoTableSpecified() { @@ -144,7 +96,7 @@ public class TableHistogramsTest extends CQLTester // format 2 : ks1 tb1 ks2 tb2 tool = invokeNodetool("tablehistograms", "system", "local", "system", "paxos"); assertNotEquals(0, tool.getExitCode()); - assertThat(tool.getStdout()).contains("nodetool: tablehistograms requires
or format argument"); + assertThat(tool.getStdout()).contains("nodetool: Unmatched arguments from index 7: 'system', 'paxos'"); // format 3 : ks1.tb1 ks2 tool = invokeNodetool("tablehistograms", "system.local", "system"); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java index c4487d64e4..6561cea4c0 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java @@ -36,6 +36,9 @@ import org.yaml.snakeyaml.Yaml; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +/** + * @see TableStats + */ public class TableStatsTest extends CQLTester { @BeforeClass @@ -45,98 +48,6 @@ public class TableStatsTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "tablestats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool tablestats - Print statistics on tables\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] tablestats\n" + - " [(-F | --format )] [(-H | --human-readable)] [-i]\n" + - " [(-l | --sstable-location-check)] [(-s | --sort )]\n" + - " [(-t | --top )] [--] [...]\n" + - "\n" + - "OPTIONS\n" + - " -F , --format \n" + - " Output format (json, yaml)\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -H, --human-readable\n" + - " Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB\n" + - "\n" + - " -i\n" + - " Ignore the list of tables and display the remaining tables\n" + - "\n" + - " -l, --sstable-location-check\n" + - " Check whether or not the SSTables are in the correct location.\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -s , --sort \n" + - " Sort tables by specified sort key\n" + - " (average_live_cells_per_slice_last_five_minutes,\n" + - " average_tombstones_per_slice_last_five_minutes,\n" + - " bloom_filter_false_positives, bloom_filter_false_ratio,\n" + - " bloom_filter_off_heap_memory_used, bloom_filter_space_used,\n" + - " compacted_partition_maximum_bytes, compacted_partition_mean_bytes,\n" + - " compacted_partition_minimum_bytes,\n" + - " compression_metadata_off_heap_memory_used, full_name,\n" + - " index_summary_off_heap_memory_used, local_read_count,\n" + - " local_read_latency_ms, local_write_latency_ms,\n" + - " maximum_live_cells_per_slice_last_five_minutes,\n" + - " maximum_tombstones_per_slice_last_five_minutes, memtable_cell_count,\n" + - " memtable_data_size, memtable_off_heap_memory_used,\n" + - " memtable_switch_count, number_of_partitions_estimate,\n" + - " off_heap_memory_used_total, pending_flushes, percent_repaired,\n" + - " read_latency, reads, space_used_by_snapshots_total, space_used_live,\n" + - " space_used_total, sstable_compression_ratio, sstable_count,\n" + - " table_name, write_latency, writes, max_sstable_size,\n" + - " local_read_write_ratio, twcs_max_duration,\n" + - " sai_local_query_latency_ms, sai_post_filtering_read_latency,\n" + - " sai_disk_used_bytes, sai_sstable_indexes_hit, sai_index_segments_hit\n" + - " sai_rows_filtered, sai_total_query_timeouts,\n" + - " sai_total_queryable_index_ratio)\n" + - "\n" + - " -t , --top \n" + - " Show only the top K tables for the sort key (specify the number K of\n" + - " tables to be shown\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [...]\n" + - " List of tables (or keyspace) names\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testTableStats() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/TpStatsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/TpStatsTest.java index 7fb626f409..d9d1517c46 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/TpStatsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/TpStatsTest.java @@ -51,53 +51,6 @@ public class TpStatsTest extends CQLTester startJMXServer(); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "tpstats"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool tpstats - Print usage statistics of thread pools\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] tpstats\n" + - " [(-F | --format )] [(-v | --verbose)]\n" + - "\n" + - "OPTIONS\n" + - " -F , --format \n" + - " Output format (json, yaml)\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " -v, --verbose\n" + - " Display detailed metrics about thread pool's sizes\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testTpStats() throws Throwable { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/UpdateCIDRGroupTest.java b/test/unit/org/apache/cassandra/tools/nodetool/UpdateCIDRGroupTest.java index 12f8a9b39d..f32c4b1240 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/UpdateCIDRGroupTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/UpdateCIDRGroupTest.java @@ -46,56 +46,6 @@ public class UpdateCIDRGroupTest extends CQLTester execute(format("TRUNCATE %s.%s", AUTH_KEYSPACE_NAME, CIDR_GROUPS)); } - @Test - @SuppressWarnings("SingleCharacterStringConcatenation") - public void testUpdateCidrGroupDoc() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "updatecidrgroup"); - tool.assertOnCleanExit(); - - String help = "NAME\n" + - " nodetool updatecidrgroup - Insert/Update a cidr group\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] updatecidrgroup [--]\n" + - " [ ...]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [ ...]\n" + - " Requires a cidr group name, followed by one or more CIDRs separated\n" + - " by space\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testUpdateCidrGroup() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java b/test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java deleted file mode 100644 index f6362adf81..0000000000 --- a/test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.cassandra.tools.nodetool; - -import org.junit.BeforeClass; -import org.junit.Test; - -import org.apache.cassandra.SchemaLoader; -import org.apache.cassandra.auth.AuthTestUtils; -import org.apache.cassandra.auth.AuthenticatedUser; -import org.apache.cassandra.cql3.CQLTester; -import org.apache.cassandra.tools.ToolRunner; - -import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A; -import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B; -import static org.assertj.core.api.Assertions.assertThat; - -public class VerifyTest extends CQLTester -{ - @BeforeClass - public static void setup() throws Exception - { - AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager(); - SchemaLoader.setupAuth(roleManager, - new AuthTestUtils.LocalPasswordAuthenticator(), - new AuthTestUtils.LocalCassandraAuthorizer(), - new AuthTestUtils.LocalCassandraNetworkAuthorizer(), - new AuthTestUtils.LocalCassandraCIDRAuthorizer()); - - roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions()); - roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions()); - - startJMXServer(); - } - - /** - * We calcify the help file as last seen as a "trigger" to notify a developer that, upon addition of a new flag or - * functionality to this tool option, they will need to update help output and/or documentation as necessary. - */ - @Test - public void testMaybeChangeDocs() - { - // If you added, modified options or help, please update docs if necessary - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "verify"); - tool.assertOnCleanExit(); - - String help = - "NAME\n" + - " nodetool verify - Verify (check data checksum for) one or more tables\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] verify\n" + - " [(-c | --check-version)] [(-d | --dfp)] [(-e | --extended-verify)]\n" + - " [(-f | --force)] [(-q | --quick)] [(-r | --rsc)] [(-t | --check-tokens)]\n" + - " [--] [ ...]\n" + - "\n" + - - "OPTIONS\n" + - " -c, --check-version\n" + - " Also check that all sstables are the latest version\n" + - "\n" + - " -d, --dfp\n" + - " Invoke the disk failure policy if a corrupt sstable is found\n" + - "\n" + - " -e, --extended-verify\n" + - " Verify each cell data, beyond simply checking sstable checksums\n" + - "\n" + - " -f, --force\n" + - " Override disabling of verify tool - see CASSANDRA-9947 for caveats\n" + - "\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -q, --quick\n" + - " Do a quick check - avoid reading all data to verify checksums\n" + - "\n" + - " -r, --rsc\n" + - " Mutate the repair status on corrupt sstables\n" + - "\n" + - " -t, --check-tokens\n" + - " Verify that all tokens in sstables are owned by this node\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " --\n" + - " This option can be used to separate command-line options from the\n" + - " list of argument, (useful when arguments might be mistaken for\n" + - " command-line options\n" + - "\n" + - " [ ...]\n" + - " The keyspace followed by one or many tables\n" + - "\n\n"; - - assertThat(tool.getStdout()).isEqualTo(help); - } -} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/VersionTest.java b/test/unit/org/apache/cassandra/tools/nodetool/VersionTest.java index 1b17fffa2a..81fcb3ba9b 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/VersionTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/VersionTest.java @@ -35,47 +35,6 @@ public class VersionTest extends CQLTester startJMXServer(); } - @Test - public void testHelp() - { - ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "version"); - tool.assertOnExitCode(); - - String help = "NAME\n" + - " nodetool version - Print cassandra version\n" + - "\n" + - "SYNOPSIS\n" + - " nodetool [(-h | --host )] [(-p | --port )]\n" + - " [(-pp | --print-port)] [(-pw | --password )]\n" + - " [(-pwf | --password-file )]\n" + - " [(-u | --username )] version [(-v | --verbose)]\n" + - "\n" + - "OPTIONS\n" + - " -h , --host \n" + - " Node hostname or ip address\n" + - "\n" + - " -p , --port \n" + - " Remote jmx agent port number\n" + - "\n" + - " -pp, --print-port\n" + - " Operate in 4.0 mode with hosts disambiguated by port number\n" + - "\n" + - " -pw , --password \n" + - " Remote jmx agent password\n" + - "\n" + - " -pwf , --password-file \n" + - " Path to the JMX password file\n" + - "\n" + - " -u , --username \n" + - " Remote jmx agent username\n" + - "\n" + - " -v, --verbose\n" + - " Include additional information\n" + - "\n" + - "\n"; - assertThat(tool.getStdout()).isEqualTo(help); - } - @Test public void testBasic() { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/AbortBootstrapMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/AbortBootstrapMockTest.java new file mode 100644 index 0000000000..054bb9a33a --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/AbortBootstrapMockTest.java @@ -0,0 +1,45 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class AbortBootstrapMockTest extends AbstractNodetoolMock +{ + @Test + public void testAbortBootstrapByNodeId() + { + String nodeId = "1"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("abortbootstrap", "--node", nodeId).assertOnCleanExit(); + Mockito.verify(mock).abortBootstrap(nodeId, ""); + } + + @Test + public void testAbortBootstrapByIp() + { + String ip = "10.20.113.11"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("abortbootstrap", "--ip", ip).assertOnCleanExit(); + Mockito.verify(mock).abortBootstrap("", ip); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/AbstractNodetoolMock.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/AbstractNodetoolMock.java new file mode 100644 index 0000000000..b7566dac91 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/AbstractNodetoolMock.java @@ -0,0 +1,180 @@ +/* + * 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.mock; + +import java.util.HashMap; +import java.util.Map; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.StandardEmitterMBean; +import javax.management.StandardMBean; + +import org.apache.cassandra.service.accord.AccordOperationsMBean; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; + +import org.apache.cassandra.batchlog.BatchlogManagerMBean; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.db.ColumnFamilyStoreMBean; +import org.apache.cassandra.db.compaction.CompactionManagerMBean; +import org.apache.cassandra.gms.FailureDetectorMBean; +import org.apache.cassandra.gms.GossiperMBean; +import org.apache.cassandra.hints.HintsServiceMBean; +import org.apache.cassandra.locator.EndpointSnitchInfoMBean; +import org.apache.cassandra.locator.LocationInfoMBean; +import org.apache.cassandra.net.MessagingServiceMBean; +import org.apache.cassandra.service.CacheServiceMBean; +import org.apache.cassandra.service.StorageProxyMBean; +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.service.snapshot.SnapshotManagerMBean; +import org.apache.cassandra.tools.NodeTool; +import org.apache.cassandra.tools.ToolRunner; +import org.apache.cassandra.utils.MBeanWrapper; +import org.mockito.Mockito; + +import static org.apache.cassandra.db.ColumnFamilyStore.getColumnFamilieMBeanName; + +public abstract class AbstractNodetoolMock extends CQLTester +{ + public static final String[] EMPTY_STRING_ARRAY = {}; + + public static final String ACCORD_OPERATIONS_MBEAN = "org.apache.cassandra.service.accord:type=AccordOperations"; + public static final String BATCHLOG_MANAGER_MBEAN = "org.apache.cassandra.db:type=BatchlogManager"; + public static final String CACHE_SERVICE_MBEAN = "org.apache.cassandra.db:type=Caches"; + public static final String COMPACTION_MANAGER_MBEAN = "org.apache.cassandra.db:type=CompactionManager"; + public static final String ENDPOINT_SNITCH_INFO_MBEAN = "org.apache.cassandra.db:type=EndpointSnitchInfo"; + public static final String FAILURE_DETECTOR_MBEAN = "org.apache.cassandra.net:type=FailureDetector"; + public static final String GOSSIPER_MBEAN = "org.apache.cassandra.net:type=Gossiper"; + public static final String HINTS_SERVICE_MBEAN = "org.apache.cassandra.hints:type=HintsService"; + public static final String LOCATION_INFO_MBEAN = "org.apache.cassandra.db:type=LocationInfo"; + public static final String MESSAGING_SERVICE_MBEAN = "org.apache.cassandra.net:type=MessagingService"; + public static final String SNAPSHOT_MANAGER_MBEAN = "org.apache.cassandra.service.snapshot:type=SnapshotManager"; + public static final String STORAGE_PROXY_MBEAN = "org.apache.cassandra.db:type=StorageProxy"; + public static final String STORAGE_SERVICE_MBEAN = "org.apache.cassandra.db:type=StorageService"; + + private static final Map> mbeans = new HashMap<>(); + private static final MBeanWrapper mbeanServer = MBeanWrapper.instance; + + static + { + mbeans.put(ACCORD_OPERATIONS_MBEAN, AccordOperationsMBean.class); + mbeans.put(BATCHLOG_MANAGER_MBEAN, BatchlogManagerMBean.class); + mbeans.put(CACHE_SERVICE_MBEAN, CacheServiceMBean.class); + mbeans.put(COMPACTION_MANAGER_MBEAN, CompactionManagerMBean.class); + mbeans.put(ENDPOINT_SNITCH_INFO_MBEAN, EndpointSnitchInfoMBean.class); + mbeans.put(FAILURE_DETECTOR_MBEAN, FailureDetectorMBean.class); + mbeans.put(GOSSIPER_MBEAN, GossiperMBean.class); + mbeans.put(HINTS_SERVICE_MBEAN, HintsServiceMBean.class); + mbeans.put(LOCATION_INFO_MBEAN, LocationInfoMBean.class); + mbeans.put(MESSAGING_SERVICE_MBEAN, MessagingServiceMBean.class); + mbeans.put(SNAPSHOT_MANAGER_MBEAN, SnapshotManagerMBean.class); + mbeans.put(STORAGE_PROXY_MBEAN, StorageProxyMBean.class); + mbeans.put(STORAGE_SERVICE_MBEAN, StorageServiceMBean.class); + } + + private MBeanMockHodler mbeanMockHodler; + + @BeforeClass + public static void setup() throws Throwable + { + requireNetwork(); + startJMXServer(); + } + + @Before + public void prepareMocks() + { + mbeanMockHodler = new MBeanMockHodler(); + mbeanMockHodler.unregisterAll(mbeanServer); + mbeanMockHodler.registerAll(mbeanServer); + } + + @After + public void unregisterMocks() + { + mbeanMockHodler.unregisterAll(mbeanServer); + } + + + protected T getMock(String mBeanName) + { + return mbeanMockHodler.getMock(mBeanName); + } + + protected ColumnFamilyStoreMBean addAndGetMockColumnFamilyStore(String keyspace, String table, boolean index) + { + String mBeanName = getColumnFamilieMBeanName(keyspace, table, index); + mbeanMockHodler.registerMBean(mBeanName, ColumnFamilyStoreMBean.class, mbeanServer); + return getMock(mBeanName); + } + + public static ToolRunner.ToolResult invokeNodetool(String... commands) + { + return ToolRunner.invokeNodetoolInJvm(NodeTool::new, commands); + } + + /** This class is used to hold the mocks for the MBeans. */ + private static class MBeanMockHodler + { + private final Map mocks = new HashMap<>(); + + MBeanMockHodler() + { + mbeans.forEach((name, clz) -> mocks.put(name, newMock(clz))); + } + + public static StandardMBean newMock(Class clz) + { + try + { + if (NotificationEmitter.class.isAssignableFrom(clz)) + return new StandardEmitterMBean(Mockito.mock(clz), clz, new NotificationBroadcasterSupport()); + + return new StandardMBean(Mockito.mock(clz), clz); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + public T getMock(String mBeanName) + { + return (T) mocks.get(mBeanName).getImplementation(); + } + + public void registerMBean(String name, Class clz, MBeanWrapper mbeanMockInstance) + { + mocks.put(name, newMock(clz)); + mbeanMockInstance.registerMBean(mocks.get(name), name); + } + + public void registerAll(MBeanWrapper mbeanMockInstance) + { + mocks.forEach((name, mock) -> mbeanMockInstance.registerMBean(mock, name)); + } + + public void unregisterAll(MBeanWrapper mbeanMockInstance) + { + mocks.keySet().forEach(name -> mbeanMockInstance.unregisterMBean(name, MBeanWrapper.OnException.IGNORE)); + } + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/AccordAdminMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/AccordAdminMockTest.java new file mode 100644 index 0000000000..1d53afd5d7 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/AccordAdminMockTest.java @@ -0,0 +1,38 @@ +/* + * 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.mock; + +import org.apache.cassandra.service.accord.AccordOperationsMBean; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.List; + +/** Test for the nodetool 'accord admin' command. */ +public class AccordAdminMockTest extends AbstractNodetoolMock +{ + @Test + public void testMarkStale() + { + List nodeIds = List.of("node1", "node2"); + AccordOperationsMBean mock = getMock(ACCORD_OPERATIONS_MBEAN); + invokeNodetool("accord", "mark_stale", "node1", "node2").assertOnCleanExit(); + Mockito.verify(mock).accordMarkStale(nodeIds); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/AssassinateMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/AssassinateMockTest.java new file mode 100644 index 0000000000..832399d9d9 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/AssassinateMockTest.java @@ -0,0 +1,41 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class AssassinateMockTest extends AbstractNodetoolMock +{ + @Test + public void testAssassinate() + { + String ip = "10.20.113.11"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("assassinate", ip).assertOnCleanExit(); + Mockito.verify(mock).assassinateEndpoint(ip); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/BootstrapResumeMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/BootstrapResumeMockTest.java new file mode 100644 index 0000000000..e531418fe5 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/BootstrapResumeMockTest.java @@ -0,0 +1,52 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; +import org.mockito.Mockito; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +public class BootstrapResumeMockTest extends AbstractNodetoolMock +{ + @Test + public void testResumeWithException() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + ToolRunner.ToolResult result = invokeNodetool("bootstrap", "resume"); + result.asserts().failure(); + assertTrue(result.getCleanedStderr().contains("'nodetool bootstrap resume' is disabled.")); + } + + @Test + public void testResume() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("bootstrap", "resume", "--force").assertOnCleanExit(); + Mockito.verify(mock).resumeBootstrap(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/CleanupMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/CleanupMockTest.java new file mode 100644 index 0000000000..f0b2c74992 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/CleanupMockTest.java @@ -0,0 +1,52 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class CleanupMockTest extends AbstractNodetoolMock +{ + @Test + public void testCleanupKeyspaceWithTables() throws Throwable + { + String table = "tableToCleanup"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("cleanup", "--jobs", "4", keyspace(), table).assertOnCleanExit(); + Mockito.verify(mock).forceKeyspaceCleanup(4, keyspace(), table); + } + + @Test + public void testCleanupAll() throws Throwable + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of("ks1", "ks2")); + when(mock.getNonLocalStrategyKeyspaces()).thenReturn(List.of("ks1", "ks2")); + invokeNodetool("cleanup").assertOnCleanExit(); + Mockito.verify(mock).forceKeyspaceCleanup(2, "ks1", EMPTY_STRING_ARRAY); + Mockito.verify(mock).forceKeyspaceCleanup(2, "ks2", EMPTY_STRING_ARRAY); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/CompactMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/CompactMockTest.java new file mode 100644 index 0000000000..5667ca9919 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/CompactMockTest.java @@ -0,0 +1,77 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; +import org.mockito.Mockito; + +import org.apache.cassandra.db.compaction.CompactionManagerMBean; +import org.apache.cassandra.service.StorageServiceMBean; + +import static org.mockito.Mockito.when; + +public class CompactMockTest extends AbstractNodetoolMock +{ + @Test + public void testCompactForceKeyspaceCompactionForPartitionKey() throws Throwable + { + long key = 43; + String table = "table"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("compact", "--partition", Long.toString(key), keyspace(), table).assertOnCleanExit(); + Mockito.verify(mock).forceKeyspaceCompactionForPartitionKey(keyspace(), Long.toString(key), table); + } + + @Test + public void testCompactForceKeyspaceCompactionForTokenRange() throws Throwable + { + long key = 34; + String startToken = Long.toString(key - 1); + String endToken = Long.toString(key + 1); + String table = "table"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("compact", "--start-token", startToken, "--end-token", endToken, keyspace(), table).assertOnCleanExit(); + Mockito.verify(mock).forceKeyspaceCompactionForTokenRange(keyspace(), startToken, endToken, table); + } + + @Test + public void testCompactForceKeyspaceCompaction() throws Throwable + { + String table = "table"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("compact", "--split-output", keyspace(), table).assertOnCleanExit(); + Mockito.verify(mock).forceKeyspaceCompaction(true, keyspace(), table); + } + + @Test + public void testCompactForceUserDefinedCompaction() throws Throwable + { + String[] ssTables = new String[] { "ssTable1", "ssTable2" }; + CompactionManagerMBean mock = getMock(COMPACTION_MANAGER_MBEAN); + invokeNodetool("compact", "--user-defined", ssTables[0], ssTables[1]).assertOnCleanExit(); + Mockito.verify(mock).forceUserDefinedCompaction(String.join(",", ssTables)); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DecommissionMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DecommissionMockTest.java new file mode 100644 index 0000000000..a19d4157de --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DecommissionMockTest.java @@ -0,0 +1,63 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; +import org.mockito.Mockito; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.when; + +public class DecommissionMockTest extends AbstractNodetoolMock +{ + @Test + public void testDecomission() throws Throwable + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.isDecommissioning()).thenReturn(false); + when(mock.getBootstrapState()).thenReturn("COMPLETED"); + invokeNodetool("decommission", "--force").assertOnCleanExit(); + Mockito.verify(mock).decommission(true); + } + + @Test + public void testDecomissionInProgress() throws Throwable + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.isDecommissioning()).thenReturn(true); + ToolRunner.ToolResult result = invokeNodetool("decommission"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("This node is still decommissioning."); + } + + @Test + public void testDecomissionedAlready() throws Throwable + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.isDecommissioning()).thenReturn(false); + when(mock.getBootstrapState()).thenReturn("DECOMMISSIONED"); + ToolRunner.ToolResult result = invokeNodetool("decommission", "--force"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("Node was already decommissioned."); + Mockito.verify(mock, Mockito.never()).decommission(anyBoolean()); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeClusterMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeClusterMockTest.java new file mode 100644 index 0000000000..32d7cd2439 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeClusterMockTest.java @@ -0,0 +1,72 @@ +/* + * 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.mock; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import org.apache.cassandra.locator.EndpointSnitchInfoMBean; +import org.apache.cassandra.locator.LocationInfoMBean; +import org.apache.cassandra.service.StorageProxyMBean; +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; +import org.mockito.ArgumentMatchers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class DescribeClusterMockTest extends AbstractNodetoolMock +{ + @Test + public void testDescribeCluster() throws Throwable + { + StorageServiceMBean ssMock = getMock(STORAGE_SERVICE_MBEAN); + LocationInfoMBean locationInfoMock = getMock(LOCATION_INFO_MBEAN); + StorageProxyMBean spMock = getMock(STORAGE_PROXY_MBEAN); + EndpointSnitchInfoMBean epSnitchInfoMock = getMock(ENDPOINT_SNITCH_INFO_MBEAN); + + when(epSnitchInfoMock.getDatacenter(ArgumentMatchers.any())).thenReturn("datacenter1"); + + when(locationInfoMock.getDatacenter()).thenReturn("datacenter1"); + when(locationInfoMock.getRack()).thenReturn("rack1"); + when(locationInfoMock.getNodeProximityName()).thenReturn("dc1-rack1"); + when(locationInfoMock.hasLegacySnitchAdapter()).thenReturn(false); + + when(ssMock.getClusterName()).thenReturn("Test Cluster"); + when(ssMock.getPartitionerName()).thenReturn("org.apache.cassandra.dht.Murmur3Partitioner"); + when(ssMock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(ssMock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + when(ssMock.getJoiningNodesWithPort()).thenReturn(List.of("node1")); + when(ssMock.getLeavingNodesWithPort()).thenReturn(List.of("node2")); + when(ssMock.getMovingNodesWithPort()).thenReturn(List.of("node3")); + when(ssMock.getLiveNodesWithPort()).thenReturn(List.of("node4")); + when(ssMock.getUnreachableNodesWithPort()).thenReturn(List.of("node5")); + when(ssMock.getTokenToEndpointWithPortMap()).thenReturn(Map.of("token1", "localhost:7000")); + when(ssMock.effectiveOwnershipWithPort(keyspace())).thenReturn(Map.of("node7", 1.0F)); + when(ssMock.getKeyspaceReplicationInfo(keyspace())).thenReturn("{'class':'SimpleStrategy', 'replication_factor':1}"); + when(spMock.getSchemaVersionsWithPort()).thenReturn(Map.of("proxy1", List.of("1.0", "2.0"))); + + ToolRunner.ToolResult result = invokeNodetool("--print-port", "describecluster"); + result.assertOnCleanExit(); + + assertThat(result.getStdout()).contains("cql_test_keyspace -> Replication class: {'class':'SimpleStrategy', 'replication_factor':1}"); + assertThat(result.getStdout()).contains("Partitioner: org.apache.cassandra.dht.Murmur3Partitioner"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeRingMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeRingMockTest.java new file mode 100644 index 0000000000..aca12d075d --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DescribeRingMockTest.java @@ -0,0 +1,50 @@ +/* + * 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.mock; + +import java.io.IOException; +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class DescribeRingMockTest extends AbstractNodetoolMock +{ + @Test + public void testDescribeRing() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("describering", keyspace()).assertOnCleanExit(); + Mockito.verify(mock).describeRingJMX(keyspace()); + } + + @Test + public void testDescribeRingWithPort() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("-pp", "describering", keyspace()).assertOnCleanExit(); + Mockito.verify(mock).describeRingWithPortJMX(keyspace()); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAuditLogMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAuditLogMockTest.java new file mode 100644 index 0000000000..aeaaeb426d --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAuditLogMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class DisableAuditLogMockTest extends AbstractNodetoolMock +{ + @Test + public void testDisableAuditLog() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("disableauditlog").assertOnCleanExit(); + Mockito.verify(mock).disableAuditLog(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAutoCompactionMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAutoCompactionMockTest.java new file mode 100644 index 0000000000..22b57e750e --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableAutoCompactionMockTest.java @@ -0,0 +1,70 @@ +/* + * 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.mock; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.IOException; +import java.util.List; + +import static org.mockito.Mockito.when; + +/** Test for the nodetool 'disableautocompaction' command. */ +public class DisableAutoCompactionMockTest extends AbstractNodetoolMock +{ + @Test + public void testDisableAutoCompactionForAllKeyspaces() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of("keyspace1", "keyspace2", "keyspace3")); + invokeNodetool("disableautocompaction").assertOnCleanExit(); + Mockito.verify(mock).disableAutoCompaction("keyspace1", EMPTY_STRING_ARRAY); + Mockito.verify(mock).disableAutoCompaction("keyspace2", EMPTY_STRING_ARRAY); + Mockito.verify(mock).disableAutoCompaction("keyspace3", EMPTY_STRING_ARRAY); + } + + @Test + public void testDisableAutoCompactionForSpecificKeyspaceAndTables() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of("test_keyspace")); + invokeNodetool("disableautocompaction", "test_keyspace", "table1", "table2", "table3").assertOnCleanExit(); + Mockito.verify(mock).disableAutoCompaction("test_keyspace", "table1", "table2", "table3"); + } + + @Test + public void testDisableAutoCompactionWithEmptyKeyspaceList() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of()); + invokeNodetool("disableautocompaction").assertOnCleanExit(); + Mockito.verify(mock, Mockito.never()).disableAutoCompaction(Mockito.anyString(), Mockito.any(String[].class)); + } + + @Test + public void testDisableAutoCompactionForSingleTable() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of("test_keyspace")); + invokeNodetool("disableautocompaction", "test_keyspace", "single_table").assertOnCleanExit(); + Mockito.verify(mock).disableAutoCompaction("test_keyspace", "single_table"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableBackupMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableBackupMockTest.java new file mode 100644 index 0000000000..13ee1195ca --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableBackupMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class DisableBackupMockTest extends AbstractNodetoolMock +{ + @Test + public void testDisableBackup() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("disablebackup").assertOnCleanExit(); + Mockito.verify(mock).setIncrementalBackupsEnabled(false); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableFullQueryLogMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableFullQueryLogMockTest.java new file mode 100644 index 0000000000..07a044aa3f --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableFullQueryLogMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class DisableFullQueryLogMockTest extends AbstractNodetoolMock +{ + @Test + public void testDisableFullQueryLog() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("disablefullquerylog").assertOnCleanExit(); + Mockito.verify(mock).stopFullQueryLogger(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableOldProtocolVersionsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableOldProtocolVersionsMockTest.java new file mode 100644 index 0000000000..5da5c96be9 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DisableOldProtocolVersionsMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class DisableOldProtocolVersionsMockTest extends AbstractNodetoolMock +{ + @Test + public void testDisableOldProtocolVersions() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("disableoldprotocolversions").assertOnCleanExit(); + Mockito.verify(mock).disableNativeTransportOldProtocolVersions(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/DrainMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/DrainMockTest.java new file mode 100644 index 0000000000..764e67578b --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/DrainMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class DrainMockTest extends AbstractNodetoolMock +{ + @Test + public void testDrain() throws Exception + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("drain").assertOnCleanExit(); + Mockito.verify(mock).drain(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableAutoCompactionMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableAutoCompactionMockTest.java new file mode 100644 index 0000000000..c69cac50df --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableAutoCompactionMockTest.java @@ -0,0 +1,50 @@ +/* + * 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.mock; + +import java.io.IOException; +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class EnableAutoCompactionMockTest extends AbstractNodetoolMock +{ + @Test + public void testEnableAutoCompaction() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("enableautocompaction").assertOnCleanExit(); + Mockito.verify(mock).enableAutoCompaction(keyspace(), EMPTY_STRING_ARRAY); + } + + @Test + public void testEnableAutoCompactionWithTables() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("enableautocompaction", keyspace(), "tbl1", "tbl2").assertOnCleanExit(); + Mockito.verify(mock).enableAutoCompaction(keyspace(), "tbl1", "tbl2"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableBackupMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableBackupMockTest.java new file mode 100644 index 0000000000..21fc82a15c --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableBackupMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class EnableBackupMockTest extends AbstractNodetoolMock +{ + @Test + public void testEnableBackup() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("enablebackup").assertOnCleanExit(); + Mockito.verify(mock).setIncrementalBackupsEnabled(true); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableOldProtocolVersionsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableOldProtocolVersionsMockTest.java new file mode 100644 index 0000000000..ed48588faf --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/EnableOldProtocolVersionsMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class EnableOldProtocolVersionsMockTest extends AbstractNodetoolMock +{ + @Test + public void testEnableOldProtocolVersions() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("enableoldprotocolversions").assertOnCleanExit(); + Mockito.verify(mock).enableNativeTransportOldProtocolVersions(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/FailureDetectorInfoMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/FailureDetectorInfoMockTest.java new file mode 100644 index 0000000000..b43c3feb9b --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/FailureDetectorInfoMockTest.java @@ -0,0 +1,66 @@ +/* + * 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.mock; + +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.junit.Test; + +import org.apache.cassandra.gms.FailureDetectorMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class FailureDetectorInfoMockTest extends AbstractNodetoolMock +{ + @Test + public void testFailureDetectorInfo() throws Exception + { + FailureDetectorMBean mock = getMock(FAILURE_DETECTOR_MBEAN); + when(mock.getPhiValues()).thenReturn(createPhiValues()); + invokeNodetool("failuredetector").assertOnCleanExit(); + Mockito.verify(mock).getPhiValues(); + } + + @Test + public void testFailureDetectorInfoWithPort() throws Exception + { + FailureDetectorMBean mock = getMock(FAILURE_DETECTOR_MBEAN); + when(mock.getPhiValuesWithPort()).thenReturn(createPhiValues()); + invokeNodetool("-pp", "failuredetector").assertOnCleanExit(); + Mockito.verify(mock).getPhiValuesWithPort(); + } + + private static TabularData createPhiValues() throws Exception + { + return new TabularDataSupport(new TabularType("PhiList", + "PhiList", + new CompositeType("Node", + "Node", + new String[]{ "Endpoint", "PHI" }, + new String[]{ "IP of the endpoint", "PHI value" }, + new OpenType[]{ SimpleType.STRING, SimpleType.DOUBLE }), + new String[]{ "Endpoint" })); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GarbageCollectMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GarbageCollectMockTest.java new file mode 100644 index 0000000000..49c10730ff --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GarbageCollectMockTest.java @@ -0,0 +1,61 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class GarbageCollectMockTest extends AbstractNodetoolMock +{ + @Test + public void testGarbageCollect() throws Exception + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("garbagecollect").assertOnCleanExit(); + Mockito.verify(mock).garbageCollect("ROW", 1, keyspace(), EMPTY_STRING_ARRAY); + } + + @Test + public void testGarbageCollectCells() throws Exception + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("garbagecollect", "-g", "CELL").assertOnCleanExit(); + Mockito.verify(mock).garbageCollect("CELL", 1, keyspace(), EMPTY_STRING_ARRAY); + } + + @Test + public void testGarbageCollectKeyspaceWithTables() throws Exception + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace())); + when(mock.getNonSystemKeyspaces()).thenReturn(List.of(keyspace())); + invokeNodetool("garbagecollect", "--jobs", "2", "-g", "CELL", keyspace(), "tbl1", "tbl2").assertOnCleanExit(); + Mockito.verify(mock).garbageCollect("CELL", 2, keyspace(), "tbl1", "tbl2"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetBatchlogReplayTrottleMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetBatchlogReplayTrottleMockTest.java new file mode 100644 index 0000000000..a6ca9a2d27 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetBatchlogReplayTrottleMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class GetBatchlogReplayTrottleMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetBatchlogReplayTrottle() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("getbatchlogreplaythrottle").assertOnCleanExit(); + Mockito.verify(mock).getBatchlogReplayThrottleInKB(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetCompactionThresholdMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetCompactionThresholdMockTest.java new file mode 100644 index 0000000000..b924026e3d --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetCompactionThresholdMockTest.java @@ -0,0 +1,51 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.db.ColumnFamilyStoreMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +public class GetCompactionThresholdMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetCompactionThreshold() + { + ToolRunner.ToolResult result = invokeNodetool("getcompactionthreshold"); + assertEquals(1, result.getExitCode()); + assertTrue(result.getStdout().contains("Missing required parameters: '', '
'")); + } + + @Test + public void testGetCompactionThresholdWithKeyspace() + { + ColumnFamilyStoreMBean mock = addAndGetMockColumnFamilyStore("ks", "cf", false); + when(mock.getMinimumCompactionThreshold()).thenReturn(4); + when(mock.getMaximumCompactionThreshold()).thenReturn(32); + ToolRunner.ToolResult result = invokeNodetool("getcompactionthreshold", "ks", "cf"); + result.assertOnCleanExit(); + assertTrue(result.getStdout().contains("Current compaction thresholds for ks/cf: \n")); + assertTrue(result.getStdout().contains(" min = 4, max = 32")); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrencyMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrencyMockTest.java new file mode 100644 index 0000000000..8bd05bdefb --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrencyMockTest.java @@ -0,0 +1,48 @@ +/* + * 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.mock; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; +import org.assertj.core.api.Assertions; + +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.when; + +public class GetConcurrencyMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetConcurrency() + { + Map> concurrency = Map.of("keyspace1", List.of(1, 2, 3), + "keyspace2", List.of(4, 5, 6)); + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getConcurrency(anyList())).thenReturn(concurrency); + ToolRunner.ToolResult tool = invokeNodetool("getconcurrency"); + tool.assertOnCleanExit(); + Assertions.assertThat(tool.getStdout()).contains("Stage CorePoolSize MaximumPoolSize"); + Assertions.assertThat(tool.getStdout()).contains("keyspace1 1 2"); + Assertions.assertThat(tool.getStdout()).contains("keyspace2 4 5"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentCompactorsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentCompactorsMockTest.java new file mode 100644 index 0000000000..da55ff38ee --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentCompactorsMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class GetConcurrentCompactorsMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetConcurrentCompactors() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("getconcurrentcompactors").assertOnCleanExit(); + Mockito.verify(mock).getConcurrentCompactors(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentViewBuildersMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentViewBuildersMockTest.java new file mode 100644 index 0000000000..43b15b3d69 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetConcurrentViewBuildersMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class GetConcurrentViewBuildersMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetConcurrentViewBuilders() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("getconcurrentviewbuilders").assertOnCleanExit(); + Mockito.verify(mock).getConcurrentViewBuilders(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetLoggingLevelsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetLoggingLevelsMockTest.java new file mode 100644 index 0000000000..f983228ad9 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetLoggingLevelsMockTest.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import java.util.Collections; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class GetLoggingLevelsMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetLoggingLevels() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getLoggingLevels()).thenReturn(Collections.singletonMap("Logger Name", "Log Level")); + invokeNodetool("getlogginglevels").assertOnCleanExit(); + Mockito.verify(mock).getLoggingLevels(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSSTablesMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSSTablesMockTest.java new file mode 100644 index 0000000000..9e7799c59b --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSSTablesMockTest.java @@ -0,0 +1,55 @@ +/* + * 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.mock; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.db.ColumnFamilyStoreMBean; +import org.apache.cassandra.tools.ToolRunner; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +public class GetSSTablesMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetSSTables() + { + ToolRunner.ToolResult result = invokeNodetool("getsstables"); + assertEquals(1, result.getExitCode()); + assertTrue(result.getStdout().contains("nodetool: getsstables requires ks, cf and key args")); + } + + @Test + public void testGetSSTablesWithFullAgruments() + { + ColumnFamilyStoreMBean mock = addAndGetMockColumnFamilyStore("ks", "cf", false); + when(mock.isLeveledCompaction()).thenReturn(true); + when(mock.getSSTablesForKeyWithLevel("key", false)).thenReturn(Collections.singletonMap(1, new HashSet<>(List.of("sstable1", "sstable2")))); + ToolRunner.ToolResult result = invokeNodetool("getsstables", "--show-levels", "ks", "cf", "key"); + result.assertOnCleanExit(); + Mockito.verify(mock).isLeveledCompaction(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSeedsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSeedsTest.java new file mode 100644 index 0000000000..1b63a4bae0 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSeedsTest.java @@ -0,0 +1,44 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.gms.GossiperMBean; +import org.apache.cassandra.tools.ToolRunner; +import org.mockito.Mockito; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class GetSeedsTest extends AbstractNodetoolMock +{ + @Test + public void testGetSeeds() + { + GossiperMBean mock = getMock(GOSSIPER_MBEAN); + when(mock.getSeeds()).thenReturn(List.of("seed1", "seed2")); + ToolRunner.ToolResult result = invokeNodetool("getseeds"); + result.assertOnCleanExit(); + Mockito.verify(mock).getSeeds(); + assertThat(result.getStdout()).contains("Current list of seed node IPs, excluding the current node's IP: seed1 seed2"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSnapshotThrottleMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSnapshotThrottleMockTest.java new file mode 100644 index 0000000000..a6e8716386 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetSnapshotThrottleMockTest.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.snapshot.SnapshotManagerMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class GetSnapshotThrottleMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetSnapshotThrottle() + { + SnapshotManagerMBean mock = getMock(SNAPSHOT_MANAGER_MBEAN); + when(mock.getSnapshotLinksPerSecond()).thenReturn(100L); + ToolRunner.ToolResult result = invokeNodetool("getsnapshotthrottle"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("Current snapshot throttle: 100 links/s"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/GetTraceProbabilityMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetTraceProbabilityMockTest.java new file mode 100644 index 0000000000..0903279b88 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/GetTraceProbabilityMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class GetTraceProbabilityMockTest extends AbstractNodetoolMock +{ + @Test + public void testGetTraceProbability() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("gettraceprobability").assertOnCleanExit(); + Mockito.verify(mock).getTraceProbability(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateKeyCacheMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateKeyCacheMockTest.java new file mode 100644 index 0000000000..15bc2fd0e6 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateKeyCacheMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.CacheServiceMBean; +import org.mockito.Mockito; + +public class InvalidateKeyCacheMockTest extends AbstractNodetoolMock +{ + @Test + public void testInvalidateKeyCache() + { + CacheServiceMBean mock = getMock(CACHE_SERVICE_MBEAN); + invokeNodetool("invalidatekeycache").assertOnCleanExit(); + Mockito.verify(mock).invalidateKeyCache(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateRowCacheMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateRowCacheMockTest.java new file mode 100644 index 0000000000..649153a60c --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/InvalidateRowCacheMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.CacheServiceMBean; +import org.mockito.Mockito; + +public class InvalidateRowCacheMockTest extends AbstractNodetoolMock +{ + @Test + public void testInvalidateRowCache() + { + CacheServiceMBean mock = getMock(CACHE_SERVICE_MBEAN); + invokeNodetool("invalidaterowcache").assertOnCleanExit(); + Mockito.verify(mock).invalidateRowCache(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ListPendingHintsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ListPendingHintsMockTest.java new file mode 100644 index 0000000000..e6dd1f5d05 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ListPendingHintsMockTest.java @@ -0,0 +1,42 @@ +/* + * 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.mock; + +import java.util.Collections; + +import org.junit.Test; + +import org.apache.cassandra.hints.HintsServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class ListPendingHintsMockTest extends AbstractNodetoolMock +{ + @Test + public void testListPendingHints() + { + HintsServiceMBean mock = getMock(HINTS_SERVICE_MBEAN); + when(mock.getPendingHints()).thenReturn(Collections.emptyList()); + ToolRunner.ToolResult result = invokeNodetool("listpendinghints"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("This node does not have any pending hints"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/RangeKeySampleMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/RangeKeySampleMockTest.java new file mode 100644 index 0000000000..3f3cd0f6bc --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/RangeKeySampleMockTest.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class RangeKeySampleMockTest extends AbstractNodetoolMock +{ + @Test + public void testRangeKeySample() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.sampleKeyRange()).thenReturn(List.of("key1", "key2")); + invokeNodetool("rangekeysample").assertOnCleanExit(); + Mockito.verify(mock).sampleKeyRange(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/RebuildIndexMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/RebuildIndexMockTest.java new file mode 100644 index 0000000000..9cffbcf0a6 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/RebuildIndexMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class RebuildIndexMockTest extends AbstractNodetoolMock +{ + @Test + public void testRebuildIndex() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("rebuild_index", keyspace(), "tbl1", "indexName1", "indexName2").assertOnCleanExit(); + Mockito.verify(mock).rebuildSecondaryIndex(Mockito.eq(keyspace()), Mockito.eq("tbl1"), Mockito.eq("indexName1"), Mockito.eq("indexName2")); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadLocalSchemaMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadLocalSchemaMockTest.java new file mode 100644 index 0000000000..a999f377f9 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadLocalSchemaMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class ReloadLocalSchemaMockTest extends AbstractNodetoolMock +{ + @Test + public void testReloadLocalSchema() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("reloadlocalschema").assertOnCleanExit(); + Mockito.verify(mock).reloadLocalSchema(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSeedsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSeedsMockTest.java new file mode 100644 index 0000000000..6d4a3637b3 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSeedsMockTest.java @@ -0,0 +1,42 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; + +import org.apache.cassandra.gms.GossiperMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class ReloadSeedsMockTest extends AbstractNodetoolMock +{ + @Test + public void testReloadSeeds() + { + GossiperMBean mock = getMock(GOSSIPER_MBEAN); + when(mock.reloadSeeds()).thenReturn(List.of("seed1")); + ToolRunner.ToolResult result = invokeNodetool("reloadseeds"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("Updated seed node IP list, excluding the current node's IP: seed1"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSslCertificatesMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSslCertificatesMockTest.java new file mode 100644 index 0000000000..b6c412bf17 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadSslCertificatesMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.net.MessagingServiceMBean; +import org.mockito.Mockito; + +public class ReloadSslCertificatesMockTest extends AbstractNodetoolMock +{ + @Test + public void testReloadSslCertificates() throws Exception + { + MessagingServiceMBean mock = getMock(MESSAGING_SERVICE_MBEAN); + invokeNodetool("reloadssl").assertOnCleanExit(); + Mockito.verify(mock).reloadSslCertificates(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadTriggersMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadTriggersMockTest.java new file mode 100644 index 0000000000..ee4f0c4da9 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReloadTriggersMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageProxyMBean; +import org.mockito.Mockito; + +public class ReloadTriggersMockTest extends AbstractNodetoolMock +{ + @Test + public void testReloadTriggers() + { + StorageProxyMBean mock = getMock(STORAGE_PROXY_MBEAN); + invokeNodetool("reloadtriggers").assertOnCleanExit(); + Mockito.verify(mock).reloadTriggerClasses(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/RelocateSSTablesMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/RelocateSSTablesMockTest.java new file mode 100644 index 0000000000..4a81bc92c8 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/RelocateSSTablesMockTest.java @@ -0,0 +1,43 @@ +/* + * 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.mock; + +import java.util.List; + +import org.junit.Test; +import org.mockito.Mockito; + +import org.apache.cassandra.service.StorageServiceMBean; + +import static org.mockito.Mockito.when; + +/** Test for the nodetool {@link org.apache.cassandra.tools.nodetool.RelocateSSTables} command. */ +public class RelocateSSTablesMockTest extends AbstractNodetoolMock +{ + @Test + public void testRelocateSSTables() throws Exception + { + String keyspace = "keyspace1"; + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(List.of(keyspace)); + when(mock.getNonLocalStrategyKeyspaces()).thenReturn(List.of(keyspace)); + invokeNodetool("relocatesstables", keyspace, "table1", "table2").assertOnCleanExit(); + Mockito.verify(mock).relocateSSTables(2, keyspace, "table1", "table2"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ReplayBatchlogMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReplayBatchlogMockTest.java new file mode 100644 index 0000000000..79fe639a66 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ReplayBatchlogMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.batchlog.BatchlogManagerMBean; +import org.mockito.Mockito; + +public class ReplayBatchlogMockTest extends AbstractNodetoolMock +{ + @Test + public void testReplayBatchlog() throws Exception + { + BatchlogManagerMBean mock = getMock(BATCHLOG_MANAGER_MBEAN); + invokeNodetool("replaybatchlog").assertOnCleanExit(); + Mockito.verify(mock).forceBatchlogReplay(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ResetLocalSchemaMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ResetLocalSchemaMockTest.java new file mode 100644 index 0000000000..aeabadb4f5 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ResetLocalSchemaMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class ResetLocalSchemaMockTest extends AbstractNodetoolMock +{ + @Test + public void testResetLocalSchema() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("resetlocalschema").assertOnCleanExit(); + Mockito.verify(mock).resetLocalSchema(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetBatchlogReplayThrottleMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetBatchlogReplayThrottleMockTest.java new file mode 100644 index 0000000000..3210fa23c5 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetBatchlogReplayThrottleMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class SetBatchlogReplayThrottleMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetBatchlogReplayThrottle() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("setbatchlogreplaythrottle", "15").assertOnCleanExit(); + Mockito.verify(mock).setBatchlogReplayThrottleInKB(15); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetCacheKeysToSaveMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetCacheKeysToSaveMockTest.java new file mode 100644 index 0000000000..ccb80ade74 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetCacheKeysToSaveMockTest.java @@ -0,0 +1,37 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.CacheServiceMBean; +import org.mockito.Mockito; + +public class SetCacheKeysToSaveMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetCacheKeysToSave() + { + CacheServiceMBean mock = getMock(CACHE_SERVICE_MBEAN); + invokeNodetool("setcachekeystosave", "5", "10", "15").assertOnCleanExit(); + Mockito.verify(mock).setKeyCacheKeysToSave(5); + Mockito.verify(mock).setRowCacheKeysToSave(10); + Mockito.verify(mock).setCounterCacheKeysToSave(15); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetCompactionThresholdMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetCompactionThresholdMockTest.java new file mode 100644 index 0000000000..709c9bc24e --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetCompactionThresholdMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.db.ColumnFamilyStoreMBean; +import org.mockito.Mockito; + +public class SetCompactionThresholdMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetCompactionThreshold() + { + ColumnFamilyStoreMBean mock = addAndGetMockColumnFamilyStore("keyspace", "table", false); + invokeNodetool("setcompactionthreshold", "keyspace", "table", "2", "5").assertOnCleanExit(); + Mockito.verify(mock).setCompactionThresholds(2, 5); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrencyMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrencyMockTest.java new file mode 100644 index 0000000000..20e02e6c6d --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrencyMockTest.java @@ -0,0 +1,58 @@ +/* + * 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.mock; + +import org.junit.Test; +import org.mockito.Mockito; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doThrow; + +public class SetConcurrencyMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetConcurrencyTwoArgs() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("setconcurrency", "stage", "15").assertOnCleanExit(); + Mockito.verify(mock).setConcurrency("stage", -1, 15); + } + + @Test + public void testSetConcurrencyThreeArgs() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("setconcurrency", "stage", "11", "22").assertOnCleanExit(); + Mockito.verify(mock).setConcurrency("stage", 11, 22); + } + + @Test + public void testSetConcurrencyIllegalException() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + doThrow(new IllegalArgumentException("Test exception of the illegal set concurrency call")) + .when(mock).setConcurrency(Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt()); + ToolRunner.ToolResult result = invokeNodetool("setconcurrency", "stage", "11", "22"); + result.asserts().failure(); + assertTrue(result.getStdout().contains("Test exception of the illegal set concurrency call")); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrentViewBuildersMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrentViewBuildersMockTest.java new file mode 100644 index 0000000000..097a562a21 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetConcurrentViewBuildersMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class SetConcurrentViewBuildersMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetConcurrentViewBuilders() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("setconcurrentviewbuilders", "15").assertOnCleanExit(); + Mockito.verify(mock).setConcurrentViewBuilders(15); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetLoggingLevelMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetLoggingLevelMockTest.java new file mode 100644 index 0000000000..e8540c2167 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetLoggingLevelMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class SetLoggingLevelMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetLoggingLevel() throws Exception + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("setlogginglevel", "o.a.c.t.n.m.SetLoggingLevelMockTest", "DEBUG").assertOnCleanExit(); + Mockito.verify(mock).setLoggingLevel("o.a.c.t.n.m.SetLoggingLevelMockTest", "DEBUG"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetSnapshotThrottleMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetSnapshotThrottleMockTest.java new file mode 100644 index 0000000000..bbdb5088f0 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetSnapshotThrottleMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.snapshot.SnapshotManagerMBean; +import org.mockito.Mockito; + +public class SetSnapshotThrottleMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetSnapshotThrottle() + { + SnapshotManagerMBean mock = getMock(SNAPSHOT_MANAGER_MBEAN); + invokeNodetool("setsnapshotthrottle", "15").assertOnCleanExit(); + Mockito.verify(mock).setSnapshotLinksPerSecond(15); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SetTraceProbabilityMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetTraceProbabilityMockTest.java new file mode 100644 index 0000000000..70981f598c --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SetTraceProbabilityMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class SetTraceProbabilityMockTest extends AbstractNodetoolMock +{ + @Test + public void testSetTraceProbability() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("settraceprobability", "0.15").assertOnCleanExit(); + Mockito.verify(mock).setTraceProbability(0.15); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SjkMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SjkMockTest.java new file mode 100644 index 0000000000..836f85afb3 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SjkMockTest.java @@ -0,0 +1,56 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SjkMockTest extends AbstractNodetoolMock +{ + + @Test + public void testSjk() + { + ToolRunner.ToolResult result = invokeNodetool("sjk"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("Usage:
[options] [command] [command options]"); + assertThat(result.getStdout()).contains(" gc [Print GC] Print GC log like information for remote process"); + } + + @Test + public void testSjkHelp() + { + ToolRunner.ToolResult result = invokeNodetool("sjk", "--help"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("Usage:
[options] [command] [command options]"); + assertThat(result.getStdout()).contains(" ttop [Thread Top] Displays threads from JVM process"); + } + + @Test + public void testSjkTtop() + { + ToolRunner.ToolResult result = invokeNodetool("sjk", "hh", "--top-number", "10", "--live"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains(" # Instances Bytes Type"); + assertThat(result.getStdout()).containsPattern(" +\\d+ +\\d+ +org\\.apache\\.cassandra\\.config\\.DatabaseDescriptor\\$1"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/SnapshotMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/SnapshotMockTest.java new file mode 100644 index 0000000000..48b4aa58c3 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/SnapshotMockTest.java @@ -0,0 +1,43 @@ +/* + * 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.mock; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.mockito.Mockito; + +import org.apache.cassandra.service.snapshot.SnapshotManagerMBean; +import org.apache.cassandra.service.snapshot.SnapshotOptions; + +public class SnapshotMockTest extends AbstractNodetoolMock +{ + @Test + public void testSnapshotWithKtList() throws Exception + { + String snapshotName = "snapshotName1"; + Map options = new HashMap<>(); + options.put(SnapshotOptions.SKIP_FLUSH, Boolean.toString(false)); + SnapshotManagerMBean mock = getMock(SNAPSHOT_MANAGER_MBEAN); + // Note: the space in the kt-list is intentional to match the expected format + invokeNodetool("snapshot", "--kt-list", "keyspace1.table1, keyspace2.table2", "--tag", snapshotName).assertOnCleanExit(); + Mockito.verify(mock).takeSnapshot(snapshotName, options, "keyspace1.table1", "keyspace2.table2"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusAutoCompactionMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusAutoCompactionMockTest.java new file mode 100644 index 0000000000..d2b9c86ce3 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusAutoCompactionMockTest.java @@ -0,0 +1,44 @@ +/* + * 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.mock; + +import java.util.Collections; +import java.util.Map; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class StatusAutoCompactionMockTest extends AbstractNodetoolMock +{ + @Test + public void testStatusAutoCompaction() throws Exception + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getKeyspaces()).thenReturn(Collections.singletonList("keyspace")); + when(mock.getAutoCompactionStatus("keyspace", "table")).thenReturn(Map.of("table", true)); + ToolRunner.ToolResult result = invokeNodetool("statusautocompaction", "keyspace", "table"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("not running"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBackupMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBackupMockTest.java new file mode 100644 index 0000000000..19a0f8f967 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBackupMockTest.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class StatusBackupMockTest extends AbstractNodetoolMock +{ + @Test + public void testStatusBackup() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.isIncrementalBackupsEnabled()).thenReturn(true); + ToolRunner.ToolResult result = invokeNodetool("statusbackup"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("running"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBinaryMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBinaryMockTest.java new file mode 100644 index 0000000000..5a7d1dd22a --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusBinaryMockTest.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class StatusBinaryMockTest extends AbstractNodetoolMock +{ + @Test + public void testStatusBinary() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.isNativeTransportRunning()).thenReturn(false); + ToolRunner.ToolResult result = invokeNodetool("statusbinary"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("not running"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusGossipMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusGossipMockTest.java new file mode 100644 index 0000000000..90b3a3a9f9 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/StatusGossipMockTest.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class StatusGossipMockTest extends AbstractNodetoolMock +{ + @Test + public void testStatusGossip() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.isGossipRunning()).thenReturn(false); + ToolRunner.ToolResult result = invokeNodetool("statusgossip"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("not running"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/StopDaemonMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/StopDaemonMockTest.java new file mode 100644 index 0000000000..f5a8885955 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/StopDaemonMockTest.java @@ -0,0 +1,35 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +public class StopDaemonMockTest extends AbstractNodetoolMock +{ + @Test + public void testStopDaemon() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + invokeNodetool("stopdaemon").assertOnCleanExit(); + Mockito.verify(mock).stopDaemon(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/StopMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/StopMockTest.java new file mode 100644 index 0000000000..71a5bac5dd --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/StopMockTest.java @@ -0,0 +1,53 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.db.compaction.CompactionManagerMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +public class StopMockTest extends AbstractNodetoolMock +{ + @Test + public void testStop() + { + CompactionManagerMBean mock = getMock(COMPACTION_MANAGER_MBEAN); + invokeNodetool("stop", "CLEANUP").assertOnCleanExit(); + mock.stopCompaction("CLEANUP"); + } + + @Test + public void testStopById() + { + CompactionManagerMBean mock = getMock(COMPACTION_MANAGER_MBEAN); + invokeNodetool("stop", "--compaction-id", "15").assertOnCleanExit(); + mock.stopCompactionById("15"); + } + + @Test + public void testStopUknown() + { + CompactionManagerMBean mock = getMock(COMPACTION_MANAGER_MBEAN); + ToolRunner.ToolResult result = invokeNodetool("stop", "unknown"); + assertThat(result.getStdout()).contains("Invalid value for positional parameter at index 0 (compaction_type):"); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/TruncateHintsMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/TruncateHintsMockTest.java new file mode 100644 index 0000000000..7facd6e9ec --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/TruncateHintsMockTest.java @@ -0,0 +1,43 @@ +/* + * 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.mock; + +import org.junit.Test; + +import org.apache.cassandra.hints.HintsServiceMBean; +import org.mockito.Mockito; + +public class TruncateHintsMockTest extends AbstractNodetoolMock +{ + @Test + public void testTruncateHints() + { + HintsServiceMBean mock = getMock(HINTS_SERVICE_MBEAN); + invokeNodetool("truncatehints", "127.0.0.1:7199").assertOnCleanExit(); + Mockito.verify(mock).deleteAllHintsForEndpoint("127.0.0.1:7199"); + } + + @Test + public void testTruncateHintsAll() + { + HintsServiceMBean mock = getMock(HINTS_SERVICE_MBEAN); + invokeNodetool("truncatehints").assertOnCleanExit(); + Mockito.verify(mock).deleteAllHints(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/VersionMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/VersionMockTest.java new file mode 100644 index 0000000000..e1ee3047e2 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/VersionMockTest.java @@ -0,0 +1,51 @@ +/* + * 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.mock; + +import java.io.IOException; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.mockito.Mockito; + +import static org.mockito.Mockito.when; + +public class VersionMockTest extends AbstractNodetoolMock +{ + @Test + public void testVersion() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getReleaseVersion()).thenReturn("4.0.0"); + invokeNodetool("version").assertOnCleanExit(); + Mockito.verify(mock).getReleaseVersion(); + } + + @Test + public void testVersionVerbose() throws IOException + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getReleaseVersion()).thenReturn("4.0.0"); + when(mock.getGitSHA()).thenReturn("abcdef"); + invokeNodetool("version", "-v").assertOnCleanExit(); + Mockito.verify(mock).getReleaseVersion(); + Mockito.verify(mock).getGitSHA(); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/mock/ViewBuildStatusMockTest.java b/test/unit/org/apache/cassandra/tools/nodetool/mock/ViewBuildStatusMockTest.java new file mode 100644 index 0000000000..cdc4865bcd --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/mock/ViewBuildStatusMockTest.java @@ -0,0 +1,56 @@ +/* + * 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.mock; + +import java.util.Map; + +import org.junit.Test; + +import org.apache.cassandra.service.StorageServiceMBean; +import org.apache.cassandra.tools.ToolRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class ViewBuildStatusMockTest extends AbstractNodetoolMock +{ + @Test + public void testViewBuildStatus() + { + StorageServiceMBean mock = getMock(STORAGE_SERVICE_MBEAN); + when(mock.getViewBuildStatuses("keyspace", "table")).thenReturn(Map.of("127.0.0.1", "SUCCESS")); + ToolRunner.ToolResult result = invokeNodetool("viewbuildstatus", "keyspace.table"); + result.assertOnCleanExit(); + assertThat(result.getStdout()).contains("keyspace.table has finished building"); + } + + @Test + public void testViewBuildStatusUnknownArgs() + { + ToolRunner.ToolResult result = invokeNodetool("viewbuildstatus", "keyspace", "table", "unknown"); + assertThat(result.getStdout()).contains("nodetool: Unmatched argument at index 7: 'unknown'"); + } + + @Test + public void testViewBuildStatusIncorrectArgs() + { + ToolRunner.ToolResult result = invokeNodetool("viewbuildstatus", "keyspace_table"); + assertThat(result.getStdout()).contains("nodetool: viewbuildstatus requires keyspace and view name arguments"); + } +} diff --git a/tools/fqltool/src/org/apache/cassandra/fqltool/FullQueryLogTool.java b/tools/fqltool/src/org/apache/cassandra/fqltool/FullQueryLogTool.java index 97e74872ec..16db4818a7 100644 --- a/tools/fqltool/src/org/apache/cassandra/fqltool/FullQueryLogTool.java +++ b/tools/fqltool/src/org/apache/cassandra/fqltool/FullQueryLogTool.java @@ -18,70 +18,48 @@ package org.apache.cassandra.fqltool; -import java.util.List; - import com.google.common.base.Throwables; -import io.airlift.airline.Cli; -import io.airlift.airline.Help; -import io.airlift.airline.ParseArgumentsMissingException; -import io.airlift.airline.ParseArgumentsUnexpectedException; -import io.airlift.airline.ParseCommandMissingException; -import io.airlift.airline.ParseCommandUnrecognizedException; -import io.airlift.airline.ParseOptionConversionException; -import io.airlift.airline.ParseOptionMissingException; -import io.airlift.airline.ParseOptionMissingValueException; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.fqltool.commands.Compare; import org.apache.cassandra.fqltool.commands.Dump; import org.apache.cassandra.fqltool.commands.Replay; +import picocli.CommandLine; +import picocli.CommandLine.Command; import static com.google.common.base.Throwables.getStackTraceAsString; -import static com.google.common.collect.Lists.newArrayList; -public class FullQueryLogTool +@Command(name = "fqltool", + description = "Manipulate the contents of full query log files", + subcommands = { CommandLine.HelpCommand.class, Dump.class, Replay.class, Compare.class }) +public class FullQueryLogTool implements Runnable { public static void main(String... args) { DatabaseDescriptor.clientInitialization(); - List> commands = newArrayList( - Help.class, - Dump.class, - Replay.class, - Compare.class - ); + CommandLine commandLine = new CommandLine(FullQueryLogTool.class); + commandLine.setExecutionExceptionHandler((ex, c, arg) -> { + // Used for backward compatibility, some commands are validated when a command is run. + if (ex instanceof IllegalArgumentException | ex instanceof IllegalStateException) + { + badUse(ex); + return 1; + } + err(Throwables.getRootCause(ex)); + return 2; + }) + .setParameterExceptionHandler((ex, arg) -> { + badUse(ex); + return 1; + }); - Cli.CliBuilder builder = Cli.builder("fqltool"); + System.exit(commandLine.execute(args)); + } - builder.withDescription("Manipulate the contents of full query log files") - .withDefaultCommand(Help.class) - .withCommands(commands); - - Cli parser = builder.build(); - - int status = 0; - try - { - parser.parse(args).run(); - } catch (IllegalArgumentException | - IllegalStateException | - ParseArgumentsMissingException | - ParseArgumentsUnexpectedException | - ParseOptionConversionException | - ParseOptionMissingException | - ParseOptionMissingValueException | - ParseCommandMissingException | - ParseCommandUnrecognizedException e) - { - badUse(e); - status = 1; - } catch (Throwable throwable) - { - err(Throwables.getRootCause(throwable)); - status = 2; - } - - System.exit(status); + @Override + public void run() + { + CommandLine.usage(this, System.out); } private static void badUse(Exception e) diff --git a/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Compare.java b/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Compare.java index 83095d2ed4..a7330f5419 100644 --- a/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Compare.java +++ b/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Compare.java @@ -27,29 +27,27 @@ import java.util.stream.Collectors; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.AbstractIterator; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import net.openhft.chronicle.core.io.Closeable; import net.openhft.chronicle.queue.ChronicleQueue; -import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.ExcerptTailer; +import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import org.apache.cassandra.fqltool.FQLQueryIterator; import org.apache.cassandra.fqltool.ResultHandler; import org.apache.cassandra.fqltool.StoredResultSet; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** */ @Command(name = "compare", description = "Compare result files generated by fqltool replay") public class Compare implements Runnable { - @Arguments(usage = " [...]", - description = "Directories containing result files to compare.", - required = true) + @Parameters(paramLabel = "path", description = "Directories containing result files to compare.", arity = "1..*") private List arguments = new ArrayList<>(); - @Option(title = "queries", - name = { "--queries"}, + @Option(paramLabel = "queries", + names = { "--queries" }, description = "Directory to read the queries from. It is produced by the fqltool replay --store-queries option. ", required = true) private String querylog; diff --git a/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Dump.java b/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Dump.java index 365d995972..f260af4055 100644 --- a/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Dump.java +++ b/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Dump.java @@ -28,24 +28,24 @@ import java.util.stream.Collectors; import com.google.common.annotations.VisibleForTesting; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import io.netty.buffer.Unpooled; import net.openhft.chronicle.bytes.Bytes; import net.openhft.chronicle.core.io.IORuntimeException; import net.openhft.chronicle.queue.ChronicleQueue; -import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.ExcerptTailer; import net.openhft.chronicle.queue.RollCycles; +import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.threads.Pauser; import net.openhft.chronicle.wire.ReadMarshallable; import net.openhft.chronicle.wire.ValueIn; import net.openhft.chronicle.wire.WireIn; -import org.apache.cassandra.fql.FullQueryLogger; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.fql.FullQueryLogger; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.binlog.BinLog; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** * Dump the contents of a list of paths containing full query logs @@ -55,13 +55,13 @@ public class Dump implements Runnable { static final char[] HEXI_DECIMAL = "0123456789ABCDEF".toCharArray(); - @Arguments(usage = " [...]", description = "Path containing the full query logs to dump.", required = true) + @Parameters(paramLabel = "path", description = "Path containing the full query logs to dump.", arity = "1..*") private List arguments = new ArrayList<>(); - @Option(title = "roll_cycle", name = {"--roll-cycle"}, description = "How often to roll the log file was rolled. May be necessary for Chronicle to correctly parse file names. (MINUTELY, HOURLY, DAILY). Default HOURLY.") + @Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file was rolled. May be necessary for Chronicle to correctly parse file names. (MINUTELY, HOURLY, DAILY). Default HOURLY.") private String rollCycle = "HOURLY"; - @Option(title = "follow", name = {"--follow"}, description = "Upon reacahing the end of the log continue indefinitely waiting for more records") + @Option(paramLabel = "follow", names = { "--follow" }, description = "Upon reacahing the end of the log continue indefinitely waiting for more records") private boolean follow = false; @Override diff --git a/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Replay.java b/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Replay.java index 492e6ac1bf..66c88c69f8 100644 --- a/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Replay.java +++ b/tools/fqltool/src/org/apache/cassandra/fqltool/commands/Replay.java @@ -29,9 +29,6 @@ import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; import net.openhft.chronicle.core.io.Closeable; import net.openhft.chronicle.queue.ChronicleQueue; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; @@ -40,6 +37,9 @@ import org.apache.cassandra.fqltool.FQLQueryIterator; import org.apache.cassandra.fqltool.QueryReplayer; import org.apache.cassandra.utils.AbstractIterator; import org.apache.cassandra.utils.MergeIterator; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; /** * replay the contents of a list of paths containing full query logs @@ -49,22 +49,22 @@ public class Replay implements Runnable { private static final Logger logger = LoggerFactory.getLogger(Replay.class); - @Arguments(usage = " [...]", description = "Paths containing the full query logs to replay.", required = true) + @Parameters(paramLabel = "path", description = "Paths containing the full query logs to replay.", arity = "1..*") private List arguments = new ArrayList<>(); - @Option(title = "target", name = {"--target"}, description = "Hosts to replay the logs to, can be repeated to replay to more hosts.", required = true) + @Option(paramLabel = "target", names = { "--target" }, description = "Hosts to replay the logs to, can be repeated to replay to more hosts.", required = true) private List targetHosts; - @Option(title = "results", name = { "--results"}, description = "Where to store the results of the queries, this should be a directory. Leave this option out to avoid storing results.") + @Option(paramLabel = "results", names = { "--results" }, description = "Where to store the results of the queries, this should be a directory. Leave this option out to avoid storing results.") private String resultPath; - @Option(title = "keyspace", name = { "--keyspace"}, description = "Only replay queries against this keyspace and queries without keyspace set.") + @Option(paramLabel = "keyspace", names = { "--keyspace" }, description = "Only replay queries against this keyspace and queries without keyspace set.") private String keyspace; - @Option(title = "store_queries", name = {"--store-queries"}, description = "Path to store the queries executed. Stores queries in the same order as the result sets are in the result files. Requires --results") + @Option(paramLabel = "store_queries", names = { "--store-queries" }, description = "Path to store the queries executed. Stores queries in the same order as the result sets are in the result files. Requires --results") private String queryStorePath; - @Option(title = "replay_ddl_statements", name = { "--replay-ddl-statements" }, description = "If specified, replays DDL statements as well, they are excluded from replaying by default.") + @Option(paramLabel = "replay_ddl_statements", names = { "--replay-ddl-statements" }, description = "If specified, replays DDL statements as well, they are excluded from replaying by default.") private boolean replayDDLStatements; @Override @@ -79,15 +79,14 @@ public class Replay implements Runnable if (!basePath.exists() || !basePath.isDirectory()) { System.err.println("The results path (" + basePath + ") should be an existing directory"); - System.exit(1); + throw new IllegalArgumentException("The results path (" + basePath + ") should be an existing directory"); } resultPaths = targetHosts.stream().map(target -> new File(basePath, target)).collect(Collectors.toList()); resultPaths.forEach(File::mkdir); } if (targetHosts.size() < 1) { - System.err.println("You need to state at least one --target host to replay the query against"); - System.exit(1); + throw new IllegalArgumentException("You need to state at least one --target host to replay the query against"); } replay(keyspace, arguments, targetHosts, resultPaths, queryStorePath, replayDDLStatements); } diff --git a/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java b/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java index 4fdb9c076f..87d18816b9 100644 --- a/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java +++ b/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java @@ -31,16 +31,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import javax.inject.Inject; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Uninterruptibles; -import io.airlift.airline.Cli; -import io.airlift.airline.Command; -import io.airlift.airline.Help; -import io.airlift.airline.HelpOption; -import io.airlift.airline.Option; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.statements.schema.CreateTableStatement; import org.apache.cassandra.db.ColumnFamilyStore; @@ -69,6 +63,9 @@ import org.apache.cassandra.tools.nodetool.formatter.TableBuilder; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.concurrent.Future; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; /** * Tool that allows fast route to loading data for arbitrary schemas to disk @@ -76,16 +73,13 @@ import org.apache.cassandra.utils.concurrent.Future; */ public abstract class CompactionStress implements Runnable { - @Inject - public HelpOption helpOption; - - @Option(name = { "-p", "--profile" }, description = "Path to stress yaml file", required = true) + @Option(names = { "-p", "--profile" }, description = "Path to stress yaml file", required = true) String profile; - @Option(name = { "-d", "--datadir" }, description = "Data directory (can be used many times to specify multiple data dirs)", required = true) + @Option(names = { "-d", "--datadir" }, description = "Data directory (can be used many times to specify multiple data dirs)", required = true) List dataDirs; - @Option(name = {"-v", "--vnodes"}, description = "number of local tokens to generate (default 256)") + @Option(names = { "-v", "--vnodes" }, description = "number of local tokens to generate (default 256)") Integer numTokens = 256; static @@ -211,17 +205,14 @@ public abstract class CompactionStress implements Runnable } } - public abstract void run(); - - @Command(name = "compact", description = "Compact data in directory") public static class Compaction extends CompactionStress { - @Option(name = {"-m", "--maximal"}, description = "Force maximal compaction (default true)") + @Option(names = { "-m", "--maximal" }, description = "Force maximal compaction (default true)") Boolean maximal = false; - @Option(name = {"-t", "--threads"}, description = "Number of compactor threads to use for bg compactions (default 4)") + @Option(names = { "-t", "--threads" }, description = "Number of compactor threads to use for bg compactions (default 4)") Integer threads = 4; public void run() @@ -286,19 +277,19 @@ public abstract class CompactionStress implements Runnable { private static double BYTES_IN_GIB = 1024 * 1014 * 1024; - @Option(name = { "-g", "--gbsize"}, description = "Total GB size on disk you wish to write", required = true) + @Option(names = { "-g", "--gbsize" }, description = "Total GB size on disk you wish to write", required = true) Integer totalSizeGiB; - @Option(name = { "-t", "--threads" }, description = "Number of sstable writer threads (default 2)") + @Option(names = { "-t", "--threads" }, description = "Number of sstable writer threads (default 2)") Integer threads = 2; - @Option(name = { "-c", "--partition-count"}, description = "Number of partitions to loop over (default 1000000)") + @Option(names = { "-c", "--partition-count" }, description = "Number of partitions to loop over (default 1000000)") Integer partitions = 1000000; - @Option(name = { "-b", "--buffer-size-mb"}, description = "Buffer in MiB writes before writing new sstable (default 128)") + @Option(names = { "-b", "--buffer-size-mb" }, description = "Buffer in MiB writes before writing new sstable (default 128)") Integer bufferSize = 128; - @Option(name = { "-r", "--range-aware"}, description = "Splits the local ranges in number of data directories and makes sure we never write the same token in two different directories (default true)") + @Option(names = { "-r", "--range-aware" }, description = "Splits the local ranges in number of data directories and makes sure we never write the same token in two different directories (default true)") Boolean makeRangeAware = true; public void run() @@ -358,26 +349,26 @@ public abstract class CompactionStress implements Runnable } } + @Command(name = "compaction-stress", + description = "benchmark for compaction", + subcommands = { CommandLine.HelpCommand.class, CompactionStress.Compaction.class, CompactionStress.DataWriter.class }) + public static class CompactionStressCommand implements Runnable + { + @Override + public void run() + { + CommandLine.usage(this, System.out); + } + } + public static void main(String[] args) { - Cli.CliBuilder builder = Cli.builder("compaction-stress") - .withDescription("benchmark for compaction") - .withDefaultCommand(Help.class) - .withCommands(Help.class, DataWriter.class, Compaction.class); - - Cli stress = builder.build(); - - try - { - stress.parse(args).run(); - } - catch (Throwable t) - { - t.printStackTrace(); - System.exit(6); - } - - System.exit(0); + CommandLine commandLine = new CommandLine(CompactionStressCommand.class); + commandLine.setExecutionExceptionHandler((ex, c, arg) -> { + ex.printStackTrace(System.out); + return 6; + }); + System.exit(commandLine.execute(args)); } }