org.opennms.ng.services.capsd.SuspectEventProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.ng.services.capsd.SuspectEventProcessor.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2006-2012 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2012 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.ng.services.capsd;

import java.net.InetAddress;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.opennms.core.db.DataSourceFactory;
import org.opennms.core.utils.ByteArrayComparator;
import org.opennms.core.utils.ConfigFileConstants;
import org.opennms.core.utils.DBUtils;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.capsd.IfSnmpCollector;
import org.opennms.netmgt.capsd.snmp.IfTable;
import org.opennms.netmgt.capsd.snmp.IfTableEntry;
import org.opennms.netmgt.capsd.snmp.IfXTableEntry;
import org.opennms.netmgt.capsd.snmp.IpAddrTable;
import org.opennms.netmgt.capsd.snmp.SystemGroup;
import org.opennms.netmgt.model.OnmsNode;
import org.opennms.netmgt.model.capsd.DbIfServiceEntry;
import org.opennms.netmgt.model.capsd.DbIpInterfaceEntry;
import org.opennms.netmgt.model.capsd.DbNodeEntry;
import org.opennms.netmgt.model.capsd.DbSnmpInterfaceEntry;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventIpcManager;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.ng.services.capsdconfig.CapsdConfig;
import org.opennms.ng.services.collectdconfig.CollectdConfigFactory;
import org.opennms.ng.services.pollerconfig.PollerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import static org.opennms.core.utils.InetAddressUtils.addr;
import static org.opennms.core.utils.InetAddressUtils.str;

/**
 * This class is designed to scan/capability check a suspect interface, update
 * the database based on the information collected from the device, and
 * generate events necessary to notify the other OpenNMS services. The
 * constructor takes a string which is the IP address of the interface to be
 * scanned.
 *
 * @author <a href="mailto:jamesz@opennms.com">James Zuo </a>
 * @author <a href="mailto:mike@opennms.org">Mike Davidson </a>
 * @author <a href="mailto:weave@oculan.com">Brian Weaver </a>
 * @author <a href="http://www.opennms.org/">OpenNMS </a>
 */
public class SuspectEventProcessor implements Runnable {

    private static final Logger LOG = LoggerFactory.getLogger(SuspectEventProcessor.class);
    private static final String EVENT_SOURCE = "OpenNMS.Capsd";
    /**
     * SQL statement to retrieve the node identifier for a given IP address
     */
    private static String SQL_RETRIEVE_INTERFACE_NODEID_PREFIX = "SELECT nodeId FROM ipinterface WHERE ";
    /**
     * SQL statement to retrieve the ipaddresses for a given node ID
     */
    private final static String SQL_RETRIEVE_IPINTERFACES_ON_NODEID = "SELECT ipaddr FROM ipinterface WHERE nodeid = ? and ismanaged != 'D'";
    private CapsdDbSyncer m_capsdDbSyncer;
    private PluginManager m_pluginManager;
    private Set<String> m_queuedSuspectTracker = new HashSet<String>();
    private PollerConfig pollerConfig;
    private CapsdConfig capsdConfig;
    private EventIpcManager eventIpcManager;
    /**
     * IP address of new suspect interface
     */
    String m_suspectIf;

    /**
     * Constructor.
     *
     * @param capsdDbSyncer for querying the database
     * @param pluginManager for accessing plugins
     * @param ifAddress     Suspect interface address.
     */
    SuspectEventProcessor(CapsdDbSyncer capsdDbSyncer, PluginManager pluginManager, String ifAddress) {
        Assert.notNull(capsdDbSyncer, "The capsdDbSyncer argument cannot be null");
        Assert.notNull(pluginManager, "The pluginManager argument cannot be null");
        Assert.notNull(ifAddress, "The ifAddress argument cannot be null");

        m_capsdDbSyncer = capsdDbSyncer;
        m_pluginManager = pluginManager;
        m_suspectIf = ifAddress;

        // Add the interface address to the Set that tracks suspect
        // scans in the queue
        synchronized (m_queuedSuspectTracker) {
            m_queuedSuspectTracker.add(ifAddress);
        }
    }

    public SuspectEventProcessor(CapsdDbSyncer capsdDbSyncer, PluginManager pluginManager, String ifAddress,
            PollerConfig pollerConfig, CapsdConfig capsdConfig, EventIpcManager eventIpcManager) {
        Assert.notNull(capsdDbSyncer, "The capsdDbSyncer argument cannot be null");
        Assert.notNull(pluginManager, "The pluginManager argument cannot be null");
        Assert.notNull(ifAddress, "The ifAddress argument cannot be null");

        m_capsdDbSyncer = capsdDbSyncer;
        m_pluginManager = pluginManager;
        m_suspectIf = ifAddress;
        this.pollerConfig = pollerConfig;
        this.eventIpcManager = eventIpcManager;
        this.capsdConfig = capsdConfig;
    }

    /**
     * Utility method which checks the provided list of supported protocols to
     * determine if the SNMP service is present.
     *
     * @param supportedProtocols List of supported protocol objects.
     * @return TRUE if service "SNMP" is present in the list, FALSE otherwise
     */
    static boolean supportsSnmp(List<IfCollector.SupportedProtocol> supportedProtocols) {
        for (IfCollector.SupportedProtocol p : supportedProtocols) {
            if (p.getProtocolName().equals("SNMP")) {
                return true;
            }
        }
        return false;
    }

    /**
     * Utility method which determines if the passed IfSnmpCollector object
     * contains an ifIndex value for the passed IP address.
     *
     * @param ipaddr IP address
     * @param snmpc  SNMP collection
     * @return TRUE if an ifIndex value was found in the SNMP collection for
     * the provided IP address, FALSE otherwise.
     */
    static boolean hasIfIndex(InetAddress ipaddr, IfSnmpCollector snmpc) {
        int ifIndex = -1;
        if (snmpc.hasIpAddrTable()) {
            ifIndex = snmpc.getIfIndex(ipaddr);
        }

        LOG.debug("hasIfIndex: ipAddress: {} has ifIndex: {}", str(ipaddr), ifIndex);
        if (ifIndex == -1) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Utility method which determines returns the ifType for the passed IP
     * address.
     *
     * @param ipaddr IP address
     * @param snmpc  SNMP collection
     * @return TRUE if an ifIndex value was found in the SNMP collection for
     * the provided IP address, FALSE otherwise.
     */
    static int getIfType(InetAddress ipaddr, IfSnmpCollector snmpc) {
        int ifIndex = snmpc.getIfIndex(ipaddr);
        int ifType = snmpc.getIfType(ifIndex);

        LOG.debug("getIfType: ipAddress: {} has ifIndex: {} and ifType: {}", str(ipaddr), ifIndex, ifType);
        return ifType;
    }

    /**
     * Responsible for setting the value of the 'isSnmpPrimary' field of the
     * ipInterface table to 'P' (Primary) for the primary SNMP interface
     * address.
     *
     * @param dbc              Database connection.
     * @param node             DbNodeEntry object representing the suspect interface's
     *                         parent node table entry
     * @param newPrimarySnmpIf New primary SNMP interface.
     * @param oldPrimarySnmpIf Old primary SNMP interface.
     * @throws java.sql.SQLException if an error occurs updating the ipInterface table
     */
    static void setPrimarySnmpInterface(Connection dbc, DbNodeEntry node, InetAddress newPrimarySnmpIf,
            InetAddress oldPrimarySnmpIf) throws SQLException {
        if (newPrimarySnmpIf == null) {
            LOG.debug("setPrimarySnmpInterface: newSnmpPrimary is null, nothing to set, returning.");
            return;
        } else {
            LOG.debug("setPrimarySnmpInterface: newSnmpPrimary = {}", newPrimarySnmpIf);
        }

        // Verify that old and new primary interfaces are different
        //
        if (oldPrimarySnmpIf != null && oldPrimarySnmpIf.equals(newPrimarySnmpIf)) {
            // Old and new primary interfaces are the same
            LOG.debug("setPrimarySnmpInterface: Old and new primary interfaces are the same");
        }

        // Set primary SNMP interface 'isSnmpPrimary' field to 'P' for primary
        //
        if (newPrimarySnmpIf != null) {
            LOG.debug("setPrimarySnmpInterface: Updating primary SNMP interface {}", str(newPrimarySnmpIf));

            // Update the appropriate entry in the 'ipInterface' table
            //

            final DBUtils d = new DBUtils(SuspectEventProcessor.class);
            try {
                PreparedStatement stmt = dbc.prepareStatement(
                        "UPDATE ipInterface SET isSnmpPrimary='P' WHERE nodeId=? AND ipaddr=? AND isManaged!='D'");
                d.watch(stmt);
                stmt.setInt(1, node.getNodeId());
                stmt.setString(2, str(newPrimarySnmpIf));

                stmt.executeUpdate();
                LOG.debug("setPrimarySnmpInterface: Completed update of new primary interface to PRIMARY.");
            } finally {
                d.cleanUp();
            }
        }
    }

    public void setEventIpcManager(EventIpcManager eventIpcManager) {
        this.eventIpcManager = eventIpcManager;
    }

    /**
     * Responsible for setting the Set used to track suspect scans that
     * are already enqueued for processing.  Should be called once by Capsd
     * at startup.
     *
     * @param queuedSuspectTracker a {@link java.util.Set} object.
     */
    public synchronized void setQueuedSuspectsTracker(Set<String> queuedSuspectTracker) {
        m_queuedSuspectTracker = Collections.synchronizedSet(queuedSuspectTracker);
    }

    /**
     * Is a suspect scan already enqueued for a given IP address?
     *
     * @param ipAddr The IP address of interest
     * @return a boolean.
     */
    public boolean isScanQueuedForAddress(String ipAddr) {
        synchronized (m_queuedSuspectTracker) {
            return (m_queuedSuspectTracker.contains(ipAddr));
        }
    }

    /**
     * Utility method which compares two InetAddress objects based on the
     * provided method (MIN/MAX) and returns the InetAddress which is to be
     * considered the primary interface. NOTE: In order for an interface to be
     * considered primary it must be managed. This method will return null if
     * the 'oldPrimary' address is null and the 'currentIf' address is
     * unmanaged.
     *
     * @param currentIf  Interface with which to compare the 'oldPrimary' address.
     * @param oldPrimary Primary interface to be compared against the 'currentIf'
     *                   address.
     * @param method     Comparison method to be used (either "min" or "max")
     * @return InetAddress object of the primary interface based on the
     * provided method or null if neither address is eligible to be
     * primary.
     */
    InetAddress compareAndSelectPrimary(InetAddress currentIf, InetAddress oldPrimary) {
        InetAddress newPrimary = null;
        if (oldPrimary == null) {
            if (!capsdConfig.isAddressUnmanaged(currentIf)) {
                return currentIf;
            } else {
                return oldPrimary;
            }
        }

        byte[] current = currentIf.getAddress();
        byte[] primary = oldPrimary.getAddress();

        // Smallest address wins
        if (new ByteArrayComparator().compare(current, primary) < 0) {
            // Replace the primary interface with the current
            // interface only if the current interface is managed!
            if (!capsdConfig.isAddressUnmanaged(currentIf)) {
                newPrimary = currentIf;
            }
        }

        if (newPrimary != null) {
            return newPrimary;
        } else {
            return oldPrimary;
        }
    }

    public CapsdConfig getCapsdConfig() {
        return capsdConfig;
    }

    public void setCapsdConfig(CapsdConfig capsdConfig) {
        this.capsdConfig = capsdConfig;
    }

    /**
     * This method is responsible for determining if a node already exists in
     * the database for the current interface. If the IfCollector object
     * contains a valid SNMP collection, an attempt will be made to look up in
     * the database each interface contained in the SNMP collection's ifTable.
     * If an interface is found to already exist in the database a DbNodeEntry
     * object will be created from it and returned. If the IfCollector object
     * does not contain a valid SNMP collection or if none of the interfaces
     * exist in the database null is returned.
     *
     * @param dbc       Connection to the database.
     * @param collector Interface collector object
     * @return dbNodeEntry Returns null if a node does not already exist in
     * the database, otherwise returns the DbNodeEntry object for the
     * node under which the current interface/IP address should be
     * added.
     * @throws java.sql.SQLException Thrown if an error occurs retrieving the parent nodeid from
     *                               the database.
     */
    private DbNodeEntry getExistingNodeEntry(Connection dbc, IfCollector collector) throws SQLException {
        LOG.debug("getExistingNodeEntry: checking for current target: {}", collector.getTarget());

        // Do we have any additional interface information collected via SNMP?
        // If not simply return, there is nothing to check
        if (!collector.hasSnmpCollection() || collector.getSnmpCollector().failed()) {
            return null;
        }

        // Next verify that ifTable and ipAddrTable entries were collected
        IfSnmpCollector snmpc = collector.getSnmpCollector();
        IfTable ifTable = null;
        IpAddrTable ipAddrTable = null;

        if (snmpc.hasIfTable()) {
            ifTable = snmpc.getIfTable();
        }

        if (snmpc.hasIpAddrTable()) {
            ipAddrTable = snmpc.getIpAddrTable();
        }

        if (ifTable == null || ipAddrTable == null) {
            return null;
        }

        // SQL statement prefix
        StringBuffer sqlBuffer = new StringBuffer(SQL_RETRIEVE_INTERFACE_NODEID_PREFIX);
        boolean firstAddress = true;

        // Loop through the interface table entries and see if any already
        // exist in the database.
        List<String> ipaddrsOfNewNode = new ArrayList<String>();
        List<String> ipaddrsOfOldNode = new ArrayList<String>();

        for (IfTableEntry ifEntry : ifTable) {

            if (ifEntry.getIfIndex() == null) {
                LOG.debug("getExistingNodeEntry:  Breaking from loop");
                break;
            }

            //
            // Get ifIndex
            //
            int ifIndex = ifEntry.getIfIndex().intValue();

            //
            // Get ALL IP Addresses for this ifIndex
            //
            List<InetAddress> ipAddrs = ipAddrTable.getIpAddresses(ifIndex);
            LOG.debug("getExistingNodeEntry: number of interfaces retrieved for ifIndex {} is: {}", ifIndex,
                    ipAddrs.size());

            // Iterate over IP address list and add each to the sql buffer
            //
            for (InetAddress ipAddress : ipAddrs) {

                //
                // Skip interface if no IP address or if IP address is
                // "0.0.0.0"
                // or if this interface is of type loopback
                if (ipAddress == null || str(ipAddress).equals("0.0.0.0") || ipAddress.isLoopbackAddress()) {
                    continue;
                }

                if (firstAddress) {
                    sqlBuffer.append("ipaddr='").append(str(ipAddress)).append("'");
                    firstAddress = false;
                } else {
                    sqlBuffer.append(" OR ipaddr='").append(str(ipAddress)).append("'");
                }

                ipaddrsOfNewNode.add(str(ipAddress));
            }
        } // end while

        // Make sure we added at least one address to the SQL query
        //
        if (firstAddress) {
            return null;
        }

        // Prepare the db statement in advance
        //
        LOG.debug("getExistingNodeEntry: issuing SQL command: {}", sqlBuffer.toString());

        int nodeID = -1;
        PreparedStatement stmt;
        final DBUtils d = new DBUtils(getClass());

        try {
            stmt = dbc.prepareStatement(sqlBuffer.toString());
            d.watch(stmt);

            // Do any of the IP addrs already exist in the database under another node?
            ResultSet rs = stmt.executeQuery();
            d.watch(rs);
            if (rs.next()) {
                nodeID = rs.getInt(1);
                LOG.debug("getExistingNodeEntry: target {}/{}", str(collector.getTarget()), nodeID);
                rs = null;
            }
        } finally {
            d.cleanUp();
        }

        if (nodeID == -1) {
            return null;
        }

        try {
            stmt = dbc.prepareStatement(SQL_RETRIEVE_IPINTERFACES_ON_NODEID);
            d.watch(stmt);
            stmt.setInt(1, nodeID);

            ResultSet rs = stmt.executeQuery();
            d.watch(rs);
            while (rs.next()) {
                String ipaddr = rs.getString(1);
                if (!ipaddr.equals("0.0.0.0")) {
                    ipaddrsOfOldNode.add(ipaddr);
                }
            }
        } finally {
            d.cleanUp();
        }

        if (ipaddrsOfNewNode.containsAll(ipaddrsOfOldNode)) {
            LOG.debug("getExistingNodeEntry: found one of the addrs under existing node: {}", nodeID);
            return DbNodeEntry.get(nodeID);
        } else {
            String dupIpaddr = getDuplicateIpaddress(ipaddrsOfOldNode, ipaddrsOfNewNode);
            createAndSendDuplicateIpaddressEvent(nodeID, dupIpaddr);
            return null;
        }
    }

    /**
     * This method is used to verify if there is a same ipaddress existing in
     * two sets of ipaddresses, and return the first ipaddress that is the
     * same in both sets as a string.
     *
     * @param ipListA a collection of ip addresses.
     * @param ipListB a collection of ip addresses.
     * @return the first ipaddress exists in both ipaddress lists.
     */
    private String getDuplicateIpaddress(List<String> ipListA, List<String> ipListB) {
        if (ipListA == null || ipListB == null) {
            return null;
        }

        String ipaddr = null;
        Iterator<String> iter = ipListA.iterator();
        while (iter.hasNext()) {
            ipaddr = iter.next();
            if (ipListB.contains(ipaddr)) {
                LOG.debug("getDuplicateIpaddress: get duplicate ip address: {}", ipaddr);
                break;
            } else {
                ipaddr = null;
            }
        }
        return ipaddr;
    }

    /**
     * This method is responsble for inserting a new node into the node table.
     *
     * @param dbc       Database connection.
     * @param ifaddr    Suspect interface
     * @param collector Interface collector containing SMB and SNMP info collected
     *                  from the remote device.
     * @return DbNodeEntry object associated with the newly inserted node
     * table entry.
     * @throws java.sql.SQLException if an error occurs inserting the new node.
     */
    private DbNodeEntry createNode(Connection dbc, InetAddress ifaddr, IfCollector collector) throws SQLException {
        // Determine primary interface for the node. Primary interface
        // is needed for determining the node label.
        //
        InetAddress primaryIf = determinePrimaryInterface(collector);

        // Get Snmp and Smb collector objects
        //
        IfSnmpCollector snmpc = collector.getSnmpCollector();
        IfSmbCollector smbc = collector.getSmbCollector();

        // First create a node entry for the new interface
        //
        DbNodeEntry entryNode = DbNodeEntry.create();

        // fill in the node information
        //
        Date now = new Date();
        entryNode.setCreationTime(now);
        entryNode.setLastPoll(now);
        entryNode.setNodeType(OnmsNode.NodeType.ACTIVE);
        entryNode.setLabel(primaryIf.getHostName());
        if (entryNode.getLabel().equals(str(primaryIf))) {
            entryNode.setLabelSource(OnmsNode.NodeLabelSource.ADDRESS);
        } else {
            entryNode.setLabelSource(OnmsNode.NodeLabelSource.HOSTNAME);
        }

        if (snmpc != null) {
            if (snmpc.hasSystemGroup()) {
                SystemGroup sysgrp = snmpc.getSystemGroup();

                // sysObjectId
                String sysObjectId = sysgrp.getSysObjectID();
                if (sysObjectId != null) {
                    entryNode.setSystemOID(sysObjectId);
                } else {
                    LOG.warn("SuspectEventProcessor: {} has NO sysObjectId!!!!", str(ifaddr));
                }

                // sysName
                String str = sysgrp.getSysName();
                LOG.debug("SuspectEventProcessor: {} has sysName: {}", str(ifaddr), str);

                if (str != null && str.length() > 0) {
                    entryNode.setSystemName(str);

                    // Hostname takes precedence over sysName so only replace
                    // label if
                    // hostname was not available.
                    if (entryNode.getLabelSource() == OnmsNode.NodeLabelSource.ADDRESS) {
                        entryNode.setLabel(str);
                        entryNode.setLabelSource(OnmsNode.NodeLabelSource.SYSNAME);
                    }
                }

                // sysDescription
                str = sysgrp.getSysDescr();
                LOG.debug("SuspectEventProcessor: {} has sysDescription: {}", str(ifaddr), str);
                if (str != null && str.length() > 0) {
                    entryNode.setSystemDescription(str);
                }

                // sysLocation
                str = sysgrp.getSysLocation();
                LOG.debug("SuspectEventProcessor: {} has sysLocation: {}", str(ifaddr), str);
                if (str != null && str.length() > 0) {
                    entryNode.setSystemLocation(str);
                }

                // sysContact
                str = sysgrp.getSysContact();
                LOG.debug("SuspectEventProcessor: {} has sysContact: {}", str(ifaddr), str);
                if (str != null && str.length() > 0) {
                    entryNode.setSystemContact(str);
                }
            }
        }

        // check for SMB information
        //
        if (smbc != null) {
            // Netbios Name and Domain
            // Note: only override if the label source is not HOSTNAME
            if (smbc.getNbtName() != null && entryNode.getLabelSource() != OnmsNode.NodeLabelSource.HOSTNAME) {
                entryNode.setLabel(smbc.getNbtName());
                entryNode.setLabelSource(OnmsNode.NodeLabelSource.NETBIOS);
                entryNode.setNetBIOSName(entryNode.getLabel());
                if (smbc.getDomainName() != null) {
                    entryNode.setDomainName(smbc.getDomainName());
                }
            }
        }

        entryNode.store(dbc);
        return entryNode;
    }

    /**
     * This method is responsible for inserting new entries into the
     * ipInterface table for each interface found to be associated with the
     * suspect interface during the capabilities scan.
     *
     * @param dbc             Database connection.
     * @param node            DbNodeEntry object representing the suspect interface's
     *                        parent node table entry
     * @param useExistingNode False if a new node was created for the suspect interface.
     *                        True if an existing node entry was found under which the the
     *                        suspect interface is to be added.
     * @param ifaddr          Suspect interface
     * @param collector       Interface collector containing SMB and SNMP info collected
     *                        from the remote device.
     * @throws java.sql.SQLException if an error occurs adding interfaces to the ipInterface
     *                               table.
     */
    private void addInterfaces(Connection dbc, DbNodeEntry node, boolean useExistingNode, InetAddress ifaddr,
            IfCollector collector) throws SQLException {
        CapsdConfig cFactory = capsdConfig;

        Date now = new Date();

        int nodeId = node.getNodeId();

        DbIpInterfaceEntry ipIfEntry = DbIpInterfaceEntry.create(nodeId, ifaddr);
        ipIfEntry.setLastPoll(now);
        ipIfEntry.setHostname(ifaddr.getHostName());

        /*
         * NOTE: (reference internal bug# 201) If the ip is 'managed', it
         * might still be 'not polled' based on the poller configuration The
         * package filter evaluation requires that the ip be in the database -
         * at this point the ip is NOT in db, so insert as active and update
         * afterward Try to avoid re-evaluating the ip against filters for
         * each service, try to get the first package here and use that for
         * service evaluation
         */
        boolean addrUnmanaged = cFactory.isAddressUnmanaged(ifaddr);
        if (addrUnmanaged) {
            LOG.debug("addInterfaces: {} is unmanaged", ifaddr);
            ipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_UNMANAGED);
        } else {
            LOG.debug("addInterfaces: {} is managed", ifaddr);
            ipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_MANAGED);
        }

        ipIfEntry.setPrimaryState(DbIpInterfaceEntry.SNMP_NOT_ELIGIBLE);

        ipIfEntry.store(dbc);

        // now update if necessary
        org.opennms.netmgt.config.poller.Package ipPkg = getPackageForNewInterface(dbc, ifaddr, ipIfEntry,
                addrUnmanaged);

        int ifIndex = addSnmpInterfaces(dbc, ifaddr, nodeId, collector, ipIfEntry);

        // Add supported protocols
        addSupportedProtocols(node, ifaddr, collector.getSupportedProtocols(), addrUnmanaged, ifIndex, ipPkg);

        /*
         * If the useExistingNode flag is true, then we're done. The interface
         * is most likely an alias and the subinterfaces collected via SNMP
         * should already be in the database.
         */
        if (useExistingNode == true) {
            return;
        }

        getSubInterfacesForNewInterface(dbc, node, ifaddr, collector, now, nodeId, ifIndex);
    }

    private int addSnmpInterfaces(Connection dbc, InetAddress ifaddr, int nodeId, IfCollector collector,
            DbIpInterfaceEntry ipIfEntry) throws SQLException {
        boolean addedSnmpInterfaceEntry = addIfTableSnmpInterfaces(dbc, ifaddr, nodeId, collector);

        int ifIndex = getIfIndexForNewInterface(dbc, ifaddr, collector, ipIfEntry);

        if (ifIndex == CapsdConfig.LAME_SNMP_HOST_IFINDEX || !addedSnmpInterfaceEntry) {
            DbSnmpInterfaceEntry snmpEntry = DbSnmpInterfaceEntry.create(nodeId, ifIndex);
            snmpEntry.store(dbc);
        }

        LOG.debug("SuspectEventProcessor: setting ifindex for {}/{} to {}", nodeId, ifaddr, ifIndex);

        ipIfEntry.setIfIndex(ifIndex);
        ipIfEntry.store(dbc);

        return ifIndex;
    }

    private org.opennms.netmgt.config.poller.Package getPackageForNewInterface(Connection dbc, InetAddress ifaddr,
            DbIpInterfaceEntry ipIfEntry, boolean addrUnmanaged) throws SQLException {
        if (addrUnmanaged) {
            return null;
        }

        // PollerConfig pollerCfgFactory = PollerConfigFactory.getInstance();

        org.opennms.netmgt.config.poller.Package ipPkg = null;

        /*
         * The newly discoveried IP addr is not in the Package IPList Mapping
         * yet, so rebuild the list.
         */
        pollerConfig.rebuildPackageIpListMap();

        boolean ipToBePolled = false;
        ipPkg = pollerConfig.getFirstPackageMatch(str(ifaddr));
        if (ipPkg != null) {
            ipToBePolled = true;
        }

        LOG.debug("addInterfaces: {} is to be polled = {}", ifaddr, ipToBePolled);

        if (!ipToBePolled) {
            // update ismanaged to 'N' in ipinterface
            ipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_NOT_POLLED);
            ipIfEntry.store(dbc);
        }

        return ipPkg;
    }

    private int getIfIndexForNewInterface(Connection dbc, InetAddress ifaddr, IfCollector collector,
            DbIpInterfaceEntry ipIfEntry) throws SQLException {
        if (!collector.hasSnmpCollection()) {
            return -1;
        }

        IfSnmpCollector snmpc = collector.getSnmpCollector();

        int ifIndex = -1;

        /*
         * Just set primary state to not eligible for now. The primary SNMP
         * interface won't be selected until after all interfaces have been
         * inserted into the database. This is because the interface must
         * already be in the database for filter rule evaluation to succeed.
         */
        ipIfEntry.setPrimaryState(DbIpInterfaceEntry.SNMP_NOT_ELIGIBLE);

        if (snmpc.hasIpAddrTable() && (ifIndex = snmpc.getIfIndex(ifaddr)) != -1) {
            if (snmpc.hasIfTable()) {
                int status = snmpc.getAdminStatus(ifIndex);
                if (status != -1) {
                    ipIfEntry.setStatus(status);
                }
            }
        } else {
            /*
             * Address does not have a valid ifIndex associated with it Assume
             * there is no ipAddrTable and set ifIndex equal to
             * CapsdConfigFactory.LAME_SNMP_HOST_IFINDEX
             */
            ifIndex = CapsdConfig.LAME_SNMP_HOST_IFINDEX;
            LOG.debug("SuspectEventProcessor: no valid ifIndex for {} Assume this is a lame SNMP host", ifaddr);
        }
        ipIfEntry.store(dbc);

        return ifIndex;
    }

    private void getSubInterfacesForNewInterface(Connection dbc, DbNodeEntry node, InetAddress ifaddr,
            IfCollector collector, Date now, int nodeId, int ifIndex) throws SQLException {
        if (!collector.hasSnmpCollection()) {
            return;
        }

        CapsdConfig cFactory = capsdConfig;

        IfSnmpCollector snmpc = collector.getSnmpCollector();

        // Made it this far...lets add the IP sub-interfaces
        addSubIpInterfaces(dbc, node, collector, now, nodeId, cFactory, pollerConfig, snmpc);
    }

    private void addSubIpInterfaces(Connection dbc, DbNodeEntry node, IfCollector collector, Date now, int nodeId,
            CapsdConfig cFactory, PollerConfig pollerCfgFactory, IfSnmpCollector snmpc) throws SQLException {

        if (!snmpc.hasIpAddrTable()) {
            return;
        }

        Map<InetAddress, List<IfCollector.SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
        for (InetAddress xifaddr : extraTargets.keySet()) {

            LOG.debug("addInterfaces: adding interface {}", str(xifaddr));

            DbIpInterfaceEntry xipIfEntry = DbIpInterfaceEntry.create(nodeId, xifaddr);
            xipIfEntry.setLastPoll(now);
            xipIfEntry.setHostname(xifaddr.getHostName());

            /*
             * NOTE: (reference internal bug# 201) If the ip is 'managed', it
             * might still be 'not polled' based on the poller configuration.
             * The package filter evaluation requires that the ip be in the
             * database - at this point the ip is NOT in db, so insert as
             * active and update afterward. Try to avoid re-evaluating the ip
             * against filters for each service, try to get the first package
             * here and use that for service evaluation.
             */
            boolean xaddrUnmanaged = cFactory.isAddressUnmanaged(xifaddr);
            if (xaddrUnmanaged) {
                xipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_UNMANAGED);
            } else {
                xipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_MANAGED);
            }

            /*
             * Just set primary state to not eligible for now. The primary
             * SNMP interface won't be selected until after all interfaces
             * have been inserted into the database. This is because the
             * interface must already be in the database for filter rule
             * evaluation to succeed.
             */
            xipIfEntry.setPrimaryState(DbIpInterfaceEntry.SNMP_NOT_ELIGIBLE);
            int xifIndex = -1;
            if ((xifIndex = snmpc.getIfIndex(xifaddr)) != -1) {
                /*
                 * XXX I'm not sure if it is always safe to call setIfIndex
                 * here.  We should only do it if an snmpInterface entry
                 * was previously created for this ifIndex.  It was likely done
                 * by addSnmpInterfaces, but I have't checked to make sure that
                 * all cases are covered. - dj@opennms.org
                 */
                xipIfEntry.setIfIndex(xifIndex);
                int status = snmpc.getAdminStatus(xifIndex);
                if (status != -1) {
                    xipIfEntry.setStatus(status);
                }

                if (!supportsSnmp(extraTargets.get(xifaddr))) {
                    LOG.debug("addInterfaces: Interface doesn't support SNMP. {} set to not eligible",
                            str(xifaddr));
                }
            } else {
                /*
                 * No ifIndex found so set primary state to NOT_ELIGIBLE
                 */
                LOG.debug("addInterfaces: No ifIndex found. {} set to not eligible", str(xifaddr));
            }

            xipIfEntry.store(dbc);

            // now update if necessary
            org.opennms.netmgt.config.poller.Package xipPkg = null;
            if (!xaddrUnmanaged) {
                // The newly discoveried IP addr is not in the Package
                // IPList
                // Mapping yet, so rebuild the list.
                //
                pollerConfig.rebuildPackageIpListMap();

                boolean xipToBePolled = false;
                xipPkg = pollerCfgFactory.getFirstPackageMatch(str(xifaddr));
                if (xipPkg != null) {
                    xipToBePolled = true;
                }

                if (!xipToBePolled) {
                    // update ismanaged to 'N' in ipinterface
                    xipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_NOT_POLLED);
                    xipIfEntry.store(dbc);
                }
            }

            // add the supported protocols
            addSupportedProtocols(node, xifaddr, extraTargets.get(xifaddr), xaddrUnmanaged, xifIndex, xipPkg);
        }
    }

    private boolean addIfTableSnmpInterfaces(Connection dbc, InetAddress ifaddr, int nodeId, IfCollector collector)
            throws SQLException {
        if (!collector.hasSnmpCollection()) {
            return false;
        }

        IfSnmpCollector snmpc = collector.getSnmpCollector();

        if (!snmpc.hasIfTable()) {
            return false;
        }

        boolean addedSnmpInterfaceEntry = false;

        for (IfTableEntry ifte : snmpc.getIfTable()) {
            // index
            if (ifte.getIfIndex() == null) {
                continue;
            }
            final int xifIndex = ifte.getIfIndex().intValue();

            /*
             * address WARNING: IfSnmpCollector.getIfAddressAndMask() ONLY
             * returns the FIRST IP address and mask for a given interface as
             * specified in the ipAddrTable.
             */
            InetAddress[] addrs = null;
            if (snmpc.hasIpAddrTable()) {
                addrs = snmpc.getIfAddressAndMask(xifIndex);
            }

            // At some point back in the day this was done with ifType
            // Skip loopback interfaces
            if (addrs != null && addrs[0].isLoopbackAddress()) {
                continue;
            }

            final DbSnmpInterfaceEntry snmpEntry = DbSnmpInterfaceEntry.create(nodeId, xifIndex);

            if (addrs == null) {
                // No IP associated with the interface
                snmpEntry.setCollect("N");
            } else {
                // IP address
                if (addrs[0].equals(ifaddr)) {
                    addedSnmpInterfaceEntry = true;
                }

                // netmask
                if (addrs[1] != null) {
                    snmpEntry.setNetmask(addrs[1]);
                }

                snmpEntry.setCollect("C");
            }

            // description
            final String str = ifte.getIfDescr();
            if (addrs != null) {
                LOG.debug("SuspectEventProcessor: {} has ifDescription: {}", str(addrs[0]), str);
            }
            if (str != null && str.length() > 0) {
                snmpEntry.setDescription(str);
            }

            // physical address
            String physAddr = null;
            try {
                physAddr = ifte.getPhysAddr();
                if (addrs != null) {
                    LOG.debug("SuspectEventProcessor: {} has physical address: -{}-", str(addrs[0]), physAddr);
                }
            } catch (IllegalArgumentException iae) {
                physAddr = null;
                if (addrs != null) {
                    LOG.debug(
                            "ifPhysAddress.{} on node {} / {} could not be converted to a hex string (not a PhysAddr / OCTET STRING?), setting to null.",
                            ifte.getIfIndex(), nodeId, str(addrs[0]));
                }
                StringBuffer errMsg = new StringBuffer("SNMP agent bug on node ");
                errMsg.append(nodeId).append(" / ").append(str(ifaddr));
                errMsg.append(": wrong type for physical address (see bug 2740). ");
                errMsg.append("Working around, but expect trouble with this node.");
                LOG.warn(errMsg.toString());
            }
            if (physAddr != null && physAddr.length() == 12) {
                snmpEntry.setPhysicalAddress(physAddr);
            }

            if (ifte.getIfType() == null) {
                snmpEntry.setType(0);
            } else {
                snmpEntry.setType(ifte.getIfType().intValue());
            }

            IfXTableEntry ifxte = snmpc.hasIfXTable() ? snmpc.getIfXTable().getEntry(xifIndex) : null;

            long speed = getInterfaceSpeed(ifte, ifxte);

            // speed
            snmpEntry.setSpeed(speed);

            // admin status
            if (ifte.getIfAdminStatus() == null) {
                snmpEntry.setAdminStatus(0);
            } else {
                snmpEntry.setAdminStatus(ifte.getIfAdminStatus().intValue());
            }

            // oper status
            if (ifte.getIfOperStatus() == null) {
                snmpEntry.setOperationalStatus(0);
            } else {
                snmpEntry.setOperationalStatus(ifte.getIfOperStatus().intValue());
            }

            // name (from interface extensions table)
            String ifName = snmpc.getIfName(xifIndex);
            if (ifName != null && ifName.length() > 0) {
                snmpEntry.setName(ifName);
            }

            // alias (from interface extensions table)
            final String ifAlias = snmpc.getIfAlias(xifIndex);
            if (ifAlias != null && ifAlias.length() > 0) {
                snmpEntry.setAlias(ifAlias);
            }

            snmpEntry.store(dbc);
        }
        return addedSnmpInterfaceEntry;
    }

    private long getInterfaceSpeed(IfTableEntry ifte, IfXTableEntry ifxte) {
        if (ifxte != null && ifxte.getIfHighSpeed() != null && ifxte.getIfHighSpeed() > 4294) {
            return ifxte.getIfHighSpeed() * 1000000L;
        }

        if (ifte != null && ifte.getIfSpeed() != null) {
            return ifte.getIfSpeed();
        }

        return 0;
    }

    /**
     * Responsible for iterating inserting an entry into the ifServices table
     * for each protocol supported by the interface.
     *
     * @param node          Node entry
     * @param ifaddr        Interface address
     * @param protocols     List of supported protocols
     * @param addrUnmanaged Boolean flag indicating if interface is managed or unmanaged
     *                      according to the Capsd configuration.
     * @param ifIndex       Interface index or -1 if index is not known
     * @param ipPkg         Poller package to which the interface belongs
     * @throws java.sql.SQLException if an error occurs adding interfaces to the ipInterface
     *                               table.
     */
    private void addSupportedProtocols(DbNodeEntry node, InetAddress ifaddr,
            List<IfCollector.SupportedProtocol> protocols, boolean addrUnmanaged, int ifIndex,
            org.opennms.netmgt.config.poller.Package ipPkg) throws SQLException {
        if (str(ifaddr).equals("0.0.0.0")) {
            LOG.debug("addSupportedProtocols: node {}: Cant add ip services for non-ip interface. Just return.",
                    node.getNodeId());
            return;
        }

        // add the supported protocols
        //
        // NOTE!!!!!: (reference internal bug# 201)
        // If the ip is 'managed', the service can still be 'not polled'
        // based on the poller configuration - at this point the ip is already
        // in the database, so package filter evaluation should go through OK
        //
        for (IfCollector.SupportedProtocol p : protocols) {

            Number sid = m_capsdDbSyncer.getServiceId(p.getProtocolName());

            DbIfServiceEntry ifSvcEntry = DbIfServiceEntry.create(node.getNodeId(), ifaddr, sid.intValue());

            // now fill in the entry
            //
            if (addrUnmanaged) {
                ifSvcEntry.setStatus(DbIfServiceEntry.STATUS_UNMANAGED);
            } else {
                if (isServicePolledLocally(str(ifaddr), p.getProtocolName(), ipPkg)) {
                    ifSvcEntry.setStatus(DbIfServiceEntry.STATUS_ACTIVE);
                } else {
                    if (isServicePolled(str(ifaddr), p.getProtocolName(), ipPkg)) {
                        ifSvcEntry.setStatus(DbIpInterfaceEntry.STATE_REMOTE);
                    } else {
                        ifSvcEntry.setStatus(DbIfServiceEntry.STATUS_NOT_POLLED);
                    }
                }
            }

            // Set qualifier if available. Currently the qualifier field
            // is used to store the port at which the protocol was found.
            //
            if (p.getQualifiers() != null && p.getQualifiers().get("port") != null) {
                try {
                    Integer port = (Integer) p.getQualifiers().get("port");
                    ifSvcEntry.setQualifier(port.toString());
                } catch (ClassCastException ccE) {
                    // Do nothing
                }
            }

            ifSvcEntry.setSource(DbIfServiceEntry.SOURCE_PLUGIN);
            ifSvcEntry.setNotify(DbIfServiceEntry.NOTIFY_ON);
            if (ifIndex != -1) {
                ifSvcEntry.setIfIndex(ifIndex);
            }
            ifSvcEntry.store();
        }
    }

    private boolean isServicePolled(String ifAddr, String svcName, org.opennms.netmgt.config.poller.Package ipPkg) {
        boolean svcToBePolled = false;
        if (ipPkg != null) {
            svcToBePolled = pollerConfig.isPolled(svcName, ipPkg);
            if (!svcToBePolled) {
                svcToBePolled = pollerConfig.isPolled(ifAddr, svcName);
            }
        }
        return svcToBePolled;
    }

    private boolean isServicePolledLocally(String ifAddr, String svcName,
            org.opennms.netmgt.config.poller.Package ipPkg) {
        boolean svcToBePolled = false;
        if (ipPkg != null && !ipPkg.getRemote()) {
            svcToBePolled = pollerConfig.isPolled(svcName, ipPkg);
            if (!svcToBePolled) {
                svcToBePolled = pollerConfig.isPolledLocally(ifAddr, svcName);
            }
        }
        return svcToBePolled;
    }

    /**
     * Builds a list of InetAddress objects representing each of the
     * interfaces from the IfCollector object which support SNMP and have a
     * valid ifIndex and is a loopback interface. This is in order to allow a
     * non-127.*.*.* loopback address to be chosen as the primary SNMP
     * interface.
     *
     * @param collector IfCollector object containing SNMP and SMB info.
     * @return List of InetAddress objects.
     */
    private List<InetAddress> buildLBSnmpAddressList(IfCollector collector) {
        List<InetAddress> addresses = new ArrayList<InetAddress>();

        // Verify that SNMP info is available
        if (collector.getSnmpCollector() == null) {
            LOG.debug("buildLBSnmpAddressList: no SNMP info for {}", collector.getTarget());
            return addresses;
        }

        // Verify that both the ifTable and ipAddrTable were
        // successfully collected.
        IfSnmpCollector snmpc = collector.getSnmpCollector();
        if (!snmpc.hasIfTable() || !snmpc.hasIpAddrTable()) {
            LOG.info("buildLBSnmpAddressList: missing SNMP info for {}", collector.getTarget());
            return addresses;
        }

        // To be eligible to be the primary SNMP interface for a node:
        //
        // 1. The interface must support SNMP
        // 2. The interface must have a valid ifIndex
        //

        // Add eligible target.
        //
        InetAddress ipAddr = collector.getTarget();
        if (supportsSnmp(collector.getSupportedProtocols()) && hasIfIndex(ipAddr, snmpc)
                && getIfType(ipAddr, snmpc) == 24) {
            LOG.debug("buildLBSnmpAddressList: adding target interface {} temporarily marked as primary!",
                    str(ipAddr));
            addresses.add(ipAddr);
        }

        // Add eligible subtargets.
        //
        if (collector.hasAdditionalTargets()) {
            Map<InetAddress, List<IfCollector.SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
            for (InetAddress currIf : extraTargets.keySet()) {
                // Test current subtarget.
                //
                if (supportsSnmp(extraTargets.get(currIf)) && getIfType(currIf, snmpc) == 24) {
                    LOG.debug(
                            "buildLBSnmpAddressList: adding subtarget interface {} temporarily marked as primary!",
                            str(currIf));
                    addresses.add(currIf);
                }
            } // end while()
        } // end if()

        return addresses;
    }

    /**
     * Builds a list of InetAddress objects representing each of the
     * interfaces from the IfCollector object which support SNMP and have a
     * valid ifIndex.
     *
     * @param collector IfCollector object containing SNMP and SMB info.
     * @return List of InetAddress objects.
     */
    private List<InetAddress> buildSnmpAddressList(IfCollector collector) {
        List<InetAddress> addresses = new ArrayList<InetAddress>();

        // Verify that SNMP info is available
        if (collector.getSnmpCollector() == null) {
            LOG.debug("buildSnmpAddressList: no SNMP info for {}", collector.getTarget());
            return addresses;
        }

        // Verify that both the ifTable and ipAddrTable were
        // successfully collected.
        IfSnmpCollector snmpc = collector.getSnmpCollector();
        if (!snmpc.hasIfTable() || !snmpc.hasIpAddrTable()) {
            LOG.info("buildSnmpAddressList: missing SNMP info for {}", collector.getTarget());
            return addresses;
        }

        // To be eligible to be the primary SNMP interface for a node:
        //
        // 1. The interface must support SNMP
        // 2. The interface must have a valid ifIndex
        //

        // Add eligible target.
        //
        InetAddress ipAddr = collector.getTarget();
        if (supportsSnmp(collector.getSupportedProtocols()) && hasIfIndex(ipAddr, snmpc)) {
            LOG.debug("buildSnmpAddressList: adding target interface {} temporarily marked as primary!",
                    str(ipAddr));
            addresses.add(ipAddr);
        }

        // Add eligible subtargets.
        //
        if (collector.hasAdditionalTargets()) {
            Map<InetAddress, List<IfCollector.SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
            for (InetAddress currIf : extraTargets.keySet()) {

                // Test current subtarget.
                //
                if (supportsSnmp(extraTargets.get(currIf)) && hasIfIndex(currIf, snmpc)) {
                    LOG.debug("buildSnmpAddressList: adding subtarget interface {} temporarily marked as primary!",
                            str(currIf));
                    addresses.add(currIf);
                }
            } // end while()
        } // end if()

        return addresses;
    }

    /**
     * This method is responsbile for determining the node's primary IP
     * interface from among all the node's IP interfaces.
     *
     * @param collector IfCollector object containing SNMP and SMB info.
     * @return InetAddress object of the primary SNMP interface or null if
     * none of the node's interfaces are eligible.
     */
    private InetAddress determinePrimaryInterface(IfCollector collector) {
        InetAddress primaryIf = null;

        // For now hard-coding primary interface address selection method to
        // MIN

        // Initially set the target interface as primary
        primaryIf = collector.getTarget();

        // Next the subtargets will be tested. If is managed and
        // has a smaller numeric IP address then it will in turn be
        // set as the primary interface.
        if (collector.hasAdditionalTargets()) {
            Map<InetAddress, List<IfCollector.SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
            for (InetAddress currIf : extraTargets.keySet()) {
                primaryIf = compareAndSelectPrimary(currIf, primaryIf);
            } // end while()
        } // end if (Collector.hasAdditionalTargets())

        if (primaryIf != null) {
            LOG.debug("determinePrimaryInterface: selected primary interface: {}", str(primaryIf));
        } else {
            LOG.debug("determinePrimaryInterface: no primary interface found");
        }
        return primaryIf;
    }

    /**
     * This is where all the work of the class is done.
     */
    @Override
    public void run() {

        // Convert interface InetAddress object
        //
        InetAddress ifaddr = null;
        ifaddr = addr(m_suspectIf);
        if (ifaddr == null) {
            LOG.warn("SuspectEventProcessor: Failed to convert interface address {} to InetAddress", m_suspectIf);
            return;
        }

        // collect the information
        //
        LOG.debug("SuspectEventProcessor: running collection for {}", str(ifaddr));

        IfCollector collector = new IfCollector(m_pluginManager, ifaddr, true);
        collector.run();

        // Track changes to primary SNMP interface
        InetAddress oldSnmpPrimaryIf = null;
        InetAddress newSnmpPrimaryIf = null;

        // Update the database
        //
        boolean updateCompleted = false;
        boolean useExistingNode = false;
        DbNodeEntry entryNode = null;
        try {
            // Synchronize on the Capsd sync lock so we can check if
            // the interface is already in the database and perform
            // the necessary inserts in one atomic operation
            //
            // The RescanProcessor class is also synchronizing on this
            // lock prior to performing database inserts or updates.
            Connection dbc = null;
            synchronized (Capsd.getDbSyncLock()) {
                // Get database connection
                //
                try {
                    dbc = DataSourceFactory.getInstance().getConnection();

                    // Only add the node/interface to the database if
                    // it isn't already in the database
                    if (!m_capsdDbSyncer.isInterfaceInDB(dbc, ifaddr)) {
                        // Using the interface collector object determine
                        // if this interface belongs under a node already
                        // in the database.
                        //
                        entryNode = getExistingNodeEntry(dbc, collector);

                        if (entryNode == null) {
                            // Create a node entry for the new interface
                            //
                            entryNode = createNode(dbc, ifaddr, collector);
                        } else {
                            // Will use existing node entry
                            //
                            useExistingNode = true;
                        }

                        // Get old primary SNMP interface(s) (if one or more
                        // exists)
                        //
                        List<InetAddress> oldPriIfs = getPrimarySnmpInterfaceFromDb(dbc, entryNode);

                        // Add interfaces
                        //
                        addInterfaces(dbc, entryNode, useExistingNode, ifaddr, collector);

                        // Now that all interfaces have been added to the
                        // database we can update the 'primarySnmpInterface'
                        // field of the ipInterface table. Necessary because
                        // the IP address must already be in the database
                        // to evaluate against a filter rule.
                        //
                        // Determine primary SNMP interface from the lists of
                        // possible addresses
                        // in this order: loopback interfaces in
                        // collectd-configuration.xml,
                        // other interfaces in collectd-configuration.xml,
                        // loopback interfaces,
                        // other interfaces
                        //
                        boolean strict = true;
                        CollectdConfigFactory.getInstance().rebuildPackageIpListMap();
                        List<InetAddress> lbAddressList = buildLBSnmpAddressList(collector);
                        List<InetAddress> addressList = buildSnmpAddressList(collector);
                        // first set the value of issnmpprimary for
                        // secondaries
                        Iterator<InetAddress> iter = addressList.iterator();
                        while (iter.hasNext()) {
                            InetAddress addr = iter.next();
                            if (CollectdConfigFactory.getInstance().isServiceCollectionEnabled(str(addr), "SNMP")) {
                                final DBUtils d = new DBUtils(getClass());
                                try {
                                    PreparedStatement stmt = dbc.prepareStatement(
                                            "UPDATE ipInterface SET isSnmpPrimary='S' WHERE nodeId=? AND "
                                                    + "ipAddr=? AND isManaged!='D'");
                                    d.watch(stmt);
                                    stmt.setInt(1, entryNode.getNodeId());
                                    stmt.setString(2, str(addr));

                                    stmt.executeUpdate();
                                    LOG.debug("updated {} to secondary.", str(addr));
                                } finally {
                                    d.cleanUp();
                                }
                            }
                        }
                        String psiType = null;
                        if (lbAddressList != null) {
                            newSnmpPrimaryIf = capsdConfig.determinePrimarySnmpInterface(lbAddressList, strict);
                            psiType = ConfigFileConstants.getFileName(ConfigFileConstants.COLLECTD_CONFIG_FILE_NAME)
                                    + " loopback addresses";
                        }
                        if (newSnmpPrimaryIf == null) {
                            newSnmpPrimaryIf = capsdConfig.determinePrimarySnmpInterface(addressList, strict);
                            psiType = ConfigFileConstants.getFileName(ConfigFileConstants.COLLECTD_CONFIG_FILE_NAME)
                                    + " addresses";
                        }
                        strict = false;
                        if ((newSnmpPrimaryIf == null) && (lbAddressList != null)) {
                            newSnmpPrimaryIf = capsdConfig.determinePrimarySnmpInterface(lbAddressList, strict);
                            psiType = "DB loopback addresses";
                        }
                        if (newSnmpPrimaryIf == null) {
                            newSnmpPrimaryIf = capsdConfig.determinePrimarySnmpInterface(addressList, strict);
                            psiType = "DB addresses";
                        }
                        if (collector.hasSnmpCollection() && newSnmpPrimaryIf == null) {
                            newSnmpPrimaryIf = ifaddr;
                            psiType = "New suspect ip address";
                        }

                        if (LOG.isDebugEnabled()) {
                            if (newSnmpPrimaryIf == null) {
                                LOG.debug("No primary SNMP interface found");
                            } else {
                                LOG.debug("primary SNMP interface is: {}, selected from {}", newSnmpPrimaryIf,
                                        psiType);
                            }
                        }
                        // iterate over list of old primaries. There should
                        // only be
                        // one or none, but in case there are more, this will
                        // clear
                        // out the extras.
                        Iterator<InetAddress> opiter = oldPriIfs.iterator();
                        if (opiter.hasNext()) {
                            while (opiter.hasNext()) {
                                setPrimarySnmpInterface(dbc, entryNode, newSnmpPrimaryIf, opiter.next());
                            }
                        } else {
                            setPrimarySnmpInterface(dbc, entryNode, newSnmpPrimaryIf, null);
                        }
                        // Update
                        updateCompleted = true;
                    }
                } finally {
                    if (dbc != null) {
                        try {
                            dbc.close();
                        } catch (SQLException e) {
                            LOG.info("run: an sql exception occured closing the database connection", e);
                        }
                    }
                    dbc = null;
                }
            }
        } // end try
        catch (Throwable t) {
            LOG.error("Error writing records", t);
        } finally {
            // remove the interface we've just scanned from the tracker set
            synchronized (m_queuedSuspectTracker) {
                m_queuedSuspectTracker.remove(str(ifaddr));
            }
        }

        // Send events
        //
        if (updateCompleted) {
            if (!useExistingNode) {
                createAndSendNodeAddedEvent(entryNode);
            }

            sendInterfaceEvents(entryNode, useExistingNode, ifaddr, collector);

            if (useExistingNode) {
                generateSnmpDataCollectionEvents(entryNode, oldSnmpPrimaryIf, newSnmpPrimaryIf);
            }
        }

        // send suspectScanCompleted event regardless of scan outcome
        LOG.debug("sendInterfaceEvents: sending suspect scan completed event for {}", str(ifaddr));
        LOG.debug("SuspectEventProcessor for {} completed.", m_suspectIf);
        createAndSendSuspectScanCompletedEvent(ifaddr);
    } // end run

    /**
     * Returns a list of InetAddress object(s) of the primary SNMP
     * interface(s) (if one or more exists).
     *
     * @param dbc  Database connection.
     * @param node DbNodeEntry object representing the interface's parent node
     *             table entry
     * @return List of Old SNMP primary interface addresses (usually just
     * one).
     * @throws java.sql.SQLException if an error occurs updating the ipInterface table
     */
    List<InetAddress> getPrimarySnmpInterfaceFromDb(Connection dbc, DbNodeEntry node) throws SQLException {
        List<InetAddress> priSnmpAddrs = new ArrayList<InetAddress>();

        LOG.debug("getPrimarySnmpInterfaceFromDb: retrieving primary SNMP interface(s) from DB for node {}",
                node.getNodeId());
        InetAddress oldPrimarySnmpIf = null;

        final DBUtils d = new DBUtils(getClass());
        try {
            PreparedStatement stmt = dbc.prepareStatement(
                    "SELECT ipAddr FROM ipInterface WHERE nodeId=? AND isSnmpPrimary='P' AND isManaged!='D'");
            d.watch(stmt);
            stmt.setInt(1, node.getNodeId());

            ResultSet rs = stmt.executeQuery();
            d.watch(rs);
            while (rs.next()) {
                String oldPrimaryAddr = rs.getString(1);
                LOG.debug("getPrimarySnmpInterfaceFromDb: String oldPrimaryAddr = {}", oldPrimaryAddr);
                if (oldPrimaryAddr != null) {
                    oldPrimarySnmpIf = addr(oldPrimaryAddr);
                    LOG.debug("getPrimarySnmpInterfaceFromDb: old primary SNMP interface is {}", oldPrimaryAddr);
                    priSnmpAddrs.add(oldPrimarySnmpIf);
                }
            }
        } catch (SQLException sqlE) {
            LOG.warn("getPrimarySnmpInterfaceFromDb: Exception: {}", sqlE);
            throw sqlE;
        } finally {
            d.cleanUp();
        }

        return priSnmpAddrs;
    }

    /**
     * Determines if any SNMP data collection related events need to be
     * generated based upon the results of the current rescan. If necessary
     * will generate one of the following events:
     * 'reinitializePrimarySnmpInterface' 'primarySnmpInterfaceChanged'
     *
     * @param nodeEntry  DbNodeEntry object of the node being rescanned.
     * @param oldPrimary Old primary SNMP interface
     * @param newPrimary New primary SNMP interface
     */
    private void generateSnmpDataCollectionEvents(DbNodeEntry nodeEntry, InetAddress oldPrimary,
            InetAddress newPrimary) {
        // Sanity check -- should not happen
        if (oldPrimary == null && newPrimary == null) {
            LOG.warn("generateSnmpDataCollectionEvents: both old and new primary SNMP interface vars are null!");
        }

        // Sanity check -- should not happen
        else {
            if (oldPrimary != null && newPrimary == null) {
                LOG.warn("generateSnmpDataCollectionEvents: old primary ({}) is not null but new primary is null!",
                        str(oldPrimary));
            }

            // Just added the primary SNMP interface to the node, the
            // nodeGainedService
            // event already generated is sufficient to start SNMP data
            // collection...no
            // additional events are required.
            else {
                if (oldPrimary == null && newPrimary != null) {
                    LOG.debug(
                            "generateSnmpDataCollectionEvents: identified {} as the primary SNMP interface for node {}",
                            str(newPrimary), nodeEntry.getNodeId());
                }

                // A PrimarySnmpInterfaceChanged event is generated if the scan
                // found a different primary SNMP interface than what is stored
                // in the database.
                //
                else {
                    if (!oldPrimary.equals(newPrimary)) {
                        LOG.debug(
                                "generateSnmpDataCollectionEvents: primary SNMP interface has changed.  Was: {} Is: {}",
                                str(oldPrimary), str(newPrimary));

                        createAndSendPrimarySnmpInterfaceChangedEvent(nodeEntry.getNodeId(), newPrimary,
                                oldPrimary);
                    }

                    // The primary SNMP interface did not change but the Capsd scan just
                    // added
                    // an interface to the node so we need to update the interface
                    // map which is maintained in memory for the purpose of doing
                    // SNMP data collection. Therefore we generate a
                    // reinitializePrimarySnmpInterface event so that this map
                    // can be refreshed based on the most up to date information
                    // in the database.
                    else {
                        LOG.debug(
                                "generateSnmpDataCollectionEvents: Generating reinitializeSnmpInterface event for interface {}",
                                str(newPrimary));
                        createAndSendReinitializePrimarySnmpInterfaceEvent(nodeEntry.getNodeId(), newPrimary);
                    }
                }
            }
        }
    }

    /**
     * This method is responsible for generating a primarySnmpInterfaceChanged
     * event and sending it to eventd..
     *
     * @param nodeId       Nodeid of node being rescanned.
     * @param newPrimaryIf new primary SNMP interface address
     * @param oldPrimaryIf old primary SNMP interface address
     */
    private void createAndSendPrimarySnmpInterfaceChangedEvent(int nodeId, InetAddress newPrimaryIf,
            InetAddress oldPrimaryIf) {

        LOG.debug(
                "createAndSendPrimarySnmpInterfaceChangedEvent: nodeId: {} oldPrimarySnmpIf: '{}' newPrimarySnmpIf: '{}'",
                nodeId, str(oldPrimaryIf), str(newPrimaryIf));

        EventBuilder bldr = createEventBuilder(EventConstants.PRIMARY_SNMP_INTERFACE_CHANGED_EVENT_UEI);
        bldr.setNodeid(nodeId);
        bldr.setInterface(newPrimaryIf);
        bldr.setService("SNMP");

        if (str(oldPrimaryIf) != null) {
            bldr.addParam(EventConstants.PARM_OLD_PRIMARY_SNMP_ADDRESS, str(oldPrimaryIf));
        }

        if (str(newPrimaryIf) != null) {
            bldr.addParam(EventConstants.PARM_NEW_PRIMARY_SNMP_ADDRESS, str(newPrimaryIf));
        }

        sendEvent(bldr.getEvent());
    }

    /**
     * This method is responsible for generating a
     * reinitializePrimarySnmpInterface event and sending it to eventd.
     *
     * @param nodeId        Nodeid of node being rescanned.
     * @param primarySnmpIf Primary SNMP interface address.
     */
    private void createAndSendReinitializePrimarySnmpInterfaceEvent(int nodeId, InetAddress primarySnmpIf) {
        LOG.debug("reinitializePrimarySnmpInterface: nodeId: {} interface: {}", nodeId, str(primarySnmpIf));

        EventBuilder bldr = createEventBuilder(EventConstants.REINITIALIZE_PRIMARY_SNMP_INTERFACE_EVENT_UEI);
        bldr.setNodeid(nodeId);
        bldr.setInterface(primarySnmpIf);

        sendEvent(bldr.getEvent());
    }

    /**
     * This method is responsible for creating all the necessary
     * interface-level events for the node and sending them to Eventd.
     *
     * @param node            DbNodeEntry object for the parent node.
     * @param useExistingNode TRUE if existing node was used, FALSE if new node was
     *                        created.
     * @param ifaddr          Target interface address
     * @param collector       Interface collector containing SNMP and SMB info.
     */
    private void sendInterfaceEvents(DbNodeEntry node, boolean useExistingNode, InetAddress ifaddr,
            IfCollector collector) {
        // nodeGainedInterface
        //
        LOG.debug("sendInterfaceEvents: sending node gained interface event for {}", str(ifaddr));

        createAndSendNodeGainedInterfaceEvent(node.getNodeId(), ifaddr);

        // nodeGainedService
        //
        LOG.debug("sendInterfaceEvents: processing supported services for {}", str(ifaddr));
        for (IfCollector.SupportedProtocol p : collector.getSupportedProtocols()) {
            LOG.debug("sendInterfaceEvents: sending event for service: {}", p.getProtocolName());
            createAndSendNodeGainedServiceEvent(node, ifaddr, p.getProtocolName(), null);
        }

        // If the useExistingNode flag is set to TRUE we're done, none of the
        // sub-targets should have been added.
        //
        if (useExistingNode) {
            return;
        }

        // If SNMP info available send events for sub-targets
        //
        if (collector.hasSnmpCollection() && !collector.getSnmpCollector().failed()) {
            Map<InetAddress, List<IfCollector.SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
            for (InetAddress xifaddr : extraTargets.keySet()) {

                // nodeGainedInterface
                //
                createAndSendNodeGainedInterfaceEvent(node.getNodeId(), xifaddr);

                // nodeGainedService
                //
                List<IfCollector.SupportedProtocol> supportedProtocols = extraTargets.get(xifaddr);
                LOG.debug("interface {} supports {} protocols.", xifaddr, supportedProtocols.size());
                if (supportedProtocols != null) {
                    for (IfCollector.SupportedProtocol p : supportedProtocols) {
                        createAndSendNodeGainedServiceEvent(node, xifaddr, p.getProtocolName(), null);
                    }
                }
            }
        }
    }

    /**
     * This method is responsible for creating and sending a 'nodeAdded' event
     * to Eventd
     *
     * @param nodeEntry DbNodeEntry object for the newly created node.
     */
    private void createAndSendNodeAddedEvent(DbNodeEntry nodeEntry) {

        EventBuilder bldr = createEventBuilder(EventConstants.NODE_ADDED_EVENT_UEI);
        bldr.setNodeid(nodeEntry.getNodeId());
        bldr.addParam(EventConstants.PARM_NODE_LABEL, nodeEntry.getLabel());
        if (nodeEntry.getLabelSource() != null) {
            bldr.addParam(EventConstants.PARM_NODE_LABEL_SOURCE, nodeEntry.getLabelSource().toString());
        }
        bldr.addParam(EventConstants.PARM_METHOD, "icmp");

        sendEvent(bldr.getEvent());
    }

    private EventBuilder createEventBuilder(String uei) {
        EventBuilder bldr = new EventBuilder(uei, EVENT_SOURCE);
        bldr.setHost(Capsd.getLocalHostAddress());
        return bldr;
    }

    private void sendEvent(Event newEvent) {
        // Send event to Eventd
        try {
            eventIpcManager.sendNow(newEvent);

            LOG.debug("sendEvent: successfully sent: {}", toString(newEvent));
        } catch (Throwable t) {
            LOG.warn("run: unexpected throwable exception caught during send to middleware", t);
        }
    }

    private String toString(Event e) {
        StringBuilder buf = new StringBuilder();
        buf.append("Event uei: ").append(e.getUei());
        buf.append(" For ").append(e.getNodeid()).append('/').append(e.getInterface()).append('/')
                .append(e.getService());
        return buf.toString();
    }

    /**
     * This method is responsible for creating and sending a
     * 'duplicateIPAddress' event to Eventd
     *
     * @param nodeId Interface's parent node identifier.
     * @param ipAddr Interface's IP address
     */
    private void createAndSendDuplicateIpaddressEvent(int nodeId, String ipAddr) {
        // create the event to be sent

        EventBuilder bldr = createEventBuilder(EventConstants.DUPLICATE_IPINTERFACE_EVENT_UEI);
        bldr.setNodeid(nodeId);
        bldr.setInterface(addr(ipAddr));
        bldr.addParam(EventConstants.PARM_IP_HOSTNAME, getHostName(ipAddr));
        bldr.addParam(EventConstants.PARM_METHOD, "icmp");

        sendEvent(bldr.getEvent());
    }

    private String getHostName(String ipAddr) {
        String hostName = InetAddressUtils.normalize(ipAddr);
        return hostName == null ? "" : hostName;
    }

    /**
     * This method is responsible for creating and sending a
     * 'nodeGainedInterface' event to Eventd
     *
     * @param nodeId Interface's parent node identifier.
     * @param ipAddr Interface's IP address
     */
    private void createAndSendNodeGainedInterfaceEvent(int nodeId, InetAddress ipAddr) {

        EventBuilder bldr = createEventBuilder(EventConstants.NODE_GAINED_INTERFACE_EVENT_UEI);
        bldr.setNodeid(nodeId);
        bldr.setInterface(ipAddr);
        bldr.addParam(EventConstants.PARM_IP_HOSTNAME, ipAddr.getHostName());
        bldr.addParam(EventConstants.PARM_METHOD, "icmp");

        sendEvent(bldr.getEvent());
    }

    /**
     * This method is responsible for creating and sending a
     * 'nodeGainedService' event to Eventd
     *
     * @param nodeEntry Interface's parent node identifier.
     * @param ipAddr    Interface's IP address
     * @param svcName   Service name
     * @param qualifier Service qualifier (typically the port on which the service
     *                  was found)
     */
    private void createAndSendNodeGainedServiceEvent(DbNodeEntry nodeEntry, InetAddress ipAddr, String svcName,
            String qualifier) {

        EventBuilder bldr = createEventBuilder(EventConstants.NODE_GAINED_SERVICE_EVENT_UEI);
        bldr.setNodeid(nodeEntry.getNodeId());
        bldr.setInterface(ipAddr);
        bldr.setService(svcName);
        bldr.addParam(EventConstants.PARM_IP_HOSTNAME, ipAddr.getHostName());
        bldr.addParam(EventConstants.PARM_NODE_LABEL, nodeEntry.getLabel());
        if (nodeEntry.getLabelSource() != null) {
            bldr.addParam(EventConstants.PARM_NODE_LABEL_SOURCE, nodeEntry.getLabelSource().toString());
        }
        // Add qualifier (if available)
        if (qualifier != null && qualifier.length() > 0) {
            bldr.addParam(EventConstants.PARM_QUALIFIER, qualifier);
        }

        // Add sysName (if available)
        if (nodeEntry.getSystemName() != null) {
            bldr.addParam(EventConstants.PARM_NODE_SYSNAME, nodeEntry.getSystemName());
        }

        // Add sysDescr (if available)
        if (nodeEntry.getSystemDescription() != null) {
            bldr.addParam(EventConstants.PARM_NODE_SYSDESCRIPTION, nodeEntry.getSystemDescription());
        }

        sendEvent(bldr.getEvent());
    }

    /**
     * This method is responsible for creating and sending a
     * 'suspectScanCompleted' event to Eventd
     *
     * @param ipAddr IP address of the interface for which the suspect scan has completed
     */
    private void createAndSendSuspectScanCompletedEvent(InetAddress ipAddr) {
        EventBuilder bldr = createEventBuilder(EventConstants.SUSPECT_SCAN_COMPLETED_EVENT_UEI);
        bldr.setInterface(ipAddr);
        bldr.addParam(EventConstants.PARM_IP_HOSTNAME, ipAddr.getHostName());
        sendEvent(bldr.getEvent());
    }
} // end class