Author: jbellis
Date: Tue Jun 16 12:38:30 2009
New Revision: 785178
URL: http://svn.apache.org/viewvc?rev=785178&view=rev
Log:
add nodeprobe cli tool. patch by Eric Evans; reviewed by jbellis for
CASSANDRA-211
Added:
incubator/cassandra/trunk/bin/nodeprobe (with props)
incubator/cassandra/trunk/src/java/org/apache/cassandra/tools/NodeProbe.java
Added: incubator/cassandra/trunk/bin/nodeprobe
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/bin/nodeprobe?rev=785178&view=auto
==============================================================================
--- incubator/cassandra/trunk/bin/nodeprobe (added)
+++ incubator/cassandra/trunk/bin/nodeprobe Tue Jun 16 12:38:30 2009
@@ -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
Propchange: incubator/cassandra/trunk/bin/nodeprobe
------------------------------------------------------------------------------
svn:executable = *
Added:
incubator/cassandra/trunk/src/java/org/apache/cassandra/tools/NodeProbe.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/tools/NodeProbe.java?rev=785178&view=auto
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/tools/NodeProbe.java
(added)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/tools/NodeProbe.java
Tue Jun 16 12:38:30 2009
@@ -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);
+ }
+
+}