/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2007], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.plugin.ntp;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
import org.hyperic.hq.product.Collector;
import org.hyperic.hq.product.PluginException;
import org.hyperic.util.StringUtil;
import org.hyperic.util.exec.Execute;
import org.hyperic.util.exec.ExecuteWatchdog;
import org.hyperic.util.exec.PumpStreamHandler;
public class NTPDCollector extends Collector {
static final String PROP_NTPDC = "ntpdc";
static final String PROP_INCLUDELOCAL = "includeLocal";
static final String PROP_LOCAL = "LOCAL(0)";
private static final String[] ARGS = {
"-c", "sysinfo",
"-c", "sysstats",
"-c", "peers"
};
//ThreadPool opt-in
public boolean isPoolable() {
return true;
}
private String getNtpdc() {
return getProperties().getProperty(PROP_NTPDC, "");
}
private String getHostname() {
return getProperties().getProperty(PROP_HOSTNAME, "");
}
protected void init() throws PluginException {
String ntpdc = getNtpdc();
if (!new File(ntpdc).exists()) {
throw new PluginException(PROP_NTPDC + "=" + ntpdc + " does not exist");
}
}
public void collect() {
String ntpdc = getNtpdc();
int timeout = getTimeoutMillis();
String host = getHostname();
Boolean hasHost = false;
int initialArraySize = 3;
if (!host.equals("")) { // if no host present don't add it to the command
initialArraySize = 4;
hasHost = true;
}
String argv[] = new String[ARGS.length + initialArraySize];
argv[0] = ntpdc;
argv[1] = "-c";
argv[2] = "'timeout " + timeout + "'";
System.arraycopy(ARGS, 0, argv, 3, ARGS.length);
if (hasHost) {
argv[argv.length -1] = host; // Last option is optional host
}
final boolean includeLocal =
"true".equals(getCollectorProperty(PROP_INCLUDELOCAL, "false"));
ByteArrayOutputStream output =
new ByteArrayOutputStream();
//peer timeout is short, use longer timeout for the exec
ExecuteWatchdog wdog =
new ExecuteWatchdog(15 * 1000);
Execute exec =
new Execute(new PumpStreamHandler(output), wdog);
exec.setCommandline(argv);
try {
int exitStatus = exec.execute();
if (exitStatus != 0 || wdog.killedProcess()) {
setErrorMessage(ntpdc+" command failed");
return;
}
} catch (Exception e) {
setErrorMessage("Unable to exec process: " + e);
return;
}
BufferedReader in = null;
try {
in = new BufferedReader(new StringReader(output.toString()));
boolean seenPeerHeader = false;
double peers = 0;
double peerDelay = 0;
double peerOffset = 0;
double peerDisp = 0;
double peersReachable = 0;
double peersUnreachable = 0;
double peersWithProbs = 0;
double clientmode = 0;
double sendingbroadcasts = 0;
double receivingbroadcasts = 0;
double peerssynchronized = 0;
String line;
while ((line = in.readLine()) != null) {
if (line.startsWith("========")) {
// Peer information comes last
seenPeerHeader = true;
continue;
}
if (line.startsWith("jitter:")) {
String[] exploded = StringUtil.explodeQuoted(line);
if (exploded.length < 2) {
setErrorMessage(
"Unable to parse ntpdc output for jitter: ");
return;
}
setValue("Jitter", Double.parseDouble(exploded[1]));
} else if (line.startsWith("system uptime:") ||
(line.startsWith("time since restart:"))) {
String[] exploded = StringUtil.explodeQuoted(line);
if (exploded.length < 1) {
setErrorMessage(
"Unable to parse ntpdc output for system uptime: ");
return;
}
setValue("Uptime",
Double.parseDouble(exploded[exploded.length - 1]));
} else if (line.startsWith("time since reset:")) {
String[] exploded = StringUtil.explodeQuoted(line);
if (exploded.length < 4) {
setErrorMessage(
"Unable to parse ntpdc output for time since reset: ");
return;
}
setValue("TimeSinceReset", Double.parseDouble(exploded[3]));
} else if (seenPeerHeader) {
String[] exploded = StringUtil.explodeQuoted(line);
if (exploded.length < 8) {
setErrorMessage(
"Unable to parse ntpdc output for seenPeerHeader: ");
return;
}
/*
* include LOCAL(0) as a time source
*/
if (exploded[0].length() > PROP_LOCAL.length() &&
exploded[0].substring(1, PROP_LOCAL.length() + 1).equals(PROP_LOCAL) &&
!includeLocal) {
continue;
}
String status = exploded[0].substring(0, 1);
if (status.equals("="))
clientmode += 1.0D;
else if (status.equals("^"))
receivingbroadcasts += 1.0D;
else if (status.equals("~"))
sendingbroadcasts += 1.0D;
else if (status.equals("*")) {
peerssynchronized += 1.0D;
}
peers++;
String reach = exploded[4];
if (reach.equals("377"))
peersReachable++;
else if (reach.equals("000") || reach.equals("0")) // Should always be 0 instead of 000 but just in case
peersUnreachable++;
else
peersWithProbs++;
double delay = Double.parseDouble(exploded[5]);
double offset = Double.parseDouble(exploded[6]);
double disp = Double.parseDouble(exploded[7]);
peerDelay = peerDelay + Math.abs(delay);
peerOffset = peerOffset + Math.abs(offset);
peerDisp = peerDisp + Math.abs(disp);
continue;
}
if (line.startsWith("stratum:")) {
String[] exploded = StringUtil.explodeQuoted(line);
if (exploded.length < 2) {
setErrorMessage("Unable to parse ntpdc output for stratum: ");
return;
}
setValue("Stratum", Double.parseDouble(exploded[1]));
}
}
setValue("PeersReachable", peersReachable);
setValue("PeersUnreachable", peersUnreachable);
setValue("PeersWithReachabilityProblems", peersWithProbs);
setValue("Peers", peers);
setValue("PeerAverageDelay", peerDelay/peers);
setValue("PeerAverageOffset", peerOffset/peers);
setValue("PeerAverageDisplacement", peerDisp/peers);
//XXX compat w/ old alias
setValue("PeerAverageDisp", peerDisp/peers);
setValue("PeersPolledClientMode", clientmode);
setValue("PeersReceivingBroadcasts", receivingbroadcasts);
setValue("PeersSendingBroadcasts", sendingbroadcasts);
setValue("PeersSynchronized", peerssynchronized);
} catch (IllegalArgumentException e) {
setErrorMessage("Unable to parse ntpdc output: " + e);
} catch (IOException e) {
setErrorMessage("Unable to process ntpdc output: " + e);
} finally {
// XXX: wait for process to exit?
if (in != null) {
try { in.close(); } catch (IOException e) {}
}
}
}
}