/*******************************************************************************
* 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.provision;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.MissingFormatArgumentException;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.LogUtils;
import org.opennms.core.utils.PropertiesUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.config.SnmpAgentConfigFactory;
import org.opennms.netmgt.config.SnmpAssetAdapterConfig;
import org.opennms.netmgt.config.snmpAsset.adapter.AssetField;
import org.opennms.netmgt.config.snmpAsset.adapter.MibObj;
import org.opennms.netmgt.config.snmpAsset.adapter.MibObjs;
import org.opennms.netmgt.dao.NodeDao;
import org.opennms.netmgt.model.OnmsAssetRecord;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsNode;
import org.opennms.netmgt.model.events.EventForwarder;
import org.opennms.netmgt.model.events.annotations.EventHandler;
import org.opennms.netmgt.model.events.annotations.EventListener;
import org.opennms.netmgt.model.events.snmp.SyntaxToEvent;
import org.opennms.netmgt.snmp.SnmpAgentConfig;
import org.opennms.netmgt.snmp.SnmpObjId;
import org.opennms.netmgt.snmp.SnmpUtils;
import org.opennms.netmgt.snmp.SnmpValue;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.util.Assert;
/**
*/
@EventListener(name="SnmpAssetProvisioningAdapter")
public class SnmpAssetProvisioningAdapter extends SimplerQueuedProvisioningAdapter implements InitializingBean {
private NodeDao m_nodeDao;
private EventForwarder m_eventForwarder;
private SnmpAssetAdapterConfig m_config;
private SnmpAgentConfigFactory m_snmpConfigDao;
/**
* Constant <code>NAME="SnmpAssetProvisioningAdapter"</code>
*/
public static final String NAME = "SnmpAssetProvisioningAdapter";
public SnmpAssetProvisioningAdapter() {
super(NAME);
// Set the default time delay to 300 seconds
this.setDelay(300);
this.setTimeUnit(TimeUnit.SECONDS);
}
/**
* Creating a custom schedule for this adapter. We need to make sure that the node has a system object ID set
* and that it has had enough time for that to have happened.
* @param nodeId
* @param adapterOperationType
* @return
*/
@Override
AdapterOperationSchedule createScheduleForNode(int nodeId, AdapterOperationType adapterOperationType) {
AdapterOperationSchedule aos = new AdapterOperationSchedule(m_delay, 60, 3, m_timeUnit);
log().info("createScheduleForNode: Scheduling " + adapterOperationType + " for nodeid " + nodeId + " with schedule: " + aos);
return aos;
}
@Override
public boolean isNodeReady(AdapterOperation op) {
boolean readyState = false;
OnmsNode node = m_nodeDao.get(op.getNodeId());
if (node != null && node.getSysObjectId() != null) {
readyState = true;
}
return readyState;
}
/**
* <p>doAdd</p>
*
* @param nodeId a int.
* @param retry a boolean.
* @throws org.opennms.netmgt.provision.ProvisioningAdapterException if any.
*/
@Override
public void doAddNode(final int nodeId) throws ProvisioningAdapterException {
log().debug("doAdd: adding nodeid: " + nodeId);
final OnmsNode node = m_nodeDao.get(nodeId);
Assert.notNull(node, "doAdd: failed to return node for given nodeId:"+nodeId);
InetAddress ipaddress = m_template.execute(new TransactionCallback<InetAddress>() {
public InetAddress doInTransaction(TransactionStatus arg0) {
return getIpForNode(node);
}
});
SnmpAgentConfig agentConfig = null;
agentConfig = m_snmpConfigDao.getAgentConfig(ipaddress);
final OnmsAssetRecord asset = node.getAssetRecord();
m_config.getReadLock().lock();
try {
for (final AssetField field : m_config.getAssetFieldsForAddress(ipaddress, node.getSysObjectId())) {
try {
final String value = fetchSnmpAssetString(agentConfig, field.getMibObjs(), field.getFormatString());
if (log().isDebugEnabled()) {
log().debug("doAdd: Setting asset field \"" + field.getName() + "\" to value: " + value);
}
// Use Spring bean-accessor classes to set the field value
final BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(asset);
try {
wrapper.setPropertyValue(field.getName(), value);
} catch (final BeansException e) {
log().warn("doAdd: Could not set property \"" + field.getName() + "\" on asset object: " + e.getMessage(), e);
}
} catch (final MissingFormatArgumentException e) {
// This exception is thrown if the SNMP operation fails or an incorrect number of
// parameters is returned by the agent or because of a misconfiguration.
log().warn("doAdd: Could not set value for asset field \"" + field.getName() + "\": " + e.getMessage(), e);
}
}
} finally {
m_config.getReadLock().unlock();
}
node.setAssetRecord(asset);
m_nodeDao.saveOrUpdate(node);
m_nodeDao.flush();
}
private static String fetchSnmpAssetString(final SnmpAgentConfig agentConfig, final MibObjs mibObjs, final String formatString) throws MissingFormatArgumentException {
final List<String> aliases = new ArrayList<String>();
final List<SnmpObjId> objs = new ArrayList<SnmpObjId>();
for (final MibObj mibobj : mibObjs.getMibObj()) {
aliases.add(mibobj.getAlias());
objs.add(SnmpObjId.get(mibobj.getOid()));
}
// Fetch the values from the SNMP agent
final SnmpValue[] values = SnmpUtils.get(agentConfig, objs.toArray(new SnmpObjId[0]));
if (values.length == aliases.size()) {
final Properties substitutions = new Properties();
boolean foundAValue = false;
for (int i = 0; i < values.length; i++) {
// If the value is a NO_SUCH_OBJECT or NO_SUCH_INSTANCE error, then skip it
if (values[i].isError()) {
// No value for this OID
continue;
}
foundAValue = true;
// Use trapd's SyntaxToEvent parser so that we format base64
// and MAC address values appropriately
Parm parm = SyntaxToEvent.processSyntax(aliases.get(i), values[i]);
substitutions.setProperty(
aliases.get(i),
parm.getValue().getContent()
);
}
if (!foundAValue) {
if (log().isDebugEnabled()) {
log().debug("fetchSnmpAssetString: Failed to fetch any SNMP values for system " + agentConfig.toString());
}
throw new MissingFormatArgumentException("fetchSnmpAssetString: Failed to fetch any SNMP values for system " + agentConfig.toString());
} else {
log().debug("fetchSnmpAssetString: Fetched asset properties from SNMP agent:\n" + formatPropertiesAsString(substitutions));
}
if (objs.size() != substitutions.size()) {
log().warn("fetchSnmpAssetString: Unexpected number of properties returned from SNMP GET:\n" + formatPropertiesAsString(substitutions));
}
return PropertiesUtils.substitute(formatString, substitutions);
} else {
log().warn("fetchSnmpAssetString: Invalid number of SNMP parameters returned: " + values.length + " != " + aliases.size());
throw new MissingFormatArgumentException("fetchSnmpAssetString: Invalid number of SNMP parameters returned: " + values.length + " != " + aliases.size());
}
}
protected static String formatPropertiesAsString(final Properties props) {
final StringBuffer propertyValues = new StringBuffer();
for (final Map.Entry<Object, Object> entry : props.entrySet()) {
propertyValues.append(" ");
propertyValues.append(entry.getKey().toString());
propertyValues.append(" => ");
propertyValues.append(entry.getValue().toString());
propertyValues.append("\n");
}
return propertyValues.toString();
}
/**
* <p>doUpdate</p>
*
* @param nodeId a int.
* @param retry a boolean.
* @throws org.opennms.netmgt.provision.ProvisioningAdapterException if any.
*/
@Override
public void doUpdateNode(final int nodeId) throws ProvisioningAdapterException {
log().debug("doUpdate: updating nodeid: " + nodeId);
final OnmsNode node = m_nodeDao.get(nodeId);
Assert.notNull(node, "doUpdate: failed to return node for given nodeId:"+nodeId);
final InetAddress ipaddress = m_template.execute(new TransactionCallback<InetAddress>() {
public InetAddress doInTransaction(final TransactionStatus arg0) {
return getIpForNode(node);
}
});
final SnmpAgentConfig agentConfig = m_snmpConfigDao.getAgentConfig(ipaddress);
final OnmsAssetRecord asset = node.getAssetRecord();
m_config.getReadLock().lock();
try {
for (AssetField field : m_config.getAssetFieldsForAddress(ipaddress, node.getSysObjectId())) {
try {
String value = fetchSnmpAssetString(agentConfig, field.getMibObjs(), field.getFormatString());
if (log().isDebugEnabled()) {
log().debug("doUpdate: Setting asset field \"" + field.getName() + "\" to value: " + value);
}
// Use Spring bean-accessor classes to set the field value
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(asset);
try {
wrapper.setPropertyValue(field.getName(), value);
} catch (BeansException e) {
log().warn("doUpdate: Could not set property \"" + field.getName() + "\" on asset object: " + e.getMessage(), e);
}
} catch (MissingFormatArgumentException e) {
// This exception is thrown if the SNMP operation fails or an incorrect number of
// parameters is returned by the agent or because of a misconfiguration.
log().warn("doUpdate: Could not set value for asset field \"" + field.getName() + "\": " + e.getMessage(), e);
}
}
} finally {
m_config.getReadLock().unlock();
}
node.setAssetRecord(asset);
m_nodeDao.saveOrUpdate(node);
m_nodeDao.flush();
}
/**
* <p>doNodeConfigChanged</p>
*
* @param nodeId a int.
* @param retry a boolean.
* @throws org.opennms.netmgt.provision.ProvisioningAdapterException if any.
*/
@Override
public void doNotifyConfigChange(final int nodeId) throws ProvisioningAdapterException {
log().debug("doNodeConfigChanged: nodeid: " + nodeId);
}
/**
* <p>getNodeDao</p>
*
* @return a {@link org.opennms.netmgt.dao.NodeDao} object.
*/
public NodeDao getNodeDao() {
return m_nodeDao;
}
/**
* <p>setNodeDao</p>
*
* @param dao a {@link org.opennms.netmgt.dao.NodeDao} object.
*/
public void setNodeDao(final NodeDao dao) {
m_nodeDao = dao;
}
/**
* <p>getEventForwarder</p>
*
* @return a {@link org.opennms.netmgt.model.events.EventForwarder} object.
*/
public EventForwarder getEventForwarder() {
return m_eventForwarder;
}
/**
* <p>setEventForwarder</p>
*
* @param eventForwarder a {@link org.opennms.netmgt.model.events.EventForwarder} object.
*/
public void setEventForwarder(final EventForwarder eventForwarder) {
m_eventForwarder = eventForwarder;
}
/**
* @return the snmpConfigDao
*/
public SnmpAgentConfigFactory getSnmpPeerFactory() {
return m_snmpConfigDao;
}
/**
* @param snmpConfigDao the snmpConfigDao to set
*/
public void setSnmpPeerFactory(final SnmpAgentConfigFactory snmpConfigDao) {
this.m_snmpConfigDao = snmpConfigDao;
}
/**
* @return the m_config
*/
public SnmpAssetAdapterConfig getSnmpAssetAdapterConfig() {
return m_config;
}
/**
* @param mConfig the m_config to set
*/
public void setSnmpAssetAdapterConfig(final SnmpAssetAdapterConfig mConfig) {
m_config = mConfig;
}
private static ThreadCategory log() {
return ThreadCategory.getInstance(SnmpAssetProvisioningAdapter.class);
}
/**
* <p>getName</p>
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return NAME;
}
private InetAddress getIpForNode(final OnmsNode node) {
LogUtils.debugf(this, "getIpForNode: node: %s Foreign Source: %s", node.getNodeId(), node.getForeignSource());
final OnmsIpInterface primaryInterface = node.getPrimaryInterface();
InetAddress ipaddr = InetAddressUtils.getLocalHostAddress();
if (primaryInterface == null) {
log().debug("getIpForNode: found null SNMP Primary Interface, getting interfaces");
final Set<OnmsIpInterface> ipInterfaces = node.getIpInterfaces();
for (final OnmsIpInterface onmsIpInterface : ipInterfaces) {
log().debug("getIpForNode: trying Interface with id: " + onmsIpInterface.getId());
if (InetAddressUtils.str(onmsIpInterface.getIpAddress()) != null)
ipaddr = onmsIpInterface.getIpAddress();
else
log().debug("getIpForNode: found null ip address on Interface with id: " + onmsIpInterface.getId());
}
} else {
log().debug("getIpForNode: found SNMP Primary Interface");
if (InetAddressUtils.str(primaryInterface.getIpAddress()) != null )
ipaddr = primaryInterface.getIpAddress();
else
log().debug("getIpForNode: found null ip address on Primary Interface");
}
return ipaddr;
}
/**
* <p>handleReloadConfigEvent</p>
*
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
*/
@EventHandler(uei = EventConstants.RELOAD_DAEMON_CONFIG_UEI)
public void handleReloadConfigEvent(final Event event) {
if (isReloadConfigEventTarget(event)) {
LogUtils.debugf(this, "Reloading the SNMP asset adapter configuration");
try {
m_config.update();
} catch (Throwable e) {
LogUtils.infof(this, e, "Unable to reload SNMP asset adapter configuration");
}
}
}
private boolean isReloadConfigEventTarget(final Event event) {
boolean isTarget = false;
for (final Parm parm : event.getParmCollection()) {
if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName()) && ("Provisiond." + NAME).equalsIgnoreCase(parm.getValue().getContent())) {
isTarget = true;
break;
}
}
log().debug("isReloadConfigEventTarget: Provisiond." + NAME + " was target of reload event: " + isTarget);
return isTarget;
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}