/******************************************************************************* * 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.threshd; import java.lang.reflect.UndeclaredThrowableException; import java.net.InetAddress; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.opennms.core.utils.DBUtils; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.ParameterMap; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.config.DataSourceFactory; import org.opennms.netmgt.config.ThresholdingConfigFactory; import org.opennms.netmgt.config.threshd.Basethresholddef; import org.opennms.netmgt.model.events.EventProxy; import org.opennms.netmgt.model.events.EventProxyException; import org.opennms.netmgt.poller.NetworkInterface; import org.opennms.netmgt.xml.event.Event; import org.opennms.netmgt.xml.event.Events; import org.opennms.netmgt.xml.event.Log; /** * <P> * The LatencyThresholder class ... * </P> * * @author <A HREF="mailto:mike@opennms.org">Mike Davidson </A> * @author <A HREF="http://www.opennms.org/">OpenNMS </A> * * FIXME: This thresholder does not support ranges yet. * */ final class LatencyThresholder implements ServiceThresholder { /** * SQL statement to retrieve interface's 'ipinterface' table information. */ private static final String SQL_GET_NODEID = "SELECT nodeid FROM ipinterface WHERE ipAddr=? AND ismanaged!='D'"; /** * Default thresholding interval (in milliseconds). * */ static final int DEFAULT_INTERVAL = 300000; // 300s or 5m /** * Default age before which a data point is considered "out of date" */ static final int DEFAULT_RANGE = 0; /** * Interface attribute key used to store the interface's node id */ static final String RRD_REPOSITORY_KEY = "org.opennms.netmgt.collectd.LatencyThresholder.RrdRepository"; /** * Interface attribute key used to store configured thresholds */ static final String THRESHOLD_MAP_KEY = "org.opennms.netmgt.collectd.LatencyThresholder.ThresholdMap"; /** * Interface attribute key used to store the interface's node id */ static final String NODE_ID_KEY = "org.opennms.netmgt.collectd.SnmpThresholder.NodeId"; /** * Specific service that this thresholder is responsible for latency * threshold checking. */ private String m_svcName; /** * <P> * Returns the name of the service that the plug-in threshold checks. * </P> * * @return The service that the plug-in collects. */ public String serviceName() { return m_svcName; } /** * {@inheritDoc} * * <P> * Initialize the service thresholder. * </P> * @exception RuntimeException * Thrown if an unrecoverable error occurs that prevents the * plug-in from functioning. */ @Override public void initialize(Map<?,?> parameters) { // Service name // m_svcName = (String) parameters.get("svcName"); if (log().isDebugEnabled()) log().debug("initialize: latency thresholder for service '" + m_svcName + "'"); } /** * <p>reinitialize</p> */ public void reinitialize() { //Nothing to do } /** * Responsible for freeing up any resources held by the thresholder. */ public void release() { // Nothing to release... } /** * {@inheritDoc} * * Responsible for performing all necessary initialization for the specified * interface in preparation for thresholding. */ public void initialize(ThresholdNetworkInterface iface, Map<?,?> parameters) { // Get interface address from NetworkInterface // if (iface.getType() != NetworkInterface.TYPE_INET) throw new RuntimeException("Unsupported interface type, only TYPE_INET currently supported"); InetAddress ipAddr = (InetAddress) iface.getAddress(); String groupName = ParameterMap.getKeyedString(parameters, "thresholding-group", "default"); // Get the threshold group's RRD repository path // String repository = null; try { repository = ThresholdingConfigFactory.getInstance().getRrdRepository(groupName); } catch (IllegalArgumentException e) { throw new RuntimeException("Thresholding group '" + groupName + "' does not exist."); } // Add RRD repository as an attribute of the interface for retrieval // by the check() method. // iface.setAttribute(RRD_REPOSITORY_KEY, repository); // Get database connection in order to retrieve the nodeid and // ifIndex from the database for this interface. // java.sql.Connection dbConn = null; final DBUtils d = new DBUtils(getClass()); try { dbConn = DataSourceFactory.getInstance().getConnection(); d.watch(dbConn); } catch (SQLException sqlE) { if (log().isEnabledFor(ThreadCategory.Level.ERROR)) log().error("initialize: Failed getting connection to the database.", sqlE); throw new UndeclaredThrowableException(sqlE); } // Use IP address to lookup the node id // // NOTE: All database calls wrapped in try/finally block so we make // certain that the connection will be closed when we are // finished. // int nodeId = -1; final String hostAddress = InetAddressUtils.str(ipAddr); try { // Prepare & execute the SQL statement to get the 'nodeid', // 'ifIndex' and 'isSnmpPrimary' fields from the ipInterface table. // PreparedStatement stmt = null; try { stmt = dbConn.prepareStatement(SQL_GET_NODEID); d.watch(stmt); stmt.setString(1, hostAddress); // interface address ResultSet rs = stmt.executeQuery(); d.watch(rs); if (rs.next()) { nodeId = rs.getInt(1); if (rs.wasNull()) nodeId = -1; } } catch (SQLException sqle) { if (log().isDebugEnabled()) log().debug("initialize: SQL exception!!", sqle); throw new RuntimeException("SQL exception while attempting to retrieve node id for interface " + hostAddress); } if (log().isDebugEnabled()) log().debug("initialize: db retrieval info: nodeid = " + nodeId + ", address = " + hostAddress); if (nodeId == -1) throw new RuntimeException("Unable to retrieve node id for interface " + hostAddress); } finally { d.cleanUp(); } // Add nodeId as an attribute of the interface for retrieval // by the check() method. // iface.setAttribute(NODE_ID_KEY, new Integer(nodeId)); // Retrieve the collection of Threshold objects associated with // the defined thresholding group and build maps of // ThresholdEntity objects keyed by datasource name. The // datasource type of the threshold determines which // map the threshold entity is added to. // // Each ThresholdEntity can wrap one high Threshold and one low // Threshold castor-generated object for a single datasource. // If more than one high or more than one low threshold is defined // for a single datasource a warning messages is generated. Only // the first threshold in such a scenario will be used for thresholding. // // Create empty map for storing threshold entities Map<String, ThresholdEntity> thresholdMap = new HashMap<String, ThresholdEntity>(); try { for (Basethresholddef thresh : ThresholdingConfigFactory.getInstance().getThresholds(groupName)) { // See if map entry already exists for this datasource // If not, create a new one. boolean newEntity = false; ThresholdEntity thresholdEntity = null; // All latency thresholds are per interface so confirm that // the datasource type is set to "if" // if (!thresh.getDsType().equals("if") && !thresh.getDsType().equals("expr")) { log().warn("initialize: invalid datasource type, latency thresholder only supports interface level datasources."); continue; // continue with the next threshold... } try { BaseThresholdDefConfigWrapper wrapper=BaseThresholdDefConfigWrapper.getConfigWrapper(thresh); // First attempt to lookup the entry in the map thresholdEntity = thresholdMap.get(wrapper.getDatasourceExpression()); // Found entry? if (thresholdEntity == null) { // Nope, create a new one newEntity = true; thresholdEntity = new ThresholdEntity(); } try { thresholdEntity.addThreshold(wrapper); } catch (IllegalStateException e) { log().warn("Encountered duplicate " + thresh.getType() + " for datasource " + wrapper.getDatasourceExpression() + ": " + e, e); } // Add new entity to the map if (newEntity) { thresholdMap.put(wrapper.getDatasourceExpression(), thresholdEntity); } } catch (ThresholdExpressionException e) { log().warn("Could not parse threshold expression: "+e.getMessage(), e); } } } catch (IllegalArgumentException e) { throw new RuntimeException("Thresholding group '" + groupName + "' does not exist."); } // Add threshold maps as attributes for retrieval by the check() method. // iface.setAttribute(THRESHOLD_MAP_KEY, thresholdMap); // Debug // if (log().isDebugEnabled()) { log().debug("initialize: dumping interface thresholds defined for " + hostAddress + "/" + groupName + ":"); Iterator<ThresholdEntity> iter = thresholdMap.values().iterator(); while (iter.hasNext()) log().debug(iter.next().toString()); } if (log().isDebugEnabled()) log().debug("initialize: initialization completed for " + hostAddress); return; } /** * {@inheritDoc} * * Responsible for releasing any resources associated with the specified * interface. */ public void release(ThresholdNetworkInterface iface) { // Nothing to release... } /** * {@inheritDoc} * * Perform threshold checking. */ public int check(ThresholdNetworkInterface iface, EventProxy eproxy, Map<?,?> parameters) { LatencyInterface latIface = new LatencyInterface(iface, m_svcName); LatencyParameters latParms = new LatencyParameters(parameters, m_svcName); try { // Get configuration parameters // // NodeId attribute if (log().isDebugEnabled()) log().debug("check: service= " + m_svcName + " interface= " + latIface.getHostAddress() + " nodeId= " + latIface.getNodeId() + " thresholding-group=" + latParms.getGroupName() + " interval=" + latParms.getInterval() + "ms"); // RRD Repository attribute // // Create empty Events object to hold any threshold // events generated during the thresholding check... Events events = checkRrdDir(latIface, latParms); // Send created events // sendEvents(eproxy, events); // return the status of the threshold check // return THRESHOLDING_SUCCEEDED; } catch(ThresholdingException e) { log().error(e.getMessage()); return e.getFailureCode(); } catch (EventProxyException e) { log().error("check: Failed sending threshold events via event proxy...", e); return THRESHOLDING_FAILED; } } private void sendEvents(EventProxy eproxy, Events events) throws EventProxyException { if (events != null && events.getEventCount() > 0) { Log eventLog = new Log(); eventLog.setEvents(events); eproxy.send(eventLog); } } /** * Performs threshold checking on an directory which contains one or more * RRD files containing latency/response time information. ThresholdEntity * objects are stored for performing threshold checking. * @param latIface TODO * @param latParms TODO * @param parameters * @param iface * @param directory * RRD repository directory * @param m_nodeId * Node identifier of interface being checked * @param ipAddr * IP address of the interface being checked * @param interval * Configured thresholding interval * @param date * Source for timestamp to be used for all generated events * @param thresholdMap * Map of configured interface level ThresholdEntity objects * keyed by datasource name. * @param events * Castor events object containing any events to be generated as * a result of threshold checking. * @throws IllegalArgumentException * if path parameter is not a directory. * @throws ThresholdingException */ Events checkRrdDir(LatencyInterface latIface, LatencyParameters latParms) throws IllegalArgumentException, ThresholdingException { Map<String,ThresholdEntity> thresholdMap = latIface.getThresholdMap(); // Sanity Check if (latIface.getInetAddress() == null || thresholdMap == null) { throw new ThresholdingException("check: Threshold checking failed for " + m_svcName + "/" + latIface.getHostAddress(), THRESHOLDING_FAILED); } Events events = new Events(); Date date = new Date(); for (Iterator<String> it = thresholdMap.keySet().iterator(); it.hasNext();) { String datasource = it.next(); ThresholdEntity threshold = thresholdMap.get(datasource); if (threshold != null) { Double dsValue = threshold.fetchLastValue(latIface, latParms); Map<String, Double> dsValues=new HashMap<String, Double>(); dsValues.put(datasource, dsValue); List<Event> eventList = threshold.evaluateAndCreateEvents(dsValues, date); if (eventList.size() == 0) { // Nothing to do, so continue continue; } completeEventListAndAddToEvents(events, eventList, latIface); /* threshold.evaluateThreshold(dsValue, events, date, latIface); */ } } return events; } private void completeEventListAndAddToEvents(Events events, List<Event> eventList, LatencyInterface latIface) throws ThresholdingException { for (Event event : eventList) { event.setNodeid((long) latIface.getNodeId()); event.setInterfaceAddress(latIface.getInetAddress()); event.setService(latIface.getServiceName()); events.addEvent(event); } } /** * <p>log</p> * * @return a {@link org.opennms.core.utils.ThreadCategory} object. */ public final ThreadCategory log() { return ThreadCategory.getInstance(LatencyThresholder.class); } }