/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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 OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.rrd;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.opennms.core.utils.ThreadCategory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Provides static methods for interacting with round robin files. Supports JNI
* and JRobin based files and provides queuing for managing differences in
* collection speed and disk write speed. This behaviour is implemented using
* the Strategy pattern with a different RrdStrategy for JRobin and JNI as well
* as a Strategy that provides Queueing on top of either one.
*
* The following System properties select which strategy is in use.
*
* <pre>
*
* org.opennms.rrd.usejni: (defaults to true)
* true - use the existing RRDTool code via the JNI interface @see JniRrdStrategy
* false - use the pure java JRobin interface @see JRobinRrdStrategy
*
* org.opennms.rrd.usequeue: (defaults to true)
* use the queueing that allows collection to occur even though the disks are
* keeping up. @see QueuingRrdStrategy
*
*
* </pre>
*/
public abstract class RrdUtils {
private static RrdStrategy<?,?> m_rrdStrategy = null;
private static BeanFactory m_context = new ClassPathXmlApplicationContext(new String[] {
// Default RRD configuration context
"org/opennms/netmgt/rrd/rrd-configuration.xml"
});
public static enum StrategyName {
basicRrdStrategy,
queuingRrdStrategy,
tcpAndBasicRrdStrategy,
tcpAndQueuingRrdStrategy
}
/**
* <p>getStrategy</p>
*
* @return a {@link org.opennms.netmgt.rrd.RrdStrategy} object.
*/
@SuppressWarnings("unchecked")
public static <D,F> RrdStrategy<D,F> getStrategy() {
RrdStrategy<D,F> retval = null;
if (m_rrdStrategy == null) {
if ((Boolean)m_context.getBean("useQueue")) {
if ((Boolean)m_context.getBean("useTcp")) {
retval = (RrdStrategy<D,F>)m_context.getBean(StrategyName.tcpAndQueuingRrdStrategy.toString());
} else {
retval = (RrdStrategy<D,F>)m_context.getBean(StrategyName.queuingRrdStrategy.toString());
}
} else {
if ((Boolean)m_context.getBean("useTcp")) {
retval = (RrdStrategy<D,F>)m_context.getBean(StrategyName.tcpAndBasicRrdStrategy.toString());
} else {
retval = (RrdStrategy<D,F>)m_context.getBean(StrategyName.basicRrdStrategy.toString());
}
}
} else {
retval = (RrdStrategy<D,F>)m_rrdStrategy;
}
if (retval == null) {
throw new IllegalStateException("RrdUtils not initialized");
}
return retval;
}
/**
* <p>getSpecificStrategy</p>
*
* @param strategy a {@link org.opennms.netmgt.rrd.RrdUtils.StrategyName} object.
* @return a {@link org.opennms.netmgt.rrd.RrdStrategy} object.
*/
@SuppressWarnings("unchecked")
public static <D,F> RrdStrategy<D,F> getSpecificStrategy(StrategyName strategy) {
RrdStrategy<D,F> retval = null;
retval = (RrdStrategy<D,F>)m_context.getBean(strategy.toString());
if (retval == null) {
throw new IllegalStateException("RrdUtils not initialized");
}
return retval;
}
/**
* <p>setStrategy</p>
*
* @param strategy a {@link org.opennms.netmgt.rrd.RrdStrategy} object.
*/
public static void setStrategy(RrdStrategy<?,?> strategy) {
m_rrdStrategy = strategy;
}
/**
* Create a round robin database file. See the man page for rrdtool create
* for definitions of each of these.
*
* @param creator -
* A string representing who is creating this file for use in log
* msgs
* @param directory -
* The directory to create the file in
* @param dsName -
* The datasource name for use in the round robin database
* @param step -
* the step for the database
* @param dsType -
* the type for the datasource
* @param dsHeartbeat -
* the heartbeat for the datasouce
* @param dsMin -
* the minimum allowable value for the datasource
* @param dsMax -
* the maximum allowable value for the datasouce
* @param rraList -
* a List of the round robin archives to create in the database
* @return true if the file was actually created, false otherwise
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public static boolean createRRD(String creator, String directory, String dsName, int step, String dsType, int dsHeartbeat, String dsMin, String dsMax, List<String> rraList) throws RrdException {
return createRRD(creator, directory, dsName, step, Collections.singletonList(new RrdDataSource(dsName, dsType, dsHeartbeat, dsMin, dsMax)), rraList);
}
/**
* <p>createRRD</p>
*
* @param creator a {@link java.lang.String} object.
* @param directory a {@link java.lang.String} object.
* @param rrdName a {@link java.lang.String} object.
* @param step a int.
* @param dataSources a {@link java.util.List} object.
* @param rraList a {@link java.util.List} object.
* @return a boolean.
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public static boolean createRRD(String creator, String directory, String rrdName, int step, List<RrdDataSource> dataSources, List<String> rraList) throws RrdException {
String fileName = rrdName + getExtension();
String completePath = directory + File.separator + fileName;
try {
Object def = getStrategy().createDefinition(creator, directory, rrdName, step, dataSources, rraList);
if (def != null) {
log().info("createRRD: creating RRD file " + completePath);
}
getStrategy().createFile(def);
return true;
} catch (Throwable e) {
log().error("createRRD: An error occured creating rrdfile " + completePath + ": " + e, e);
throw new org.opennms.netmgt.rrd.RrdException("An error occured creating rrdfile " + completePath + ": " + e, e);
}
}
private static ThreadCategory log() {
return ThreadCategory.getInstance(RrdUtils.class);
}
/**
* Add datapoints to a round robin database using the current system time as the timestamp for the values
*
* @param owner
* the owner of the file. This is used in log messages
* @param repositoryDir
* the directory the file resides in
* @param rrdName
* the name for the rrd file.
* @param val
* a colon separated list of values representing the updates for datasources for this rrd
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public static void updateRRD(String owner, String repositoryDir, String rrdName, String val) throws RrdException {
updateRRD(owner, repositoryDir, rrdName, System.currentTimeMillis(), val);
}
/**
* Add datapoints to a round robin database.
*
* @param owner
* the owner of the file. This is used in log messages
* @param repositoryDir
* the directory the file resides in
* @param rrdName
* the name for the rrd file.
* @param timestamp
* the timestamp in millis to use for the rrd update (this gets rounded to the nearest second)
* @param val
* a colon separated list of values representing the updates for datasources for this rrd
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public static void updateRRD(String owner, String repositoryDir, String rrdName, long timestamp, String val) throws RrdException {
// Issue the RRD update
String rrdFile = repositoryDir + File.separator + rrdName + getExtension();
long time = (timestamp + 500L) / 1000L;
String updateVal = Long.toString(time) + ":" + val;
log().info("updateRRD: updating RRD file " + rrdFile + " with values '" + updateVal + "'");
Object rrd = null;
try {
rrd = getStrategy().openFile(rrdFile);
getStrategy().updateFile(rrd, owner, updateVal);
} catch (Throwable e) {
log().error("updateRRD: Error updating RRD file " + rrdFile + " with values '" + updateVal + "': " + e, e);
throw new org.opennms.netmgt.rrd.RrdException("Error updating RRD file " + rrdFile + " with values '" + updateVal + "': " + e, e);
} finally {
try {
if (rrd != null) {
getStrategy().closeFile(rrd);
}
} catch (Throwable e) {
log().error("updateRRD: Exception closing RRD file " + rrdFile + ": " + e, e);
throw new org.opennms.netmgt.rrd.RrdException("Exception closing RRD file " + rrdFile + ": " + e, e);
}
}
if (log().isDebugEnabled()) {
log().debug("updateRRD: RRD update command completed.");
}
}
/**
* This method issues an round robin fetch command to retrieve the last
* value of the datasource stored in the specified RRD file. The retrieved
* value returned to the caller.
*
* NOTE: This method assumes that each RRD file contains a single
* datasource.
*
* @param rrdFile
* RRD file from which to fetch the data.
* @param interval
* Thresholding interval (should equal RRD step size)
* @param ds
* Name of the Data Source to be used
* @return Retrived datasource value as a java.lang.Double
* @throws java.lang.NumberFormatException
* if the retrieved value fails to convert to a double
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public static Double fetchLastValue(String rrdFile, String ds, int interval) throws NumberFormatException, RrdException {
return getStrategy().fetchLastValue(rrdFile, ds, interval);
}
/**
* This method issues an round robing fetch command to retrieve the last
* value of the datasource stored in the specified RRD file within given
* tolerance (which should be a multiple of the RRD interval). This is useful
* If you are not entirely sure when an RRD might have been updated, but you
* want to retrieve the last value which is not NaN
* NOTE: This method assumes that each RRD file contains a single
* datasource.
*
* @param rrdFile
* RRD file from which to fetch the data.
* @param interval
* Thresholding interval (should equal RRD step size)
* @param ds
* Name of the Data Source to be used
* @return Retrived datasource value as a java.lang.Double
* @throws java.lang.NumberFormatException
* if the retrieved value fails to convert to a double
* @param range a int.
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public static Double fetchLastValueInRange(String rrdFile, String ds, int interval, int range) throws NumberFormatException, RrdException {
return getStrategy().fetchLastValueInRange(rrdFile, ds, interval, range);
}
/**
* Creates an InputStream representing the bytes of a graph created from
* round robin data. It accepts an rrdtool graph command. The underlying
* implementation converts this command to a format appropriate for it .
*
* @param command
* the command needed to create the graph
* @param workDir
* the directory that all referenced files are relative to
* @return an input stream representing the bytes of a graph image as a PNG
* file
* @throws java.io.IOException
* if an IOError occurs
* @throws org.opennms.netmgt.rrd.RrdException
* if an RRD error occurs
*/
public static InputStream createGraph(String command, File workDir) throws IOException, RrdException {
return getStrategy().createGraph(command, workDir);
}
/**
* <p>getExtension</p>
*
* @return a {@link java.lang.String} object.
*/
public static String getExtension() {
String rrdExtension = (String)m_context.getBean("rrdFileExtension");
if (rrdExtension == null || "".equals(rrdExtension)) {
return getStrategy().getDefaultFileExtension();
}
return rrdExtension;
}
/**
* <p>promoteEnqueuedFiles</p>
*
* @param files a {@link java.util.Collection} object.
*/
public static void promoteEnqueuedFiles(Collection<String> files) {
getStrategy().promoteEnqueuedFiles(files);
}
}