maedhroz commented on code in PR #2497: URL: https://github.com/apache/cassandra/pull/2497#discussion_r2232040346
########## 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<String, String> 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<String> concatArgs(String first, String second) + { + return concat(ofNullable(first), ofNullable(second)).collect(Collectors.toList()); + } + + public static List<String> concatArgs(String first, String second, String third) + { + return concat(ofNullable(first), concatArgs(second, third).stream()).collect(Collectors.toList()); + } + + public static List<String> concatArgs(String first, String second, String third, String fourth) + { + return concat(ofNullable(first), concatArgs(second, third, fourth).stream()).collect(Collectors.toList()); + } + + public static List<String> concatArgs(String first, String[] second) + { + return concat(ofNullable(first), (second == null ? Stream.empty() : Arrays.stream(second))).collect(Collectors.toList()); + } + + public static List<String> concatArgs(String first, List<String> second) + { + return concat(ofNullable(first), (second == null ? Stream.empty() : second.stream())).collect(Collectors.toList()); + } + + public static List<String> concatArgs(String first, String second, String[] third) + { + return concat(ofNullable(first), concatArgs(second, third).stream()).collect(Collectors.toList()); + } + + public static List<String> concatArgs(String first, String second, List<String> third) + { + return concat(ofNullable(first), concatArgs(second, third).stream()).collect(Collectors.toList()); + } + + public static List<String> 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<String> 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<String> parseOptionalKeyspaceAccordManaged(List<String> cmdArgs, NodeProbe nodeProbe) + { + return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.ACCORD_MANAGED); + } + + public static List<String> parseOptionalKeyspace(List<String> cmdArgs, NodeProbe nodeProbe) + { + return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.ALL); + } + + public static List<String> parseOptionalKeyspaceNonLocal(List<String> cmdArgs, NodeProbe nodeProbe) + { + return parseOptionalKeyspace(cmdArgs, nodeProbe, KeyspaceSet.NON_LOCAL_STRATEGY); + } + + private static List<String> parseOptionalKeyspace(List<String> cmdArgs, NodeProbe nodeProbe, KeyspaceSet defaultKeyspaceSet) + { + List<String> 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. + * <p> + * 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. + * <p> + * 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<String> 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. + * <p> + * 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. + * <p> + * 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<String> cmdArgs) + { + return cmdArgs.size() <= 2 ? EMPTY_STRING_ARRAY : toArray(cmdArgs.subList(2, cmdArgs.size()), String.class); + } + + public static SortedMap<String, SetHostStatWithPort> getOwnershipByDcWithPort(NodeProbe probe, boolean resolveIp, + Map<String, String> tokenToEndpoint, + Map<String, Float> ownerships) + { + SortedMap<String, SetHostStatWithPort> ownershipByDc = Maps.newTreeMap(); + EndpointSnitchInfoMBean epSnitchInfo = probe.getEndpointSnitchInfoProxy(); Review Comment: This MBean has been deprecated, so it might be a good idea to use `LocationInfoMBean` instead? -- 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: pr-unsubscr...@cassandra.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org For additional commands, e-mail: pr-h...@cassandra.apache.org