/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2007-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.dao.support;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.IntSet;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.config.CollectdConfigFactory;
import org.opennms.netmgt.config.DataCollectionConfigDao;
import org.opennms.netmgt.config.StorageStrategy;
import org.opennms.netmgt.config.datacollection.ResourceType;
import org.opennms.netmgt.dao.LocationMonitorDao;
import org.opennms.netmgt.dao.NodeDao;
import org.opennms.netmgt.dao.ResourceDao;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsLocationMonitor;
import org.opennms.netmgt.model.OnmsNode;
import org.opennms.netmgt.model.OnmsResource;
import org.opennms.netmgt.model.OnmsResourceType;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.Assert;
/**
* Encapsulates all SNMP performance reporting for the web user interface.
*
* @author <a href="mailto:seth@opennms.org">Seth Leger </a>
* @author <a href="mailto:larry@opennms.org">Lawrence Karnowski </a>
* @author <a href="mailto:dj@opennms.org">DJ Gregor</a>
*/
public class DefaultResourceDao implements ResourceDao, InitializingBean {
/**
* File name to look for in a resource directory for string attributes.
*/
public static final String STRINGS_PROPERTIES_FILE_NAME = "strings.properties";
/** Constant <code>INTERFACE_GRAPH_TYPE="interface"</code> */
public static final String INTERFACE_GRAPH_TYPE = "interface";
/** Constant <code>RESPONSE_DIRECTORY="response"</code> */
public static final String RESPONSE_DIRECTORY = "response";
/** Constant <code>SNMP_DIRECTORY="snmp"</code> */
public static final String SNMP_DIRECTORY = "snmp";
private NodeDao m_nodeDao;
private LocationMonitorDao m_locationMonitorDao;
private File m_rrdDirectory;
private CollectdConfigFactory m_collectdConfig;
private DataCollectionConfigDao m_dataCollectionConfigDao;
private Map<String, OnmsResourceType> m_resourceTypes;
private NodeResourceType m_nodeResourceType;
private DomainResourceType m_domainResourceType;
/**
* <p>Constructor for DefaultResourceDao.</p>
*/
public DefaultResourceDao() {
}
/**
* <p>setRrdDirectory</p>
*
* @param rrdDirectory a {@link java.io.File} object.
*/
public void setRrdDirectory(File rrdDirectory) {
m_rrdDirectory = rrdDirectory;
}
/**
* <p>getRrdDirectory</p>
*
* @return a {@link java.io.File} object.
*/
public File getRrdDirectory() {
return m_rrdDirectory;
}
/** {@inheritDoc} */
public File getRrdDirectory(boolean verify) {
if (verify && !getRrdDirectory().isDirectory()) {
throw new ObjectRetrievalFailureException("RRD directory does not exist: " + getRrdDirectory().getAbsolutePath(), getRrdDirectory());
}
return getRrdDirectory();
}
/**
* <p>getDataCollectionConfig</p>
*
* @return a {@link org.opennms.netmgt.config.DataCollectionConfigDao} object.
*/
public DataCollectionConfigDao getDataCollectionConfigDao() {
return m_dataCollectionConfigDao;
}
/**
* <p>setDataCollectionConfig</p>
*
* @param dataCollectionConfigDao a {@link org.opennms.netmgt.config.DataCollectionConfigDao} object.
*/
public void setDataCollectionConfigDao(DataCollectionConfigDao dataCollectionConfigDao) {
m_dataCollectionConfigDao = dataCollectionConfigDao;
}
/**
* <p>getNodeDao</p>
*
* @return a {@link org.opennms.netmgt.dao.NodeDao} object.
*/
public NodeDao getNodeDao() {
return m_nodeDao;
}
/**
* <p>setNodeDao</p>
*
* @param nodeDao a {@link org.opennms.netmgt.dao.NodeDao} object.
*/
public void setNodeDao(NodeDao nodeDao) {
m_nodeDao = nodeDao;
}
/**
* <p>getCollectdConfig</p>
*
* @return a {@link org.opennms.netmgt.config.CollectdConfigFactory} object.
*/
public CollectdConfigFactory getCollectdConfig() {
return m_collectdConfig;
}
/**
* <p>setCollectdConfig</p>
*
* @param collectdConfig a {@link org.opennms.netmgt.config.CollectdConfigFactory} object.
*/
public void setCollectdConfig(CollectdConfigFactory collectdConfig) {
m_collectdConfig = collectdConfig;
}
/**
* <p>getLocationMonitorDao</p>
*
* @return a {@link org.opennms.netmgt.dao.LocationMonitorDao} object.
*/
public LocationMonitorDao getLocationMonitorDao() {
return m_locationMonitorDao;
}
/**
* <p>setLocationMonitorDao</p>
*
* @param locationMonitorDao a {@link org.opennms.netmgt.dao.LocationMonitorDao} object.
*/
public void setLocationMonitorDao(LocationMonitorDao locationMonitorDao) {
m_locationMonitorDao = locationMonitorDao;
}
/**
* <p>afterPropertiesSet</p>
*
* @throws java.io.IOException if any.
*/
@Override
public void afterPropertiesSet() throws IOException {
if (m_rrdDirectory == null) {
throw new IllegalStateException("rrdDirectory property has not been set");
}
if (m_collectdConfig == null) {
throw new IllegalStateException("collectdConfig property has not been set");
}
if (m_dataCollectionConfigDao == null) {
throw new IllegalStateException("dataCollectionConfig property has not been set");
}
if (m_nodeDao == null) {
throw new IllegalStateException("nodeDao property has not been set");
}
if (m_locationMonitorDao == null) {
throw new IllegalStateException("locationMonitorDao property has not been set");
}
initResourceTypes();
}
private void initResourceTypes() throws IOException {
Map<String, OnmsResourceType> resourceTypes;
resourceTypes = new LinkedHashMap<String, OnmsResourceType>();
OnmsResourceType resourceType;
resourceType = new NodeSnmpResourceType(this);
resourceTypes.put(resourceType.getName(), resourceType);
resourceType = new InterfaceSnmpResourceType(this, m_nodeDao);
resourceTypes.put(resourceType.getName(), resourceType);
resourceType = new ResponseTimeResourceType(this, m_nodeDao);
resourceTypes.put(resourceType.getName(), resourceType);
resourceType = new DistributedStatusResourceType(this, m_locationMonitorDao);
resourceTypes.put(resourceType.getName(), resourceType);
resourceTypes.putAll(getGenericIndexResourceTypes());
m_nodeResourceType = new NodeResourceType(this);
resourceTypes.put(m_nodeResourceType.getName(), m_nodeResourceType);
m_domainResourceType = new DomainResourceType(this);
resourceTypes.put(m_domainResourceType.getName(), m_domainResourceType);
m_resourceTypes = resourceTypes;
}
private Map<String, GenericIndexResourceType> getGenericIndexResourceTypes() {
Map<String, GenericIndexResourceType> resourceTypes;
resourceTypes = new LinkedHashMap<String, GenericIndexResourceType>();
Map<String, ResourceType> configuredResourceTypes = m_dataCollectionConfigDao.getConfiguredResourceTypes();
List<ResourceType> resourceTypeList = new LinkedList<ResourceType>(configuredResourceTypes.values());
Collections.sort(resourceTypeList, new Comparator<ResourceType>() {
@Override
public int compare(ResourceType r0, ResourceType r1) {
return r0.getLabel().compareTo(r1.getLabel());
}
});
for (ResourceType resourceType : resourceTypeList) {
String className = resourceType.getStorageStrategy().getClazz();
Class<?> cinst;
try {
cinst = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new ObjectRetrievalFailureException(StorageStrategy.class, className,
"Could not load class '" + className + "' for resource type '" + resourceType.getName() + "'", e);
}
StorageStrategy storageStrategy;
try {
storageStrategy = (StorageStrategy) cinst.newInstance();
} catch (InstantiationException e) {
throw new ObjectRetrievalFailureException(StorageStrategy.class, className,
"Could not instantiate class '" + className + "' for resource type '" + resourceType.getName() + "'", e);
} catch (IllegalAccessException e) {
throw new ObjectRetrievalFailureException(StorageStrategy.class, className,
"Could not instantiate class '" + className + "' for resource type '" + resourceType.getName() + "'", e);
}
storageStrategy.setResourceTypeName(resourceType.getName());
GenericIndexResourceType genericIndexResourceType =
new GenericIndexResourceType(this,
resourceType.getName(),
resourceType.getLabel(),
resourceType.getResourceLabel(),
storageStrategy);
resourceTypes.put(genericIndexResourceType.getName(), genericIndexResourceType);
}
return resourceTypes;
}
/**
* <p>getResourceTypes</p>
*
* @return a {@link java.util.Collection} object.
*/
public Collection<OnmsResourceType> getResourceTypes() {
return m_resourceTypes.values();
}
/**
* Fetch a specific resource by string ID.
* @return Resource or null if resource cannot be found.
* @throws IllegalArgumentException When the resource ID string does not match the expected regex pattern
* @throws ObjectRetrievalFailureException If any exceptions are thrown while searching for the resource
*/
public OnmsResource getResourceById(String id) {
OnmsResource resource = null;
Pattern p = Pattern.compile("([^\\[]+)\\[([^\\]]*)\\](?:\\.|$)");
Matcher m = p.matcher(id);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String resourceTypeName = DefaultResourceDao.decode(m.group(1));
String resourceName = DefaultResourceDao.decode(m.group(2));
try {
if (resource == null) {
resource = getTopLevelResource(resourceTypeName, resourceName);
} else {
resource = getChildResource(resource, resourceTypeName, resourceName);
}
} catch (Throwable e) {
log().warn("Could not get resource for resource ID \"" + id + "\"", e);
return null;
}
m.appendReplacement(sb, "");
}
m.appendTail(sb);
if (sb.length() > 0) {
log().warn("resource ID '" + id + "' does not match pattern '"
+ p.toString() + "' at '"
+ sb + "'");
return null;
} else {
return resource;
}
}
/**
* Fetch a specific list of resources by string ID.
* @return Resources or null if resources cannot be found.
* @throws IllegalArgumentException When the resource ID string does not match the expected regex pattern
* @throws ObjectRetrievalFailureException If any exceptions are thrown while searching for the resource
*/
public List<OnmsResource> getResourceListById(String id) throws IllegalArgumentException, ObjectRetrievalFailureException {
OnmsResource topLevelResource = null;
Pattern p = Pattern.compile("([^\\[]+)\\[([^\\]]*)\\](?:\\.|$)");
Matcher m = p.matcher(id);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String resourceTypeName = DefaultResourceDao.decode(m.group(1));
String resourceName = DefaultResourceDao.decode(m.group(2));
try {
if (topLevelResource == null) {
topLevelResource = getTopLevelResource(resourceTypeName, resourceName);
} else {
return getChildResourceList(topLevelResource);
}
} catch (Throwable e) {
throw new ObjectRetrievalFailureException(OnmsResource.class, id, "Could not get resource for resource ID '" + id + "'", e);
}
m.appendReplacement(sb, "");
}
m.appendTail(sb);
if (sb.length() > 0) {
throw new IllegalArgumentException("resource ID '" + id
+ "' does not match pattern '"
+ p.toString() + "' at '"
+ sb + "'");
}
return null;
}
/**
* <p>getTopLevelResource</p>
*
* @param resourceType a {@link java.lang.String} object.
* @param resource a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.model.OnmsResource} object.
*/
protected OnmsResource getTopLevelResource(String resourceType, String resource) throws ObjectRetrievalFailureException {
if ("node".equals(resourceType)) {
return getNodeEntityResource(resource);
} else if ("nodeSource".equals(resourceType)) {
return getForeignSourceNodeEntityResource(resource);
} else if ("domain".equals(resourceType)) {
return getDomainEntityResource(resource);
} else {
throw new ObjectRetrievalFailureException("Top-level resource type of '" + resourceType + "' is unknown", resourceType);
}
}
/**
* <p>getChildResource</p>
*
* @param parentResource a {@link org.opennms.netmgt.model.OnmsResource} object.
* @param resourceType a {@link java.lang.String} object.
* @param resource a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.model.OnmsResource} object.
*/
protected OnmsResource getChildResource(OnmsResource parentResource, String resourceType, String resource) {
for (OnmsResource r : parentResource.getChildResources()) {
if (resourceType.equals(r.getResourceType().getName())
&& resource.equals(r.getName())) {
return r;
}
}
throw new ObjectRetrievalFailureException(OnmsResource.class, resourceType + "/" + resource, "Could not find child resource '"
+ resource + "' with resource type '" + resourceType + "' on resource '" + resource + "'", null);
}
/**
* <p>getChildResourceList</p>
*
* @param parentResource a {@link org.opennms.netmgt.model.OnmsResource} object.
* @return a {@link java.util.List} object.
*/
protected List<OnmsResource> getChildResourceList(OnmsResource parentResource) {
if (log().isDebugEnabled()) {
log().debug("DefaultResourceDao: getChildResourceList for " + parentResource.toString());
}
return parentResource.getChildResources();
}
/**
* Returns a list of resources for a node.
*
* XXX It does not currently fully check that an IP address that is found to have
* distributed response time data is in the database on the proper node so it can have false positives.
*
* @return a {@link java.util.List} object.
*/
public List<OnmsResource> findNodeResources() {
List<OnmsResource> resources = new LinkedList<OnmsResource>();
IntSet snmpNodes = findSnmpNodeDirectories();
Set<String> responseTimeInterfaces = findChildrenMatchingFilter(new File(getRrdDirectory(), RESPONSE_DIRECTORY), RrdFileConstants.INTERFACE_DIRECTORY_FILTER);
Set<String> distributedResponseTimeInterfaces = findChildrenChildrenMatchingFilter(new File(new File(getRrdDirectory(), RESPONSE_DIRECTORY), "distributed"), RrdFileConstants.INTERFACE_DIRECTORY_FILTER);
// Only returns non-deleted nodes to fix NMS-2977
// http://issues.opennms.org/browse/NMS-2977
Collection<Integer> nodeIds = m_nodeDao.getNodeIds();
IntSet nodesFound = new IntSet();
for (Integer nodeId : nodeIds) {
if (nodesFound.contains(nodeId.intValue())) {
continue;
}
boolean found = false;
OnmsNode node = m_nodeDao.get(nodeId);
if (snmpNodes.contains(nodeId)) {
found = true;
} else if (responseTimeInterfaces.size() > 0 || distributedResponseTimeInterfaces.size() > 0) {
for (final OnmsIpInterface ip : m_nodeDao.get(nodeId).getIpInterfaces()) {
final String addr = InetAddressUtils.str(ip.getIpAddress());
if (responseTimeInterfaces.contains(addr) || distributedResponseTimeInterfaces.contains(addr)) {
found = true;
break;
}
}
}
if (found) {
resources.add(m_nodeResourceType.createChildResource(node));
nodesFound.add(nodeId);
}
}
return resources;
}
/**
* Returns a list of resources for domains.
*
* @return a {@link java.util.List} object.
*/
public List<OnmsResource> findDomainResources() {
List<OnmsResource> resources = new LinkedList<OnmsResource>();
File snmp = new File(getRrdDirectory(), SNMP_DIRECTORY);
// Get all of the non-numeric directory names in the RRD directory; these
// are the names of the domains that have performance data
File[] domainDirs = snmp.listFiles(RrdFileConstants.DOMAIN_DIRECTORY_FILTER);
if (domainDirs != null && domainDirs.length > 0) {
for (File domainDir : domainDirs) {
resources.add(m_domainResourceType.createChildResource(domainDir.getName()));
}
}
return resources;
}
/**
* <p>getNodeEntityResource</p>
*
* @param resource a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.model.OnmsResource} object.
*/
protected OnmsResource getNodeEntityResource(String resource) {
int nodeId;
try {
nodeId = Integer.parseInt(resource);
} catch (NumberFormatException e) {
throw new ObjectRetrievalFailureException(OnmsNode.class, resource, "Top-level resource of resource type node is not numeric: " + resource, null);
}
OnmsNode node = m_nodeDao.get(nodeId);
if (node == null) {
throw new ObjectRetrievalFailureException(OnmsNode.class, resource, "Top-level resource of resource type node could not be found: " + resource, null);
}
OnmsResource onmsResource = getResourceForNode(node);
return onmsResource;
}
/**
* <p>getForeignSourceNodeEntityResource</p>
*
* @param resource a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.model.OnmsResource} object.
*/
protected OnmsResource getForeignSourceNodeEntityResource(String resource) {
String[] ident = resource.split(":");
OnmsNode node = m_nodeDao.findByForeignId(ident[0], ident[1]);
if (node == null) {
throw new ObjectRetrievalFailureException(OnmsNode.class, resource, "Top-level resource of resource type node could not be found: " + resource, null);
}
OnmsResource onmsResource = getResourceForNode(node);
return onmsResource;
}
/**
* <p>getDomainEntityResource</p>
*
* @param domain a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.model.OnmsResource} object.
*/
protected OnmsResource getDomainEntityResource(String domain) {
File directory = new File(getRrdDirectory(), SNMP_DIRECTORY);
File domainDir = new File(directory, domain);
if (!domainDir.isDirectory()) {
throw new ObjectRetrievalFailureException(OnmsResource.class, domain, "Domain not found due to domain RRD directory not existing or not a directory: " + domainDir.getAbsolutePath(), null);
}
if (!RrdFileConstants.DOMAIN_DIRECTORY_FILTER.accept(domainDir)) {
throw new ObjectRetrievalFailureException(OnmsResource.class, domain, "Domain not found due to domain RRD directory not matching the domain directory filter: " + domainDir.getAbsolutePath(), null);
}
return m_domainResourceType.createChildResource(domain);
}
private IntSet findSnmpNodeDirectories() {
IntSet nodes = new IntSet();
File directory = new File(getRrdDirectory(), SNMP_DIRECTORY);
File[] nodeDirs = directory.listFiles(RrdFileConstants.NODE_DIRECTORY_FILTER);
if (nodeDirs == null || nodeDirs.length == 0) {
return nodes;
}
for (File nodeDir : nodeDirs) {
try {
int nodeId = Integer.parseInt(nodeDir.getName());
nodes.add(nodeId);
} catch (NumberFormatException e) {
// skip... don't add
}
}
return nodes;
}
private static Set<String> findChildrenMatchingFilter(File directory, FileFilter filter) {
Set<String> children = new HashSet<String>();
File[] nodeDirs = directory.listFiles(filter);
if (nodeDirs == null || nodeDirs.length == 0) {
return children;
}
for (File nodeDir : nodeDirs) {
children.add(nodeDir.getName());
}
return children;
}
/**
*
* @param directory
* @param filter
* @return
*
* XXX should include the location monitor in the returned data
*/
private static Set<String> findChildrenChildrenMatchingFilter(File directory, FileFilter filter) {
Set<String> children = new HashSet<String>();
File[] locationMonitorDirs = directory.listFiles();
if (locationMonitorDirs == null) {
return children;
}
for (File locationMonitorDir : locationMonitorDirs) {
File[] intfDirs = locationMonitorDir.listFiles(filter);
if (intfDirs == null || intfDirs.length == 0) {
continue;
}
for (File intfDir : intfDirs) {
children.add(intfDir.getName());
}
}
return children;
}
/**
* Encapsulate the deprecated decode method to fix it in one place.
*
* @param string
* string to be decoded
* @return decoded string
*/
public static String decode(String string) {
try {
return URLDecoder.decode(string, "UTF-8");
} catch (UnsupportedEncodingException e) {
// UTF-8 should *never* throw this
throw new UndeclaredThrowableException(e);
}
}
/** {@inheritDoc} */
public OnmsResource getResourceForNode(OnmsNode node) {
Assert.notNull(node, "node argument must not be null");
return m_nodeResourceType.createChildResource(node);
}
private OnmsResource getChildResourceForNode(OnmsNode node, String resourceTypeName, String resourceName) {
OnmsResource nodeResource = getResourceForNode(node);
if (nodeResource == null) {
return null;
}
List<OnmsResource> childResources = nodeResource.getChildResources();
for (OnmsResource childResource : childResources) {
if (!resourceTypeName.equals(childResource.getResourceType().getName())) {
continue;
}
if (resourceName.equals(childResource.getName())) {
return childResource;
}
}
return null;
}
/**
* @return OnmsResource for the <code>responseTime</code> resource on the interface or
* null if the <code>responseTime</code> resource cannot be found for the given IP interface.
*/
public OnmsResource getResourceForIpInterface(OnmsIpInterface ipInterface) {
Assert.notNull(ipInterface, "ipInterface argument must not be null");
Assert.notNull(ipInterface.getNode(), "getNode() on ipInterface must not return null");
final String ipAddress = InetAddressUtils.str(ipInterface.getIpAddress());
return getChildResourceForNode(ipInterface.getNode(), "responseTime", ipAddress);
}
/**
* @return OnmsResource for the <code>distributedStatus</code> resource on the interface or
* null if the <code>distributedStatus</code> resource cannot be found for the given IP interface.
*/
public OnmsResource getResourceForIpInterface(OnmsIpInterface ipInterface, OnmsLocationMonitor locMon) {
Assert.notNull(ipInterface, "ipInterface argument must not be null");
Assert.notNull(locMon, "locMon argument must not be null");
Assert.notNull(ipInterface.getNode(), "getNode() on ipInterface must not return null");
final String ipAddress = InetAddressUtils.str(ipInterface.getIpAddress());
return getChildResourceForNode(ipInterface.getNode(), "distributedStatus", locMon.getId() + File.separator + ipAddress);
}
/**
* <p>findTopLevelResources</p>
*
* @return a {@link java.util.List} object.
*/
public List<OnmsResource> findTopLevelResources() {
List<OnmsResource> resources = new ArrayList<OnmsResource>();
resources.addAll(findNodeResources());
resources.addAll(findDomainResources());
return resources;
}
private static ThreadCategory log() {
return ThreadCategory.getInstance(DefaultResourceDao.class);
}
}