/******************************************************************************* * 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.collectd; import static org.opennms.core.utils.InetAddressUtils.str; import java.net.InetAddress; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.opennms.core.utils.ConfigFileConstants; import org.opennms.core.utils.LogUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.EventConstants; import org.opennms.netmgt.capsd.EventUtils; import org.opennms.netmgt.capsd.InsufficientInformationException; import org.opennms.netmgt.config.CollectdConfigFactory; import org.opennms.netmgt.config.CollectdPackage; import org.opennms.netmgt.config.SnmpEventInfo; import org.opennms.netmgt.config.SnmpPeerFactory; import org.opennms.netmgt.config.ThreshdConfigFactory; import org.opennms.netmgt.config.ThresholdingConfigFactory; import org.opennms.netmgt.config.collectd.Collector; import org.opennms.netmgt.daemon.AbstractServiceDaemon; import org.opennms.netmgt.dao.CollectorConfigDao; import org.opennms.netmgt.dao.IpInterfaceDao; import org.opennms.netmgt.dao.NodeDao; import org.opennms.netmgt.eventd.EventIpcManager; import org.opennms.netmgt.model.AbstractEntityVisitor; import org.opennms.netmgt.model.OnmsIpInterface; import org.opennms.netmgt.model.OnmsMonitoredService; import org.opennms.netmgt.model.OnmsNode; import org.opennms.netmgt.model.events.EventBuilder; import org.opennms.netmgt.model.events.EventListener; import org.opennms.netmgt.scheduler.LegacyScheduler; import org.opennms.netmgt.scheduler.ReadyRunnable; import org.opennms.netmgt.scheduler.Scheduler; import org.opennms.netmgt.xml.event.Event; import org.opennms.netmgt.xml.event.Parm; import org.opennms.netmgt.xml.event.Value; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * <p>Collectd class.</p> * * @author ranger * @version $Id: $ */ public class Collectd extends AbstractServiceDaemon implements EventListener { private static CollectdInstrumentation s_instrumentation = null; /** * <p>instrumentation</p> * * @return a {@link org.opennms.netmgt.collectd.CollectdInstrumentation} object. */ public static CollectdInstrumentation instrumentation() { if (s_instrumentation == null) { String className = System.getProperty("org.opennms.collectd.instrumentationClass", DefaultCollectdInstrumentation.class.getName()); try { s_instrumentation = (CollectdInstrumentation) ClassUtils.forName(className, Thread.currentThread().getContextClassLoader()).newInstance(); } catch (Throwable e) { s_instrumentation = new DefaultCollectdInstrumentation(); } } return s_instrumentation; } /** * Log4j category */ private final static String LOG4J_CATEGORY = "OpenNMS.Collectd"; /** * Instantiated service collectors specified in config file */ private final Map<String,ServiceCollector> m_collectors = new HashMap<String,ServiceCollector>(4); /** * List of all CollectableService objects. */ private final List<CollectableService> m_collectableServices; /** * Reference to the collection scheduler */ private volatile Scheduler m_scheduler; /** * Indicates if scheduling of existing interfaces has been completed */ private volatile CollectorConfigDao m_collectorConfigDao; private volatile IpInterfaceDao m_ifaceDao; static class SchedulingCompletedFlag { volatile boolean m_schedulingCompleted = false; public synchronized void setSchedulingCompleted( boolean schedulingCompleted) { m_schedulingCompleted = schedulingCompleted; } public synchronized boolean isSchedulingCompleted() { return m_schedulingCompleted; } } private final SchedulingCompletedFlag m_schedulingCompletedFlag = new SchedulingCompletedFlag(); private volatile EventIpcManager m_eventIpcManager; private volatile TransactionTemplate m_transTemplate; private volatile NodeDao m_nodeDao; /** * Constructor. */ public Collectd() { super(LOG4J_CATEGORY); m_collectableServices = Collections.synchronizedList(new LinkedList<CollectableService>()); } /** * <p>onInit</p> */ protected void onInit() { Assert.notNull(m_collectorConfigDao, "collectorConfigDao must not be null"); Assert.notNull(m_eventIpcManager, "eventIpcManager must not be null"); Assert.notNull(m_transTemplate, "transTemplate must not be null"); Assert.notNull(m_ifaceDao, "ifaceDao must not be null"); Assert.notNull(m_nodeDao, "nodeDao must not be null"); log().debug("init: Initializing collection daemon"); // make sure the instrumentation gets initialized instrumentation(); instantiateCollectors(); getScheduler().schedule(0, ifScheduler()); installMessageSelectors(); } private void installMessageSelectors() { // Add the EventListeners for the UEIs in which this service is // interested List<String> ueiList = new ArrayList<String>(); // nodeGainedService ueiList.add(EventConstants.NODE_GAINED_SERVICE_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); // outageConfigurationChanged ueiList.add(EventConstants.SCHEDOUTAGES_CHANGED_EVENT_UEI); // configureSNMP ueiList.add(EventConstants.CONFIGURE_SNMP_EVENT_UEI); // thresholds configuration change ueiList.add(EventConstants.THRESHOLDCONFIG_CHANGED_EVENT_UEI); // daemon configuration change ueiList.add(EventConstants.RELOAD_DAEMON_CONFIG_UEI); // node category membership changes ueiList.add(EventConstants.NODE_CATEGORY_MEMBERSHIP_CHANGED_EVENT_UEI); getEventIpcManager().addEventListener(this, ueiList); } /** * <p>setEventIpcManager</p> * * @param eventIpcManager a {@link org.opennms.netmgt.eventd.EventIpcManager} object. */ public void setEventIpcManager(EventIpcManager eventIpcManager) { m_eventIpcManager = eventIpcManager; } /** * <p>getEventIpcManager</p> * * @return a {@link org.opennms.netmgt.eventd.EventIpcManager} object. */ public EventIpcManager getEventIpcManager() { return m_eventIpcManager; } private ReadyRunnable ifScheduler() { // Schedule existing interfaces for data collection ReadyRunnable interfaceScheduler = new ReadyRunnable() { public boolean isReady() { return true; } public void run() { final String prefix = ThreadCategory.getPrefix(); try { ThreadCategory.setPrefix(LOG4J_CATEGORY); scheduleExistingInterfaces(); } catch (SQLException e) { log().error( "start: Failed to schedule existing interfaces", e); } finally { setSchedulingCompleted(true); ThreadCategory.setPrefix(prefix); } } }; return interfaceScheduler; } private void createScheduler() { // Create a scheduler try { log().debug("init: Creating collectd scheduler"); setScheduler(new LegacyScheduler( "Collectd", getCollectorConfigDao().getSchedulerThreads())); } catch (RuntimeException e) { log().fatal("init: Failed to create collectd scheduler", e); throw e; } } /** {@inheritDoc} */ @Override protected void onStart() { // start the scheduler try { log().debug("start: Starting collectd scheduler"); getScheduler().start(); } catch (RuntimeException e) { log().fatal("start: Failed to start scheduler", e); throw e; } } /** {@inheritDoc} */ @Override protected void onStop() { getScheduler().stop(); deinstallMessageSelectors(); setScheduler(null); } /** {@inheritDoc} */ @Override protected void onPause() { getScheduler().pause(); } /** {@inheritDoc} */ @Override protected void onResume() { getScheduler().resume(); } /** * Schedule existing interfaces for data collection. * * @throws SQLException * if database errors encountered. */ private void scheduleExistingInterfaces() throws SQLException { instrumentation().beginScheduleExistingInterfaces(); try { m_transTemplate.execute(new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { // Loop through collectors and schedule for each one present for(String name : getCollectorNames()) { scheduleInterfacesWithService(name); } return null; } }); } finally { instrumentation().endScheduleExistingInterfaces(); } } private void scheduleInterfacesWithService(String svcName) { instrumentation().beginScheduleInterfacesWithService(svcName); try { log().info("scheduleInterfacesWithService: svcName = " + svcName); Collection<OnmsIpInterface> ifsWithServices = findInterfacesWithService(svcName); for (OnmsIpInterface iface : ifsWithServices) { scheduleInterface(iface, svcName, true); } } finally { instrumentation().endScheduleInterfacesWithService(svcName); } } private Collection<OnmsIpInterface> findInterfacesWithService(String svcName) { instrumentation().beginFindInterfacesWithService(svcName); int count = -1; try { Collection<OnmsIpInterface> ifaces = getIpInterfaceDao().findByServiceType(svcName); count = ifaces.size(); return ifaces; } finally { instrumentation().endFindInterfacesWithService(svcName, count); } } /** * This method is responsible for scheduling the specified * node/address/svcname tuple for data collection. * * @param nodeId * Node id * @param ipAddress * IP address * @param svcName * Service name * @param existing * True if called by scheduleExistingInterfaces(), false * otheriwse */ private void scheduleInterface(int nodeId, String ipAddress, String svcName, boolean existing) { OnmsIpInterface iface = getIpInterface(nodeId, ipAddress); if (iface == null) { log().error("Unable to find interface with address "+ipAddress+" on node "+nodeId); return; } OnmsMonitoredService svc = iface.getMonitoredServiceByServiceType(svcName); if (svc == null) { log().error("Unable to find service "+svcName+" on interface with address "+ipAddress+" on node "+nodeId); return; } scheduleInterface(iface, svc.getServiceType().getName(), existing); } private void scheduleNode(final int nodeId, final boolean existing) { getCollectorConfigDao().rebuildPackageIpListMap(); OnmsNode node = m_nodeDao.getHierarchy(nodeId); node.visit(new AbstractEntityVisitor() { @Override public void visitMonitoredService(OnmsMonitoredService monSvc) { scheduleInterface(monSvc.getIpInterface(), monSvc.getServiceName(), existing); } }); } private OnmsIpInterface getIpInterface(int nodeId, String ipAddress) { OnmsNode node = m_nodeDao.load(nodeId); OnmsIpInterface iface = node.getIpInterfaceByIpAddress(ipAddress); return iface; } private void scheduleInterface(OnmsIpInterface iface, String svcName, boolean existing) { final String ipAddress = str(iface.getIpAddress()); if (ipAddress == null) { LogUtils.warnf(this, "Unable to schedule interface %s, could not determine IP address.", iface); return; } instrumentation().beginScheduleInterface(iface.getNode().getId(), ipAddress, svcName); try { Collection<CollectionSpecification> matchingSpecs = getSpecificationsForInterface(iface, svcName); StringBuffer sb; if (log().isDebugEnabled()) { sb = new StringBuffer(); sb.append("scheduleInterface: found "); sb.append(Integer.toString(matchingSpecs.size())); sb.append(" matching specs for interface: "); sb.append(iface); log().debug(sb.toString()); } for (CollectionSpecification spec : matchingSpecs) { if (existing == false) { /* * It is possible that both a nodeGainedService and a * primarySnmpInterfaceChanged event are generated for an * interface during a rescan. To handle this scenario we must * verify that the ipAddress/pkg pair identified by this event * does not already exist in the collectable services list. */ if (alreadyScheduled(iface, spec)) { if (log().isDebugEnabled()) { sb = new StringBuffer(); sb.append("scheduleInterface: svc/pkgName "); sb.append(iface); sb.append('/'); sb.append(spec); sb.append(" already in collectable service list, skipping."); log().debug(sb.toString()); } continue; } } try { /* * Criteria checks have all passed. The interface/service pair * can be scheduled. */ if (log().isDebugEnabled()) { sb = new StringBuffer(); sb.append("scheduleInterface: now scheduling interface: "); sb.append(iface); sb.append('/'); sb.append(svcName); log().debug(sb.toString()); } CollectableService cSvc = null; /* * Create a new SnmpCollector object representing this node, * interface, service and package pairing */ cSvc = new CollectableService(iface, m_ifaceDao, spec, getScheduler(), m_schedulingCompletedFlag, m_transTemplate.getTransactionManager()); // Add new collectable service to the collectable service list. m_collectableServices.add(cSvc); // Schedule the collectable service for immediate collection getScheduler().schedule(0, cSvc.getReadyRunnable()); if (log().isDebugEnabled()) { sb = new StringBuffer(); sb.append("scheduleInterface: "); sb.append(iface); sb.append('/'); sb.append(svcName); sb.append(" collection, scheduled"); log().debug(sb.toString()); } } catch (CollectionInitializationException e) { sb = new StringBuffer(); sb.append("scheduleInterface: Unable to schedule "); sb.append(iface); sb.append('/'); sb.append(svcName); sb.append(", reason: "); sb.append(e.getMessage()); // Only log the stack trace if TRACE level logging is enabled. // Fixes bug NMS-3324. // http://issues.opennms.org/browse/NMS-3324 if (log().isTraceEnabled()) { log().trace(sb.toString(), e); } else { log().info(sb.toString()); } } catch (Throwable t) { sb = new StringBuffer(); sb.append("scheduleInterface: Uncaught exception, failed to schedule interface "); sb.append(iface); sb.append('/'); sb.append(svcName); sb.append(". "); sb.append(t); log().error(sb.toString(), t); } } // end while more specifications exist } finally { instrumentation().endScheduleInterface(iface.getNode().getId(), ipAddress, svcName); } } /** * <p>getSpecificationsForInterface</p> * * @param iface a {@link org.opennms.netmgt.model.OnmsIpInterface} object. * @param svcName a {@link java.lang.String} object. * @return a {@link java.util.Collection} object. */ public Collection<CollectionSpecification> getSpecificationsForInterface(OnmsIpInterface iface, String svcName) { Collection<CollectionSpecification> matchingPkgs = new LinkedList<CollectionSpecification>(); /* * Compare interface/service pair against each collectd package * For each match, create new SnmpCollector object and * schedule it for collection */ for(CollectdPackage wpkg : getCollectorConfigDao().getPackages()) { /* * Make certain the the current service is in the package * and enabled! */ if (!wpkg.serviceInPackageAndEnabled(svcName)) { if (log().isDebugEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("getSpecificationsForInterface: address/service: "); sb.append(iface); sb.append("/"); sb.append(svcName); sb.append(" not scheduled, service is not enabled or does not exist in package: "); sb.append(wpkg.getName()); log().debug(sb.toString()); } continue; } // Is the interface in the package? final String ipAddress = str(iface.getIpAddress()); if (!wpkg.interfaceInPackage(ipAddress)) { if (log().isDebugEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("getSpecificationsForInterface: address/service: "); sb.append(iface); sb.append("/"); sb.append(svcName); sb.append(" not scheduled, interface does not belong to package: "); sb.append(wpkg.getName()); log().debug(sb.toString()); } continue; } if (log().isDebugEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("getSpecificationsForInterface: address/service: "); sb.append(iface); sb.append("/"); sb.append(svcName); sb.append(" scheduled, interface does belong to package: "); sb.append(wpkg.getName()); log().debug(sb.toString()); } matchingPkgs.add(new CollectionSpecification(wpkg, svcName, getServiceCollector(svcName))); } return matchingPkgs; } /** * Returns true if specified address/pkg pair is already represented in * the collectable services list. False otherwise. * * @param iface * TODO * @param spec * TODO * @param svcName * TODO */ private boolean alreadyScheduled(OnmsIpInterface iface, CollectionSpecification spec) { String ipAddress = str(iface.getIpAddress()); if (ipAddress == null) { LogUtils.warnf(this, "Cannot determine if interface %s is already scheduled. Unable to look up IP address.", iface); return false; } String svcName = spec.getServiceName(); String pkgName = spec.getPackageName(); StringBuffer sb; boolean isScheduled = false; if (log().isDebugEnabled()) { sb = new StringBuffer(); sb.append("alreadyScheduled: determining if interface: "); sb.append(iface); sb.append(" is already scheduled."); } synchronized (m_collectableServices) { for (CollectableService cSvc : m_collectableServices) { InetAddress addr = (InetAddress) cSvc.getAddress(); if (str(addr).equals(ipAddress) && cSvc.getPackageName().equals(pkgName) && cSvc.getServiceName().equals(svcName)) { isScheduled = true; break; } } } if (log().isDebugEnabled()) { sb = new StringBuffer(); sb.append("alreadyScheduled: interface "); sb.append(iface); sb.append("already scheduled check: "); sb.append(isScheduled); } return isScheduled; } /** * @param schedulingCompleted * The schedulingCompleted to set. */ private void setSchedulingCompleted(boolean schedulingCompleted) { m_schedulingCompletedFlag.setSchedulingCompleted(schedulingCompleted); } private void refreshServicePackages() { for (CollectableService thisService : m_collectableServices) { thisService.refreshPackage(getCollectorConfigDao()); } } private List<CollectableService> getCollectableServices() { return m_collectableServices; } /** * {@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(final Event event) { String prefix = ThreadCategory.getPrefix(); try { ThreadCategory.setPrefix(getName()); m_transTemplate.execute(new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { onEventInTransaction(event); return null; } }); } finally { ThreadCategory.setPrefix(prefix); } } private void onEventInTransaction(Event event) { // print out the uei // log().debug("received event, uei = " + event.getUei()); try { if (event.getUei().equals(EventConstants.SCHEDOUTAGES_CHANGED_EVENT_UEI)) { handleScheduledOutagesChanged(event); } else if (event.getUei().equals(EventConstants.CONFIGURE_SNMP_EVENT_UEI)) { handleConfigureSNMP(event); } else if (event.getUei().equals(EventConstants.NODE_GAINED_SERVICE_EVENT_UEI)) { handleNodeGainedService(event); } else if (event.getUei().equals(EventConstants.PRIMARY_SNMP_INTERFACE_CHANGED_EVENT_UEI)) { handlePrimarySnmpInterfaceChanged(event); } else if (event.getUei().equals(EventConstants.REINITIALIZE_PRIMARY_SNMP_INTERFACE_EVENT_UEI)) { handleReinitializePrimarySnmpInterface(event); } else if (event.getUei().equals(EventConstants.INTERFACE_REPARENTED_EVENT_UEI)) { handleInterfaceReparented(event); } else if (event.getUei().equals(EventConstants.NODE_DELETED_EVENT_UEI)) { handleNodeDeleted(event); } else if (event.getUei().equals(EventConstants.DUP_NODE_DELETED_EVENT_UEI)) { handleDupNodeDeleted(event); } else if (event.getUei().equals(EventConstants.INTERFACE_DELETED_EVENT_UEI)) { handleInterfaceDeleted(event); } else if (event.getUei().equals(EventConstants.SERVICE_DELETED_EVENT_UEI)) { handleServiceDeleted(event); } else if (event.getUei().equals(EventConstants.RELOAD_DAEMON_CONFIG_UEI)) { handleReloadDaemonConfig(event); } else if (event.getUei().equals(EventConstants.NODE_CATEGORY_MEMBERSHIP_CHANGED_EVENT_UEI)) { handleNodeCategoryMembershipChanged(event); } } catch (InsufficientInformationException e) { handleInsufficientInfo(e); } } /** * <p>handleInsufficientInfo</p> * * @param e a {@link org.opennms.netmgt.capsd.InsufficientInformationException} object. */ protected void handleInsufficientInfo(InsufficientInformationException e) { log().info(e.getMessage()); } private void handleDupNodeDeleted(Event event) throws InsufficientInformationException { handleNodeDeleted(event); } private void handleScheduledOutagesChanged(Event event) { try { log().info("Reloading Collectd config factory"); CollectdConfigFactory.reload(); refreshServicePackages(); } catch (Throwable e) { log().error( "Failed to reload CollectdConfigFactory because " + e.getMessage(), e); } } /** * </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> */ private void deinstallMessageSelectors() { getEventIpcManager().removeEventListener(this); } /** * This method is responsible for handling configureSNMP events. * * @param event * The event to process. */ private void handleConfigureSNMP(final Event event) { if (log().isDebugEnabled()) { log().debug("configureSNMPHandler: processing configure SNMP event..."+event); } SnmpEventInfo info = null; try { info = new SnmpEventInfo(event); if (StringUtils.isBlank(info.getFirstIPAddress())) { log().error("configureSNMPHandler: event contained invalid firstIpAddress. "+event); return; } log().debug("configureSNMPHandler: processing configure SNMP event: "+info); SnmpPeerFactory.getInstance().define(info); SnmpPeerFactory.saveCurrent(); log().debug("configureSNMPHandler: process complete. "+info); } catch (Throwable e) { log().error("configureSNMPHandler: ",e); } } /** * This method is responsible for handling interfaceDeleted events. * * @param event * The event to process. * @throws InsufficientInformationException */ private void handleInterfaceDeleted(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); ThreadCategory log = log(); String ipAddr = event.getInterface(); if(EventUtils.isNonIpInterface(ipAddr) ) { log().debug("handleInterfaceDeleted: the deleted interface was a non-ip interface. Nothing to do here."); return; } Long nodeId = event.getNodeid(); // Iterate over the collectable services list and mark any entries // which match the deleted nodeId/IP address pair for deletion synchronized (getCollectableServices()) { CollectableService cSvc = null; ListIterator<CollectableService> liter = getCollectableServices().listIterator(); while (liter.hasNext()) { cSvc = liter.next(); // Only interested in entries with matching nodeId and IP // address InetAddress addr = (InetAddress) cSvc.getAddress(); if (!(cSvc.getNodeId() == nodeId && addr.getHostName().equals(ipAddr))) continue; synchronized (cSvc) { // Retrieve the CollectorUpdates object associated with // this CollectableService if one exists. CollectorUpdates updates = cSvc.getCollectorUpdates(); // Now set the update's deletion flag so the next // time it is selected for execution by the scheduler // the collection will be skipped and the service will not // be rescheduled. log().debug("Marking CollectableService for deletion because an interface was deleted: Service nodeid="+cSvc.getNodeId()+ ", deleted node:"+nodeId+ "service address:"+addr.getHostName()+ "deleted interface:"+ipAddr); 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 processing 'interfacReparented' events. * An 'interfaceReparented' event will have old and new nodeId parms * associated with it. All CollectableService 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 * collection 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. * @throws InsufficientInformationException */ private void handleInterfaceReparented(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); EventUtils.checkInterface(event); ThreadCategory log = log(); 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 CollectableService 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 collections will then be updating the appropriate RRDs. // OnmsIpInterface iface = null; synchronized (getCollectableServices()) { CollectableService cSvc = null; Iterator<CollectableService> iter = getCollectableServices().iterator(); while (iter.hasNext()) { cSvc = iter.next(); InetAddress addr = (InetAddress) cSvc.getAddress(); if (addr.equals(event.getInterfaceAddress())) { synchronized (cSvc) { // Got a match! if (log.isDebugEnabled()) log.debug("interfaceReparentedHandler: got a CollectableService match for " + event.getInterface()); // Retrieve the CollectorUpdates object associated // with // this CollectableService. CollectorUpdates updates = cSvc.getCollectorUpdates(); if (iface == null) { iface = getIpInterface(event.getNodeid().intValue(), event.getInterface()); } // Now set the reparenting flag updates.markForReparenting(oldNodeIdStr, newNodeIdStr, iface); 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. * @throws InsufficientInformationException */ private void handleNodeDeleted(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); EventUtils.checkInterface(event); ThreadCategory log = log(); Long nodeId = event.getNodeid(); unscheduleNodeAndMarkForDeletion(nodeId); if (log.isDebugEnabled()) log.debug("nodeDeletedHandler: processing of nodeDeleted event for nodeid " + nodeId + " completed."); } /** * This method is responsible for handling nodeDeleted events. * * @param event * The event to process. * @throws InsufficientInformationException */ private void handleNodeCategoryMembershipChanged(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); ThreadCategory log = log(); Long nodeId = event.getNodeid(); unscheduleNodeAndMarkForDeletion(nodeId); if (log.isDebugEnabled()) { log.debug("nodeCategoryMembershipChanged: unscheduling nodeid " + nodeId + " completed."); } scheduleNode(nodeId.intValue(), true); } private void unscheduleNodeAndMarkForDeletion(Long nodeId) { // Iterate over the collectable service list and mark any entries // which match the deleted nodeId for deletion. synchronized (getCollectableServices()) { CollectableService cSvc = null; final ListIterator<CollectableService> liter = getCollectableServices().listIterator(); while (liter.hasNext()) { cSvc = liter.next(); // Only interested in entries with matching nodeId if (!(cSvc.getNodeId() == nodeId)) continue; synchronized (cSvc) { // Retrieve the CollectorUpdates object associated // with this CollectableService. CollectorUpdates updates = cSvc.getCollectorUpdates(); // Now set the update's deletion flag so the next // time it is selected for execution by the scheduler // the collection will be skipped and the service will not // be rescheduled. log().debug("Marking CollectableService for deletion because a node was deleted: Service nodeid="+cSvc.getNodeId()+ ", deleted node:"+nodeId); updates.markForDeletion(); } // Now safe to remove the collectable service from // the collectable services list liter.remove(); } } } /** * Process the event, construct a new CollectableService object * representing the node/interface combination, and schedule the interface * for collection. If any errors occur scheduling the interface no error * is returned. * * @param event * The event to process. * @throws InsufficientInformationException */ private void handleNodeGainedService(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); EventUtils.checkInterface(event); EventUtils.checkService(event); // Schedule the interface // scheduleForCollection(event); } private void handleReloadDaemonConfig(Event event) { final String daemonName = "Threshd"; boolean isTarget = false; for (Parm parm : event.getParmCollection()) { if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName()) && daemonName.equalsIgnoreCase(parm.getValue().getContent())) { isTarget = true; break; } } if (isTarget) { String thresholdsFile = ConfigFileConstants.getFileName(ConfigFileConstants.THRESHOLDING_CONF_FILE_NAME); String threshdFile = ConfigFileConstants.getFileName(ConfigFileConstants.THRESHD_CONFIG_FILE_NAME); String targetFile = thresholdsFile; // Default for (Parm parm : event.getParmCollection()) { if (EventConstants.PARM_CONFIG_FILE_NAME.equals(parm.getParmName()) && threshdFile.equalsIgnoreCase(parm.getValue().getContent())) { targetFile = threshdFile; } } EventBuilder ebldr = null; try { // Reloading Factories if (targetFile.equals(thresholdsFile)) { ThresholdingConfigFactory.reload(); } if (targetFile.equals(threshdFile)) { ThreshdConfigFactory.reload(); ThresholdingConfigFactory.reload(); // This is required if the threshold packages has been changed. } // Sending the threshold configuration change event ebldr = new EventBuilder(EventConstants.THRESHOLDCONFIG_CHANGED_EVENT_UEI, "Collectd"); getEventIpcManager().sendNow(ebldr.getEvent()); // Updating thresholding visitors to use the new configuration log().debug("handleReloadDaemonConfig: Reloading thresholding configuration in collectd"); synchronized (m_collectableServices) { for(CollectableService service: m_collectableServices) { service.reinitializeThresholding(); } } // Preparing successful event ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_SUCCESSFUL_UEI, "Collectd"); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, daemonName); ebldr.addParam(EventConstants.PARM_CONFIG_FILE_NAME, targetFile); } catch (Throwable e) { // Preparing failed event log().error("handleReloadDaemonConfig: Error reloading/processing thresholds configuration: " + e.getMessage(), e); ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, "Collectd"); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, daemonName); ebldr.addParam(EventConstants.PARM_CONFIG_FILE_NAME, targetFile); ebldr.addParam(EventConstants.PARM_REASON, e.getMessage()); } finally { if (ebldr != null) { getEventIpcManager().sendNow(ebldr.getEvent()); } } } } private void scheduleForCollection(Event event) { // This moved to here from the scheduleInterface() for better behavior // during initialization getCollectorConfigDao().rebuildPackageIpListMap(); scheduleInterface(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 * CollectableService 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 CollectableService objects are created, initialized and scheduled * for collection. * * @param event * The event to process. * @throws InsufficientInformationException */ private void handlePrimarySnmpInterfaceChanged(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); EventUtils.checkInterface(event); ThreadCategory log = log(); if (log.isDebugEnabled()) log.debug("primarySnmpInterfaceChangedHandler: processing primary SNMP interface changed event..."); // Currently only support SNMP data collection. // if (!event.getService().equals("SNMP")) return; // Extract the old and new primary SNMP interface addresses from the // event parms. // String oldPrimaryIfAddr = 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 primary SNMP interface (optional parameter) if (parmName.equals(EventConstants.PARM_OLD_PRIMARY_SNMP_ADDRESS)) { oldPrimaryIfAddr = parmContent; } } if (oldPrimaryIfAddr != null) { // Mark the service for deletion so that it will not be // rescheduled // for // collection. // // Iterate over the CollectableService 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 (getCollectableServices()) { CollectableService cSvc = null; ListIterator<CollectableService> liter = getCollectableServices().listIterator(); while (liter.hasNext()) { cSvc = liter.next(); final InetAddress addr = (InetAddress) cSvc.getAddress(); final String addrString = str(addr); if (addrString != null && addrString.equals(oldPrimaryIfAddr)) { synchronized (cSvc) { // Got a match! Retrieve the CollectorUpdates // object // associated // with this CollectableService. CollectorUpdates updates = cSvc.getCollectorUpdates(); // 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... // scheduleForCollection(event); if (log.isDebugEnabled()) log.debug("primarySnmpInterfaceChangedHandler: processing of primarySnmpInterfaceChanged event for nodeid " + event.getNodeid() + " completed."); } /** * Process the event. This event is generated when a managed node which * supports SNMP gains a new interface. In this situation the * CollectableService object representing the primary SNMP interface of * the node must be reinitialized. The CollectableService object * associated with the primary SNMP interface for the node will be marked * for reinitialization. Reinitializing the CollectableService object * consists of calling the ServiceCollector.release() method followed by * the ServiceCollector.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 CollectableService is popped from an interval queue for * collection. If any errors occur scheduling the service no error is * returned. * * @param event * The event to process. * @throws InsufficientInformationException */ private void handleReinitializePrimarySnmpInterface(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); EventUtils.checkInterface(event); Long nodeid = event.getNodeid(); String ipAddress = event.getInterface(); // Mark the primary SNMP interface for reinitialization in // order to update any modified attributes associated with // the collectable service.. // // Iterate over the CollectableService objects in the // updates map and mark any which have the same interface // address for reinitialization // ThreadCategory log = log(); OnmsIpInterface iface = null; synchronized (getCollectableServices()) { Iterator<CollectableService> iter = getCollectableServices().iterator(); while (iter.hasNext()) { CollectableService cSvc = iter.next(); final InetAddress addr = (InetAddress) cSvc.getAddress(); final String addrString = str(addr); if (log.isDebugEnabled()) log.debug("Comparing CollectableService ip address = " + addrString + " and event ip interface = " + ipAddress); if (addrString != null && addrString.equals(ipAddress)) { synchronized (cSvc) { if (iface == null) { iface = getIpInterface(nodeid.intValue(), ipAddress); } // Got a match! Retrieve the CollectorUpdates object // associated // with this CollectableService. CollectorUpdates updates = cSvc.getCollectorUpdates(); // Now set the reinitialization flag updates.markForReinitialization(iface); if (log.isDebugEnabled()) log.debug("reinitializePrimarySnmpInterfaceHandler: marking " + ipAddress + " for reinitialization for service SNMP."); } } } } } /** * This method is responsible for handling serviceDeleted events. * * @param event * The event to process. * @throws InsufficientInformationException * */ private void handleServiceDeleted(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); EventUtils.checkInterface(event); EventUtils.checkService(event); ThreadCategory log = log(); //INCORRECT; we now support all *sorts* of data collection. This is *way* out of date // Currently only support SNMP data collection. // //if (!event.getService().equals("SNMP")) // return; Long nodeId = event.getNodeid(); String ipAddr = event.getInterface(); 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 (getCollectableServices()) { CollectableService cSvc = null; ListIterator<CollectableService> liter = getCollectableServices().listIterator(); while (liter.hasNext()) { cSvc = liter.next(); // Only interested in entries with matching nodeId, IP address // and service InetAddress addr = (InetAddress) cSvc.getAddress(); //WATCH the brackets; there used to be an extra close bracket after the ipAddr comparison which borked this whole expression if (!(cSvc.getNodeId() == nodeId && addr.getHostName().equals(ipAddr) && cSvc.getServiceName().equals(svcName))) continue; synchronized (cSvc) { // Retrieve the CollectorUpdates object associated with // this CollectableService if one exists. CollectorUpdates updates = cSvc.getCollectorUpdates(); // Now set the update's deletion flag so the next // time it is selected for execution by the scheduler // the collection will be skipped and the service will not // be rescheduled. log().debug("Marking CollectableService for deletion because a service was deleted: Service nodeid="+cSvc.getNodeId()+ ", deleted node:"+nodeId+ ", service address:"+addr.getHostName()+ ", deleted interface:"+ipAddr+ ", service servicename:"+cSvc.getServiceName()+ ", deleted service name:"+svcName+ ", event source "+event.getSource()); 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."); } /** * <p>setScheduler</p> * * @param scheduler a {@link org.opennms.netmgt.scheduler.Scheduler} object. */ public void setScheduler(Scheduler scheduler) { m_scheduler = scheduler; } private Scheduler getScheduler() { if (m_scheduler == null) { createScheduler(); } return m_scheduler; } /** * <p>setCollectorConfigDao</p> * * @param collectorConfigDao a {@link org.opennms.netmgt.dao.CollectorConfigDao} object. */ public void setCollectorConfigDao(CollectorConfigDao collectorConfigDao) { m_collectorConfigDao = collectorConfigDao; } private CollectorConfigDao getCollectorConfigDao() { return m_collectorConfigDao; } /** * <p>setIpInterfaceDao</p> * * @param ifSvcDao a {@link org.opennms.netmgt.dao.IpInterfaceDao} object. */ public void setIpInterfaceDao(IpInterfaceDao ifSvcDao) { m_ifaceDao = ifSvcDao; } private IpInterfaceDao getIpInterfaceDao() { return m_ifaceDao; } /** * <p>setTransactionTemplate</p> * * @param transTemplate a {@link org.springframework.transaction.support.TransactionTemplate} object. */ public void setTransactionTemplate(TransactionTemplate transTemplate) { m_transTemplate = transTemplate; } /** * <p>setNodeDao</p> * * @param nodeDao a {@link org.opennms.netmgt.dao.NodeDao} object. */ public void setNodeDao(NodeDao nodeDao) { m_nodeDao = nodeDao; } /** * <p>setServiceCollector</p> * * @param svcName a {@link java.lang.String} object. * @param collector a {@link org.opennms.netmgt.collectd.ServiceCollector} object. */ public void setServiceCollector(String svcName, ServiceCollector collector) { m_collectors.put(svcName, collector); } /** * <p>getServiceCollector</p> * * @param svcName a {@link java.lang.String} object. * @return a {@link org.opennms.netmgt.collectd.ServiceCollector} object. */ public ServiceCollector getServiceCollector(String svcName) { return m_collectors.get(svcName); } /** * <p>getCollectorNames</p> * * @return a {@link java.util.Set} object. */ public Set<String> getCollectorNames() { return m_collectors.keySet(); } private void instantiateCollectors() { log().debug("instantiateCollectors: Loading collectors"); /* * Load up an instance of each collector from the config * so that the event processor will have them for * new incoming events to create collectable service objects. */ Collection<Collector> collectors = getCollectorConfigDao().getCollectors(); for (Collector collector : collectors) { String svcName = collector.getService(); try { if (log().isDebugEnabled()) { log().debug("instantiateCollectors: Loading collector " + svcName + ", classname " + collector.getClassName()); } Class<?> cc = Class.forName(collector.getClassName()); ServiceCollector sc = (ServiceCollector) cc.newInstance(); sc.initialize(Collections.<String, String>emptyMap()); setServiceCollector(svcName, sc); } catch (Throwable t) { log().warn("instantiateCollectors: Failed to load collector " + collector.getClassName() + " for service " + svcName + ": " + t, t); } } } }