/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 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.lang.reflect.UndeclaredThrowableException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.log4j.Level;
import org.opennms.core.utils.BeanUtils;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.LogUtils;
import org.opennms.core.utils.ParameterMap;
import org.opennms.core.utils.PropertiesUtils;
import org.opennms.core.utils.TimeoutTracker;
import org.opennms.netmgt.config.SnmpPeerFactory;
import org.opennms.netmgt.dao.NodeDao;
import org.opennms.netmgt.model.OnmsNode;
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.snmp.SnmpAgentConfig;
import org.opennms.netmgt.snmp.SnmpObjId;
import org.opennms.netmgt.snmp.SnmpUtils;
import org.opennms.netmgt.snmp.SnmpValue;
import org.opennms.netmgt.snmp.SnmpValueFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* <P>
* This class is designed to be used by the service poller framework to test the
* availability of the ICMP service on remote interfaces, using the CISCO-PING-MIB
* as a proxy of sorts. The class implements the ServiceMonitor interface that
* allows it to be used along with other plug-ins by the service poller framework.
*
* Derived from the SnmpMonitor class. See that class' docs for authorship history.
* </P>
*
* @author <A HREF="mailto:jeffg@opennms.org">Jeff Gehlbach</A>
* @author <A HREF="http://www.opennms.org/">OpenNMS </A>
*/
// This monitor uses SNMP and therefore relies on the SNMP peer factory,
// so it is not distributable
@Distributable(DistributionContext.DAEMON)
public class CiscoPingMibMonitor extends SnmpMonitorStrategy {
@SuppressWarnings("unused")
private static final class CiscoPingEntry {
private int m_ciscoPingSerialNumber;
private int m_ciscoPingProtocol;
private InetAddress m_ciscoPingAddress;
private int m_ciscoPingPacketCount;
private int m_ciscoPingPacketSize;
private int m_ciscoPingPacketTimeout;
private int m_ciscoPingPacketDelay;
private String m_ciscoPingEntryOwner;
private String m_ciscoPingVrfName;
private int m_ciscoPingEntryStatus;
public int getCiscoPingSerialNumber() {
return m_ciscoPingSerialNumber;
}
public void setCiscoPingSerialNumber(int ciscoPingSerialNumber) {
m_ciscoPingSerialNumber = ciscoPingSerialNumber;
}
public int getCiscoPingProtocol() {
return m_ciscoPingProtocol;
}
public void setCiscoPingProtocol(int ciscoPingProtocol) {
m_ciscoPingProtocol = ciscoPingProtocol;
}
public InetAddress getCiscoPingAddress() {
return m_ciscoPingAddress;
}
public byte[] getCiscoPingAddressBytes() {
return m_ciscoPingAddress.getAddress();
}
public void setCiscoPingAddress(InetAddress ciscoPingAddress) {
m_ciscoPingAddress = ciscoPingAddress;
}
public int getCiscoPingPacketCount() {
return m_ciscoPingPacketCount;
}
public void setCiscoPingPacketCount(int ciscoPingPacketCount) {
m_ciscoPingPacketCount = ciscoPingPacketCount;
}
public int getCiscoPingPacketSize() {
return m_ciscoPingPacketSize;
}
public void setCiscoPingPacketSize(int ciscoPingPacketSize) {
m_ciscoPingPacketSize = ciscoPingPacketSize;
}
public int getCiscoPingPacketTimeout() {
return m_ciscoPingPacketTimeout;
}
public void setCiscoPingPacketTimeout(int ciscoPingPacketTimeout) {
m_ciscoPingPacketTimeout = ciscoPingPacketTimeout;
}
public int getCiscoPingPacketDelay() {
return m_ciscoPingPacketDelay;
}
public void setCiscoPingPacketDelay(int ciscoPingPacketDelay) {
m_ciscoPingPacketDelay = ciscoPingPacketDelay;
}
public String getCiscoPingEntryOwner() {
return m_ciscoPingEntryOwner;
}
public void setCiscoPingEntryOwner(String ciscoPingEntryOwner) {
m_ciscoPingEntryOwner = ciscoPingEntryOwner;
}
public String getCiscoPingVrfName() {
return m_ciscoPingVrfName;
}
public void setCiscoPingVrfName(String ciscoPingVrfName) {
m_ciscoPingVrfName = ciscoPingVrfName;
}
public int getCiscoPingEntryStatus() {
return m_ciscoPingEntryStatus;
}
public void setCiscoPingEntryStatus(int ciscoPingEntryStatus) {
m_ciscoPingEntryStatus= ciscoPingEntryStatus;
}
public int calculateMinInitialWait() {
return m_ciscoPingPacketCount * (m_ciscoPingPacketTimeout + m_ciscoPingPacketDelay);
}
public SnmpObjId[] generateCreateOids() {
SnmpObjId[] oids = {
SnmpObjId.get(PING_ENTRY_OID + "." + PING_PROTOCOL + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_ADDRESS + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_PACKET_COUNT+ "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_PACKET_SIZE + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_PACKET_TIMEOUT + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_DELAY + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_ENTRY_OWNER + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_ENTRY_STATUS + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_VRF_NAME + "." + m_ciscoPingSerialNumber)
};
return oids;
}
public SnmpValue[] generateCreateValues() {
SnmpValueFactory vf = SnmpUtils.getValueFactory();
SnmpValue[] values = {
vf.getInt32(m_ciscoPingProtocol),
vf.getOctetString(m_ciscoPingAddress.getAddress()),
vf.getInt32(m_ciscoPingPacketCount),
vf.getInt32(m_ciscoPingPacketSize),
vf.getInt32(m_ciscoPingPacketTimeout),
vf.getInt32(m_ciscoPingPacketDelay),
vf.getOctetString(m_ciscoPingEntryOwner.getBytes()),
vf.getInt32(m_ciscoPingEntryStatus),
vf.getOctetString(m_ciscoPingVrfName.getBytes())
};
return values;
}
public SnmpObjId[] generateRowStatusOids() {
SnmpObjId[] oids = {
SnmpObjId.get(PING_ENTRY_OID + "." + PING_ENTRY_STATUS + "." + m_ciscoPingSerialNumber)
};
return oids;
}
public SnmpValue[] generateRowStatusValues() {
SnmpValueFactory vf = SnmpUtils.getValueFactory();
SnmpValue[] values = {
vf.getInt32(m_ciscoPingEntryStatus)
};
return values;
}
public SnmpObjId[] generateResultsOids() {
SnmpObjId[] oids = {
SnmpObjId.get(PING_ENTRY_OID + "." + PING_SENT_PACKETS + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_RECEIVED_PACKETS + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_MIN_RTT + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_AVG_RTT + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_MAX_RTT + "." + m_ciscoPingSerialNumber),
SnmpObjId.get(PING_ENTRY_OID + "." + PING_COMPLETED + "." + m_ciscoPingSerialNumber)
};
return oids;
}
public String toString() {
StringBuilder sb = new StringBuilder("CiscoPingEntry: [ciscoPingSerialNumber=");
sb.append(m_ciscoPingSerialNumber).append(",");
sb.append("ciscoPingProtocol=").append(m_ciscoPingProtocol).append(",");
sb.append("ciscoPingAddress=").append(m_ciscoPingAddress).append(",");
sb.append("ciscoPingPacketCount=").append(m_ciscoPingPacketCount).append(",");
sb.append("ciscoPingPacketSize=").append(m_ciscoPingPacketSize).append(",");
sb.append("ciscoPingPacketTimeout=").append(m_ciscoPingPacketTimeout).append(",");
sb.append("ciscoPingPacketDelay=").append(m_ciscoPingPacketDelay).append(",");
sb.append("ciscoPingEntryOwner=").append(m_ciscoPingEntryOwner).append(",");
sb.append("ciscoPingVrfName=").append(m_ciscoPingVrfName);
sb.append("]");
return sb.toString();
}
}
private static NodeDao s_nodeDao = null;
/**
* Name of monitored service.
*/
private static final String SERVICE_NAME = "CiscoPing";
/**
* Default timeout, in milliseconds, for the SNMP operations underlying this poll
*/
private static final int DEFAULT_TIMEOUT = 1800;
/**
* Default retry count for the SNMP operations underlying this poll
*/
private static final int DEFAULT_RETRY = 1;
/**
* Identifier of the ciscoPingEntry object
*/
private static final String PING_ENTRY_OID = ".1.3.6.1.4.1.9.9.16.1.1.1"; // Enterprises / cisco / ciscoMgmt /
// ciscoPingMIB / ciscoPingMIBObjects /
// ciscoPingTable / ciscoPingEntry
@SuppressWarnings("unused")
private static final String PING_SERIAL = "1";
private static final String PING_PROTOCOL = "2";
private static final String PING_ADDRESS = "3";
private static final String PING_PACKET_COUNT = "4";
private static final String PING_PACKET_SIZE = "5";
private static final String PING_PACKET_TIMEOUT = "6";
private static final String PING_DELAY = "7";
private static final String PING_SENT_PACKETS = "9";
private static final String PING_RECEIVED_PACKETS = "10";
private static final String PING_MIN_RTT = "11";
private static final String PING_AVG_RTT = "12";
private static final String PING_MAX_RTT = "13";
private static final String PING_COMPLETED = "14";
private static final String PING_ENTRY_OWNER = "15";
private static final String PING_ENTRY_STATUS = "16";
private static final String PING_VRF_NAME = "17";
@SuppressWarnings("unused")
private static final int ROWSTATUS_ACTIVE = 1;
@SuppressWarnings("unused")
private static final int ROWSTATUS_NOT_IN_SERVICE = 2;
@SuppressWarnings("unused")
private static final int ROWSTATUS_NOT_READY = 3;
private static final int ROWSTATUS_CREATE_AND_GO = 4;
@SuppressWarnings("unused")
private static final int ROWSTATUS_CREATE_WAIT = 5;
private static final int ROWSTATUS_DESTROY = 6;
private static final int PING_PROTOCOL_IPV4 = 1;
private static final int PING_PROTOCOL_IPV6 = 20;
/* Number of ping packets that IOS should send */
private static final String PARM_PACKET_COUNT = "packet-count";
private static final int PARM_PACKET_COUNT_DEFAULT = 5;
/* Size in bytes of each ping packet that IOS should send */
private static final String PARM_PACKET_SIZE = "packet-size";
private static final int PARM_PACKET_SIZE_DEFAULT = 100;
/* Timeout in milliseconds for each ping packet that IOS will send */
private static final String PARM_PACKET_TIMEOUT = "packet-timeout";
private static final int PARM_PACKET_TIMEOUT_DEFAULT = 2000;
/* Delay in milliseconds among the ping packets that IOS will send */
private static final String PARM_PACKET_DELAY = "packet-delay";
private static final int PARM_PACKET_DELAY_DEFAULT = 0;
/* A string identifying which management application "owns" this entry in the ciscoPingTable */
private static final String PARM_ENTRY_OWNER = "entry-owner";
private static final String PARM_ENTRY_OWNER_DEFAULT = "OpenNMS CiscoPingMibMonitor";
/* A string indicating the VPN name in which IOS will perform the ping. Normally blank. */
private static final String PARM_VRF_NAME = "vrf-name";
private static final String PARM_VRF_NAME_DEFAULT = "";
/* Maximum age (in ms, but with 1s accuracy) of a ciscoPingEntry that will not be deleted.
* Set to zero to disable cleanup of the ciscoPingTable. Best to leave set to default.
*/
@SuppressWarnings("unused")
private static final String PARM_CLEANUP_INTERVAL = "cleanup-interval";
@SuppressWarnings("unused")
private static final int PARM_CLEANUP_INTERVAL_DEFAULT = 86400000;
/* The node ID of the node that will act as our IOS ping proxy */
private static final String PARM_PROXY_NODE_ID = "proxy-node-id";
/* The foreign-source name of the node that will act as our IOS ping proxy */
private static final String PARM_PROXY_FOREIGN_SOURCE = "proxy-node-foreign-source";
/* The foreign-id of the node that will act as our IOS ping proxy */
private static final String PARM_PROXY_FOREIGN_ID = "proxy-node-foreign-id";
/* The IP address of the interface to use as our IOS ping proxy */
private static final String PARM_PROXY_IP_ADDR = "proxy-ip-addr";
/* The IP address of the interface we ultimately want to ping */
private static final String PARM_TARGET_IP_ADDR = "target-ip-addr";
/* The percent of proxied pings that must succeed for the service to be up */
private static final String PARM_SUCCESS_PERCENT = "success-percent";
private static final int PARM_SUCCESS_PERCENT_DEFAULT = 100;
/**
* <P>
* Returns the name of the service that the plug-in monitors ("CiscoPing").
* </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 (final Throwable 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) {
// Get the NodeDao from the common context so we can look up nodes in poll()
// Cannot use the pollerdContext because it creates a circular reference
// Cannot do this in the one-time initialize for the same reason, so do it here instead.
if (s_nodeDao == null) s_nodeDao = (NodeDao) BeanUtils.getFactory("commonContext", ClassPathXmlApplicationContext.class).getBean("nodeDao");
super.initialize(svc);
return;
}
/**
* {@inheritDoc}
*
* <P>
* The poll() method is responsible for setting up and following up the IOS
* ping entry that proxies monitoring of the specified address for ICMP
* service availability.
* </P>
* @exception RuntimeException
* Thrown for any unrecoverable errors.
*/
public PollStatus poll(MonitoredService svc, Map<String, Object> parameters) {
InetAddress targetIpAddr = (InetAddress) determineTargetAddress(svc, parameters);
int pingProtocol = 0;
try {
pingProtocol = determineAddrType(targetIpAddr);
} catch (RuntimeException e) {
return logDown(Level.ERROR, "Unknown address type - neither IPv4 nor IPv6", e);
}
// Get configuration parameters into a CiscoPingEntry object
//
CiscoPingEntry pingEntry = new CiscoPingEntry();
pingEntry.setCiscoPingPacketCount(ParameterMap.getKeyedInteger(parameters, PARM_PACKET_COUNT, PARM_PACKET_COUNT_DEFAULT));
pingEntry.setCiscoPingPacketSize(ParameterMap.getKeyedInteger(parameters, PARM_PACKET_SIZE, PARM_PACKET_SIZE_DEFAULT));
pingEntry.setCiscoPingPacketTimeout(ParameterMap.getKeyedInteger(parameters, PARM_PACKET_TIMEOUT, PARM_PACKET_TIMEOUT_DEFAULT));
pingEntry.setCiscoPingPacketDelay(ParameterMap.getKeyedInteger(parameters, PARM_PACKET_DELAY, PARM_PACKET_DELAY_DEFAULT));
pingEntry.setCiscoPingEntryOwner(ParameterMap.getKeyedString(parameters, PARM_ENTRY_OWNER, PARM_ENTRY_OWNER_DEFAULT));
pingEntry.setCiscoPingVrfName(ParameterMap.getKeyedString(parameters, PARM_VRF_NAME, PARM_VRF_NAME_DEFAULT));
pingEntry.setCiscoPingSerialNumber(Double.valueOf(System.currentTimeMillis() / 1000).intValue());
pingEntry.setCiscoPingProtocol(pingProtocol);
pingEntry.setCiscoPingAddress(targetIpAddr);
pingEntry.setCiscoPingEntryStatus(ROWSTATUS_CREATE_AND_GO);
int minSuccessPercent = ParameterMap.getKeyedInteger(parameters, PARM_SUCCESS_PERCENT, PARM_SUCCESS_PERCENT_DEFAULT);
// FIXME: Should the cleanup stuff be fixed to actually use this? Not clear if it really matters.
// int cleanupInterval = ParameterMap.getKeyedInteger(parameters, PARM_CLEANUP_INTERVAL, PARM_CLEANUP_INTERVAL_DEFAULT);
// Determine the node to use as our IOS ping proxy
//
InetAddress proxyIpAddr = determineProxyAddress(parameters, svc);
if (proxyIpAddr == null) return logDown(Level.ERROR, "Unable to determine proxy address for this service");
// Retrieve the *proxy* interface's SNMP peer object
//
SnmpAgentConfig agentConfig = SnmpPeerFactory.getInstance().getAgentConfig(proxyIpAddr);
if (agentConfig == null) throw new RuntimeException("SnmpAgentConfig object not available for proxy-ping interface " + proxyIpAddr);
LogUtils.debugf("poll: setting SNMP peer attribute for interface %s", proxyIpAddr.getHostAddress());
// 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()));
LogUtils.debugf(getClass(), "Setting up CISCO-PING-MIB proxy poll for service %s on interface %s -- %s",
svc.getSvcName(), targetIpAddr, pingEntry);
PollStatus serviceStatus = null;
TimeoutTracker timeoutTracker = new TimeoutTracker(parameters, DEFAULT_RETRY, DEFAULT_TIMEOUT);
// Send the SET-REQUEST PDU to create the ciscoPingEntry in createAndGo mode
SnmpValue[] setResult = SnmpUtils.set(agentConfig, pingEntry.generateCreateOids(), pingEntry.generateCreateValues());
if (setResult == null) {
LogUtils.warnf(getClass(), "SNMP SET operation unsuccessful for proxy-ping entry for target %s -- %s", targetIpAddr, pingEntry);
return PollStatus.unknown("SNMP SET failed for ciscoPingTable entry on proxy interface " + proxyIpAddr + " with instance ID " + pingEntry.getCiscoPingSerialNumber());
}
// With the ciscoPingEntry created, we now wait until the specified pings have had time to
// complete. Twice the time it would take assuming a zero response time per ping seems like
// a good starting point.
try {
Thread.sleep(pingEntry.calculateMinInitialWait() * 2);
} catch (InterruptedException e) { }
// Now check whether the ping has completed and, if so, whether it succeeded and its times
SnmpValue[] statusValues = null;
for (timeoutTracker.reset(); (timeoutTracker.shouldRetry() && (statusValues == null || statusValues.length < 6 || statusValues[5].toInt() != 1)); timeoutTracker.nextAttempt()) {
statusValues = SnmpUtils.get(agentConfig, pingEntry.generateResultsOids());
}
// If we didn't get the results back, mark the service as unknown
if (statusValues == null || (statusValues.length == 1 && statusValues[0] == null)) {
LogUtils.warnf(getClass(), "SNMP GET operation unsuccessful for proxy-ping entry for target %s -- %s", targetIpAddr, pingEntry);
return PollStatus.unknown("SNMP GET failed for ciscoPingTable entry on proxy interface " + proxyIpAddr + " with instance ID " + pingEntry.getCiscoPingSerialNumber());
}
// If we got results back but they do not contain the pingCompleted column is missing,
// mark the service unknown
if (statusValues.length < 6) {
LogUtils.warnf(getClass(), "Proxy-ping entry did not indicate whether ping completed after retries exhausted for target %s -- %s", targetIpAddr, pingEntry);
return PollStatus.unknown("ciscoPingTable entry is missing pingCompleted column on proxy interface " + proxyIpAddr + " with instance ID " + pingEntry.getCiscoPingSerialNumber());
}
// If we got the results back but they indicate that the ping still has not completed,
// mark the service unknown
if (statusValues[5].toInt() != 1) {
LogUtils.warnf(getClass(), "Proxy-ping entry marked not completed after retries exhausted for target %s -- %s", targetIpAddr, pingEntry);
return PollStatus.unknown("ciscoPingTable entry marked not completed on proxy interface " + proxyIpAddr + " with instance ID " + pingEntry.getCiscoPingSerialNumber());
}
// If the ping has completed, verify that the percent of completed pings meets our minimum
// success percent. If not, mark the service down.
double sentPings = statusValues[0].toInt();
double receivedPings = statusValues[1].toInt();
double successPct = receivedPings / sentPings * 100;
if (receivedPings == 0) {
LogUtils.infof(getClass(), "Proxy-ping entry indicates no pings succeeded for target %s -- %s", targetIpAddr, pingEntry);
cleanupCurrentEntry(pingEntry, proxyIpAddr, agentConfig);
return PollStatus.unavailable("All remote pings (" + sentPings + " of " + sentPings + ") failed");
} else if (successPct < minSuccessPercent) {
LogUtils.infof(getClass(), "Proxy-ping entry indicates %f%% success, which misses the success-percent target of %d%% for target %s -- %s", successPct, minSuccessPercent, targetIpAddr, pingEntry);
cleanupCurrentEntry(pingEntry, proxyIpAddr, agentConfig);
return PollStatus.unavailable(successPct + " percent (" + receivedPings + "/" + sentPings+ ") pings succeeded, less than target " + minSuccessPercent + " percent");
}
// If we've arrived here, then enough pings completed to consider the service up!
Map<String,Number> pingProps = new HashMap<String,Number>();
double minRtt = statusValues[2].toInt();
double avgRtt = statusValues[3].toInt();
double maxRtt = statusValues[4].toInt();
LogUtils.debugf(getClass(), "Logging successful poll: sent=%f, received=%f, minRtt=%f, avgRtt=%f, maxRtt=%f for proxy-ping of target %s -- %s", sentPings, receivedPings, minRtt, avgRtt, maxRtt, targetIpAddr, pingEntry);
pingProps.put("sent", sentPings);
pingProps.put("received", receivedPings);
pingProps.put("minRtt", minRtt);
pingProps.put("avgRtt", avgRtt);
pingProps.put("maxRtt", maxRtt);
cleanupCurrentEntry(pingEntry, proxyIpAddr, agentConfig);
// TODO: Find and clean up defunct rows before returning
// Actually it's not clear that this is necessary, seems IOS cleans up old
// entries on its own some minutes after their creation. Need to investigate.
serviceStatus = PollStatus.available(avgRtt);
serviceStatus.setProperties(pingProps);
return serviceStatus;
}
private void cleanupCurrentEntry(CiscoPingEntry pingEntry, InetAddress proxyIpAddr, SnmpAgentConfig agentConfig) {
pingEntry.setCiscoPingEntryStatus(ROWSTATUS_DESTROY);
SnmpValue[] destroyValues = SnmpUtils.set(agentConfig, pingEntry.generateRowStatusOids(), pingEntry.generateRowStatusValues());
if (destroyValues == null) LogUtils.warnf(getClass(), "SNMP SET failed to delete just-used ciscoPingEntry on proxy interface %s with instance ID %d", proxyIpAddr, pingEntry.getCiscoPingSerialNumber());
if (destroyValues[0].toInt() != ROWSTATUS_DESTROY) LogUtils.warnf(getClass(), "SNMP SET to delete just-used ciscoPingEntry indicated row not deleted on proxy interface %s with instance ID %d", proxyIpAddr, pingEntry.getCiscoPingSerialNumber());
}
private InetAddress determineTargetAddress(MonitoredService svc, Map<String, Object> parameters) {
String rawOverrideTarget = ParameterMap.getKeyedString(parameters, PARM_TARGET_IP_ADDR, null);
String overrideTarget = rawOverrideTarget;
if (rawOverrideTarget != null) {
overrideTarget = PropertiesUtils.substitute(rawOverrideTarget, getServiceProperties(svc));
LogUtils.debugf(getClass(), "Expanded value '%s' of parameter %s to '%s' for service %s on interface %s", rawOverrideTarget, PARM_TARGET_IP_ADDR, overrideTarget, svc.getSvcName(), svc.getAddress());
}
if (overrideTarget == null) return svc.getAddress();
LogUtils.debugf(getClass(), "Using user-specified override target IP address %s instead of service address %s for service %s", overrideTarget, svc.getAddress(), svc.getSvcName());
try {
final InetAddress overrideAddr = InetAddressUtils.addr(overrideTarget);
LogUtils.debugf(getClass(), "Overriding service address (%s) with user-specified target address (%s) for service %s", svc.getAddress(), overrideAddr, svc.getSvcName());
return overrideAddr;
} catch (final IllegalArgumentException e) {
LogUtils.warnf(getClass(), "Failed to look up %s override value %s for service %s. Using service interface %s instead", PARM_TARGET_IP_ADDR, overrideTarget, svc.getSvcName(), svc.getAddress());
}
return svc.getAddress();
}
private InetAddress determineProxyAddress(Map<String, Object> parameters, MonitoredService svc) {
LogUtils.debugf(getClass(), "Determining the proxy address on which to set up the ciscoPingEntry for target interface %s", svc.getAddress());
OnmsNode proxyNode = null;
InetAddress proxyAddress = null;
String proxyNodeId = ParameterMap.getKeyedString(parameters, PARM_PROXY_NODE_ID, null);
String proxyNodeFS = ParameterMap.getKeyedString(parameters, PARM_PROXY_FOREIGN_SOURCE, null);
String proxyNodeFI = ParameterMap.getKeyedString(parameters, PARM_PROXY_FOREIGN_ID, null);
String rawProxyIpAddr = ParameterMap.getKeyedString(parameters, PARM_PROXY_IP_ADDR, null);
String proxyIpAddr = rawProxyIpAddr;
if (rawProxyIpAddr != null) {
proxyIpAddr = PropertiesUtils.substitute(rawProxyIpAddr, getServiceProperties(svc));
LogUtils.debugf(getClass(), "Expanded value '%s' of parameter %s to '%s' for service %s on interface %s", rawProxyIpAddr, PARM_PROXY_IP_ADDR, proxyIpAddr, svc.getSvcName(), svc.getAddress());
}
/* If we have a foreign-source and foreign-id, short circuit to use that */
if (proxyNodeFS != null && !proxyNodeFS.equals("") && proxyNodeFI != null && !proxyNodeFI.equals("")) {
LogUtils.debugf(getClass(), "Trying to look up proxy node with foreign-source %s, foreign-id %s for target interface %s", proxyNodeFS, proxyNodeFI, svc.getAddress());
proxyNode = s_nodeDao.findByForeignId(proxyNodeFS, proxyNodeFI);
LogUtils.debugf(getClass(), "Found a node via foreign-source / foreign-id '%s'/'%s' to use as proxy", proxyNodeFS, proxyNodeFI);
if (proxyNode != null && proxyNode.getPrimaryInterface() != null) proxyAddress = proxyNode.getPrimaryInterface().getIpAddress();
}
if (proxyAddress != null) {
LogUtils.infof(getClass(), "Using address %s from node '%s':'%s' as proxy for service '%s' on interface %s", proxyAddress, proxyNodeFS, proxyNodeFI, svc.getSvcName(), svc.getIpAddr());
return proxyAddress;
}
/* No match with foreign-source / foreign-id? Try with a node ID */
if (proxyNodeId != null && Integer.valueOf(proxyNodeId) != null) {
LogUtils.debugf(getClass(), "Trying to look up proxy node with database ID %d for target interface %s", proxyNodeId, svc.getAddress());
proxyNode = s_nodeDao.get(Integer.valueOf(proxyNodeId));
if (proxyNode != null && proxyNode.getPrimaryInterface() != null) proxyAddress = proxyNode.getPrimaryInterface().getIpAddress();
}
if (proxyAddress != null) {
LogUtils.infof(getClass(), "Using address %s from node with DBID %s as proxy for service '%s' on interface %s", proxyAddress, proxyNodeId, svc.getSvcName(), svc.getIpAddr());
return proxyAddress;
}
/* No match with any node criteria? Try for a plain old IP address. */
LogUtils.infof(getClass(), "Trying to use address %s as proxy-ping agent address for target interface %s", proxyIpAddr, svc.getAddress());
try {
if (!"".equals(proxyIpAddr)) {
proxyAddress = InetAddressUtils.addr(proxyIpAddr);
}
} catch (final IllegalArgumentException e) {}
if (proxyAddress != null) {
LogUtils.infof(getClass(), "Using address %s (user-specified) as proxy for service '%s' on interface %s", proxyAddress, svc.getSvcName(), svc.getIpAddr());
return proxyAddress;
}
LogUtils.errorf(getClass(), "Unable to determine proxy address for service '%s' on interface '%s'. The poll will be unable to proceed.", svc.getSvcName(), svc.getIpAddr());
return null;
}
private int determineAddrType(InetAddress ipaddr) {
if (ipaddr instanceof Inet6Address) {
LogUtils.debugf(getClass(), "The address %s is IPv6", ipaddr);
return PING_PROTOCOL_IPV6;
} else if (ipaddr instanceof Inet4Address) {
LogUtils.debugf(getClass(), "The address %s is IPv4", ipaddr);
return PING_PROTOCOL_IPV4;
}
LogUtils.errorf(getClass(), "The address %s is neither IPv4 nor IPv6. Don't know how to proceed, giving up.", ipaddr);
throw new RuntimeException("Cannot work with address " + ipaddr + " because it is neither IPv4 nor IPv6.");
}
private Properties getServiceProperties(MonitoredService svc) {
Properties properties = new Properties();
properties.put("ipaddr", svc.getIpAddr());
properties.put("nodeid", svc.getNodeId());
properties.put("nodelabel", svc.getNodeLabel());
properties.put("svcname", svc.getSvcName());
return properties;
}
}