/******************************************************************************* * 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 java.lang.reflect.UndeclaredThrowableException; import java.net.InetAddress; import java.net.NoRouteToHostException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.opennms.core.utils.InetAddressComparator; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.capsd.snmp.IfTableEntry; import org.opennms.netmgt.config.CapsdConfigFactory; import org.opennms.netmgt.config.CapsdProtocolInfo; /** * This class is designed to collect all the relevant information from the * target designated during construction. The target is initially polled using * all the configured plugins, then tested for SMB and SNMP. If either of those * plugins were detected then an additional collection of the SMB/SNMP * information is performed. If any node has multiple interfaces in it then * addition probes of those interfaces are performed. The SNMP/SMB collections * are performed only once though. * * @author <a href="mailto:weave@oculan.com">Weave </a> * @author <a href="http://www.opennms.org">OpenNMS </a> */ public final class IfCollector implements Runnable { private PluginManager m_pluginManager; /** * The primary target internet address */ private final InetAddress m_target; /** * The SMB collector. If the interface is determine to have SMB connectivity * then the collector is run. */ private IfSmbCollector m_smbCollector; /** * If the interface is determined to have SNMP capabilities then the * collector is run. */ private IfSnmpCollector m_snmpCollector; /** * The list of supported protocols on this interface. */ private List<SupportedProtocol> m_protocols; /** * The list of sub-targets found via SNMP. Indexed by InetAddress */ private Map<InetAddress, List<SupportedProtocol>> m_subTargets; /** * List of SnmpInt32 objects representing each of the unnamed/non-IP * interfaces found via SNMP */ private List<Integer> m_nonIpInterfaces; /** * Boolean flag which indicates if SNMP collection is to be done. */ private boolean m_doSnmpCollection; private Set<InetAddress> m_previouslyProbed; /** * This class is used to encapsulate the supported protocol information * discovered for an interface. The is the combination of the protocol name * and the in/out qualifiers for the plugin. * * @author <a href="mailto:weave@oculan.com">Weave </a> * */ static final class SupportedProtocol { /** * The protocol name */ private final String m_name; /** * The map of qualifiers from the plugin that discovered this protocol. */ private final Map<String, Object> m_qualifiers; /** * Creates a new supported protocol based upon the protocol string and * the qualifier map. * * @param protoName * The name of the protocol. * @param qualifiers * The protocol qualifiers. */ SupportedProtocol(String protoName, Map<String, Object> qualifiers) { m_name = protoName; m_qualifiers = qualifiers; } /** * Returns the name of the discovered protocol. */ String getProtocolName() { return m_name; } /** * Returns the map of qualifiers from the plugin that discovered this * protocol. */ Map<String, Object> getQualifiers() { return m_qualifiers; } } /** * This method is used to <em>probe</em> the target addresses using the * configured list of protocol specifications from the Configuration * Manager. The list of supported protocols are added to the supports list. * Any failures in the plugins are logged and discarded. * * @param target * The target to probe * @param supports * The supported protocols (SupportedProtocol) * */ private void probe(InetAddress target, List<SupportedProtocol> supports) { String logAddr = InetAddressUtils.str(target); CapsdProtocolInfo[] plugins = m_pluginManager.getProtocolSpecification(target); // First run the plugins to find out all the capabilities // for the interface // for (int i = 0; i < plugins.length; i++) { if (log().isDebugEnabled()) { log().debug(logAddr + " testing plugin " + plugins[i].getProtocol()); } if (plugins[i].isAutoEnabled()) { if (log().isDebugEnabled()) { log().debug(logAddr + " protocol " + plugins[i].getProtocol() + " is auto enabled"); } supports.add(new SupportedProtocol(plugins[i].getProtocol(), null)); continue; } try { Plugin p = plugins[i].getPlugin(); Map<String, Object> q = plugins[i].getParameters(); boolean r = p.isProtocolSupported(target, q); if (log().isDebugEnabled()) { log().debug(logAddr + " protocol " + plugins[i].getProtocol() + " supported? " + (r ? "true" : "false")); } if (r) { supports.add(new SupportedProtocol(plugins[i].getProtocol(), q)); } } catch (UndeclaredThrowableException utE) { Throwable t = utE.getUndeclaredThrowable(); if (t instanceof NoRouteToHostException) { if (CapsdConfigFactory.getInstance().getAbortProtocolScansFlag()) { log().info("IfCollector: No route to host " + logAddr + ", aborting protocol scans."); break; // Break out of plugin loop } else { log().info("IfCollector: No route to host " + logAddr + ", continuing protocol scans."); } } else { log().warn("IfCollector: Caught undeclared throwable exception when testing for protocol " + plugins[i].getProtocol() + " on host " + logAddr, utE); } } catch (Throwable t) { log().warn("IfCollector: Caught an exception when testing for protocol " + plugins[i].getProtocol() + " on host " + logAddr, t); } if (log().isDebugEnabled()) { log().debug(logAddr + " plugin " + plugins[i].getProtocol() + " completed!"); } } } /** * Constructs a new collector instance. The collector's target is passed as * an argument to the constructor. Very little initialization is preformed * in the constructor. The main work of the class is preformed in the * {@link #run run}method. This provides a well known interface that can be * collected in a thread pool or directly invoked. * * @param addr * The target of the poll. * @param doSnmpCollection * Flag which indicates if SNMP collection should be done. * */ IfCollector(PluginManager pluginManager, InetAddress addr, boolean doSnmpCollection) { this(pluginManager, addr, doSnmpCollection, new HashSet<InetAddress>()); } IfCollector(PluginManager pluginManager, InetAddress addr, boolean doSnmpCollection, Set<InetAddress> previouslyProbed) { m_pluginManager = pluginManager; m_target = addr; m_doSnmpCollection = doSnmpCollection; m_smbCollector = null; m_snmpCollector = null; m_protocols = new ArrayList<SupportedProtocol>(8); m_subTargets = null; m_nonIpInterfaces = null; m_previouslyProbed = previouslyProbed; } /** * Returns the target of this collection */ InetAddress getTarget() { return m_target; } /** * Returns the supported protocols for this interface. */ List<SupportedProtocol> getSupportedProtocols() { return m_protocols; } /** * Returns true if this target had additional interfaces found by SNMP */ boolean hasAdditionalTargets() { return m_subTargets != null && !m_subTargets.isEmpty(); } /** * Returns the map of additional interface targets. The keys are instances * of {@link java.net.InetAddress addresses}and the mapped values are * {@link java.util.List lists}of supported protocols. * */ Map<InetAddress, List<SupportedProtocol>> getAdditionalTargets() { return m_subTargets; } /** * Returns true if this target has non-IP interfaces found by SNMP */ boolean hasNonIpInterfaces() { return m_nonIpInterfaces != null && !m_nonIpInterfaces.isEmpty(); } /** * Returns the list of non-IP interfaces.. * */ List<Integer> getNonIpInterfaces() { return m_nonIpInterfaces; } /** * Returns true if the node supported SMB and the collection succeeded */ boolean hasSmbCollection() { return (m_smbCollector != null); } /** * Returns the collected SMB information for the node. */ IfSmbCollector getSmbCollector() { return m_smbCollector; } /** * Returns true if the target supported SNMP and the collection succeeded. */ boolean hasSnmpCollection() { return (m_snmpCollector != null); } /** * Returns the Snmp Collection of information */ IfSnmpCollector getSnmpCollector() { return m_snmpCollector; } void deleteSnmpCollector() { m_snmpCollector = null; } /** * The main collection routine of the class. This method is used to poll the * address, and any additional interfaces discovered via SNMP. */ public void run() { if (log().isDebugEnabled()) { log().debug("IfCollector.run: run method invoked to collect information for address " + InetAddressUtils.str(m_target)); } // Now go throught the successful plugin checks // and see if either SMB, MSExchange, or SNMP is // supported on the target node // boolean isSnmp = false; boolean isSnmpV2 = false; boolean isSmb = false; boolean hasExchange = false; probe(m_target, m_protocols); m_previouslyProbed.add(m_target); // First run the plugins to find out all the capabilities // for the interface // Iterator<SupportedProtocol> iter = m_protocols.iterator(); while (iter.hasNext()) { SupportedProtocol proto = iter.next(); if (proto.getProtocolName().equalsIgnoreCase("snmp")) { isSnmp = true; } else if (proto.getProtocolName().equalsIgnoreCase("smb")) { isSmb = true; } else if (proto.getProtocolName().equalsIgnoreCase("msexchange")) { isSmb = true; hasExchange = true; } } // collect the SMB information // if (isSmb) { log().debug("IfCollector.run: starting SMB collection"); try { m_smbCollector = new IfSmbCollector(m_target, hasExchange); m_smbCollector.run(); } catch (Throwable t) { m_smbCollector = null; log().warn("IfCollector.run: Caught an exception when collecting SMB information from target " + InetAddressUtils.str(m_target), t); } log().debug("IfCollector.run: SMB collection completed"); } // collect the snmp information if necessary // if ((isSnmp || isSnmpV2) && m_doSnmpCollection) { log().debug("IfCollector.run: starting SNMP collection"); try { m_snmpCollector = new IfSnmpCollector(m_target); m_snmpCollector.run(); if (m_snmpCollector.hasIpAddrTable() && m_snmpCollector.hasIfTable()) { m_subTargets = new TreeMap<InetAddress, List<SupportedProtocol>>(new InetAddressComparator()); m_nonIpInterfaces = new ArrayList<Integer>(); // Iterate over ifTable entries // for (IfTableEntry ifEntry : m_snmpCollector.getIfTable()) { // Get the ifIndex // Integer ifIndex = ifEntry.getIfIndex(); if (ifIndex == null) continue; // Get list of all IP addresses for the current ifIndex // int index = ifIndex.intValue(); List<InetAddress> ipAddrs = m_snmpCollector.getIpAddrTable().getIpAddresses(index); if (ipAddrs == null || ipAddrs.size() == 0) { // Non IP interface InetAddress nonIpAddr = null; nonIpAddr = InetAddressUtils.addr("0.0.0.0"); if (ipAddrs == null) { ipAddrs = new ArrayList<InetAddress>(); } ipAddrs.add(nonIpAddr); } // Iterate over this interface's IP address list // Iterator<InetAddress> s = ipAddrs.iterator(); while (s.hasNext()) { InetAddress subtarget = s.next(); // if the target failed to convert or if it // is equal to the current target then skip it // if (subtarget == null || subtarget.equals(m_target) || m_previouslyProbed.contains(subtarget)) continue; // now find the ifType // Integer ifType = ifEntry.getIfType(); // lookup of if type failed, next! // if (ifType == null) continue; // now check for loopback if (subtarget.isLoopbackAddress()) { // Skip if loopback if (log().isDebugEnabled()) { log().debug("ifCollector.run: Loopback interface: " + InetAddressUtils.str(subtarget) + ", skipping..."); } continue; } // now check for non-IP interface // if (InetAddressUtils.str(subtarget).equals("0.0.0.0")) { // its a non-IP interface...add its ifIndex to // the non-IP interface list // m_nonIpInterfaces.add(ifIndex); continue; } // ok it appears to be ok, so probe it! // List<SupportedProtocol> probelist = new ArrayList<SupportedProtocol>(); if (log().isDebugEnabled()) { log().debug("----------------------------------------------------------------------------------------"); log().debug("ifCollector.run: probing subtarget " + InetAddressUtils.str(subtarget)); } probe(subtarget, probelist); m_previouslyProbed.add(subtarget); if (log().isDebugEnabled()) { log().debug("ifCollector.run: adding subtarget " + InetAddressUtils.str(subtarget) + " # supported protocols: " + probelist.size()); log().debug("----------------------------------------------------------------------------------------"); } m_subTargets.put(subtarget, probelist); } // end while(more ip addresses) } // end while(more interfaces) } // end if(ipAddrTable and ifTable entries collected) else if (m_snmpCollector.hasIpAddrTable()) { m_subTargets = new TreeMap<InetAddress, List<SupportedProtocol>>(new InetAddressComparator()); List<InetAddress> ipAddrs = m_snmpCollector.getIpAddrTable().getIpAddresses(); // Iterate over this interface's IP address list // Iterator<InetAddress> s = ipAddrs.iterator(); while (s.hasNext()) { InetAddress subtarget = s.next(); // if the target failed to convert or if it // is equal to the current target then skip it // if (subtarget == null || subtarget.equals(m_target)) { continue; } // now check for loopback if (subtarget.isLoopbackAddress()) { // Skip if loopback if (log().isDebugEnabled()) { log().debug("ifCollector.run: Loopback interface: " + InetAddressUtils.str(subtarget) + ", skipping..."); } continue; } // ok it appears to be ok, so probe it! // List<SupportedProtocol> probelist = new ArrayList<SupportedProtocol>(); if (log().isDebugEnabled()) { log().debug("----------------------------------------------------------------------------------------"); log().debug("ifCollector.run: probing subtarget " + InetAddressUtils.str(subtarget)); } probe(subtarget, probelist); m_previouslyProbed.add(subtarget); if (log().isDebugEnabled()) { log().debug("ifCollector.run: adding subtarget " + InetAddressUtils.str(subtarget) + " # supported protocols: " + probelist.size()); log().debug("----------------------------------------------------------------------------------------"); } m_subTargets.put(subtarget, probelist); } // end while(more ip addresses) } // end if(ipAddrTable entries collected) } // end try() catch (Throwable t) { m_snmpCollector = null; log().warn("IfCollector.run: Caught an exception when collecting SNMP information from target " + InetAddressUtils.str(m_target), t); } log().debug("IfCollector.run: SNMP collection completed"); } // end if(SNMP supported) if (log().isDebugEnabled()) { log().debug("IfCollector.run: run method exiting after collecting information from address " + InetAddressUtils.str(m_target)); } } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } }