/******************************************************************************* * 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.net.InetAddress; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.EventConstants; import org.opennms.netmgt.config.ThresholdingConfigFactory; import org.opennms.netmgt.eventd.EventIpcManagerFactory; import org.opennms.netmgt.model.events.EventListener; import org.opennms.netmgt.xml.event.Event; import org.opennms.netmgt.xml.event.Parm; import org.opennms.netmgt.xml.event.Value; /** * * @author <a href="mailto:mike@opennms.org">Mike Davidson</a> * @author <a href="http://www.opennms.org/">OpenNMS</a> */ final class BroadcastEventProcessor implements EventListener { /** * List of ThresholdableService objects. */ private final List<ThresholdableService> m_thresholdableServices; private final Threshd m_threshd; /** * This constructor is called to initialize the JMS event receiver. A * connection to the message server is opened and this instance is setup as * the endpoint for broadcast events. When a new event arrives it is * processed and the appropriate action is taken. * * @param thresholdableServices * List of all the ThresholdableService objects scheduled for * thresholding. * */ BroadcastEventProcessor(Threshd threshd, List<ThresholdableService> thresholdableServices) { // Set the configuration for this event // receiver. // m_threshd = threshd; m_thresholdableServices = thresholdableServices; // Create the message selector installMessageSelector(); } /** * Create message selector to set to the subscription */ private void installMessageSelector() { // Create the JMS selector for the UEIs this service is interested in // List<String> ueiList = new ArrayList<String>(); // nodeGainedService ueiList.add(EventConstants.NODE_GAINED_SERVICE_EVENT_UEI); // interfaceIndexChanged // NOTE: No longer interested in this event...if Capsd detects // that in interface's index has changed a // 'reinitializePrimarySnmpInterface' event is generated. // ueiList.add(EventConstants.INTERFACE_INDEX_CHANGED_EVENT_UEI); // primarySnmpInterfaceChanged ueiList.add(EventConstants.PRIMARY_SNMP_INTERFACE_CHANGED_EVENT_UEI); // reinitializePrimarySnmpInterface ueiList.add(EventConstants.REINITIALIZE_PRIMARY_SNMP_INTERFACE_EVENT_UEI); // interfaceReparented ueiList.add(EventConstants.INTERFACE_REPARENTED_EVENT_UEI); // nodeDeleted ueiList.add(EventConstants.NODE_DELETED_EVENT_UEI); // duplicateNodeDeleted ueiList.add(EventConstants.DUP_NODE_DELETED_EVENT_UEI); // interfaceDeleted ueiList.add(EventConstants.INTERFACE_DELETED_EVENT_UEI); // serviceDeleted ueiList.add(EventConstants.SERVICE_DELETED_EVENT_UEI); // scheduled outage configuration change ueiList.add(EventConstants.SCHEDOUTAGES_CHANGED_EVENT_UEI); //thresholds configuration change ueiList.add(EventConstants.THRESHOLDCONFIG_CHANGED_EVENT_UEI); EventIpcManagerFactory.getIpcManager().addEventListener(this, ueiList); } /** * </p> * Closes the current connections to the Java Message Queue if they are * still active. This call may be invoked more than once safely and may be * invoked during object finalization. * </p> * */ synchronized void close() { EventIpcManagerFactory.getIpcManager().removeEventListener(this); } /** * This method may be invoked by the garbage thresholding. Once invoked it * ensures that the <code>close</code> method is called <em>at least</em> * once during the cycle of this object. * * @throws java.lang.Throwable if any. */ protected void finalize() throws Throwable { close(); // ensure it's closed } /** * <p>getName</p> * * @return a {@link java.lang.String} object. */ public String getName() { return "Threshd:BroadcastEventProcessor"; } /** * {@inheritDoc} * * This method is invoked by the JMS topic session when a new event is * available for processing. Currently only text based messages are * processed by this callback. Each message is examined for its Universal * Event Identifier and the appropriate action is taking based on each UEI. */ public void onEvent(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); // print out the uei // if (log.isDebugEnabled()) { log.debug("received event, uei = " + event.getUei()); } if(event.getUei().equals(EventConstants.SCHEDOUTAGES_CHANGED_EVENT_UEI)) { m_threshd.refreshServicePackages(); } else if (event.getUei().equals(EventConstants.THRESHOLDCONFIG_CHANGED_EVENT_UEI)) { thresholdConfigurationChangedHandler(event); } else if(!event.hasNodeid()) { // For all other events, if the event doesn't have a nodeId it can't be processed. log.info("no database node id found, discarding event"); } else if (event.getUei().equals(EventConstants.NODE_GAINED_SERVICE_EVENT_UEI)) { // If there is no interface then it cannot be processed // if (event.getInterface() == null) { log.info("no interface found, discarding event"); } else { nodeGainedServiceHandler(event); } } else if (event.getUei().equals(EventConstants.PRIMARY_SNMP_INTERFACE_CHANGED_EVENT_UEI)) { // If there is no interface then it cannot be processed // if (event.getInterface() == null) { log.info("no interface found, discarding event"); } else { primarySnmpInterfaceChangedHandler(event); } } else if (event.getUei().equals(EventConstants.REINITIALIZE_PRIMARY_SNMP_INTERFACE_EVENT_UEI)) { // If there is no interface then it cannot be processed // if (event.getInterface() == null) { log.info("no interface found, discarding event"); } else { reinitializePrimarySnmpInterfaceHandler(event); } } else if (event.getUei().equals(EventConstants.INTERFACE_REPARENTED_EVENT_UEI)) { // If there is no interface then it cannot be processed // if (event.getInterface() == null) { log.info("no interface found, discarding event"); } else { interfaceReparentedHandler(event); } } // NEW NODE OUTAGE EVENTS else if (event.getUei().equals(EventConstants.NODE_DELETED_EVENT_UEI) || event.getUei().equals(EventConstants.DUP_NODE_DELETED_EVENT_UEI)) { nodeDeletedHandler(event); } else if (event.getUei().equals(EventConstants.INTERFACE_DELETED_EVENT_UEI)) { // If there is no interface then it cannot be processed // if (event.getInterface() == null) { log.info("no interface found, discarding event"); } else { interfaceDeletedHandler(event); } } else if (event.getUei().equals(EventConstants.SERVICE_DELETED_EVENT_UEI)) { // If there is no interface then it cannot be processed // if (event.getInterface() == null) { log.info("no interface found, discarding event"); } else if (event.getService() == null || event.getService().length() == 0) { // If there is no service then it cannot be processed // log.info("no service found, discarding event"); } else { serviceDeletedHandler(event); } } } // end onEvent() /** * Process the event. * * This event is generated when a managed node which supports SNMP gains a * new interface. In this situation the ThresholdableService object * representing the primary SNMP interface of the node must be * reinitialized. * * The ThresholdableService object associated with the primary SNMP * interface for the node will be marked for reinitialization. * Reinitializing the ThresholdableService object consists of calling the * ServiceThresholder.release() method followed by the * ServiceThresholder.initialize() method which will refresh attributes such * as the interface key list and number of interfaces (both of which most * likely have changed). * * Reinitialization will take place the next time the ThresholdableService * is popped from an interval queue for thresholding. * * If any errors occur scheduling the service no error is returned. * * @param event * The event to process. */ private void reinitializePrimarySnmpInterfaceHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); if (event.getInterface() == null) { log.error("reinitializePrimarySnmpInterface event is missing an interface."); return; } // Mark the primary SNMP interface for reinitialization in // order to update any modified attributes associated with // the collectable service.. // // Iterate over the ThresholdableService objects in the // updates map and mark any which have the same interface // address for reinitialization // synchronized (m_thresholdableServices) { for (ThresholdableService tSvc : m_thresholdableServices) { InetAddress addr = tSvc.getAddress(); if (addr.equals(event.getInterfaceAddress())) { synchronized (tSvc) { // Got a match! Retrieve the ThresholderUpdates object // associated // with this ThresholdableService. ThresholderUpdates updates = tSvc.getThresholderUpdates(); // Now set the reinitialization flag updates.markForReinitialization(); if (log.isDebugEnabled()) log.debug("markServicesForReinit: marking " + event.getInterface() + " for reinitialization for service SNMP."); } } } } } /** * Process the event. * * This event is generated when the threshold configuration files are modified. * In this situation the ThresholdableService object * representing the primary SNMP interface of the node must be * reinitialized. * * The ThresholdableService object associated with the primary SNMP * interface for the node will be marked for reinitialization. * Reinitializing the ThresholdableService object consists of calling the * ServiceThresholder.release() method followed by the * ServiceThresholder.initialize() method which will refresh various attributes * * Reinitialization will take place the next time the ThresholdableService * is popped from an interval queue for thresholding. * * If any errors occur scheduling the service no error is returned. * * @param event * The event to process. */ private void thresholdConfigurationChangedHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); //Force a reload of the configuration, then tell the thresholders to reinitialize try { ThresholdingConfigFactory.reload(); } catch (Throwable e) { log.error("thresholdConfigurationChangedHandler: Failed to reload threshold configuration because "+e.getMessage(), e); return; //Do nothing else - the config is borked, so we carry on with what we've got which should still be relatively ok } //Tell the service thresholders to reinit m_threshd.reinitializeThresholders(); //Mark *all* thresholdable Services for reinit (very similar to reinitializePrimarySnmpInterfaceHandler but without the interface check) synchronized (m_thresholdableServices) { for (ThresholdableService tSvc : m_thresholdableServices) { InetAddress addr = (InetAddress) tSvc.getAddress(); synchronized (tSvc) { ThresholderUpdates updates = tSvc.getThresholderUpdates(); updates.markForReinitialization(); if (log.isDebugEnabled()) log.debug("thresholdConfigurationChangedHandler: marking " + InetAddressUtils.str(addr) + " for reinitialization for service SNMP."); } } } } /** * Process the event, construct a new ThresholdableService object * representing the node/interface combination, and schedule the interface * for thresholding. * * If any errors occur scheduling the interface no error is returned. * * @param event * The event to process. * */ private void nodeGainedServiceHandler(Event event) { // Currently only support SNMP data thresholding. // if (!event.getService().equals("SNMP")) return; // Schedule the new service... // m_threshd.scheduleService(event.getNodeid().intValue(), event.getInterface(), event.getService(), false); } /** * Process the 'primarySnmpInterfaceChanged' event. * * Extract the old and new primary SNMP interface addresses from the event * parms. Any ThresholdableService objects located in the collectable * services list which match the IP address of the old primary interface and * have a service name of "SNMP" are flagged for deletion. This will ensure * that the old primary interface is no longer collected against. * * Finally the new primary SNMP interface is scheduled. The packages are * examined and new ThresholdableService objects are created, initialized * and scheduled for thresholding. * * @param event * The event to process. * */ private void primarySnmpInterfaceChangedHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); if (log.isDebugEnabled()) log.debug("primarySnmpInterfaceChangedHandler: processing primary SNMP interface changed event..."); // Extract the old and new primary SNMP interface addresses from the // event parms. // String oldPrimaryIfAddr = null; @SuppressWarnings("unused") String newPrimaryIfAddr = null; for (final Parm parm : event.getParmCollection()) { final String parmName = parm.getParmName(); final Value parmValue = parm.getValue(); final String parmContent; if (parmValue == null) { continue; } else { parmContent = parmValue.getContent(); } // old primary SNMP interface (optional parameter) if (parmName.equals(EventConstants.PARM_OLD_PRIMARY_SNMP_ADDRESS)) { oldPrimaryIfAddr = parmContent; } // new primary SNMP interface (optional parameter) else if (parmName.equals(EventConstants.PARM_NEW_PRIMARY_SNMP_ADDRESS)) { newPrimaryIfAddr = parmContent; } } if (oldPrimaryIfAddr != null) { // Mark the service for deletion so that it will not be rescheduled // for // thresholding. // // Iterate over the ThresholdableService objects in the service // updates map // and mark any which have the same interface address as the old // primary SNMP interface and a service name of "SNMP" for deletion. // synchronized (m_thresholdableServices) { ThresholdableService tSvc = null; ListIterator<ThresholdableService> liter = m_thresholdableServices.listIterator(); while (liter.hasNext()) { tSvc = liter.next(); InetAddress addr = (InetAddress) tSvc.getAddress(); oldPrimaryIfAddr = InetAddressUtils.normalize(oldPrimaryIfAddr); if (InetAddressUtils.str(addr).equals(oldPrimaryIfAddr)) { synchronized (tSvc) { // Got a match! Retrieve the ThresholderUpdates // object associated // with this ThresholdableService. ThresholderUpdates updates = tSvc.getThresholderUpdates(); // Now set the deleted flag updates.markForDeletion(); if (log.isDebugEnabled()) log.debug("primarySnmpInterfaceChangedHandler: marking " + oldPrimaryIfAddr + " as deleted for service SNMP."); } // Now safe to remove the collectable service from // the collectable services list liter.remove(); } } } } // Now we can schedule the new service... // m_threshd.scheduleService(event.getNodeid().intValue(), event.getInterface(), event.getService(), false); if (log.isDebugEnabled()) log.debug("primarySnmpInterfaceChangedHandler: processing of primarySnmpInterfaceChanged event for nodeid " + event.getNodeid() + " completed."); } /** * This method is responsible for processing 'interfacReparented' events. An * 'interfaceReparented' event will have old and new nodeId parms associated * with it. All ThresholdableService objects in the service updates map * which match the event's interface address and the SNMP service have a * reparenting update associated with them. When the scheduler next pops one * of these services from an interval queue for thresholding all of the RRDs * associated with the old nodeId are moved under the new nodeId and the * nodeId of the collectable service is updated to reflect the interface's * new parent nodeId. * * @param event * The event to process. * */ private void interfaceReparentedHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); if (log.isDebugEnabled()) log.debug("interfaceReparentedHandler: processing interfaceReparented event for " + event.getInterface()); // Verify that the event has an interface associated with it if (event.getInterface() == null) return; // Extract the old and new nodeId's from the event parms String oldNodeIdStr = null; String newNodeIdStr = null; String parmName = null; Value parmValue = null; String parmContent = null; for (Parm parm : event.getParmCollection()) { parmName = parm.getParmName(); parmValue = parm.getValue(); if (parmValue == null) continue; else parmContent = parmValue.getContent(); // old nodeid if (parmName.equals(EventConstants.PARM_OLD_NODEID)) { oldNodeIdStr = parmContent; } // new nodeid else if (parmName.equals(EventConstants.PARM_NEW_NODEID)) { newNodeIdStr = parmContent; } } // Only proceed provided we have both an old and a new nodeId // if (oldNodeIdStr == null || newNodeIdStr == null) { log.warn("interfaceReparentedHandler: old and new nodeId parms are required, unable to process."); return; } // Iterate over the ThresholdableService objects in the services // list looking for entries which share the same interface // address as the reparented interface. Mark any matching objects // for reparenting. // // The next time the service is scheduled for execution it // will move all of the RRDs associated // with the old nodeId under the new nodeId and update the service's // SnmpMonitor.NodeInfo attribute to reflect the new nodeId. All // subsequent thresholdings will then be updating the appropriate RRDs. // //unused - commented out //boolean isPrimarySnmpInterface = false; synchronized (m_thresholdableServices) { ThresholdableService tSvc = null; Iterator<ThresholdableService> iter = m_thresholdableServices.iterator(); while (iter.hasNext()) { tSvc = iter.next(); InetAddress addr = (InetAddress) tSvc.getAddress(); if (addr.equals(event.getInterfaceAddress())) { synchronized (tSvc) { // Got a match! if (log.isDebugEnabled()) log.debug("interfaceReparentedHandler: got a ThresholdableService match for " + event.getInterface()); // Retrieve the ThresholderUpdates object associated // with this ThresholdableService. ThresholderUpdates updates = tSvc.getThresholderUpdates(); // Now set the reparenting flag updates.markForReparenting(oldNodeIdStr, newNodeIdStr); if (log.isDebugEnabled()) log.debug("interfaceReparentedHandler: marking " + event.getInterface() + " for reparenting for service SNMP."); } } } } if (log.isDebugEnabled()) log.debug("interfaceReparentedHandler: processing of interfaceReparented event for interface " + event.getInterface() + " completed."); } /** * This method is responsible for handling nodeDeleted events. * * @param event * The event to process. * */ private void nodeDeletedHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); long nodeId = event.getNodeid(); // Iterate over the collectable service list and mark any entries // which match the deleted nodeId for deletion. synchronized (m_thresholdableServices) { ThresholdableService tSvc = null; ListIterator<ThresholdableService> liter = m_thresholdableServices.listIterator(); while (liter.hasNext()) { tSvc = liter.next(); // Only interested in entries with matching nodeId if (!(tSvc.getNodeId() == nodeId)) continue; synchronized (tSvc) { // Retrieve the ThresholderUpdates object associated // with this ThresholdableService. ThresholderUpdates updates = tSvc.getThresholderUpdates(); // Now set the update's deletion flag so the next // time it is selected for execution by the scheduler // the thresholding will be skipped and the service will not // be rescheduled. updates.markForDeletion(); } // Now safe to remove the collectable service from // the collectable services list liter.remove(); } } if (log.isDebugEnabled()) log.debug("nodeDeletedHandler: processing of nodeDeleted event for nodeid " + nodeId + " completed."); } /** * This method is responsible for handling interfaceDeleted events. * * @param event * The event to process. * */ private void interfaceDeletedHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); long nodeId = event.getNodeid(); InetAddress ipAddr = event.getInterfaceAddress(); // Iterate over the collectable services list and mark any entries // which match the deleted nodeId/IP address pair for deletion synchronized (m_thresholdableServices) { ThresholdableService tSvc = null; ListIterator<ThresholdableService> liter = m_thresholdableServices.listIterator(); while (liter.hasNext()) { tSvc = liter.next(); // Only interested in entries with matching nodeId and IP // address InetAddress addr = (InetAddress) tSvc.getAddress(); if (!(tSvc.getNodeId() == nodeId && addr.equals(ipAddr))) continue; synchronized (tSvc) { // Retrieve the ThresholderUpdates object associated with // this ThresholdableService if one exists. ThresholderUpdates updates = tSvc.getThresholderUpdates(); // Now set the update's deletion flag so the next // time it is selected for execution by the scheduler // the thresholding will be skipped and the service will not // be rescheduled. updates.markForDeletion(); } // Now safe to remove the collectable service from // the collectable services list liter.remove(); } } if (log.isDebugEnabled()) log.debug("interfaceDeletedHandler: processing of interfaceDeleted event for " + nodeId + "/" + ipAddr + " completed."); } /** * This method is responsible for handling serviceDeleted events. * * @param event * The event to process. * */ private void serviceDeletedHandler(Event event) { ThreadCategory log = ThreadCategory.getInstance(getClass()); // Currently only support SNMP data thresholding. // if (!event.getService().equals("SNMP")) return; long nodeId = event.getNodeid(); InetAddress ipAddr = event.getInterfaceAddress(); String svcName = event.getService(); // Iterate over the collectable services list and mark any entries // which match the nodeId/ipAddr of the deleted service // for deletion. synchronized (m_thresholdableServices) { ThresholdableService tSvc = null; ListIterator<ThresholdableService> liter = m_thresholdableServices.listIterator(); while (liter.hasNext()) { tSvc = liter.next(); // Only interested in entries with matching nodeId, IP address // and service InetAddress addr = (InetAddress) tSvc.getAddress(); if (!(tSvc.getNodeId() == nodeId && addr.equals(ipAddr)) && tSvc.getServiceName().equals(svcName)) continue; synchronized (tSvc) { // Retrieve the ThresholderUpdates object associated with // this ThresholdableService if one exists. ThresholderUpdates updates = tSvc.getThresholderUpdates(); // Now set the update's deletion flag so the next // time it is selected for execution by the scheduler // the thresholding will be skipped and the service will not // be rescheduled. updates.markForDeletion(); } // Now safe to remove the collectable service from // the collectable services list liter.remove(); } } if (log.isDebugEnabled()) log.debug("serviceDeletedHandler: processing of serviceDeleted event for " + nodeId + "/" + ipAddr + "/" + svcName + " completed."); } } // end class