/*******************************************************************************
* 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.web.svclayer.support;
import java.io.File;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.opennms.core.utils.LogUtils;
import org.opennms.netmgt.dao.GraphDao;
import org.opennms.netmgt.dao.ResourceDao;
import org.opennms.netmgt.dao.RrdDao;
import org.opennms.netmgt.dao.support.RrdFileConstants;
import org.opennms.netmgt.model.AdhocGraphType;
import org.opennms.netmgt.model.OnmsResource;
import org.opennms.netmgt.model.PrefabGraph;
import org.opennms.netmgt.model.PrefabGraphType;
import org.opennms.netmgt.model.RrdGraphAttribute;
import org.opennms.web.graph.Graph;
import org.opennms.web.svclayer.RrdGraphService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <p>DefaultRrdGraphService class.</p>
*
* @author <a href="mailto:dj@opennms.org">DJ Gregor</a>
* @author <a href="mailto:cmiskell@opennms.org">Craig Miskell</a>
*/
public class DefaultRrdGraphService implements RrdGraphService, InitializingBean {
// private static final String s_missingParamsPath = "/images/rrd/missingparams.png";
private static final String s_rrdError = "/images/rrd/error.png";
private GraphDao m_graphDao;
private ResourceDao m_resourceDao;
private RrdDao m_rrdDao;
/** {@inheritDoc} */
public InputStream getAdhocGraph(String resourceId, String title,
String[] dataSources, String[] aggregateFunctions,
String[] colors, String[] dataSourceTitles, String[] styles,
long start, long end) {
Assert.notNull(resourceId, "resourceId argument cannot be null");
Assert.notNull(title, "title argument cannot be null");
Assert.notNull(dataSources, "dataSources argument cannot be null");
Assert.notNull(aggregateFunctions, "aggregateFunctions argument cannot be null");
Assert.notNull(colors, "colors argument cannot be null");
Assert.notNull(dataSourceTitles, "dataSourceTitles argument cannot be null");
Assert.notNull(styles, "styles argument cannot be null");
Assert.isTrue(end > start, "end time must be after start time");
AdhocGraphType t = m_graphDao.findAdhocGraphTypeByName("performance");
OnmsResource r = m_resourceDao.getResourceById(resourceId);
Assert.notNull(r, "resource \"" + resourceId + "\" could not be located");
String command = createAdHocCommand(t,
r,
start, end,
title,
dataSources,
aggregateFunctions,
colors,
dataSourceTitles,
styles);
return getInputStreamForCommand(command);
}
private InputStream getInputStreamForCommand(String command) {
boolean debug = true;
File workDir = m_resourceDao.getRrdDirectory(true);
InputStream tempIn = null;
try {
LogUtils.debugf(this, "Executing RRD command in directory '%s': %s", workDir, command);
tempIn = m_rrdDao.createGraph(command, workDir);
} catch (final DataAccessException e) {
LogUtils.warnf(this, e, "Exception while creating graph.");
if (debug) {
throw e;
} else {
return returnErrorImage(s_rrdError);
}
}
return tempIn;
}
/**
* <p>returnErrorImage</p>
*
* @param file a {@link java.lang.String} object.
* @return a {@link java.io.InputStream} object.
*/
public InputStream returnErrorImage(String file) {
InputStream is = getClass().getResourceAsStream(file);
if (is == null) {
throw new ObjectRetrievalFailureException(InputStream.class, file, "Could not find error image for '" + file + "' or could open", null);
}
return is;
}
/** {@inheritDoc} */
public InputStream getPrefabGraph(String resourceId, String report, long start, long end) {
Assert.notNull(resourceId, "resourceId argument cannot be null");
Assert.notNull(report, "report argument cannot be null");
Assert.isTrue(end > start, "end time " + end + " must be after start time" + start);
PrefabGraphType t = m_graphDao.findPrefabGraphTypeByName("performance");
if (t == null) {
throw new IllegalArgumentException("graph type \"" + "performance"
+ "\" is not valid");
}
OnmsResource r = m_resourceDao.getResourceById(resourceId);
Assert.notNull(r, "resource could not be located");
PrefabGraph prefabGraph = m_graphDao.getPrefabGraph(report);
Graph graph = new Graph(prefabGraph, r, new Date(start), new Date(end));
String command = createPrefabCommand(graph,
t.getCommandPrefix(),
m_resourceDao.getRrdDirectory(true),
report);
return getInputStreamForCommand(command);
}
/**
* <p>createAdHocCommand</p>
*
* @param adhocType a {@link org.opennms.netmgt.model.AdhocGraphType} object.
* @param resource a {@link org.opennms.netmgt.model.OnmsResource} object.
* @param start a long.
* @param end a long.
* @param graphtitle a {@link java.lang.String} object.
* @param dsNames an array of {@link java.lang.String} objects.
* @param dsAggregFxns an array of {@link java.lang.String} objects.
* @param colors an array of {@link java.lang.String} objects.
* @param dsTitles an array of {@link java.lang.String} objects.
* @param dsStyles an array of {@link java.lang.String} objects.
* @return a {@link java.lang.String} object.
*/
protected String createAdHocCommand(AdhocGraphType adhocType,
OnmsResource resource,
long start, long end,
String graphtitle,
String[] dsNames,
String[] dsAggregFxns,
String[] colors,
String[] dsTitles,
String[] dsStyles) {
String commandPrefix = adhocType.getCommandPrefix();
String title = adhocType.getTitleTemplate();
String ds = adhocType.getDataSourceTemplate();
String graphline = adhocType.getGraphLineTemplate();
/*
* Remember that rrdtool wants the time in seconds, not milliseconds;
* java.util.Date.getTime() returns milliseconds, so divide by 1000
*/
String starttime = Long.toString(start / 1000);
String endtime = Long.toString(end / 1000);
StringBuffer buf = new StringBuffer();
buf.append(commandPrefix);
buf.append(" ");
buf.append(title);
String[] rrdFiles = getRrdNames(resource, dsNames);
List<String> defs = new ArrayList<String>(dsNames.length);
List<String> lines = new ArrayList<String>(dsNames.length);
for (int i = 0; i < dsNames.length; i++) {
String dsAbbrev = "ds" + Integer.toString(i);
String dsName = dsNames[i];
String rrd = rrdFiles[i];
String dsAggregFxn = dsAggregFxns[i];
String color = colors[i];
String dsTitle = dsTitles[i];
String dsStyle = dsStyles[i];
defs.add(MessageFormat.format(ds, rrd, starttime,
endtime, graphtitle,
dsAbbrev, dsName,
dsAggregFxn, dsStyle,
color, dsTitle));
lines.add(MessageFormat.format(graphline, rrd,
starttime, endtime, graphtitle,
dsAbbrev, dsName, dsAggregFxn,
dsStyle, color, dsTitle));
}
for (String def : defs) {
buf.append(" ");
buf.append(def);
}
for (String line : lines) {
buf.append(" ");
buf.append(line);
}
LogUtils.debugf(this, "formatting: %s, bogus-rrd, %s, %s, %s", buf, starttime, endtime, graphtitle);
return MessageFormat.format(buf.toString(), "bogus-rrd", starttime, endtime, graphtitle);
}
private static String[] getRrdNames(OnmsResource resource, String[] dsNames) {
String[] rrds = new String[dsNames.length];
Map<String, RrdGraphAttribute> attributes = resource.getRrdGraphAttributes();
for (int i=0; i < dsNames.length; i++) {
RrdGraphAttribute attribute = attributes.get(dsNames[i]);
if (attribute == null) {
throw new IllegalArgumentException("RRD attribute '" + dsNames[i] + "' is not available on resource '" + resource.getId() + "'. Available RRD attributes: " + StringUtils.collectionToDelimitedString(attributes.keySet(), ", "));
}
rrds[i] = RrdFileConstants.escapeForGraphing(attribute.getRrdRelativePath());
}
return rrds;
}
private static Map<String, String> getTranslationsForAttributes(Map<String, String> attributes, String[] requiredAttributes, String type) {
if (requiredAttributes == null) {
// XXX Nothing to do; not sure if we need this check
return new HashMap<String, String>(0);
}
Map<String, String> translations = new HashMap<String, String>(requiredAttributes.length);
for (String requiredAttribute : requiredAttributes) {
String attributeValue = attributes.get(requiredAttribute);
if (attributeValue == null) {
throw new IllegalArgumentException(type + " '" + requiredAttribute + "' is not available in the list of provided attributes. Available " + type + "s: " + StringUtils.collectionToDelimitedString(attributes.keySet(), ", "));
}
// Replace any single backslashes in the value with escaped backslashes so other parsing won't barf
String replacedValue = attributeValue.replace("\\", "\\\\");
translations.put(RE.simplePatternToFullRegularExpression("{" + requiredAttribute + "}"), replacedValue);
}
return translations;
}
/**
* <p>createPrefabCommand</p>
*
* @param graph a {@link org.opennms.web.graph.Graph} object.
* @param commandPrefix a {@link java.lang.String} object.
* @param workDir a {@link java.io.File} object.
* @param reportName a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
protected String createPrefabCommand(Graph graph,
String commandPrefix,
File workDir, String reportName) {
PrefabGraph prefabGraph = graph.getPrefabGraph();
String[] rrds = getRrdNames(graph.getResource(), graph.getPrefabGraph().getColumns());
StringBuffer buf = new StringBuffer();
buf.append(commandPrefix);
buf.append(" ");
buf.append(prefabGraph.getCommand());
String command = buf.toString();
long startTime = graph.getStart().getTime();
long endTime = graph.getEnd().getTime();
long diffTime = endTime - startTime;
/*
* remember rrdtool wants the time in seconds, not milliseconds;
* java.util.Date.getTime() returns milliseconds, so divide by 1000
*/
String startTimeString = Long.toString(startTime / 1000);
String endTimeString = Long.toString(endTime / 1000);
String diffTimeString = Long.toString(diffTime / 1000);
HashMap<String, String> translationMap = new HashMap<String, String>();
for (int i = 0; i < rrds.length; i++) {
String key = "{rrd" + (i + 1) + "}";
translationMap.put(RE.simplePatternToFullRegularExpression(key), rrds[i]);
}
translationMap.put(RE.simplePatternToFullRegularExpression("{startTime}"), startTimeString);
translationMap.put(RE.simplePatternToFullRegularExpression("{endTime}"), endTimeString);
translationMap.put(RE.simplePatternToFullRegularExpression("{diffTime}"), diffTimeString);
try {
translationMap.putAll(getTranslationsForAttributes(graph.getResource().getExternalValueAttributes(), prefabGraph.getExternalValues(), "external value attribute"));
translationMap.putAll(getTranslationsForAttributes(graph.getResource().getStringPropertyAttributes(), prefabGraph.getPropertiesValues(), "string property attribute"));
} catch (RuntimeException e) {
LogUtils.errorf(this, "Invalid attributes were found on resource '%s'", graph.getResource().getId());
throw e;
}
try {
for (Map.Entry<String, String> translation : translationMap.entrySet()) {
// replace s1 with s2
RE re = new RE(translation.getKey());
command = re.subst(command, translation.getValue());
}
} catch (RESyntaxException e) {
throw new IllegalArgumentException("Invalid regular expression "
+ "syntax, check "
+ "rrd-properties file", e);
}
return command;
}
/**
* <p>afterPropertiesSet</p>
*/
@Override
public void afterPropertiesSet() {
Assert.state(m_resourceDao != null, "resourceDao property has not been set");
Assert.state(m_graphDao != null, "graphDao property has not been set");
Assert.state(m_rrdDao != null, "rrdDao property has not been set");
}
/**
* <p>getResourceDao</p>
*
* @return a {@link org.opennms.netmgt.dao.ResourceDao} object.
*/
public ResourceDao getResourceDao() {
return m_resourceDao;
}
/**
* <p>setResourceDao</p>
*
* @param resourceDao a {@link org.opennms.netmgt.dao.ResourceDao} object.
*/
public void setResourceDao(ResourceDao resourceDao) {
m_resourceDao = resourceDao;
}
/**
* <p>getGraphDao</p>
*
* @return a {@link org.opennms.netmgt.dao.GraphDao} object.
*/
public GraphDao getGraphDao() {
return m_graphDao;
}
/**
* <p>setGraphDao</p>
*
* @param graphDao a {@link org.opennms.netmgt.dao.GraphDao} object.
*/
public void setGraphDao(GraphDao graphDao) {
m_graphDao = graphDao;
}
/**
* <p>getRrdDao</p>
*
* @return a {@link org.opennms.netmgt.dao.RrdDao} object.
*/
public RrdDao getRrdDao() {
return m_rrdDao;
}
/**
* <p>setRrdDao</p>
*
* @param rrdDao a {@link org.opennms.netmgt.dao.RrdDao} object.
*/
public void setRrdDao(RrdDao rrdDao) {
m_rrdDao = rrdDao;
}
}