/******************************************************************************* * 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.trapd; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.net.InetAddress; import java.sql.SQLException; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import javax.annotation.Resource; import org.apache.log4j.Category; import org.apache.log4j.Logger; import org.opennms.core.concurrent.WaterfallExecutor; import org.opennms.core.utils.BeanUtils; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.LogUtils; import org.opennms.netmgt.daemon.AbstractServiceDaemon; import org.opennms.netmgt.snmp.SnmpUtils; import org.opennms.netmgt.snmp.SnmpV3User; import org.opennms.netmgt.snmp.TrapNotification; import org.opennms.netmgt.snmp.TrapNotificationListener; import org.opennms.netmgt.snmp.TrapProcessor; import org.opennms.netmgt.snmp.TrapProcessorFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; /** * <p> * The Trapd listens for SNMP traps on the standard port(162). Creates a * SnmpTrapSession and implements the SnmpTrapHandler to get callbacks when * traps are received. * </p> * * <p> * The received traps are converted into XML and sent to eventd. * </p> * * <p> * <strong>Note: </strong>Trapd is a PausableFiber so as to receive control * events. However, a 'pause' on Trapd has no impact on the receiving and * processing of traps. * </p> * * @author <A HREF="mailto:weave@oculan.com">Brian Weaver </A> * @author <A HREF="mailto:sowmya@opennms.org">Sowmya Nataraj </A> * @author <A HREF="mailto:larry@opennms.org">Lawrence Karnowski </A> * @author <A HREF="mailto:mike@opennms.org">Mike Davidson </A> * @author <A HREF="mailto:tarus@opennms.org">Tarus Balog </A> * @author <A HREF="http://www.opennms.org">OpenNMS.org </A> */ public class Trapd extends AbstractServiceDaemon implements TrapProcessorFactory, TrapNotificationListener { /** * The last status sent to the service control manager. */ private int m_status = START_PENDING; /** * The thread pool that processes traps */ private ExecutorService m_backlogQ; /** * The queue processing thread */ @Autowired private TrapQueueProcessorFactory m_processorFactory; /** * The class instance used to receive new events from for the system. */ @Autowired private BroadcastEventProcessor m_eventReader; /** * Trapd IP manager. Contains IP address -> node ID mapping. */ @Autowired private TrapdIpMgr m_trapdIpMgr; @Resource(name="snmpTrapAddress") private String m_snmpTrapAddress; @Resource(name="snmpTrapPort") private Integer m_snmpTrapPort; @Resource(name="snmpV3Users") private List<SnmpV3User> m_snmpV3Users; private boolean m_registeredForTraps; /** * <P> * Constructs a new Trapd object that receives and forwards trap messages * via JSDT. The session is initialized with the default client name of <EM> * OpenNMS.trapd</EM>. The trap session is started on the default port, as * defined by the SNMP library. * </P> * * @see org.opennms.protocols.snmp.SnmpTrapSession */ public Trapd() { super("OpenNMS.Trapd"); } /** * <p>createTrapProcessor</p> * * @return a {@link org.opennms.netmgt.snmp.TrapProcessor} object. */ @Override public TrapProcessor createTrapProcessor() { return new EventCreator(m_trapdIpMgr); } /** {@inheritDoc} */ @Override public void trapReceived(TrapNotification trapNotification) { try { WaterfallExecutor.waterfall(Collections.singletonList(m_backlogQ), m_processorFactory.getInstance(trapNotification)); } catch (InterruptedException e) { LogUtils.warnf(this, e, "addTrap: Error adding trap to queue"); } catch (ExecutionException e) { LogUtils.warnf(this, e, "addTrap: Error adding trap to queue"); } } /** * <p>onInit</p> */ @Override public synchronized void onInit() { BeanUtils.assertAutowiring(this); Assert.state(m_backlogQ != null, "backlogQ must be set"); try { m_trapdIpMgr.dataSourceSync(); } catch (final SQLException e) { LogUtils.errorf(this, e, "init: Failed to load known IP address list"); throw new UndeclaredThrowableException(e); } try { InetAddress address = getInetAddress(); LogUtils.infof(this, "Listening on %s:%d", address == null ? "[all interfaces]" : InetAddressUtils.str(address), m_snmpTrapPort); SnmpUtils.registerForTraps(this, this, address, m_snmpTrapPort, m_snmpV3Users); m_registeredForTraps = true; LogUtils.debugf(this, "init: Creating the trap session"); } catch (final IOException e) { if (e instanceof java.net.BindException) { managerLog().error("init: Failed to listen on SNMP trap port, perhaps something else is already listening?", e); LogUtils.errorf(this, e, "init: Failed to listen on SNMP trap port, perhaps something else is already listening?"); } else { LogUtils.errorf(this, e, "init: Failed to initialize SNMP trap socket"); } throw new UndeclaredThrowableException(e); } try { m_eventReader.open(); } catch (final Throwable e) { LogUtils.errorf(this, e, "init: Failed to open event reader"); throw new UndeclaredThrowableException(e); } } private InetAddress getInetAddress() { if (m_snmpTrapAddress.equals("*")) { return null; } return InetAddressUtils.addr(m_snmpTrapAddress); } private Category managerLog() { return Logger.getLogger("OpenNMS.Manager"); } /** * Create the SNMP trap session and create the communication channel * to communicate with eventd. * * @exception java.lang.reflect.UndeclaredThrowableException * if an unexpected database, or IO exception occurs. * @see org.opennms.protocols.snmp.SnmpTrapSession * @see org.opennms.protocols.snmp.SnmpTrapHandler */ @Override public synchronized void onStart() { m_status = STARTING; LogUtils.debugf(this, "start: Initializing the trapd config factory"); m_status = RUNNING; LogUtils.debugf(this, "start: Trapd ready to receive traps"); } /** * Pauses Trapd */ @Override public void onPause() { if (m_status != RUNNING) { return; } m_status = PAUSE_PENDING; LogUtils.debugf(this, "pause: Calling pause on processor"); m_status = PAUSED; LogUtils.debugf(this, "pause: Trapd paused"); } /** * Resumes Trapd */ @Override public void onResume() { if (m_status != PAUSED) { return; } m_status = RESUME_PENDING; LogUtils.debugf(this, "resume: Calling resume on processor"); m_status = RUNNING; LogUtils.debugf(this, "resume: Trapd resumed"); } /** * Stops the currently running service. If the service is not running then * the command is silently discarded. */ @Override public synchronized void onStop() { m_status = STOP_PENDING; // shutdown and wait on the background processing thread to exit. LogUtils.debugf(this, "stop: closing communication paths."); try { if (m_registeredForTraps) { LogUtils.debugf(this, "stop: Closing SNMP trap session."); SnmpUtils.unregisterForTraps(this, getInetAddress(), m_snmpTrapPort); LogUtils.debugf(this, "stop: SNMP trap session closed."); } else { LogUtils.debugf(this, "stop: not attemping to closing SNMP trap session--it was never opened"); } } catch (final IOException e) { LogUtils.warnf(this, e, "stop: exception occurred closing session"); } catch (final IllegalStateException e) { LogUtils.debugf(this, e, "stop: The SNMP session was already closed"); } LogUtils.debugf(this, "stop: Stopping queue processor."); m_backlogQ.shutdown(); m_eventReader.close(); m_status = STOPPED; LogUtils.debugf(this, "stop: Trapd stopped"); } /** * Returns the current status of the service. * * @return The service's status. */ @Override public synchronized int getStatus() { return m_status; } /** {@inheritDoc} */ @Override public void trapError(final int error, final String msg) { LogUtils.warnf(this, "Error Processing Received Trap: error = " + error + (msg != null ? ", ref = " + msg : "")); } /** * <p>getEventReader</p> * * @return a {@link org.opennms.netmgt.trapd.BroadcastEventProcessor} object. */ public BroadcastEventProcessor getEventReader() { return m_eventReader; } /** * <p>setEventReader</p> * * @param eventReader a {@link org.opennms.netmgt.trapd.BroadcastEventProcessor} object. */ public void setEventReader(BroadcastEventProcessor eventReader) { m_eventReader = eventReader; } /** * <p>getBacklogQ</p> * * @return a {@link java.util.concurrent.ExecutorService} object. */ public ExecutorService getBacklogQ() { return m_backlogQ; } /** * <p>setBacklogQ</p> * * @param backlogQ a {@link java.util.concurrent.ExecutorService} object. */ public void setBacklogQ(ExecutorService backlogQ) { m_backlogQ = backlogQ; } }