| /* |
| * 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 io.airlift.command.Arguments; |
| import io.airlift.command.Command; |
| import io.airlift.command.Option; |
| |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.text.DecimalFormat; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.SortedMap; |
| |
| 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.ArrayListMultimap; |
| |
| @Command(name = "status", description = "Print cluster information (state, load, IDs, ...)") |
| public class Status extends NodeToolCmd |
| { |
| @Arguments(usage = "[<keyspace>]", description = "The keyspace name") |
| private String keyspace = null; |
| |
| @Option(title = "resolve_ip", name = {"-r", "--resolve-ip"}, description = "Show node domain names instead of IPs") |
| private boolean resolveIp = false; |
| |
| private boolean isTokenPerNode = true; |
| private int maxAddressLength = 0; |
| private String format = null; |
| private Collection<String> joiningNodes, leavingNodes, movingNodes, liveNodes, unreachableNodes; |
| private Map<String, String> loadMap, hostIDMap; |
| private EndpointSnitchInfoMBean epSnitchInfo; |
| |
| @Override |
| public void execute(NodeProbe probe) |
| { |
| joiningNodes = probe.getJoiningNodes(); |
| leavingNodes = probe.getLeavingNodes(); |
| movingNodes = probe.getMovingNodes(); |
| loadMap = probe.getLoadMap(); |
| Map<String, String> tokensToEndpoints = probe.getTokenToEndpointMap(); |
| liveNodes = probe.getLiveNodes(); |
| unreachableNodes = probe.getUnreachableNodes(); |
| hostIDMap = probe.getHostIdMap(); |
| epSnitchInfo = probe.getEndpointSnitchInfoProxy(); |
| |
| StringBuffer errors = new StringBuffer(); |
| |
| Map<InetAddress, Float> ownerships = null; |
| boolean hasEffectiveOwns = false; |
| try |
| { |
| ownerships = probe.effectiveOwnership(keyspace); |
| hasEffectiveOwns = true; |
| } |
| catch (IllegalStateException e) |
| { |
| ownerships = probe.getOwnership(); |
| errors.append("Note: " + e.getMessage() + "%n"); |
| } |
| catch (IllegalArgumentException ex) |
| { |
| System.out.printf("%nError: %s%n", ex.getMessage()); |
| System.exit(1); |
| } |
| |
| SortedMap<String, SetHostStat> dcs = NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships); |
| |
| // More tokens than nodes (aka vnodes)? |
| if (dcs.values().size() < tokensToEndpoints.keySet().size()) |
| isTokenPerNode = false; |
| |
| findMaxAddressLength(dcs); |
| |
| // Datacenters |
| for (Map.Entry<String, SetHostStat> dc : dcs.entrySet()) |
| { |
| String dcHeader = String.format("Datacenter: %s%n", dc.getKey()); |
| System.out.printf(dcHeader); |
| for (int i = 0; i < (dcHeader.length() - 1); i++) System.out.print('='); |
| System.out.println(); |
| |
| // Legend |
| System.out.println("Status=Up/Down"); |
| System.out.println("|/ State=Normal/Leaving/Joining/Moving"); |
| |
| printNodesHeader(hasEffectiveOwns, isTokenPerNode); |
| |
| ArrayListMultimap<InetAddress, HostStat> hostToTokens = ArrayListMultimap.create(); |
| for (HostStat stat : dc.getValue()) |
| hostToTokens.put(stat.endpoint, stat); |
| |
| for (InetAddress endpoint : hostToTokens.keySet()) |
| { |
| Float owns = ownerships.get(endpoint); |
| List<HostStat> tokens = hostToTokens.get(endpoint); |
| printNode(endpoint.getHostAddress(), owns, tokens, hasEffectiveOwns, isTokenPerNode); |
| } |
| } |
| |
| System.out.printf("%n" + errors.toString()); |
| |
| } |
| |
| private void findMaxAddressLength(Map<String, SetHostStat> dcs) |
| { |
| maxAddressLength = 0; |
| for (Map.Entry<String, SetHostStat> dc : dcs.entrySet()) |
| { |
| for (HostStat stat : dc.getValue()) |
| { |
| maxAddressLength = Math.max(maxAddressLength, stat.ipOrDns().length()); |
| } |
| } |
| } |
| |
| private void printNodesHeader(boolean hasEffectiveOwns, boolean isTokenPerNode) |
| { |
| String fmt = getFormat(hasEffectiveOwns, isTokenPerNode); |
| String owns = hasEffectiveOwns ? "Owns (effective)" : "Owns"; |
| |
| if (isTokenPerNode) |
| System.out.printf(fmt, "-", "-", "Address", "Load", owns, "Host ID", "Token", "Rack"); |
| else |
| System.out.printf(fmt, "-", "-", "Address", "Load", "Tokens", owns, "Host ID", "Rack"); |
| } |
| |
| private void printNode(String endpoint, Float owns, List<HostStat> tokens, boolean hasEffectiveOwns, boolean isTokenPerNode) |
| { |
| String status, state, load, strOwns, hostID, rack, fmt; |
| fmt = getFormat(hasEffectiveOwns, isTokenPerNode); |
| if (liveNodes.contains(endpoint)) status = "U"; |
| else if (unreachableNodes.contains(endpoint)) status = "D"; |
| else status = "?"; |
| if (joiningNodes.contains(endpoint)) state = "J"; |
| else if (leavingNodes.contains(endpoint)) state = "L"; |
| else if (movingNodes.contains(endpoint)) state = "M"; |
| else state = "N"; |
| |
| load = loadMap.containsKey(endpoint) ? loadMap.get(endpoint) : "?"; |
| strOwns = owns != null && hasEffectiveOwns ? new DecimalFormat("##0.0%").format(owns) : "?"; |
| hostID = hostIDMap.get(endpoint); |
| |
| try |
| { |
| rack = epSnitchInfo.getRack(endpoint); |
| } catch (UnknownHostException e) |
| { |
| throw new RuntimeException(e); |
| } |
| |
| String endpointDns = tokens.get(0).ipOrDns(); |
| if (isTokenPerNode) |
| System.out.printf(fmt, status, state, endpointDns, load, strOwns, hostID, tokens.get(0).token, rack); |
| else |
| System.out.printf(fmt, status, state, endpointDns, load, tokens.size(), strOwns, hostID, rack); |
| } |
| |
| private String getFormat( |
| boolean hasEffectiveOwns, |
| boolean isTokenPerNode) |
| { |
| if (format == null) |
| { |
| StringBuilder buf = new StringBuilder(); |
| String addressPlaceholder = String.format("%%-%ds ", maxAddressLength); |
| buf.append("%s%s "); // status |
| buf.append(addressPlaceholder); // address |
| buf.append("%-9s "); // load |
| if (!isTokenPerNode) |
| buf.append("%-11s "); // "Tokens" |
| if (hasEffectiveOwns) |
| buf.append("%-16s "); // "Owns (effective)" |
| else |
| buf.append("%-6s "); // "Owns |
| buf.append("%-36s "); // Host ID |
| if (isTokenPerNode) |
| buf.append("%-39s "); // token |
| buf.append("%s%n"); // "Rack" |
| |
| format = buf.toString(); |
| } |
| |
| return format; |
| } |
| } |