add nodeprobe cli tool. patch by Eric Evans; reviewed by jbellis for CASSANDRA-211

git-svn-id: https://svn.apache.org/repos/asf/incubator/cassandra/trunk@785178 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jonathan Ellis 2009-06-16 12:38:30 +00:00
parent b356537196
commit 01adb7a1aa
2 changed files with 483 additions and 0 deletions

49
bin/nodeprobe Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
# 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.
if [ "x$CASSANDRA_INCLUDE" = "x" ]; then
for include in /usr/share/cassandra/cassandra.in.sh \
/usr/local/share/cassandra/cassandra.in.sh \
/opt/cassandra/cassandra.in.sh \
`dirname $0`/cassandra.in.sh; do
if [ -r $include ]; then
. $include
break
fi
done
elif [ -r $CASSANDRA_INCLUDE ]; then
. $CASSANDRA_INCLUDE
fi
if [ -z $CASSANDRA_CONF -o -z $CLASSPATH ]; then
echo "You must set the CASSANDRA_CONF and CLASSPATH vars" >&2
exit 1
fi
# Special-case path variables.
case "`uname`" in
CYGWIN*)
CLASSPATH=`cygpath -p -w "$CLASSPATH"`
CASSANDRA_CONF=`cygpath -p -w "$CASSANDRA_CONF"`
;;
esac
java -cp $CLASSPATH -Dstorage-config=$CASSANDRA_CONF \
org.apache.cassandra.tools.NodeProbe $@
# vi:ai sw=4 ts=4 tw=0 et

View File

@ -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.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.util.List;
import java.util.Map;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.net.EndPoint;
import org.apache.cassandra.service.StorageServiceMBean;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
/**
* JMX client operations for Cassandra.
*/
public class NodeProbe
{
private static final String fmtUrl = "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi";
private static final String ssObjName = "org.apache.cassandra.service:type=StorageService";
private static final String HOST_OPTION = "host";
private static final String PORT_OPTION = "port";
private static final int defaultPort = 8080;
private static Options options = null;
private CommandLine cmd = null;
private String host;
private int port;
private MBeanServerConnection mbeanServerConn;
private StorageServiceMBean ssProxy;
private MemoryMXBean memProxy;
private RuntimeMXBean runtimeProxy;
static
{
options = new Options();
Option optHost = new Option(HOST_OPTION, true, "node hostname or ip address");
optHost.setRequired(true);
options.addOption(optHost);
options.addOption(PORT_OPTION, true, "remote jmx agent port number");
}
/**
* Creates a NodeProbe using command-line arguments.
*
* @param cmdArgs list of arguments passed on the command line
* @throws ParseException for missing required, or unrecognized options
* @throws IOException on connection failures
*/
private NodeProbe(String[] cmdArgs) throws ParseException, IOException
{
parseArgs(cmdArgs);
this.host = cmd.getOptionValue(HOST_OPTION);
String portNum = cmd.getOptionValue(PORT_OPTION);
if (portNum != null)
{
try
{
this.port = Integer.parseInt(portNum);
}
catch (NumberFormatException e)
{
throw new ParseException("Port must be a number");
}
}
else
{
this.port = defaultPort;
}
connect();
}
/**
* Creates a NodeProbe using the specified JMX host and port.
*
* @param host hostname or IP address of the JMX agent
* @param port TCP port of the remote JMX agent
* @throws IOException on connection failures
*/
public NodeProbe(String host, int port) throws IOException
{
this.host = host;
this.port = port;
connect();
}
/**
* Creates a NodeProbe using the specified JMX host and default port.
*
* @param host hostname or IP address of the JMX agent
* @throws IOException on connection failures
*/
public NodeProbe(String host) throws IOException
{
this.host = host;
this.port = defaultPort;
connect();
}
/**
* Create a connection to the JMX agent and setup the M[X]Bean proxies.
*
* @throws IOException on connection failures
*/
private void connect() throws IOException
{
JMXServiceURL jmxUrl = new JMXServiceURL(String.format(fmtUrl, host, port));
JMXConnector jmxc = JMXConnectorFactory.connect(jmxUrl, null);
mbeanServerConn = jmxc.getMBeanServerConnection();
try
{
ObjectName name = new ObjectName(ssObjName);
ssProxy = JMX.newMBeanProxy(mbeanServerConn, name, StorageServiceMBean.class);
} catch (MalformedObjectNameException e)
{
throw new RuntimeException(
"Invalid ObjectName? Please report this as a bug.", e);
}
memProxy = ManagementFactory.newPlatformMXBeanProxy(mbeanServerConn,
ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class);
runtimeProxy = ManagementFactory.newPlatformMXBeanProxy(
mbeanServerConn, ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class);
}
/**
* Retrieve a map of range to end points that describe the ring topology
* of a Cassandra cluster.
*
* @return mapping of ranges to end points
*/
public Map<Range, List<EndPoint>> getRangeToEndpointMap()
{
return ssProxy.getRangeToEndPointMap();
}
/**
* Retrieve the list of live nodes in the cluster, where "liveness" is
* determined by the failure detector of the node being queried. The
* returned string is a space delimited list of host:port end points.
*
* @return space delimited list of nodes
*/
public String getLiveNodes()
{
return ssProxy.getLiveNodes();
}
/**
* Retrieve the list of unreachable nodes in the cluster, as determined
* by this node's failure detector. The returned string is a space
* delimited list of host:port end points.
*
* @return space delimited list of nodes
*/
public String getUnreachableNodes()
{
return ssProxy.getUnreachableNodes();
}
/**
* Fetch a string representation of the token.
*
* @return a string token
*/
public String getToken()
{
return ssProxy.getToken();
}
/**
* Return the generation value for this node.
*
* @return generation number
*/
public int getCurrentGenerationNumber()
{
return ssProxy.getCurrentGenerationNumber();
}
/**
* Retrieve a textual representation of the on-disk size of data
* stored on this node.
*
* @return the size description
*/
public String getLoadInfo()
{
return ssProxy.getLoadInfo();
}
/**
* Trigger a cleanup of keys on all tables.
*/
public void forceTableCleanup()
{
ssProxy.forceTableCleanup();
}
/**
* Trigger compaction of all tables.
*/
public void forceTableCompaction()
{
ssProxy.forceTableCompaction();
}
/**
* Write a textual representation of the Cassandra ring.
*
* @param outs the stream to write to
*/
public void printRing(PrintStream outs)
{
Map<Range, List<EndPoint>> rangeMap = getRangeToEndpointMap();
// Print range-to-endpoint mapping
int counter = 0;
for (Range range : rangeMap.keySet()) {
List<EndPoint> endpoints = rangeMap.get(range);
outs.print(String.format("%-46s ", range.left()));
outs.print(String.format("%2d ", endpoints.size()));
outs.print(String.format("%-15s", endpoints.get(0).getHost()));
String asciiRingArt;
if (counter == 0)
{
asciiRingArt = "|<--|";
}
else if (counter == (rangeMap.size() - 1))
{
asciiRingArt = "|-->|";
}
else
{
if ((rangeMap.size() > 4) && ((counter % 2) == 0))
{
asciiRingArt = "v |";
}
else if ((rangeMap.size() > 4) && ((counter % 2) != 0))
{
asciiRingArt = "| ^";
}
else
{
asciiRingArt = "| |";
}
}
outs.println(asciiRingArt);
counter++;
}
}
/**
* Write a list of nodes with corresponding status.
*
* @param outs the stream to write to
*/
public void printCluster(PrintStream outs)
{
for (String upNode : getLiveNodes().split("\\s+"))
{
if (upNode.length() > 0)
{
outs.println(String.format("%-21s up", upNode));
}
}
for (String downNode : getUnreachableNodes().split("\\s+"))
{
if (downNode.length() > 0)
{
outs.println(String.format("%-21s down", downNode));
}
}
}
/**
* Write node information.
*
* @param outs the stream to write to
*/
public void printInfo(PrintStream outs)
{
outs.println(getToken());
outs.println(String.format("%-17s: %s", "Load Info", getLoadInfo()));
outs.println(String.format("%-17s: %s", "Generation No", getCurrentGenerationNumber()));
// Uptime
long secondsUp = runtimeProxy.getUptime() / 1000;
outs.println(String.format("%-17s: %d", "Uptime (seconds)", secondsUp));
// Memory usage
MemoryUsage heapUsage = memProxy.getHeapMemoryUsage();
double memUsed = (double)heapUsage.getUsed() / (1024 * 1024);
double memMax = (double)heapUsage.getMax() / (1024 * 1024);
outs.println(String.format("%-17s: %.2f / %.2f", "Heap Memory (MB)", memUsed, memMax));
}
/**
* Retrieve any non-option arguments passed on the command line.
*
* @return non-option command args
*/
private String[] getArgs()
{
return cmd.getArgs();
}
/**
* Parse the supplied command line arguments.
*
* @param args arguments passed on the command line
* @throws ParseException for missing required, or unrecognized options
*/
private void parseArgs(String[] args) throws ParseException
{
CommandLineParser parser = new PosixParser();
cmd = parser.parse(options, args);
}
/**
* Prints usage information to stdout.
*/
private static void printUsage()
{
HelpFormatter hf = new HelpFormatter();
String header = String.format(
"%nAvailable commands: ring, cluster, info, cleanup, compact");
String usage = String.format("java %s -host <arg> <command>%n", NodeProbe.class.getName());
hf.printHelp(usage, "", options, header);
}
/**
* @param args
*/
public static void main(String[] args)
{
NodeProbe probe = null;
try
{
probe = new NodeProbe(args);
}
catch (ParseException pe)
{
System.err.println(pe.getMessage());
NodeProbe.printUsage();
System.exit(1);
}
catch (IOException ioe)
{
System.err.println("Error connecting to remote JMX agent!");
ioe.printStackTrace();
System.exit(3);
}
if (probe.getArgs().length < 1)
{
System.err.println("Missing argument for command.");
NodeProbe.printUsage();
System.exit(1);
}
// Execute the requested command.
String cmdName = probe.getArgs()[0];
if (cmdName.equals("ring"))
{
probe.printRing(System.out);
}
else if (cmdName.equals("cluster"))
{
probe.printCluster(System.out);
}
else if (cmdName.equals("info"))
{
probe.printInfo(System.out);
}
else if (cmdName.equals("cleanup"))
{
probe.forceTableCleanup();
}
else if (cmdName.equals("compact"))
{
probe.forceTableCompaction();
}
else
{
System.err.println("Unrecognized command: " + cmdName + ".");
NodeProbe.printUsage();
System.exit(1);
}
System.exit(0);
}
}