/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2009-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.poller.monitors;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.InetAddress;
import java.util.Map;
import org.apache.log4j.Level;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.ParameterMap;
import org.opennms.netmgt.config.SnmpPeerFactory;
import org.opennms.netmgt.model.PollStatus;
import org.opennms.netmgt.poller.Distributable;
import org.opennms.netmgt.poller.DistributionContext;
import org.opennms.netmgt.poller.MonitoredService;
import org.opennms.netmgt.poller.NetworkInterface;
import org.opennms.netmgt.snmp.SnmpAgentConfig;
import org.opennms.netmgt.snmp.SnmpInstId;
import org.opennms.netmgt.snmp.SnmpObjId;
import org.opennms.netmgt.snmp.SnmpUtils;
import org.opennms.netmgt.snmp.SnmpValue;
/**
* <p>
* This class is used to monitor if a particular Cisco IP-SLA runs within
* thresholds and has no timeouts. The configured IP-SLA is monitored by the
* specified "ip sla tag"
* </p>
* <p>
* This does SNMP and therefore relies on the SNMP configuration so it is not distributable.
* </p>
*
* @author <A HREF="mailto:r.trommer@open-factory.org">Ronny Trommer</A>
* @version $Id: $
*/
@Distributable(DistributionContext.DAEMON)
final public class CiscoIpSlaMonitor extends SnmpMonitorStrategy {
/**
* Name of monitored service.
*/
private static final String SERVICE_NAME = "Cisco_IP_SLA";
/**
* A string which is used by a managing application to identify the RTT
* target.
*/
private static final String RTT_ADMIN_TAG_OID = ".1.3.6.1.4.1.9.9.42.1.2.1.1.3";
/**
* The RttMonOperStatus object is used to manage the state.
*/
private static final String RTT_OPER_STATE_OID = ".1.3.6.1.4.1.9.9.42.1.2.9.1.10";
/**
* A sense code for the completion status of the latest RTT operation.
*/
private static final String RTT_LATEST_OPERSENSE_OID = ".1.3.6.1.4.1.9.9.42.1.2.10.1.2";
/**
* his object defines an administrative threshold limit. If the RTT operation
* time exceeds this limit and if the conditions specified in
* rttMonReactAdminThresholdType or rttMonHistoryAdminFilter are satisfied,
* a threshold is generated.
*/
private static final String RTT_ADMIN_THRESH_OID = ".1.3.6.1.4.1.9.9.42.1.2.1.1.5";
/**
* The type of RTT operation to be performed.
*/
private static final String RTT_ADMIN_TYPE_OID = ".1.3.6.1.4.1.9.9.42.1.2.1.1.4";
/**
* The completion time of the latest RTT operation successfully completed.
*/
private static final String RTT_LATEST_OID = ".1.3.6.1.4.1.9.9.42.1.2.10.1.1";
/**
* Implement the rttMonCtrlOperState
*/
private enum RTT_MON_OPER_STATE {
RESET(1), ORDERLY_STOP(2), IMMEDIATE_STOP(3), PENDING(4), INACTIVE(5), ACTIVE(
6), RESTART(7);
private final int state; // state code
RTT_MON_OPER_STATE(int s) {
this.state = s;
}
private int value() {
return this.state;
}
};
/**
* Implement the rttMonCtrlOperSense
*/
private enum RTT_MON_OPER_SENSE {
OTHER(0), OK(1), DISCONNECTED(2), OVER_THRESHOLD(3), TIMEOUT(4), BUSY(
5), NOT_CONNECTED(6), DROPPED(7), SEQUENCE_ERROR(8), VERIFY_ERROR(
9), APPLICATION_SPECIFIC(10), DNS_SERVER_TIMEOUT(11), TCP_CONNECT_TIMEOUT(
12), HTTP_TRANSACTION_TIMEOUT(13), DNS_QUERY_ERROR(14), HTTP_ERROR(
15), ERROR(16);
private final int state; // state code
RTT_MON_OPER_SENSE(int s) {
this.state = s;
}
private int value() {
return this.state;
}
};
/**
* Implement the rttMonCtrlAdminRttType
*/
private enum RTT_MON_ADMIN_TYPE {
ECHO(1), PATH_ECHO(2), FILE_IO(3), SCRIPT(4), UDP_ECHO(5),TCP_CONNECT(
6), HTTP(7), DNS(8), JITTER(9), DLSW(10), DHCP(11), FTP(12);
private final int state; // state code
RTT_MON_ADMIN_TYPE(int s) {
this.state = s;
}
private int value() {
return this.state;
}
};
/**
* <P>
* Returns the name of the service that the plug-in monitors
* ("Cisco IP-SLA monitor").
* </P>
*
* @return The service that the plug-in monitors.
*/
public String serviceName() {
return SERVICE_NAME;
}
/**
* {@inheritDoc}
*
* <P>
* Initialize the service monitor.
* </P>
* @exception RuntimeException
* Thrown if an unrecoverable error occurs that prevents
* the plug-in from functioning.
*/
public void initialize(Map<String,Object> parameters) {
// Initialize the SnmpPeerFactory
//
try {
SnmpPeerFactory.init();
} catch (IOException ex) {
log().fatal("initialize: Failed to load SNMP configuration", ex);
throw new UndeclaredThrowableException(ex);
}
return;
}
/**
* <P>
* Called by the poller framework when an interface is being added to the
* scheduler. Here we perform any necessary initialization to prepare the
* NetworkInterface object for polling.
* </P>
*
* @exception RuntimeException
* Thrown if an unrecoverable error occurs that prevents
* the interface from being monitored.
* @param svc a {@link org.opennms.netmgt.poller.MonitoredService} object.
*/
public void initialize(MonitoredService svc) {
super.initialize(svc);
return;
}
/**
* {@inheritDoc}
*
* <P>
* The poll() method is responsible for polling the specified address for
* SNMP service availability.
* </P>
* @exception RuntimeException
* Thrown for any uncrecoverable errors.
*/
public PollStatus poll(MonitoredService svc, Map<String,Object> parameters) {
NetworkInterface<InetAddress> iface = svc.getNetInterface();
String returnValue = "SNMP request failed or Cisco IP SLA tag ";
boolean monitorThresh = false;
PollStatus status = PollStatus.unavailable();
InetAddress ipaddr = (InetAddress) iface.getAddress();
// Retrieve this interface's SNMP peer object
//
SnmpAgentConfig agentConfig = SnmpPeerFactory.getInstance().getAgentConfig(ipaddr);
if (agentConfig == null) throw new RuntimeException("SnmpAgentConfig object not available for interface " + ipaddr);
final String hostAddress = InetAddressUtils.str(ipaddr);
log().debug("poll: setting SNMP peer attribute for interface " + hostAddress);
// Get configuration parameters for tag to monitor
String adminTag = ParameterMap.getKeyedString(parameters,"admin-tag", null);
if (adminTag == null) {
status = logDown(Level.ERROR, "No IP SLA admin-tag parameter defined! ");
return status;
}
// If no ip sla tag found, tell which ip sla tag is configured
returnValue += adminTag + " not found";
// Get configuration parameter to check if threshold should monitor
String ignoreThreshold = ParameterMap.getKeyedString(parameters,"ignore-thresh",null);
if (ignoreThreshold == null) {
status = logDown(Level.ERROR, "No ignoreThreshold parmater defined! ");
return status;
}
// Convert threshold parameter to boolean
if (ignoreThreshold.equals("false")) {
monitorThresh = true;
}
// set timeout and retries on SNMP peer object
//
agentConfig.setTimeout(ParameterMap.getKeyedInteger(parameters,"timeout",agentConfig.getTimeout()));
agentConfig.setRetries(ParameterMap.getKeyedInteger(parameters,"retry",ParameterMap.getKeyedInteger(parameters,"retries",agentConfig.getRetries())));
agentConfig.setPort(ParameterMap.getKeyedInteger(parameters,"port",agentConfig.getPort()));
// Establish SNMP session with interface
try {
if (log().isDebugEnabled()) {
log().debug("poll: SnmpAgentConfig address: " + agentConfig);
}
// Get all configured ip sla tags
Map<SnmpInstId, SnmpValue> tagResults = SnmpUtils.getOidValues(agentConfig,"CiscoIpSlaMonitor",SnmpObjId.get(RTT_ADMIN_TAG_OID));
if (tagResults == null) {
status = logDown(Level.ERROR,"No admin tags received! ");
return status;
}
// Iterate over the list of configured IP SLAs
for (SnmpInstId ipslaInstance : tagResults.keySet()) {
// Check if a given IP-SLA with the specific "tag" exist
if (tagResults.get(ipslaInstance).toString().equals(adminTag)) {
// Get all operation sense
Map<SnmpInstId, SnmpValue> operSenseResults = SnmpUtils.getOidValues(agentConfig,"CiscoIpSlaMonitor",SnmpObjId.get(RTT_LATEST_OPERSENSE_OID));
if (operSenseResults == null) {
status = logDown(Level.ERROR,"No latest oper sense received! ");
return status;
}
// Get all operation states
Map<SnmpInstId, SnmpValue> operStateResults = SnmpUtils.getOidValues(agentConfig,"CiscoIpSlaMonitor",SnmpObjId.get(RTT_OPER_STATE_OID));
if (operStateResults == null) {
status = logDown(Level.ERROR, "No oper state received! ");
return status;
}
// Get all configured ip sla types
Map<SnmpInstId, SnmpValue> adminTypeResults = SnmpUtils.getOidValues(agentConfig,"CiscoIpSlaMonitor",SnmpObjId.get(RTT_ADMIN_TYPE_OID));
if (adminTypeResults == null) {
status = logDown(Level.ERROR, "No ip sla types received! ");
return status;
}
// Get all configured ip sla latest RTT
Map<SnmpInstId, SnmpValue> latestRttResults = SnmpUtils.getOidValues(agentConfig,"CiscoIpSlaMonitor",SnmpObjId.get(RTT_LATEST_OID));
if (latestRttResults == null) {
status = logDown(Level.ERROR, "No ip sla latest RTT received! ");
return status;
}
log().debug(
"poll: " + "instance=" + ipslaInstance.toInt()
+ "admin tag=" + adminTag + " value="
+ tagResults.get(ipslaInstance)
+ " oper state="
+ operStateResults.get(ipslaInstance)
+ " ignoreThreshold=" + ignoreThreshold
+ " latest RTT" + latestRttResults.get(ipslaInstance));
// Build return value for service down
returnValue = "Cisco IP SLA tag "
+ adminTag
+ " with oper state "
+ resolveOperSate(operStateResults.get(ipslaInstance).toInt())
+ " returned with oper sense "
+ resolveOperSense(operSenseResults.get(ipslaInstance).toInt())
+ ". Configured IP SLA type is " + resolveAdminType(adminTypeResults.get(ipslaInstance).toInt())
+ ". Latest RTT is " + latestRttResults.get(ipslaInstance);
log().debug(returnValue);
// Check if thresholding is relevant
if (monitorThresh
&& (operSenseResults.get(ipslaInstance).toInt() == RTT_MON_OPER_SENSE.OVER_THRESHOLD.value())) {
// Get all configured ip sla thresholds
Map<SnmpInstId, SnmpValue> threshResults = SnmpUtils.getOidValues(agentConfig,"CiscoIpSlaMonitor",SnmpObjId.get(RTT_ADMIN_THRESH_OID));
if (monitorThresh && threshResults == null) {
status = logDown(Level.ERROR,"No ip sla thresholds received! ");
return status;
}
// Threshold monitoring
log().debug(
"IP SLA: " + tagResults.get(ipslaInstance)
+ " threshold exceeded.");
returnValue += ". Monitoring threshold is enabled. Threshold value is "
+ threshResults.get(ipslaInstance);
// Configured threshold is exceeded, service unavailable
return PollStatus.unavailable(returnValue);
} else {
/*
* Threshold should be ignored, check if OK or
* OVER_THRESHOLD.
* Over threshold means also OK, no timeout or other
* error occurred.
*/
if (operSenseResults.get(ipslaInstance).toInt() == RTT_MON_OPER_SENSE.OK.value()
|| operSenseResults.get(ipslaInstance).toInt() == RTT_MON_OPER_SENSE.OVER_THRESHOLD.value()) {
log().debug("Threshold is ignored rttMonLatestOperSense: " + operSenseResults.get(ipslaInstance).toInt());
status = logUp(Level.INFO, Double.parseDouble(latestRttResults.get(ipslaInstance).toString()), returnValue);
// No error or connection timeout, service available
return status;
}
}
} // else no configured IP SLA tag exist
}
// Otherwise set service down
status = PollStatus.unavailable(returnValue);
} catch (NullPointerException e) {
status = logDown(Level.WARN,
"Unexpected error during SNMP poll of interface "
+ hostAddress, e);
} catch (NumberFormatException e) {
status = logDown(Level.WARN,
"Number operator used on a non-number "
+ e.getMessage());
} catch (IllegalArgumentException e) {
status = logDown(Level.WARN, "Invalid SNMP Criteria: "
+ e.getMessage());
} catch (Throwable t) {
status = logDown(Level.WARN,
"Unexpected exception during SNMP poll of interface "
+ hostAddress, t);
}
// Otherwise, the service will be unavailable.
return status;
}
/**
* Method to resolve a given ip sla operation state code to human readable
* string.
* TODO: Check if there is a better way to resolve the states backward
*
* @param sc
* operation state code
* @return Human readable operation state
*/
private String resolveOperSate(int sc) {
String name = "UNKNOWN";
if (RTT_MON_OPER_STATE.RESET.value() == sc)
name = RTT_MON_OPER_STATE.RESET.name();
if (RTT_MON_OPER_STATE.ORDERLY_STOP.value() == sc)
name = RTT_MON_OPER_STATE.ORDERLY_STOP.name();
if (RTT_MON_OPER_STATE.IMMEDIATE_STOP.value() == sc)
name = RTT_MON_OPER_STATE.IMMEDIATE_STOP.name();
if (RTT_MON_OPER_STATE.PENDING.value() == sc)
name = RTT_MON_OPER_STATE.PENDING.name();
if (RTT_MON_OPER_STATE.INACTIVE.value() == sc)
name = RTT_MON_OPER_STATE.INACTIVE.name();
if (RTT_MON_OPER_STATE.ACTIVE.value() == sc)
name = RTT_MON_OPER_STATE.ACTIVE.name();
if (RTT_MON_OPER_STATE.RESTART.value() == sc)
name = RTT_MON_OPER_STATE.RESTART.name();
return name;
}
/**
* Method to resolve a given ip sla operation sense code to human readable string.
* TODO: Check if there is a better way to resolve the states backward
*
* @param sc
* operation sense code
* @return Human readable operation sense
*/
private String resolveOperSense(int sc) {
String name = "UNKNOWN";
if (RTT_MON_OPER_SENSE.OTHER.value() == sc)
name = RTT_MON_OPER_SENSE.OTHER.name();
if (RTT_MON_OPER_SENSE.OK.value() == sc)
name = RTT_MON_OPER_SENSE.OK.name();
if (RTT_MON_OPER_SENSE.DISCONNECTED.value() == sc)
name = RTT_MON_OPER_SENSE.DISCONNECTED.name();
if (RTT_MON_OPER_SENSE.OVER_THRESHOLD.value() == sc)
name = RTT_MON_OPER_SENSE.OVER_THRESHOLD.name();
if (RTT_MON_OPER_SENSE.TIMEOUT.value() == sc)
name = RTT_MON_OPER_SENSE.TIMEOUT.name();
if (RTT_MON_OPER_SENSE.BUSY.value() == sc)
name = RTT_MON_OPER_SENSE.BUSY.name();
if (RTT_MON_OPER_SENSE.NOT_CONNECTED.value() == sc)
name = RTT_MON_OPER_SENSE.NOT_CONNECTED.name();
if (RTT_MON_OPER_SENSE.DROPPED.value() == sc)
name = RTT_MON_OPER_SENSE.DROPPED.name();
if (RTT_MON_OPER_SENSE.SEQUENCE_ERROR.value() == sc)
name = RTT_MON_OPER_SENSE.SEQUENCE_ERROR.name();
if (RTT_MON_OPER_SENSE.VERIFY_ERROR.value() == sc)
name = RTT_MON_OPER_SENSE.VERIFY_ERROR.name();
if (RTT_MON_OPER_SENSE.APPLICATION_SPECIFIC.value() == sc)
name = RTT_MON_OPER_SENSE.APPLICATION_SPECIFIC.name();
if (RTT_MON_OPER_SENSE.DNS_SERVER_TIMEOUT.value() == sc)
name = RTT_MON_OPER_SENSE.DNS_SERVER_TIMEOUT.name();
if (RTT_MON_OPER_SENSE.TCP_CONNECT_TIMEOUT.value() == sc)
name = RTT_MON_OPER_SENSE.TCP_CONNECT_TIMEOUT.name();
if (RTT_MON_OPER_SENSE.HTTP_TRANSACTION_TIMEOUT.value() == sc)
name = RTT_MON_OPER_SENSE.HTTP_TRANSACTION_TIMEOUT.name();
if (RTT_MON_OPER_SENSE.DNS_QUERY_ERROR.value() == sc)
name = RTT_MON_OPER_SENSE.DNS_QUERY_ERROR.name();
if (RTT_MON_OPER_SENSE.HTTP_ERROR.value() == sc)
name = RTT_MON_OPER_SENSE.HTTP_ERROR.name();
if (RTT_MON_OPER_SENSE.ERROR.value() == sc)
name = RTT_MON_OPER_SENSE.ERROR.name();
return name;
}
/**
* Method to resolve a given ip sla admin type to human readable string.
* TODO: Check if there is a better way to resolve the states backward
*
* @param sc
* oper state code
* @return Human readable oper state
*/
private String resolveAdminType(int sc) {
String name = "UNKNOWN";
if (RTT_MON_ADMIN_TYPE.ECHO.value() == sc)
name = RTT_MON_ADMIN_TYPE.ECHO.name();
if (RTT_MON_ADMIN_TYPE.PATH_ECHO.value() == sc)
name = RTT_MON_ADMIN_TYPE.PATH_ECHO.name();
if (RTT_MON_ADMIN_TYPE.FILE_IO.value() == sc)
name = RTT_MON_ADMIN_TYPE.FILE_IO.name();
if (RTT_MON_ADMIN_TYPE.SCRIPT.value() == sc)
name = RTT_MON_ADMIN_TYPE.SCRIPT.name();
if (RTT_MON_ADMIN_TYPE.UDP_ECHO.value() == sc)
name = RTT_MON_ADMIN_TYPE.UDP_ECHO.name();
if (RTT_MON_ADMIN_TYPE.TCP_CONNECT.value() == sc)
name = RTT_MON_ADMIN_TYPE.TCP_CONNECT.name();
if (RTT_MON_ADMIN_TYPE.HTTP.value() == sc)
name = RTT_MON_ADMIN_TYPE.HTTP.name();
if (RTT_MON_ADMIN_TYPE.DNS.value() == sc)
name = RTT_MON_ADMIN_TYPE.DNS.name();
if (RTT_MON_ADMIN_TYPE.JITTER.value() == sc)
name = RTT_MON_ADMIN_TYPE.JITTER.name();
if (RTT_MON_ADMIN_TYPE.DLSW.value() == sc)
name = RTT_MON_ADMIN_TYPE.DLSW.name();
if (RTT_MON_ADMIN_TYPE.DHCP.value() == sc)
name = RTT_MON_ADMIN_TYPE.DHCP.name();
if (RTT_MON_ADMIN_TYPE.FTP.value() == sc)
name = RTT_MON_ADMIN_TYPE.FTP.name();
return name;
}
}