/*******************************************************************************
* 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.capsd;
import static org.opennms.core.utils.InetAddressUtils.addr;
import static org.opennms.core.utils.InetAddressUtils.str;
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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.core.utils.ThreadCategory;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.capsd.IfCollector.SupportedProtocol;
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.config.CapsdConfig;
import org.opennms.netmgt.config.CapsdConfigFactory;
import org.opennms.netmgt.config.CollectdConfigFactory;
import org.opennms.netmgt.config.DataSourceFactory;
import org.opennms.netmgt.config.PollerConfig;
import org.opennms.netmgt.config.PollerConfigFactory;
import org.opennms.netmgt.eventd.EventIpcManagerFactory;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.xml.event.Event;
import org.springframework.util.Assert;
/**
* 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>
*/
final class SuspectEventProcessor implements Runnable {
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'";
/**
* IP address of new suspect interface
*/
String m_suspectIf;
private CapsdDbSyncer m_capsdDbSyncer;
private PluginManager m_pluginManager;
private static Set<String> m_queuedSuspectTracker;
/**
* 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);
}
}
/**
* 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 SQLException
* Thrown if an error occurs retrieving the parent nodeid from
* the database.
*/
private DbNodeEntry getExistingNodeEntry(java.sql.Connection dbc,
IfCollector collector) throws SQLException {
if (log().isDebugEnabled())
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);
if (log().isDebugEnabled())
log().debug("getExistingNodeEntry: number of interfaces retrieved for ifIndex "
+ ifIndex + " is: " + 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
//
if (log().isDebugEnabled())
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);
if (log().isDebugEnabled())
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)) {
if (log().isDebugEnabled())
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)) {
if (log().isDebugEnabled())
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 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(DbNodeEntry.NODE_TYPE_ACTIVE);
entryNode.setLabel(primaryIf.getHostName());
if (entryNode.getLabel().equals(str(primaryIf)))
entryNode.setLabelSource(DbNodeEntry.LABEL_SOURCE_ADDRESS);
else
entryNode.setLabelSource(DbNodeEntry.LABEL_SOURCE_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: "
+ str(ifaddr)
+ " has NO sysObjectId!!!!");
// sysName
String str = sysgrp.getSysName();
if (log().isDebugEnabled())
log().debug("SuspectEventProcessor: "
+ str(ifaddr) + " has sysName: "
+ 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() == DbNodeEntry.LABEL_SOURCE_ADDRESS) {
entryNode.setLabel(str);
entryNode.setLabelSource(DbNodeEntry.LABEL_SOURCE_SYSNAME);
}
}
// sysDescription
str = sysgrp.getSysDescr();
if (log().isDebugEnabled())
log().debug("SuspectEventProcessor: "
+ str(ifaddr)
+ " has sysDescription: " + str);
if (str != null && str.length() > 0)
entryNode.setSystemDescription(str);
// sysLocation
str = sysgrp.getSysLocation();
if (log().isDebugEnabled())
log().debug("SuspectEventProcessor: "
+ str(ifaddr) + " has sysLocation: "
+ str);
if (str != null && str.length() > 0)
entryNode.setSystemLocation(str);
// sysContact
str = sysgrp.getSysContact();
if (log().isDebugEnabled())
log().debug("SuspectEventProcessor: "
+ str(ifaddr) + " has sysContact: "
+ 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() != DbNodeEntry.LABEL_SOURCE_HOSTNAME) {
entryNode.setLabel(smbc.getNbtName());
entryNode.setLabelSource(DbNodeEntry.LABEL_SOURCE_NETBIOS);
entryNode.setNetBIOSName(entryNode.getLabel());
if (smbc.getDomainName() != null) {
entryNode.setDomainName(smbc.getDomainName());
}
}
}
entryNode.store(dbc);
return entryNode;
}
/**
* This method is responsble 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 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 = CapsdConfigFactory.getInstance();
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: " + ifaddr + " is unmanaged");
ipIfEntry.setManagedState(DbIpInterfaceEntry.STATE_UNMANAGED);
} else {
log().debug("addInterfaces: " + ifaddr + " is managed");
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);
}
if (log().isDebugEnabled()) {
log().debug("SuspectEventProcessor: setting ifindex for "
+ nodeId + "/" + ifaddr + " to " + 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.
*/
pollerCfgFactory.rebuildPackageIpListMap();
boolean ipToBePolled = false;
ipPkg = pollerCfgFactory.getFirstPackageMatch(str(ifaddr));
if (ipPkg != null) {
ipToBePolled = true;
}
if (log().isDebugEnabled()) {
log().debug("addInterfaces: " + ifaddr + " is to be polled = "
+ 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;
if (log().isDebugEnabled()) {
log().debug("SuspectEventProcessor: no valid ifIndex for "
+ ifaddr + " Assume this is a lame SNMP host");
}
}
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 = CapsdConfigFactory.getInstance();
PollerConfig pollerCfgFactory = PollerConfigFactory.getInstance();
IfSnmpCollector snmpc = collector.getSnmpCollector();
// Made it this far...lets add the IP sub-interfaces
addSubIpInterfaces(dbc, node, collector, now, nodeId, cFactory, pollerCfgFactory,
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<SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
for(InetAddress xifaddr : extraTargets.keySet()) {
if (log().isDebugEnabled()) {
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. "
+ str(xifaddr)
+ " set to not eligible");
}
} else {
/*
* No ifIndex found so set primary state to NOT_ELIGIBLE
*/
log().debug("addInterfaces: No ifIndex found. "
+ str(xifaddr) + " set to not eligible");
}
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.
//
PollerConfigFactory.getInstance().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 (log().isDebugEnabled() && addrs != null) {
log().debug("SuspectEventProcessor: "
+ str(addrs[0]) + " has ifDescription: "
+ str);
}
if (str != null && str.length() > 0) {
snmpEntry.setDescription(str);
}
// physical address
String physAddr = null;
try {
physAddr = ifte.getPhysAddr();
if (log().isDebugEnabled() && addrs != null) {
log().debug("SuspectEventProcessor: "
+ str(addrs[0])
+ " has physical address: -" + physAddr + "-");
}
} catch (IllegalArgumentException iae) {
physAddr = null;
if (log().isDebugEnabled() && addrs != null) {
log().debug("ifPhysAddress." + ifte.getIfIndex() + " on node "
+ nodeId + " / " + str(addrs[0])
+ " could not be converted to a hex string (not a PhysAddr / OCTET STRING?), setting to null.");
}
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 SQLException
* if an error occurs adding interfaces to the ipInterface
* table.
*/
private void addSupportedProtocols(DbNodeEntry node, InetAddress ifaddr,
List<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 "
+ node.getNodeId()
+ ": Cant add ip services for non-ip interface. Just return.");
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(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 = PollerConfigFactory.getInstance().isPolled(svcName, ipPkg);
if (!svcToBePolled)
svcToBePolled = PollerConfigFactory.getInstance().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 = PollerConfigFactory.getInstance().isPolled(svcName, ipPkg);
if (!svcToBePolled)
svcToBePolled = PollerConfigFactory.getInstance().isPolledLocally(ifAddr, svcName);
}
return svcToBePolled;
}
/**
* 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<SupportedProtocol> supportedProtocols) {
for(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);
if (log().isDebugEnabled())
log().debug("hasIfIndex: ipAddress: " + str(ipaddr)
+ " has ifIndex: " + 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);
if (log().isDebugEnabled())
log().debug("getIfType: ipAddress: " + str(ipaddr)
+ " has ifIndex: " + ifIndex + " and ifType: " + ifType);
return ifType;
}
/**
* 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.
*/
static InetAddress compareAndSelectPrimary(InetAddress currentIf, InetAddress oldPrimary) {
InetAddress newPrimary = null;
if (oldPrimary == null) {
if (!CapsdConfigFactory.getInstance().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 (!CapsdConfigFactory.getInstance().isAddressUnmanaged(currentIf)) {
newPrimary = currentIf;
}
}
if (newPrimary != null) {
return newPrimary;
}
else {
return oldPrimary;
}
}
/**
* 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) {
if (log().isDebugEnabled())
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) {
if (log().isDebugEnabled())
log().debug("buildLBSnmpAddressList: adding target interface "
+ str(ipAddr)
+ " temporarily marked as primary!");
addresses.add(ipAddr);
}
// Add eligible subtargets.
//
if (collector.hasAdditionalTargets()) {
Map<InetAddress, List<SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
for(InetAddress currIf : extraTargets.keySet()) {
// Test current subtarget.
//
if (supportsSnmp(extraTargets.get(currIf))
&& getIfType(currIf, snmpc) == 24) {
if (log().isDebugEnabled())
log().debug("buildLBSnmpAddressList: adding subtarget interface "
+ str(currIf)
+ " temporarily marked as primary!");
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) {
if (log().isDebugEnabled())
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)) {
if (log().isDebugEnabled())
log().debug("buildSnmpAddressList: adding target interface "
+ str(ipAddr)
+ " temporarily marked as primary!");
addresses.add(ipAddr);
}
// Add eligible subtargets.
//
if (collector.hasAdditionalTargets()) {
Map<InetAddress, List<SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
for(InetAddress currIf : extraTargets.keySet()) {
// Test current subtarget.
//
if (supportsSnmp(extraTargets.get(currIf))
&& hasIfIndex(currIf, snmpc)) {
if (log().isDebugEnabled())
log().debug("buildSnmpAddressList: adding subtarget interface "
+ str(currIf)
+ " temporarily marked as primary!");
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<SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
for(InetAddress currIf : extraTargets.keySet()) {
primaryIf = compareAndSelectPrimary(currIf, primaryIf);
} // end while()
} // end if (Collector.hasAdditionalTargets())
if (log().isDebugEnabled())
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.
*/
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 "
+ m_suspectIf + " to InetAddress");
return;
}
// collect the information
//
if (log().isDebugEnabled())
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 " + str(addr) + " to secondary.");
} finally {
d.cleanUp();
}
}
}
String psiType = null;
if (lbAddressList != null) {
newSnmpPrimaryIf = CapsdConfigFactory.getInstance().determinePrimarySnmpInterface(lbAddressList, strict);
psiType = ConfigFileConstants.getFileName(ConfigFileConstants.COLLECTD_CONFIG_FILE_NAME) + " loopback addresses";
}
if (newSnmpPrimaryIf == null) {
newSnmpPrimaryIf = CapsdConfigFactory.getInstance().determinePrimarySnmpInterface(addressList, strict);
psiType = ConfigFileConstants.getFileName(ConfigFileConstants.COLLECTD_CONFIG_FILE_NAME) + " addresses";
}
strict = false;
if ((newSnmpPrimaryIf == null) && (lbAddressList != null)) {
newSnmpPrimaryIf = CapsdConfigFactory.getInstance().determinePrimarySnmpInterface(lbAddressList, strict);
psiType = "DB loopback addresses";
}
if (newSnmpPrimaryIf == null) {
newSnmpPrimaryIf = CapsdConfigFactory.getInstance().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: "
+ newSnmpPrimaryIf
+ ", selected from " + 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) {
if (log().isInfoEnabled())
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
if (log().isDebugEnabled()) {
log().debug("sendInterfaceEvents: sending suspect scan completed event for " + str(ifaddr));
log().debug("SuspectEventProcessor for " + m_suspectIf + " completed.");
}
createAndSendSuspectScanCompletedEvent(ifaddr);
} // end run
private static ThreadCategory log() {
return ThreadCategory.getInstance(SuspectEventProcessor.class);
}
/**
* 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
* @throws SQLException
* if an error occurs updating the ipInterface table
* @return List of Old SNMP primary interface addresses (usually just
* one).
*/
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;
}
/**
* 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 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) {
if (log().isDebugEnabled())
log().debug("setPrimarySnmpInterface: newSnmpPrimary is null, nothing to set, returning.");
return;
} else {
if (log().isDebugEnabled())
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
if (log().isDebugEnabled())
log().debug("setPrimarySnmpInterface: Old and new primary interfaces are the same");
}
// Set primary SNMP interface 'isSnmpPrimary' field to 'P' for primary
//
if (newPrimarySnmpIf != null) {
if (log().isDebugEnabled())
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();
if (log().isDebugEnabled())
log().debug("setPrimarySnmpInterface: Completed update of new primary interface to PRIMARY.");
} finally {
d.cleanUp();
}
}
}
/**
* 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 static 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 static boolean isScanQueuedForAddress(String ipAddr) {
synchronized(m_queuedSuspectTracker) {
return (m_queuedSuspectTracker.contains(ipAddr));
}
}
/**
* 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 ("
+ str(oldPrimary)
+ ") is not null but new primary is null!");
}
// 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) {
if (log().isDebugEnabled())
log().debug("generateSnmpDataCollectionEvents: identified "
+ str(newPrimary)
+ " as the primary SNMP interface for node "
+ 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)) {
if (log().isDebugEnabled()) {
log().debug("generateSnmpDataCollectionEvents: primary SNMP interface has changed. Was: "
+ str(oldPrimary)
+ " Is: "
+ 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 {
if (log().isDebugEnabled())
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) {
if (log().isDebugEnabled())
log().debug("createAndSendPrimarySnmpInterfaceChangedEvent: nodeId: "
+ nodeId
+ " oldPrimarySnmpIf: '"
+ str(oldPrimaryIf)
+ "' newPrimarySnmpIf: '"
+ 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) {
if (log().isDebugEnabled())
log().debug("reinitializePrimarySnmpInterface: nodeId: " + nodeId
+ " interface: " + 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
//
if (log().isDebugEnabled())
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(SupportedProtocol p : collector.getSupportedProtocols()) {
if (log().isDebugEnabled())
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<SupportedProtocol>> extraTargets = collector.getAdditionalTargets();
for(InetAddress xifaddr : extraTargets.keySet()) {
// nodeGainedInterface
//
createAndSendNodeGainedInterfaceEvent(node.getNodeId(),
xifaddr);
// nodeGainedService
//
List<SupportedProtocol> supportedProtocols = extraTargets.get(xifaddr);
log().debug("interface " + xifaddr + " supports "
+ supportedProtocols.size() + " protocols.");
if (supportedProtocols != null) {
for(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());
bldr.addParam(EventConstants.PARM_NODE_LABEL_SOURCE, nodeEntry.getLabelSource());
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 {
EventIpcManagerFactory.getIpcManager().sendNow(newEvent);
if (log().isDebugEnabled())
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());
bldr.addParam(EventConstants.PARM_NODE_LABEL_SOURCE, nodeEntry.getLabelSource());
// 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