blob: 03d9449d58b5e829c636306608b5fd8a4f413163 [file] [log] [blame]
/*
* 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 static java.lang.String.format;
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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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;
@Command(name = "ring", description = "Print information about the token ring")
public class Ring extends NodeToolCmd
{
@Arguments(description = "Specify a keyspace for accurate ownership information (topology awareness)")
private String keyspace = null;
@Option(title = "resolve_ip", name = {"-r", "--resolve-ip"}, description = "Show node domain names instead of IPs")
private boolean resolveIp = false;
@Override
public void execute(NodeProbe probe)
{
Map<String, String> tokensToEndpoints = probe.getTokenToEndpointMap();
LinkedHashMultimap<String, String> endpointsToTokens = LinkedHashMultimap.create();
boolean haveVnodes = false;
for (Map.Entry<String, String> entry : tokensToEndpoints.entrySet())
{
haveVnodes |= endpointsToTokens.containsKey(entry.getValue());
endpointsToTokens.put(entry.getValue(), entry.getKey());
}
int maxAddressLength = Collections.max(endpointsToTokens.keys(), new Comparator<String>()
{
@Override
public int compare(String first, String second)
{
return Integer.compare(first.length(), second.length());
}
}).length();
String formatPlaceholder = "%%-%ds %%-12s%%-7s%%-8s%%-16s%%-20s%%-44s%%n";
String format = format(formatPlaceholder, maxAddressLength);
StringBuffer errors = new StringBuffer();
boolean showEffectiveOwnership = true;
// Calculate per-token ownership of the ring
Map<InetAddress, Float> ownerships;
try
{
ownerships = probe.effectiveOwnership(keyspace);
}
catch (IllegalStateException ex)
{
ownerships = probe.getOwnership();
errors.append("Note: " + ex.getMessage() + "%n");
showEffectiveOwnership = false;
}
catch (IllegalArgumentException ex)
{
System.out.printf("%nError: " + ex.getMessage() + "%n");
return;
}
System.out.println();
for (Entry<String, SetHostStat> entry : NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships).entrySet())
printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(),showEffectiveOwnership);
if (haveVnodes)
{
System.out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node.");
System.out.println(" To view status related info of a node use \"nodetool status\" instead.\n");
}
System.out.printf("%n " + errors.toString());
}
private void printDc(NodeProbe probe, String format,
String dc,
LinkedHashMultimap<String, String> endpointsToTokens,
SetHostStat hoststats,boolean showEffectiveOwnership)
{
Collection<String> liveNodes = probe.getLiveNodes();
Collection<String> deadNodes = probe.getUnreachableNodes();
Collection<String> joiningNodes = probe.getJoiningNodes();
Collection<String> leavingNodes = probe.getLeavingNodes();
Collection<String> movingNodes = probe.getMovingNodes();
Map<String, String> loadMap = probe.getLoadMap();
System.out.println("Datacenter: " + dc);
System.out.println("==========");
// get the total amount of replicas for this dc and the last token in this dc's ring
List<String> tokens = new ArrayList<>();
String lastToken = "";
for (HostStat stat : hoststats)
{
tokens.addAll(endpointsToTokens.get(stat.endpoint.getHostAddress()));
lastToken = tokens.get(tokens.size() - 1);
}
System.out.printf(format, "Address", "Rack", "Status", "State", "Load", "Owns", "Token");
if (hoststats.size() > 1)
System.out.printf(format, "", "", "", "", "", "", lastToken);
else
System.out.println();
for (HostStat stat : hoststats)
{
String endpoint = stat.endpoint.getHostAddress();
String rack;
try
{
rack = probe.getEndpointSnitchInfoProxy().getRack(endpoint);
}
catch (UnknownHostException e)
{
rack = "Unknown";
}
String status = liveNodes.contains(endpoint)
? "Up"
: deadNodes.contains(endpoint)
? "Down"
: "?";
String state = "Normal";
if (joiningNodes.contains(endpoint))
state = "Joining";
else if (leavingNodes.contains(endpoint))
state = "Leaving";
else if (movingNodes.contains(endpoint))
state = "Moving";
String load = loadMap.containsKey(endpoint)
? loadMap.get(endpoint)
: "?";
String owns = stat.owns != null && showEffectiveOwnership? new DecimalFormat("##0.00%").format(stat.owns) : "?";
System.out.printf(format, stat.ipOrDns(), rack, status, state, load, owns, stat.token);
}
System.out.println();
}
}