/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2010-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.castor; import java.io.File; import java.io.FilenameFilter; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.opennms.core.utils.ThreadCategory; import org.opennms.core.xml.JaxbUtils; import org.opennms.netmgt.config.datacollection.DatacollectionGroup; import org.opennms.netmgt.config.datacollection.Group; import org.opennms.netmgt.config.datacollection.Groups; import org.opennms.netmgt.config.datacollection.IncludeCollection; import org.opennms.netmgt.config.datacollection.ResourceType; import org.opennms.netmgt.config.datacollection.SnmpCollection; import org.opennms.netmgt.config.datacollection.SystemDef; import org.opennms.netmgt.config.datacollection.Systems; import org.springframework.core.io.FileSystemResource; import org.springframework.dao.DataAccessResourceFailureException; /** * DataCollectionConfigParser * * @author <a href="mail:agalue@opennms.org">Alejandro Galue</a> */ // FIXME How to deal with duplications outside snmp-collection boundaries? That make sense?; for example: check externalGroupsMap? // FIXME What are the real ways to validate if two elements are the same? Just the element name? additional parameters? // FIXME How to apply rules about duplicates? Just warn?, Override?, Override with priorities? Silent ignore? public class DataCollectionConfigParser { private String configDirectory; private final Map<String,DatacollectionGroup> externalGroupsMap; public DataCollectionConfigParser(String configDirectory) { this.configDirectory = configDirectory; this.externalGroupsMap = new ConcurrentHashMap<String, DatacollectionGroup>(); } protected Map<String,DatacollectionGroup> getExternalGroupMap() { return Collections.unmodifiableMap(externalGroupsMap); } /** * Update/Validate SNMP collection. * * @param collection */ public void parseCollection(SnmpCollection collection) { if (collection.getIncludeCollectionCount() > 0) { parseExternalResources(); checkCollection(collection); // Add systemDefs and dependencies for (IncludeCollection include : collection.getIncludeCollection()) { if (include.getDataCollectionGroup() != null) { // Include All system definitions from a specific datacollection group addDatacollectionGroup(collection, include.getDataCollectionGroup(), include.getExcludeFilterCollection()); } else { if (include.getSystemDef() == null) { throwException("You must specify at least the data collection group name or system definition name for the include-collection attribute", null); } else { // Include One system definition addSystemDef(collection, include.getSystemDef()); } } } } else { log().info("parse: SNMP collection " + collection.getName() + " doesn't have any external reference."); } } /** * Get all configured resource types. * * @return the resource type list */ public Set<ResourceType> getAllResourceTypes() { parseExternalResources(); Set<ResourceType> resourceTypes = new HashSet<ResourceType>(); for (DatacollectionGroup group : externalGroupsMap.values()) { for (ResourceType rt : group.getResourceTypeCollection()) { if (!contains(resourceTypes, rt)) resourceTypes.add(rt); } } return resourceTypes; } /** * Verify the sub-groups of SNMP collection. * * @param collection */ private void checkCollection(SnmpCollection collection) { if (collection.getSystems() == null) collection.setSystems(new Systems()); if (collection.getGroups() == null) collection.setGroups(new Groups()); } /** * Verify if the resourceTypes list contains a specific resourceType. * <p>One resource type will be considered the same as another one, if they have the same name.</p> * * @param globalContainer * @param resourceType * * @return true, if the list contains the resourceType */ private boolean contains(Collection<ResourceType> resourceTypes, ResourceType resourceType) { for (ResourceType rt : resourceTypes) { if (resourceType.getName().equals(rt.getName())) return true; } return false; } /** * Verify if the groups list contains a specific group. * <p>One group will be considered the same as another one, if they have the same name.</p> * * @param globalContainer * @param group * @return true, if the list contains the mib object group */ private boolean contains(Collection<Group> groups, Group group) { for (Group g : groups) { if (group.getName().equals(g.getName())) return true; } return false; } /** * Verify if the systemDefs list contains a specific system definition. * <p>One system definition will be considered the same as another one, if they have the same name.</p> * * @param globalContainer * @param systemDef * * @return true, if the list contains the system definition */ // TODO Include sysoid and sysoidMask on validation process private boolean contains(List<SystemDef> systemDefs, SystemDef systemDef) { for (SystemDef sd : systemDefs) { if (systemDef.getName().equals(sd.getName())) return true; } return false; } /** * Read all XML files from datacollection directory and parse them to create a list of DatacollectionGroup objects. */ private void parseExternalResources() { // Ensure that this is called only once. if (externalGroupsMap != null && externalGroupsMap.size() > 0) { log().info("parseExternalResources: external data collection groups are already parsed"); return; } // Check configuration files repository File folder = new File(configDirectory); if (!folder.exists() || !folder.isDirectory()) { log().info("parseExternalResources: directory " + folder + " does not exist or is not a folder."); return; } // Get external configuration files File[] listOfFiles = folder.listFiles(new FilenameFilter() { public boolean accept(File file, String name) { return name.endsWith(".xml"); } }); // Parse configuration files (populate external groups map) final CountDownLatch latch = new CountDownLatch(listOfFiles.length); int i = 0; for (final File file : listOfFiles) { Thread thread = new Thread("DataCollectionConfigParser-Thread-" + i++) { public void run() { try { log().debug("parseExternalResources: parsing " + file); DatacollectionGroup group = JaxbUtils.unmarshal(DatacollectionGroup.class, new FileSystemResource(file)); // Synchronize around the map that holds the results synchronized(externalGroupsMap) { externalGroupsMap.put(group.getName(), group); } } catch (Throwable e) { throwException("Can't parse XML file " + file + "; nested exception: " + e.getMessage(), e); } finally { latch.countDown(); } } }; thread.start(); } try { latch.await(); } catch (InterruptedException e) { throwException("Exception while waiting for XML parsing threads to complete: " + e.getMessage(), e); } } /** * Get a system definition from datacollection-group map. * * @param systemDefName the systemDef object name. * @return the systemDef object. */ private SystemDef getSystemDef(String systemDefName) { for (DatacollectionGroup group : externalGroupsMap.values()) { for (SystemDef sd : group.getSystemDefCollection()) { if (sd.getName().equals(systemDefName)) { return sd; } } } return null; } /** * Get a MIB object group from datacollection-group map. * * @param groupName the group name * @return the group object */ private Group getMibObjectGroup(String groupName) { for (DatacollectionGroup group : externalGroupsMap.values()) { for (Group g : group.getGroupCollection()) { if (g.getName().equals(groupName)) { return g; } } } return null; } /** * Add a specific system definition into a SNMP collection. * * @param collection the target SNMP collection object. * @param systemDefName the system definition name. */ private void addSystemDef(SnmpCollection collection, String systemDefName) { log().debug("addSystemDef: merging system defintion " + systemDefName + " into snmp-collection " + collection.getName()); // Find System Definition SystemDef systemDef = getSystemDef(systemDefName); if (systemDef == null) { throwException("Can't find system definition " + systemDefName, null); } // Add System Definition to target SNMP collection if (contains(collection.getSystems().getSystemDefCollection(), systemDef)) { log().warn("addSystemDef: system definition " + systemDefName + " already exist on SNMP collection " + collection.getName()); } else { log().debug("addSystemDef: adding system definition " + systemDef.getName() + " to snmp-collection " + collection.getName()); collection.getSystems().addSystemDef(systemDef); // Add Groups for (String groupName : systemDef.getCollect().getIncludeGroupCollection()) { Group group = getMibObjectGroup(groupName); if (group == null) { log().warn("addSystemDef: group " + groupName + " does not exist on global container"); } else { if (contains(collection.getGroups().getGroupCollection(), group)) { log().debug("addSystemDef: group " + groupName + " already exist on SNMP collection " + collection.getName()); } else { log().debug("addSystemDef: adding mib object group " + group.getName() + " to snmp-collection " + collection.getName()); collection.getGroups().addGroup(group); } } } } } /** * Add all system definitions defined on a specific data collection group, into a SNMP collection. * * @param collection the target SNMP collection object. * @param dataCollectionGroupName the data collection group name. * @param excludeList the list of regular expression to exclude certain system definitions. */ private void addDatacollectionGroup(SnmpCollection collection, String dataCollectionGroupName, List<String> excludeList) { DatacollectionGroup group = externalGroupsMap.get(dataCollectionGroupName); if (group == null) { throwException("Group " + dataCollectionGroupName + " does not exist.", null); } log().debug("addDatacollectionGroup: adding all definitions from group " + group.getName() + " to snmp-collection " + collection.getName()); for (SystemDef systemDef : group.getSystemDefCollection()) { String sysDef = systemDef.getName(); if (shouldAdd(sysDef, excludeList)) { addSystemDef(collection, sysDef); } } } private boolean shouldAdd(String sysDef, List<String> excludeList) { if (excludeList != null) { for (String re : excludeList) { try { final Pattern p = Pattern.compile(re); final Matcher m = p.matcher(sysDef); if (m.matches()) { log().info("addDatacollectionGroup: system definition " + sysDef + " is blacklisted by filter " + re); return false; } } catch (PatternSyntaxException e) { log().warn("the regular expression " + re + " is invalid: " + e.getMessage(), e); } } } return true; } private void throwException(String msg, Throwable e) { if (e == null) { log().error(msg); throw new DataAccessResourceFailureException(msg); } else { log().error(msg, e); throw new DataAccessResourceFailureException(msg, e); } } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } }