/*******************************************************************************
* 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.config;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.ValidationException;
import org.opennms.core.utils.ByteArrayComparator;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.core.xml.CastorUtils;
import org.opennms.netmgt.config.capsd.CapsdConfiguration;
import org.opennms.netmgt.config.capsd.IpManagement;
import org.opennms.netmgt.config.capsd.Property;
import org.opennms.netmgt.config.capsd.ProtocolConfiguration;
import org.opennms.netmgt.config.capsd.ProtocolPlugin;
import org.opennms.netmgt.config.capsd.Range;
import org.opennms.netmgt.config.capsd.SmbAuth;
import org.opennms.netmgt.config.capsd.SmbConfig;
/**
* <p>Abstract CapsdConfigManager class.</p>
*
* @author ranger
* @version $Id: $
*/
public abstract class CapsdConfigManager implements CapsdConfig {
/**
* The string indicating the start of the comments in a line containing the
* IP address in a file URL
*/
private static final String COMMENT_STR = " #";
/**
* This character at the start of a line indicates a comment line in a URL
* file
*/
private static final char COMMENT_CHAR = '#';
//
// /**
// * The SQL statement used to mark all ifservices table entries which refer
// * to the specified serviceId as deleted.
// */
// private static final String DELETE_IFSERVICES_SQL =
// "update ifservices " +
// " set status = 'D' " +
// " where serviceid = ?" +
// " and id in (" +
// " select svc.id" +
// " from ifservices as svc" +
// " join ipinterface as ip" +
// " on (ip.id = svc.ipinterfaceid)" +
// " join node as n" +
// " on (n.nodeid = ip.nodeid)" +
// " where n.foreignsource is null)";
/**
* The config class loaded from the config file
*/
private CapsdConfiguration m_config;
/**
* Maps url names to a list of addresses specified by the url. Initialized
* at factory construction.
*/
private Map<String, List<String>> m_urlMap;
/**
* <p>Constructor for CapsdConfigManager.</p>
*/
public CapsdConfigManager() {
}
/**
* <p>Constructor for CapsdConfigManager.</p>
*
* @param is a {@link java.io.InputStream} object.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public CapsdConfigManager(InputStream is) throws MarshalException, ValidationException {
m_config = CastorUtils.unmarshal(CapsdConfiguration.class, is);
}
/**
* <p>saveXml</p>
*
* @param xml a {@link java.lang.String} object.
* @throws java.io.IOException if any.
*/
protected abstract void saveXml(String xml) throws IOException;
/**
* <p>update</p>
*
* @throws java.io.IOException if any.
* @throws java.io.FileNotFoundException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
protected abstract void update() throws IOException, FileNotFoundException, MarshalException, ValidationException;
/**
* <p>loadXml</p>
*
* @param is a {@link java.io.InputStream} object.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
protected void loadXml(InputStream is) throws MarshalException, ValidationException {
m_config = CastorUtils.unmarshal(CapsdConfiguration.class, is);
loadIncludeUrls();
}
/**
* Load addresses from any urls which have been specified.
*/
private void loadIncludeUrls() {
m_urlMap = new HashMap<String, List<String>>();
for (IpManagement mgt : getIpManagements()) {
for (String url : getIncludeUrls(mgt)) {
if (!m_urlMap.containsKey(url)) {
m_urlMap.put(url, getAddressesFromURL(url));
}
}
}
}
/**
* Saves the current in-memory configuration to disk and reloads
*
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public synchronized void save() throws MarshalException, IOException, ValidationException {
log().debug("Saving capsd configuration");
// marshall to a string first, then write the string to the file. This
// way the original config
// isn't lost if the xml from the marshall is hosed.
StringWriter stringWriter = new StringWriter();
Marshaller.marshal(m_config, stringWriter);
saveXml(stringWriter.toString());
log().info("Saved capsd configuration");
update();
}
/**
* Return the Capsd configuration object.
*
* @return a {@link org.opennms.netmgt.config.capsd.CapsdConfiguration} object.
*/
public CapsdConfiguration getConfiguration() {
return m_config;
}
/** {@inheritDoc} */
public ProtocolPlugin getProtocolPlugin(String svcName) {
for (ProtocolPlugin plugin : getProtocolPlugins()) {
if (plugin.getProtocol().equals(svcName)) {
return plugin;
}
}
return null;
}
/** {@inheritDoc} */
public void addProtocolPlugin(ProtocolPlugin plugin) {
m_config.addProtocolPlugin(plugin);
}
/**
* {@inheritDoc}
*
* Finds the SMB authentication object using the netbios name.
*
* The target of the search.
*/
public SmbAuth getSmbAuth(String target) {
SmbConfig cfg = m_config.getSmbConfig();
if (cfg == null) {
return null;
}
List<SmbAuth> smbAuths = getSmbAuths(cfg);
for (SmbAuth a : smbAuths) {
if (target.equalsIgnoreCase(a.getContent())) {
return a;
}
}
return null;
}
/**
* {@inheritDoc}
*
* Checks the configuration to determine if the target is managed or
* unmanaged.
*/
public boolean isAddressUnmanaged(InetAddress target) {
String managementPolicy = m_config.getManagementPolicy();
boolean managedByDefault = (managementPolicy == null || managementPolicy.equalsIgnoreCase("managed"));
boolean found_denial = false;
boolean found_accept = false;
List<IpManagement> ipManagements = getIpManagements();
Iterator<IpManagement> ipIter = ipManagements.iterator();
while (ipIter.hasNext() && !found_denial) {
IpManagement mgt = ipIter.next();
for (String saddr : getSpecifics(mgt)) {
InetAddress addr;
addr = InetAddressUtils.addr(saddr);
if (addr == null) {
log().info("Failed to convert specific address '" + saddr + "' to an InetAddress.");
continue;
}
if (addr.equals(target)) {
if (mgt.getPolicy() == null || mgt.getPolicy().equalsIgnoreCase("managed")) {
found_accept = true;
} else {
found_denial = true;
}
break;
}
}
// now check the ranges
List<Range> ranges = getRanges(mgt);
Iterator<Range> rangeIter = ranges.iterator();
while (!found_denial && rangeIter.hasNext()) {
Range range = rangeIter.next();
InetAddress saddr;
saddr = InetAddressUtils.addr(range.getBegin());
if (saddr == null) {
log().info("Failed to convert begin address '" + range.getBegin() + "' to an InetAddress.");
continue;
}
InetAddress eaddr;
eaddr = InetAddressUtils.addr(range.getEnd());
if (eaddr == null) {
log().info("Failed to convert end address '" + range.getEnd() + "' to an InetAddress.");
continue;
}
int compareStartToTarget = new ByteArrayComparator().compare(saddr.getAddress(), target.getAddress());
int compareTargetToEnd = new ByteArrayComparator().compare(target.getAddress(), eaddr.getAddress());
if (compareStartToTarget <= 0 && compareTargetToEnd <= 0) {
if (mgt.getPolicy() == null || mgt.getPolicy().equalsIgnoreCase("managed")) {
found_accept = true;
} else {
found_denial = true;
}
break;
}
}
// now check urls
List<String> includeUrls = getIncludeUrls(mgt);
Iterator<String> includeUrlIter = includeUrls.iterator();
boolean match = false;
while (!found_denial && !match && includeUrlIter.hasNext()) {
String url = includeUrlIter.next();
/*
* Retrieve address list from url map.
* Iterate over addresses looking for target match.
*/
for (String saddr : m_urlMap.get(url)) {
InetAddress addr;
addr = InetAddressUtils.addr(saddr);
if (addr == null) {
log().info("Failed to convert address '" + saddr + "' from include URL '" + url + "' to an InetAddress.");
continue;
}
if (addr.equals(target)) {
if (mgt.getPolicy() == null || mgt.getPolicy().equalsIgnoreCase("managed")) {
found_accept = true;
} else {
found_denial = true;
}
match = true;
break;
}
}
}
}
boolean result = !managedByDefault;
if (found_denial) {
result = true;
} else if (found_accept) {
result = false;
}
return result;
}
/**
* The file URL is read and a 'specific IP' is added for each entry in this
* file. Each line in the URL file can be one of -<IP><space># <comments>
* or <IP>or #<comments>
*
* Lines starting with a '#' are ignored and so are characters after a '
* <space>#' in a line.
*
* @param url
* the URL file
*
* @return List of addresses retrieved from the URL
*/
private static List<String> getAddressesFromURL(String url) {
List<String> addrList = new ArrayList<String>();
try {
// open the file indicated by the url
URL fileURL = new URL(url);
InputStream file = fileURL.openStream();
// check to see if the file exists
if (file != null) {
BufferedReader buffer = new BufferedReader(new InputStreamReader(file, "UTF-8"));
String ipLine = null;
String specIP = null;
// get each line of the file and turn it into a specific address
while ((ipLine = buffer.readLine()) != null) {
ipLine = ipLine.trim();
if (ipLine.length() == 0 || ipLine.charAt(0) == COMMENT_CHAR) {
// blank line or skip comment
continue;
}
// check for comments after IP
int comIndex = ipLine.indexOf(COMMENT_STR);
if (comIndex == -1) {
specIP = ipLine;
} else {
specIP = ipLine.substring(0, comIndex);
ipLine = ipLine.trim();
}
addrList.add(specIP);
specIP = null;
}
buffer.close();
} else {
log().warn("URL does not exist: " + url.toString());
}
} catch (MalformedURLException e) {
log().error("Error reading URL: " + url.toString() + ": " + e.getLocalizedMessage());
} catch (FileNotFoundException e) {
log().error("Error reading URL: " + url.toString() + ": " + e.getLocalizedMessage());
} catch (IOException e) {
log().error("Error reading URL: " + url.toString() + ": " + e.getLocalizedMessage());
}
return addrList;
}
/**
* <p>getRescanFrequency</p>
*
* @return a long.
*/
public long getRescanFrequency() {
long frequency = -1;
if (m_config.hasRescanFrequency()) {
frequency = m_config.getRescanFrequency();
} else {
log().warn("Capsd configuration file is missing rescan interval, defaulting to 24 hour interval.");
frequency = 86400000; // default is 24 hours
}
return frequency;
}
/**
* <p>getInitialSleepTime</p>
*
* @return a long.
*/
public long getInitialSleepTime() {
long sleep = -1;
if (m_config.hasInitialSleepTime()) {
sleep = m_config.getInitialSleepTime();
} else {
log().warn("Capsd configuration file is missing rescan interval, defaulting to 24 hour interval.");
sleep = 300000; // default is 5 minutes
}
return sleep;
}
/**
* <p>getMaxSuspectThreadPoolSize</p>
*
* @return a int.
*/
public int getMaxSuspectThreadPoolSize() {
return m_config.getMaxSuspectThreadPoolSize();
}
/**
* <p>getMaxRescanThreadPoolSize</p>
*
* @return a int.
*/
public int getMaxRescanThreadPoolSize() {
return m_config.getMaxRescanThreadPoolSize();
}
/**
* Defines Capsd's behavior when, during a protocol scan, it gets a
* java.net.NoRouteToHostException exception. If abort rescan property is
* set to "true" then Capsd will not perform any additional protocol scans.
*
* @return a boolean.
*/
public boolean getAbortProtocolScansFlag() {
boolean abortFlag = false;
String abortProperty = m_config.getAbortProtocolScansIfNoRoute();
if (abortProperty != null && abortProperty.equals("true")) {
abortFlag = true;
}
return abortFlag;
}
/**
* <p>getDeletePropagationEnabled</p>
*
* @return a boolean.
*/
public boolean getDeletePropagationEnabled() {
boolean propagationEnabled = true;
String propagationProperty = m_config.getDeletePropagationEnabled();
if (propagationProperty != null && propagationProperty.equals("false")) {
propagationEnabled = false;
}
return propagationEnabled;
}
/**
* Return the boolean xmlrpc as string to indicate if notification to
* external xmlrpc server is needed.
*
* @return boolean flag as a string value
*/
public String getXmlrpc() {
return m_config.getXmlrpc();
}
/**
* <p>isXmlRpcEnabled</p>
*
* @return a boolean.
*/
public boolean isXmlRpcEnabled() {
return "true".equalsIgnoreCase(getXmlrpc());
}
/**
* 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, if strict is
* true, it must be included by a Collectd package which supports the
* specified service. This method will return null if the 'oldPrimary'
* address is null and the 'currentIf' address does not pass the Collectd
* package check, if strict is true..
* @param svcName
* Service name
* @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")
* @param strict
* require interface to be part of a Collectd package
*
* @return InetAddress object of the primary interface based on the provided
* method or null if neither address is eligible to be primary.
*/
private InetAddress compareAndSelectPrimaryCollectionInterface(String svcName, InetAddress currentIf, InetAddress oldPrimary, String method, boolean strict) {
InetAddress newPrimary = null;
CollectdConfigFactory factory = CollectdConfigFactory.getInstance();
if (oldPrimary == null && strict) {
if (factory.isServiceCollectionEnabled(InetAddressUtils.str(currentIf), svcName)) {
return currentIf;
} else {
return oldPrimary;
}
}
if (oldPrimary == null) {
return currentIf;
}
int comparison = new ByteArrayComparator().compare(currentIf.getAddress(), oldPrimary.getAddress());
if (method.equals(CollectdConfigFactory.SELECT_METHOD_MIN)) {
// Smallest address wins
if (comparison < 0) {
/*
* Replace the primary interface with the current
* interface only if the current interface is managed!
*/
if (strict) {
if (factory.isServiceCollectionEnabled(InetAddressUtils.str(currentIf), svcName)) {
newPrimary = currentIf;
}
} else {
newPrimary = currentIf;
}
}
} else {
// Largest address wins
if (comparison > 0) {
/*
* Replace the primary interface with the current
* interface only if the current interface is managed!
*/
if (strict) {
if (factory.isServiceCollectionEnabled(InetAddressUtils.str(currentIf),
svcName)) {
newPrimary = currentIf;
}
} else {
newPrimary = currentIf;
}
}
}
if (newPrimary != null) {
return newPrimary;
} else {
return oldPrimary;
}
}
/**
* {@inheritDoc}
*
* This method is responsbile for determining the node's primary SNMP
* interface from the specified list of InetAddress objects.
*/
public InetAddress determinePrimarySnmpInterface(List<InetAddress> addressList, boolean strict) {
InetAddress primaryIf = null;
// For now hard-coding primary interface address selection method to MIN
String method = CollectdConfigFactory.SELECT_METHOD_MIN;
/*
* To be selected as the the primary SNMP interface for a node
* the interface must be included by a Collectd package if strict
* is true, and that package must include the SNMP service and
* the service must be enabled.
*
* Iterate over interface list and test each interface
*/
for (InetAddress ipAddr : addressList) {
if (log().isDebugEnabled()) {
log().debug("determinePrimarySnmpIf: checking interface "
+ InetAddressUtils.str(ipAddr));
}
primaryIf = compareAndSelectPrimaryCollectionInterface("SNMP", ipAddr, primaryIf, method, strict);
}
if (log().isDebugEnabled()) {
if (primaryIf != null) {
log().debug("determinePrimarySnmpInterface: candidate primary SNMP interface: "
+ InetAddressUtils.str(primaryIf));
} else {
log().debug("determinePrimarySnmpInterface: no candidate primary SNMP interface found");
}
}
return primaryIf;
}
/**
* Return a list of configured protocols from the loaded configuration.
*
* @return a {@link java.util.List} object.
*/
public List<String> getConfiguredProtocols() {
List<String> protocols = new ArrayList<String>();
for (ProtocolPlugin plugin : getProtocolPlugins()) {
protocols.add(plugin.getProtocol());
}
return protocols;
}
private static ThreadCategory log() {
return ThreadCategory.getInstance(CapsdConfigManager.class);
}
/**
* <p>getProtocolPlugins</p>
*
* @return a {@link java.util.List} object.
*/
public List<ProtocolPlugin> getProtocolPlugins() {
return m_config.getProtocolPluginCollection();
}
private List<IpManagement> getIpManagements() {
return m_config.getIpManagementCollection();
}
private List<Range> getRanges(IpManagement mgt) {
return mgt.getRangeCollection();
}
private List<String> getSpecifics(IpManagement mgt) {
return mgt.getSpecificCollection();
}
private List<String> getIncludeUrls(IpManagement mgt) {
return mgt.getIncludeUrlCollection();
}
/** {@inheritDoc} */
public List<ProtocolConfiguration> getProtocolConfigurations(ProtocolPlugin plugin) {
return plugin.getProtocolConfigurationCollection();
}
/** {@inheritDoc} */
public List<Property> getPluginProperties(ProtocolPlugin plugin) {
return plugin.getPropertyCollection();
}
/** {@inheritDoc} */
public List<Property> getProtocolConfigurationProperties(ProtocolConfiguration pluginConf) {
return pluginConf.getPropertyCollection();
}
/** {@inheritDoc} */
public List<Range> getRanges(ProtocolConfiguration pluginConf) {
return pluginConf.getRangeCollection();
}
/** {@inheritDoc} */
public List<String> getSpecifics(ProtocolConfiguration pluginConf) {
return pluginConf.getSpecificCollection();
}
private List<SmbAuth> getSmbAuths(SmbConfig cfg) {
return cfg.getSmbAuthCollection();
}
}