ifesdjeen commented on code in PR #4149:
URL: https://github.com/apache/cassandra/pull/4149#discussion_r2229212171
##########
src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java:
##########
@@ -269,4 +271,76 @@ public void execute(NodeProbe probe)
probe.getCMSOperationsProxy().resumeDropAccordTable(tableId);
}
}
+
+ @Command(name = "dumpclustermetadata", description = "Dumps Cluster
Metadata into a file")
+ public static class DumpClusterMetadata extends NodeTool.NodeToolCmd
+ {
+
+ @Option(title = "Epoch", name = { "-e", "--epoch" }, required = false,
+ description = "Epoch at which cluster metadata should be dumped")
+ private Long epoch;
+
+ @Option(title = "Transform Epoch", name = { "-te", "--transform-epoch"
}, required = false,
+ description = "The epoch to which the cluster meta data should be
transformed before dumping")
+ private Long transformEpoch;
+
+ @Option(title = "Searialization Version", name = { "-sv",
"--serialization-version" }, required = false,
Review Comment:
typo in word Searialization
##########
src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java:
##########
@@ -269,4 +271,76 @@ public void execute(NodeProbe probe)
probe.getCMSOperationsProxy().resumeDropAccordTable(tableId);
}
}
+
+ @Command(name = "dumpclustermetadata", description = "Dumps Cluster
Metadata into a file")
+ public static class DumpClusterMetadata extends NodeTool.NodeToolCmd
+ {
+
+ @Option(title = "Epoch", name = { "-e", "--epoch" }, required = false,
+ description = "Epoch at which cluster metadata should be dumped")
+ private Long epoch;
+
+ @Option(title = "Transform Epoch", name = { "-te", "--transform-epoch"
}, required = false,
+ description = "The epoch to which the cluster meta data should be
transformed before dumping")
Review Comment:
maybe something like "force metadata to X epoch while dumping"
##########
src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java:
##########
@@ -269,4 +271,76 @@ public void execute(NodeProbe probe)
probe.getCMSOperationsProxy().resumeDropAccordTable(tableId);
}
}
+
+ @Command(name = "dumpclustermetadata", description = "Dumps Cluster
Metadata into a file")
Review Comment:
nit: since this is already gated by CMS admin, I would just use
`dump_metadata` here maybe?
##########
src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java:
##########
@@ -269,4 +271,76 @@ public void execute(NodeProbe probe)
probe.getCMSOperationsProxy().resumeDropAccordTable(tableId);
}
}
+
+ @Command(name = "dumpclustermetadata", description = "Dumps Cluster
Metadata into a file")
+ public static class DumpClusterMetadata extends NodeTool.NodeToolCmd
+ {
+
+ @Option(title = "Epoch", name = { "-e", "--epoch" }, required = false,
+ description = "Epoch at which cluster metadata should be dumped")
+ private Long epoch;
+
+ @Option(title = "Transform Epoch", name = { "-te", "--transform-epoch"
}, required = false,
+ description = "The epoch to which the cluster meta data should be
transformed before dumping")
+ private Long transformEpoch;
+
+ @Option(title = "Searialization Version", name = { "-sv",
"--serialization-version" }, required = false,
+ description = "Searialization Version")
Review Comment:
typo in Searialization
##########
src/java/org/apache/cassandra/tools/nodetool/CMSAdmin.java:
##########
@@ -269,4 +271,76 @@ public void execute(NodeProbe probe)
probe.getCMSOperationsProxy().resumeDropAccordTable(tableId);
}
}
+
+ @Command(name = "dumpclustermetadata", description = "Dumps Cluster
Metadata into a file")
+ public static class DumpClusterMetadata extends NodeTool.NodeToolCmd
+ {
+
+ @Option(title = "Epoch", name = { "-e", "--epoch" }, required = false,
+ description = "Epoch at which cluster metadata should be dumped")
+ private Long epoch;
+
+ @Option(title = "Transform Epoch", name = { "-te", "--transform-epoch"
}, required = false,
+ description = "The epoch to which the cluster meta data should be
transformed before dumping")
+ private Long transformEpoch;
+
+ @Option(title = "Searialization Version", name = { "-sv",
"--serialization-version" }, required = false,
+ description = "Searialization Version")
+ private Version version;
+
+ protected void execute(NodeProbe probe)
+ {
+ if (epoch == null && transformEpoch == null && version == null)
+ {
+ try
+ {
+ String fileLocation =
probe.getCMSOperationsProxy().dumpClusterMetadata();
+ printCMSDumpLocation(probe, fileLocation);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ else if (epoch != null && transformEpoch != null && version !=
null)
+ {
+ try
+ {
+ String fileLocation =
probe.getCMSOperationsProxy().dumpClusterMetadata(epoch,
+
transformEpoch,
+
version.name());
+ printCMSDumpLocation(probe, fileLocation);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ else
+ {
+ List<String> invalidArgs = new ArrayList<>(2);
+ if (null == epoch)
+ {
Review Comment:
nit: unnecessary brackets
##########
src/java/org/apache/cassandra/tools/CMSOfflineTool.java:
##########
@@ -0,0 +1,434 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import io.airlift.airline.Cli;
+import io.airlift.airline.Command;
+import io.airlift.airline.Help;
+import io.airlift.airline.Option;
+import io.airlift.airline.OptionType;
+import io.airlift.airline.ParseException;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.Range;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.io.util.File;
+import org.apache.cassandra.io.util.FileInputStreamPlus;
+import org.apache.cassandra.io.util.FileOutputStreamPlus;
+import org.apache.cassandra.locator.InetAddressAndPort;
+import org.apache.cassandra.locator.MetaStrategy;
+import org.apache.cassandra.locator.Replica;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.ReplicationParams;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.ClusterMetadataService;
+import org.apache.cassandra.tcm.membership.Directory;
+import org.apache.cassandra.tcm.membership.Location;
+import org.apache.cassandra.tcm.membership.NodeAddresses;
+import org.apache.cassandra.tcm.membership.NodeId;
+import org.apache.cassandra.tcm.membership.NodeVersion;
+import org.apache.cassandra.tcm.ownership.DataPlacement;
+import org.apache.cassandra.tcm.ownership.ReplicaGroups;
+import org.apache.cassandra.tcm.serialization.VerboseMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+import org.apache.cassandra.utils.FBUtilities;
+
+import static com.google.common.base.Throwables.getStackTraceAsString;
+import static
org.apache.cassandra.tcm.transformations.cms.PrepareCMSReconfiguration.needsReconfiguration;
+
+/**
+ * Offline tool to print or update cluster metadata dump.
+ */
+public class CMSOfflineTool
+{
+
+ private static final String TOOL_NAME = "cmsofflinetool";
+ private final Output output;
+
+ public CMSOfflineTool(Output output)
+ {
+ this.output = output;
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ //noinspection UseOfSystemOutOrSystemErr
+ System.exit(new CMSOfflineTool(new Output(System.out,
System.err)).execute(args));
+ }
+
+ public int execute(String... args)
+ {
+
Review Comment:
nit: extra line
##########
src/java/org/apache/cassandra/tools/CMSOfflineTool.java:
##########
@@ -0,0 +1,434 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import io.airlift.airline.Cli;
+import io.airlift.airline.Command;
+import io.airlift.airline.Help;
+import io.airlift.airline.Option;
+import io.airlift.airline.OptionType;
+import io.airlift.airline.ParseException;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.Range;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.io.util.File;
+import org.apache.cassandra.io.util.FileInputStreamPlus;
+import org.apache.cassandra.io.util.FileOutputStreamPlus;
+import org.apache.cassandra.locator.InetAddressAndPort;
+import org.apache.cassandra.locator.MetaStrategy;
+import org.apache.cassandra.locator.Replica;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.ReplicationParams;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.ClusterMetadataService;
+import org.apache.cassandra.tcm.membership.Directory;
+import org.apache.cassandra.tcm.membership.Location;
+import org.apache.cassandra.tcm.membership.NodeAddresses;
+import org.apache.cassandra.tcm.membership.NodeId;
+import org.apache.cassandra.tcm.membership.NodeVersion;
+import org.apache.cassandra.tcm.ownership.DataPlacement;
+import org.apache.cassandra.tcm.ownership.ReplicaGroups;
+import org.apache.cassandra.tcm.serialization.VerboseMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+import org.apache.cassandra.utils.FBUtilities;
+
+import static com.google.common.base.Throwables.getStackTraceAsString;
+import static
org.apache.cassandra.tcm.transformations.cms.PrepareCMSReconfiguration.needsReconfiguration;
+
+/**
+ * Offline tool to print or update cluster metadata dump.
+ */
+public class CMSOfflineTool
+{
+
+ private static final String TOOL_NAME = "cmsofflinetool";
+ private final Output output;
+
+ public CMSOfflineTool(Output output)
+ {
+ this.output = output;
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ //noinspection UseOfSystemOutOrSystemErr
+ System.exit(new CMSOfflineTool(new Output(System.out,
System.err)).execute(args));
+ }
+
+ public int execute(String... args)
+ {
+
+ Cli.CliBuilder<ClusterMetadataToolRunnable> builder =
Cli.builder(TOOL_NAME);
+
+ List<Class<? extends ClusterMetadataToolRunnable>> commands = new
ArrayList<>()
+ {{
+ add(ClusterMetadataToolHelp.class);
+ add(AddToCMS.class);
+ add(AssignTokens.class);
+ add(Describe.class);
+ add(ForceJoin.class);
+ add(ForgetNode.class);
+ add(PrintDataPlacements.class);
+ add(PrintDirectoryCmd.class);
+ }};
+
+ builder.withDescription("Offline tool to print or update cluster
metadata dump")
+ .withDefaultCommand(ClusterMetadataToolHelp.class)
+ .withCommands(commands);
+
+ Cli<ClusterMetadataToolRunnable> parser = builder.build();
+ int status = 0;
+ try
+ {
+ ClusterMetadataToolRunnable parse = parser.parse(args);
+ parse.run(output);
+ }
+ catch (ParseException pe)
+ {
+ status = 1;
+ badUse(pe);
+ }
+ catch (Exception e)
+ {
+ status = 2;
+ err(e);
+ }
+ return status;
+ }
+
+
+ private void badUse(Exception e)
+ {
+ output.err.println(TOOL_NAME + ": " + e.getMessage());
+ output.err.printf("See '%s help' or '%s help <command>'.%n",
TOOL_NAME, TOOL_NAME);
+ }
+
+ private void err(Exception e)
+ {
+ output.err.println("error: " + e.getMessage());
+ output.err.println("-- StackTrace --");
+ output.err.println(getStackTraceAsString(e));
+ }
+
+
+ interface ClusterMetadataToolRunnable
+ {
+ void run(Output output) throws IOException;
+ }
+
+ public static abstract class ClusterMetadataToolCmd implements
ClusterMetadataToolRunnable
+ {
+ @Option(type = OptionType.COMMAND, name = { "-f", "--file" },
description = "Cluster metadata dump file path", required = true)
+ protected String metadataDumpPath;
+
+ @Option(type = OptionType.COMMAND, name = { "-sv",
"--serialization-version" }, description = "Serialization version to use")
+ private Version serializationVersion;
+
+
+ public ClusterMetadata parseClusterMetadata() throws IOException
+ {
+ File file = new File(metadataDumpPath);
+ if (!file.exists())
+ {
+ throw new IllegalArgumentException("Cluster metadata dump file
" + metadataDumpPath + " does not exist");
+ }
+
+ Version serializationVersion =
NodeVersion.CURRENT.serializationVersion();
+ // Make sure the partitioner we use to manipulate the metadata is
the same one used to generate it
+ IPartitioner partitioner;
+ try (FileInputStreamPlus fisp = new
FileInputStreamPlus(metadataDumpPath))
+ {
+ // skip over the prefix specifying the metadata version
+ fisp.readUnsignedVInt32();
+ partitioner = ClusterMetadata.Serializer.getPartitioner(fisp,
serializationVersion);
+ }
+ DatabaseDescriptor.toolInitialization();
+ DatabaseDescriptor.setPartitionerUnsafe(partitioner);
+ ClusterMetadataService.initializeForTools(false);
+
+ return
ClusterMetadataService.deserializeClusterMetadata(metadataDumpPath);
+ }
+
+ public void writeMetadata(Output output, ClusterMetadata metadata,
String outputFilePath) throws IOException
+ {
+ Path p = outputFilePath != null ?
+ Files.createFile(Path.of(outputFilePath)) :
+ Files.createTempFile("clustermetadata", "dump");
+
+
+ try (FileOutputStreamPlus out = new FileOutputStreamPlus(p))
+ {
+ VerboseMetadataSerializer.serialize(ClusterMetadata.serializer,
+ metadata,
+ out,
+ getSerializationVersion());
+ output.out.println("Updated cluster metadata written to file "
+ p.toAbsolutePath());
+ }
+ }
+
+ Version getSerializationVersion()
+ {
+ return serializationVersion != null ? serializationVersion :
NodeVersion.CURRENT.serializationVersion();
+ }
+ }
+
+ public static class ClusterMetadataToolHelp extends Help implements
ClusterMetadataToolRunnable
+ {
+
+ @Override
+ public void run(Output output)
+ {
+ run();
+ }
+ }
+
+ @Command(name = "addtocms", description = "Makes a node as CMS member")
+ public static class AddToCMS extends ClusterMetadataToolCmd
+ {
+ @Option(name = { "-ip", "--ip-address" }, description = "IP address of
node to make CMS", required = true)
+ private String ipAddress;
+
+ @Option(type = OptionType.COMMAND, name = { "-o", "--output-file" },
description = "Ouput file path for storing the updated Cluster Metadata")
+ private String outputFilePath;
+
+ @Override
+ public void run(Output output) throws IOException
+ {
+ ClusterMetadata metadata = parseClusterMetadata();
+ InetAddressAndPort nodeAddress =
InetAddressAndPort.getByNameUnchecked(ipAddress);
+ metadata = makeCMS(metadata, nodeAddress);
+ writeMetadata(output, metadata, outputFilePath);
+ }
+
+ ClusterMetadata makeCMS(ClusterMetadata metadata, InetAddressAndPort
endpoint)
+ {
+ ReplicationParams metaParams = ReplicationParams.meta(metadata);
+ DataPlacement.Builder builder =
metadata.placements.get(metaParams).unbuild();
+
+ Replica newCMS = MetaStrategy.replica(endpoint);
+ builder.withReadReplica(metadata.epoch, newCMS)
+ .withWriteReplica(metadata.epoch, newCMS);
+ return
metadata.transformer().with(metadata.placements.unbuild().with(metaParams,
+
builder.build())
+ .build())
+ .build().metadata;
+ }
+ }
+
+ @Command(name = "assigntokens", description = "Assigns a token for given
instance")
+ public static class AssignTokens extends ClusterMetadataToolCmd
+ {
+ @Option(name = { "-ip", "--ip-address" }, description = "IP address of
endpoint. Port can be specified as well.", required = true)
Review Comment:
nit: describe how to specify port, i.e. with semicolon, for completenesss
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]