/*******************************************************************************
* 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.poller.pollables;
import java.io.File;
import java.net.InetAddress;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.ParameterMap;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.config.PollerConfig;
import org.opennms.netmgt.config.poller.Package;
import org.opennms.netmgt.model.PollStatus;
import org.opennms.netmgt.model.RrdRepository;
import org.opennms.netmgt.poller.MonitoredService;
import org.opennms.netmgt.poller.ServiceMonitor;
import org.opennms.netmgt.rrd.RrdDataSource;
import org.opennms.netmgt.rrd.RrdException;
import org.opennms.netmgt.rrd.RrdUtils;
import org.opennms.netmgt.threshd.LatencyThresholdingSet;
import org.opennms.netmgt.threshd.ThresholdingEventProxy;
import org.opennms.netmgt.xml.event.Event;
/**
* <p>LatencyStoringServiceMonitorAdaptor class.</p>
*
* @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a>
* @author <a href="mailto:ranger@opennms.org">Ben Reed</a>
*/
public class LatencyStoringServiceMonitorAdaptor implements ServiceMonitor {
/** Constant <code>DEFAULT_BASENAME="response-time"</code> */
public static final String DEFAULT_BASENAME = "response-time";
private ServiceMonitor m_serviceMonitor;
private PollerConfig m_pollerConfig;
private Package m_pkg;
private LatencyThresholdingSet m_thresholdingSet;
/**
* <p>Constructor for LatencyStoringServiceMonitorAdaptor.</p>
*
* @param monitor a {@link org.opennms.netmgt.poller.ServiceMonitor} object.
* @param config a {@link org.opennms.netmgt.config.PollerConfig} object.
* @param pkg a {@link org.opennms.netmgt.config.poller.Package} object.
*/
public LatencyStoringServiceMonitorAdaptor(ServiceMonitor monitor, PollerConfig config, Package pkg) {
m_serviceMonitor = monitor;
m_pollerConfig = config;
m_pkg = pkg;
}
/** {@inheritDoc} */
public void initialize(Map<String, Object> parameters) {
m_serviceMonitor.initialize(parameters);
}
/**
* <p>initialize</p>
*
* @param svc a {@link org.opennms.netmgt.poller.MonitoredService} object.
*/
public void initialize(MonitoredService svc) {
m_serviceMonitor.initialize(svc);
}
/** {@inheritDoc} */
public PollStatus poll(MonitoredService svc, Map<String, Object> parameters) {
PollStatus status = m_serviceMonitor.poll(svc, parameters);
if (!status.getProperties().isEmpty()) {
storeResponseTime(svc, new LinkedHashMap<String, Number>(status.getProperties()), parameters);
}
if ("true".equals(ParameterMap.getKeyedString(parameters, "invert-status", "false"))) {
if (status.isAvailable()) {
return PollStatus.unavailable("This is an inverted service and the underlying service has started responding");
} else {
return PollStatus.available();
}
}
return status;
}
private void storeResponseTime(MonitoredService svc, LinkedHashMap<String, Number> entries, Map<String,Object> parameters) {
String rrdPath = ParameterMap.getKeyedString(parameters, "rrd-repository", null);
String dsName = ParameterMap.getKeyedString(parameters, "ds-name", DEFAULT_BASENAME);
String rrdBaseName = ParameterMap.getKeyedString(parameters, "rrd-base-name", dsName);
String thresholds = ParameterMap.getKeyedString(parameters, "thresholding-enabled", "false");
if (!entries.containsKey(dsName) && entries.containsKey(DEFAULT_BASENAME)) {
entries.put(dsName, entries.get(DEFAULT_BASENAME));
entries.remove(DEFAULT_BASENAME);
}
if (thresholds.toLowerCase().equals("true")) {
applyThresholds(rrdPath, svc, dsName, entries);
} else {
log().debug("storeResponseTime: Thresholds processing is not enabled. Check thresholding-enabled parameter on service definition");
}
if (rrdPath == null) {
log().debug("storeResponseTime: RRD repository not specified in parameters, latency data will not be stored.");
return;
}
updateRRD(rrdPath, svc.getAddress(), rrdBaseName, entries);
}
private void applyThresholds(String rrdPath, MonitoredService service, String dsName, LinkedHashMap<String, Number> entries) {
try {
if (m_thresholdingSet == null) {
RrdRepository repository = new RrdRepository();
repository.setRrdBaseDir(new File(rrdPath));
m_thresholdingSet = new LatencyThresholdingSet(service.getNodeId(), service.getIpAddr(), service.getSvcName(), repository);
}
LinkedHashMap<String, Double> attributes = new LinkedHashMap<String, Double>();
for (String ds : entries.keySet()) {
attributes.put(ds, entries.get(ds).doubleValue());
}
if (m_thresholdingSet.isNodeInOutage()) {
log().info("applyThresholds: the threshold processing will be skipped because the service " + service + " is on a scheduled outage.");
} else if (m_thresholdingSet.hasThresholds(attributes)) {
List<Event> events = m_thresholdingSet.applyThresholds(dsName, attributes);
if (events.size() > 0) {
ThresholdingEventProxy proxy = new ThresholdingEventProxy();
proxy.add(events);
proxy.sendAllEvents();
}
}
} catch(Throwable e) {
log().error("Failed to threshold on " + service + " for " + dsName + " because of an exception", e);
}
}
/**
* Update an RRD database file with latency/response time data.
*
* @param repository
* path to the RRD file repository
* @param addr
* interface address
* @param dsName
* the datasource name to update
* @param value
* value to update the RRD file with
* @param rrdBaseName a {@link java.lang.String} object.
*/
public void updateRRD(String repository, InetAddress addr, String rrdBaseName, String dsName, long value) {
LinkedHashMap<String, Number> lhm = new LinkedHashMap<String, Number>();
lhm.put(dsName, value);
updateRRD(repository, addr, rrdBaseName, lhm);
}
/**
* Update an RRD database file with multiple latency/response time data sources.
*
* @param repository
* path to the RRD file repository
* @param addr
* interface address
* @param entries
* the entries for the rrd, containing a Map of dsNames to values
* @param rrdBaseName a {@link java.lang.String} object.
*/
public void updateRRD(String repository, InetAddress addr, String rrdBaseName, LinkedHashMap<String, Number> entries) {
try {
// Create RRD if it doesn't already exist
List<RrdDataSource> dsList = new ArrayList<RrdDataSource>(entries.size());
for (String dsName : entries.keySet()) {
dsList.add(new RrdDataSource(dsName, "GAUGE", m_pollerConfig.getStep(m_pkg)*2, "U", "U"));
}
createRRD(repository, addr, rrdBaseName, dsList);
// add interface address to RRD repository path
final String hostAddress = InetAddressUtils.str(addr);
String path = repository + File.separator + hostAddress;
StringBuffer value = new StringBuffer();
Iterator<String> i = entries.keySet().iterator();
while (i.hasNext()) {
Number num = entries.get(i.next());
if (num == null || Double.isNaN(num.doubleValue())) {
value.append("U");
} else {
NumberFormat nf = NumberFormat.getInstance(Locale.US);
nf.setGroupingUsed(false);
nf.setMinimumFractionDigits(0);
nf.setMaximumFractionDigits(Integer.MAX_VALUE);
nf.setMinimumIntegerDigits(0);
nf.setMaximumIntegerDigits(Integer.MAX_VALUE);
value.append(nf.format(num.doubleValue()));
}
if (i.hasNext()) {
value.append(":");
}
}
RrdUtils.updateRRD(hostAddress, path, rrdBaseName, value.toString());
} catch (RrdException e) {
if (log().isEnabledFor(ThreadCategory.Level.ERROR)) {
String msg = e.getMessage();
log().error(msg);
throw new RuntimeException(msg, e);
}
}
}
/**
* Create an RRD database file with a single dsName for storing latency/response time data.
*
* @param repository
* path to the RRD file repository
* @param addr
* interface address
* @param dsName
* data source/RRD file name
* @return true if RRD file successfully created, false otherwise
* @param rrdBaseName a {@link java.lang.String} object.
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public boolean createRRD(String repository, InetAddress addr, String rrdBaseName, String dsName) throws RrdException {
List<RrdDataSource> dsList = Collections.singletonList(new RrdDataSource(dsName, "GAUGE", m_pollerConfig.getStep(m_pkg)*2, "U", "U"));
return createRRD(repository, addr, rrdBaseName, dsList);
}
/**
* Create an RRD database file with multiple dsNames for storing latency/response time data.
*
* @param repository
* path to the RRD file repository
* @param addr
* interface address
* @return true if RRD file successfully created, false otherwise
* @param rrdBaseName a {@link java.lang.String} object.
* @param dsList a {@link java.util.List} object.
* @throws org.opennms.netmgt.rrd.RrdException if any.
*/
public boolean createRRD(String repository, InetAddress addr, String rrdBaseName, List<RrdDataSource> dsList) throws RrdException {
List<String> rraList = m_pollerConfig.getRRAList(m_pkg);
// add interface address to RRD repository path
final String hostAddress = InetAddressUtils.str(addr);
String path = repository + File.separator + hostAddress;
return RrdUtils.createRRD(hostAddress, path, rrdBaseName, m_pollerConfig.getStep(m_pkg), dsList, rraList);
}
private ThreadCategory log() {
return ThreadCategory.getInstance(getClass());
}
/**
* <p>release</p>
*/
public void release() {
m_serviceMonitor.release();
}
/** {@inheritDoc} */
public void release(MonitoredService svc) {
m_serviceMonitor.release(svc);
}
/**
* Should be called when thresholds configuration has been reloaded
*/
public void refreshThresholds() {
if (m_thresholdingSet != null)
m_thresholdingSet.reinitialize();
}
}