/* * 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, 2005, 2006], 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.bind; import java.util.HashMap; import java.util.Iterator; import java.util.Properties; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.RandomAccessFile; import org.hyperic.hq.product.Metric; import org.hyperic.hq.product.MetricNotFoundException; import org.hyperic.hq.product.MetricUnreachableException; import org.hyperic.hq.product.MetricValue; import org.hyperic.hq.product.PluginException; import org.hyperic.hq.product.SigarMeasurementPlugin; import org.hyperic.util.JDK; public class BindMeasurementPlugin extends SigarMeasurementPlugin { static final String PROP_RNDC = "rndc"; static final String PROP_NAMED_STATS = "named.stats"; private static final HashMap queryInfo = new HashMap(); private static void processStatsFile(File file) throws MetricNotFoundException { // Process the stats file. BufferedReader in = null; try { in = new BufferedReader(new FileReader(file)); String line; Double val; while ((line = in.readLine()) != null) { String trimmed = line.trim(); if (trimmed.startsWith("success")) { val = new Double(trimmed.substring(8 ,trimmed.length())); queryInfo.put("SuccessfulQueries", val); } else if (trimmed.startsWith("referral")) { val = new Double(trimmed.substring(9, trimmed.length())); queryInfo.put("ReferralQueries", val); } else if (trimmed.startsWith("nxrrset")) { val = new Double(trimmed.substring(8, trimmed.length())); queryInfo.put("NoRecordQueries", val); } else if (trimmed.startsWith("nxdomain")) { val = new Double(trimmed.substring(9, trimmed.length())); queryInfo.put("NoDomainQueries", val); } else if (trimmed.startsWith("recursion")) { val = new Double(trimmed.substring(10, trimmed.length())); queryInfo.put("RecursiveQueries", val); } else if (trimmed.startsWith("failure")) { val = new Double(trimmed.substring(8, trimmed.length())); queryInfo.put("FailedQueries", val); } // [HHQ-3939] Add forward compatibility for existing Bind 9.x // metrics. Should be expanded to cover all new metrics found // in Bind 9.5 and up. else if (trimmed.endsWith("queries resulted in successful answer")) { val = new Double(trimmed.substring(0, trimmed.indexOf(' '))); queryInfo.put("SuccessfulQueries", val); } else if (trimmed.endsWith("queries resulted in referral")) { val = new Double(trimmed.substring(0, trimmed.indexOf(' '))); queryInfo.put("ReferralQueries", val); } else if (trimmed.endsWith("queries resulted in nxrrset")) { val = new Double(trimmed.substring(0, trimmed.indexOf(' '))); queryInfo.put("NoRecordQueries", val); } else if (trimmed.endsWith("queries resulted in NXDOMAIN")) { val = new Double(trimmed.substring(0, trimmed.indexOf(' '))); queryInfo.put("NoDomainQueries", val); } else if (trimmed.endsWith("queries caused recursion")) { val = new Double(trimmed.substring(0, trimmed.indexOf(' '))); queryInfo.put("RecursiveQueries", val); } else if (trimmed.endsWith("queries resulted in SERVFAIL")) { val = new Double(trimmed.substring(0, trimmed.indexOf(' '))); queryInfo.put("FailedQueries", val); } } } catch (IOException e) { throw new MetricNotFoundException("Unable to process rndc " + "output: " + e); } finally { if (in != null) { try { in.close(); } catch (IOException e) {} } } } public void getQueryInfo(Properties props) throws MetricNotFoundException { String rndc = props.getProperty(PROP_RNDC); String namedStats = props.getProperty(PROP_NAMED_STATS); File statsFile = new File(namedStats); // Test the stats file exists, and is writeable by the // agent process. if (!statsFile.exists()) { throw new MetricNotFoundException("Stats file " + namedStats + " does not exist. Please " + "create this file and make " + "sure it is writeable by " + "the agent process"); } if (!statsFile.canWrite()) { throw new MetricNotFoundException("Stats file " + namedStats + " is not writable by agent " + "process."); } // Before doing anything, truncate the current stats file. try { String mode = "rws"; RandomAccessFile toTrunc = new RandomAccessFile(statsFile, mode); toTrunc.setLength(0); toTrunc.close(); } catch (IOException e) { throw new MetricNotFoundException("IO Error reading stats file: " + e); } // Execute rndc to generate named.stats Process proc; try { String[] argv = new String[] { rndc, "stats" }; proc = Runtime.getRuntime().exec(argv); // Check error condition. BufferedReader err = new BufferedReader(new InputStreamReader(proc. getErrorStream())); String line = err.readLine(); if (line != null) { throw new MetricNotFoundException("Unable to exec process: " + line); } try { err.close(); } catch (IOException e) {} } catch (IOException e) { throw new MetricNotFoundException("Unable to exec process: " + e); } try { proc.waitFor(); } catch (InterruptedException e) { } processStatsFile(statsFile); } public MetricValue getValue(Metric metric) throws PluginException, MetricNotFoundException, MetricUnreachableException { String domain = metric.getDomainName(); String attr = metric.getAttributeName(); if (domain.equals("sigar.ptql")) { return super.getValue(metric); } // rndc statistic Double val = (Double)queryInfo.get(attr); if (val == null) { // not yet cached getQueryInfo(metric.getProperties()); val = (Double)queryInfo.get(attr); if (val == null) { throw new MetricNotFoundException("No metric mapped to " + " metric: " + attr); } } // remove the metric from the cache to force a refresh // next time around queryInfo.remove(attr); return new MetricValue(val.doubleValue(), System.currentTimeMillis()); } public static void main(String[] args) throws Exception { if (args.length == 1) { File statsFile = new File(args[0]); if (!statsFile.exists()) { System.err.println("Unable to find stats file " + statsFile); } else { System.out.println("Parsing stats file: " + args[0]); processStatsFile(statsFile); for (Iterator i = queryInfo.keySet().iterator(); i.hasNext(); ) { String key = (String)i.next(); System.out.println(key + "=" + queryInfo.get(key)); } System.out.println("Done"); } } else { System.err.println("Usage: BindMeasurementPlugin <stats.file>"); } } }