/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-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.threshd;
import java.net.InetAddress;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.LogUtils;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.config.PollOutagesConfigFactory;
import org.opennms.netmgt.config.threshd.Package;
import org.opennms.netmgt.config.threshd.Parameter;
import org.opennms.netmgt.config.threshd.Service;
import org.opennms.netmgt.eventd.EventIpcManagerFactory;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventProxy;
import org.opennms.netmgt.poller.InetNetworkInterface;
import org.opennms.netmgt.scheduler.LegacyScheduler;
import org.opennms.netmgt.scheduler.ReadyRunnable;
/**
* <P>
* The ThresholdableService class ...
* </P>
*
* @author <A HREF="mailto:mike@opennms.org">Mike Davidson </A>
* @author <A HREF="http://www.opennms.org/">OpenNMS </A>
*
* @deprecated Thresholding now done in CollectableService (in collectd)
*/
final class ThresholdableService extends InetNetworkInterface implements ThresholdNetworkInterface, ReadyRunnable {
/**
*
*/
private static final long serialVersionUID = 2477161545461824755L;
/**
* Interface's parent node identifier
*/
private int m_nodeId;
/**
* The package information for this interface/service pair
*/
private Package m_package;
/**
* The service information for this interface/service pair
*/
private final Service m_service;
/**
* Last known/current status
*/
private int m_status;
/**
* The last time a threshold check occurred
*/
private long m_lastThresholdCheckTime;
/**
* The last time this service was scheduled for threshold checking.
*/
private long m_lastScheduledThresholdCheckTime;
/**
* The proxy used to send events.
*/
private final EventProxy m_proxy;
/**
* The scheduler for threshd
*/
private final LegacyScheduler m_scheduler;
/**
* Service updates
*/
private ThresholderUpdates m_updates;
/**
*
*/
private static final boolean ABORT_THRESHOLD_CHECK = true;
private ServiceThresholder m_thresholder;
/**
* The key used to lookup the service properties that are passed to the
* thresholder.
*/
private final String m_svcPropKey;
/**
* The map of service parameters. These parameters are mapped by the
* composite key <em>(package name, service name)</em>.
*/
private static Map<String,Map<?,?>> SVC_PROP_MAP = new ConcurrentSkipListMap<String,Map<?,?>>();
private Threshd m_threshd;
/**
* Constructs a new instance of a ThresholdableService object.
*
* @param dbNodeId
* The database identifier key for the interfaces' node
* @param address
* InetAddress of the interface to collect from
* @param svcName
* Service name
* @param pkg
* The package containing parms for this collectable service.
*
*/
ThresholdableService(Threshd threshd, int dbNodeId, InetAddress address, String svcName, org.opennms.netmgt.config.threshd.Package pkg) {
super(address);
m_nodeId = dbNodeId;
m_package = pkg;
m_status = ServiceThresholder.THRESHOLDING_SUCCEEDED;
m_threshd = threshd;
m_proxy = EventIpcManagerFactory.getIpcManager();
m_scheduler = threshd.getScheduler();
m_thresholder = threshd.getServiceThresholder(svcName);
m_updates = new ThresholderUpdates();
// Initialize last scheduled threshold check and last threshold
// check times to current time.
m_lastScheduledThresholdCheckTime = System.currentTimeMillis();
m_lastThresholdCheckTime = System.currentTimeMillis();
// find the service matching the name
//
Service svc = null;
for (final Service s : m_package.getServiceCollection()) {
if (s.getName().equalsIgnoreCase(svcName)) {
svc = s;
break;
}
}
if (svc == null)
throw new RuntimeException("Service name not part of package!");
// save reference to the service
m_service = svc;
// add property list for this service/package combination if
// it doesn't already exist in the service property map
//
m_svcPropKey = m_package.getName() + "." + m_service.getName();
synchronized (SVC_PROP_MAP) {
if (!SVC_PROP_MAP.containsKey(m_svcPropKey)) {
Map<String,String> m = new ConcurrentSkipListMap<String,String>();
for (final Parameter p : m_service.getParameterCollection()) {
m.put(p.getKey(), p.getValue());
}
// Add configured service 'interval' attribute as
// a property as well. Needed by the ServiceThresholder
// check() method in order to generate the
// correct rrdtool fetch command.
m.put("interval", Integer.toString((int) m_service.getInterval()));
SVC_PROP_MAP.put(m_svcPropKey, m);
}
}
}
/**
* Returns node identifier
*
* @return a int.
*/
public int getNodeId() {
return m_nodeId;
}
/**
* Set node identifier
*
* @param nodeId a int.
*/
public void setNodeId(int nodeId) {
m_nodeId = nodeId;
}
/**
* Returns the service name
*
* @return a {@link java.lang.String} object.
*/
public String getServiceName() {
return m_service.getName();
}
/**
* Returns the service name
*
* @return a {@link java.lang.String} object.
*/
public String getPackageName() {
return m_package.getName();
}
/**
* Uses the existing package name to try and re-obtain the package from the threshd config factory.
* Should be called when the threshd config has been reloaded.
*/
public void refreshPackage() {
Package refreshedPackage=m_threshd.getPackage(getPackageName());
if(refreshedPackage!=null) {
this.m_package=refreshedPackage;
}
}
/**
* Returns updates object
*
* @return a {@link org.opennms.netmgt.threshd.ThresholderUpdates} object.
*/
public ThresholderUpdates getThresholderUpdates() {
return m_updates;
}
/**
* This method is used to evaluate the status of this interface and service
* pair. If it is time to run the threshold check again then a value of true
* is returned. If the interface is not ready then a value of false is
* returned.
*
* @return a boolean.
*/
public boolean isReady() {
boolean ready = false;
if (!m_threshd.isSchedulingCompleted())
return false;
if (m_service.getInterval() < 1) {
ready = true;
} else {
ready = ((m_service.getInterval() - (System.currentTimeMillis() - m_lastScheduledThresholdCheckTime)) < 1);
}
return ready;
}
/**
* Returns the service's configured thresholding interval.
*
* @return a long.
*/
public long getInterval() {
return m_service.getInterval();
}
/**
* Generate event and send it to eventd via the event proxy.
*
* uei Universal event identifier of event to generate.
*/
private void sendEvent(String uei) {
EventBuilder bldr = new EventBuilder(uei, "OpenNMS.Threshd");
bldr.setNodeid(m_nodeId);
bldr.setInterface(m_address);
bldr.setService("SNMP");
bldr.setHost(InetAddressUtils.getLocalHostName());
// Send the event
//
try {
m_proxy.send(bldr.getEvent());
} catch (final Exception ex) {
LogUtils.errorf(this, ex, "Failed to send the event %s for interface %s", uei, getHostAddress());
}
LogUtils.debugf(this, "sendEvent: Sent event %s for %s/%s/%s", uei, m_nodeId, getHostAddress(), m_service.getName());
}
private String getHostAddress() {
return InetAddressUtils.str(m_address);
}
/**
* This is the main method of the class. An instance is normally enqueued on
* the scheduler which checks its <code>isReady</code> method to determine
* execution. If the instance is ready for execution then it is started with
* it's own thread context to execute the query. The last step in the method
* before it exits is to reschedule the interface.
*/
public void run() {
// Process any oustanding updates.
if (processUpdates() == ABORT_THRESHOLD_CHECK)
return;
// Update last scheduled poll time
m_lastScheduledThresholdCheckTime = System.currentTimeMillis();
// Check scheduled outages to see if any apply indicating
// that threshold checking should be skipped
if (scheduledOutage()) {
// Outage applied...reschedule the service and return
m_scheduler.schedule(this, m_service.getInterval());
return;
}
// Perform threshold checking
LogUtils.debugf(this, "run: starting new threshold check for %s", getHostAddress());
int status = ServiceThresholder.THRESHOLDING_FAILED;
final Map<?,?> propertiesMap = SVC_PROP_MAP.get(m_svcPropKey);
try {
status = m_thresholder.check(this, m_proxy, propertiesMap);
} catch (final Throwable t) {
LogUtils.errorf(this, t, "run: An undeclared throwable was caught during SNMP thresholding for interface %s", getHostAddress());
}
// Update last threshold check time
m_lastThresholdCheckTime = System.currentTimeMillis();
// Any change in status?
if (status != m_status) {
// Generate transition events
LogUtils.debugf(this, "run: change in thresholding status, generating event.");
// Send the appropriate event
switch (status) {
case ServiceThresholder.THRESHOLDING_SUCCEEDED:
sendEvent(EventConstants.THRESHOLDING_SUCCEEDED_EVENT_UEI);
break;
case ServiceThresholder.THRESHOLDING_FAILED:
sendEvent(EventConstants.THRESHOLDING_FAILED_EVENT_UEI);
break;
default:
break;
}
}
// Set the new status
m_status = status;
// Reschedule ourselves
//
m_scheduler.schedule(this, this.getInterval());
return;
}
Map<?,?> getPropertyMap() {
return Collections.unmodifiableMap((Map<?,?>) SVC_PROP_MAP.get(m_svcPropKey));
}
/**
* Checks the package information for the thresholdable service and
* determines if any of the calendar outages associated with the package
* apply to the current time and the service's interface. If an outage
* applies true is returned...otherwise false is returned.
*
* @return false if no outage found (indicating thresholding may be
* performed) or true if applicable outage is found (indicating
* thresholding should be skipped).
*/
private boolean scheduledOutage() {
boolean outageFound = false;
PollOutagesConfigFactory outageFactory = PollOutagesConfigFactory.getInstance();
// Iterate over the outage names defined in the interface's package.
// For each outage...if the outage contains a calendar entry which
// applies to the current time and the outage applies to this
// interface then break and return true. Otherwise process the
// next outage.
//
for (final String outageName : m_package.getOutageCalendarCollection()) {
// Does the outage apply to the current time?
if (outageFactory.isCurTimeInOutage(outageName)) {
// Does the outage apply to this interface?
if ((outageFactory.isNodeIdInOutage((long) m_nodeId, outageName)) || (outageFactory.isInterfaceInOutage(getHostAddress(), outageName))) {
LogUtils.debugf(this, "scheduledOutage: configured outage '%s' applies, interface %s will not be thresholded for %s", outageName, getHostAddress(), m_service);
outageFound = true;
break;
}
}
}
return outageFound;
}
/**
* Process any outstanding updates.
*
* @return true if update indicates that threshold check should be aborted
* (for example due to deletion flag being set), false otherwise.
*/
private boolean processUpdates() {
// All update processing takes place within synchronized block
// to ensure that no updates are missed.
//
synchronized (this) {
if (!m_updates.hasUpdates())
return !ABORT_THRESHOLD_CHECK;
// Update: deletion flag
//
if (m_updates.isDeletionFlagSet()) {
// Deletion flag is set, simply return without polling
// or rescheduling this collector.
//
LogUtils.debugf(this, "Collector for %s is marked for deletion...skipping thresholding, will not reschedule.", getHostAddress());
return ABORT_THRESHOLD_CHECK;
}
// Update: reinitialization flag
//
if (m_updates.isReinitializationFlagSet()) {
// Reinitialization flag is set, call initialize() to
// reinit the collector for this interface
//
LogUtils.debugf(this, "ReinitializationFlag set for %s", getHostAddress());
try {
m_thresholder.release(this);
m_thresholder.initialize(this, this.getPropertyMap());
LogUtils.debugf(this, "Completed reinitializing SNMP collector for %s", getHostAddress());
} catch (final RuntimeException e) {
LogUtils.warnf(this, e, "Unable to reschedule %s for %s thresholding.", getHostAddress(), m_service.getName());
} catch (final Throwable t) {
LogUtils.errorf(this, t, "Uncaught exception, failed to reschedule interface %s for %s thresholding.", getHostAddress(), m_service.getName());
}
}
// Update: reparenting flag
//
if (m_updates.isReparentingFlagSet()) {
LogUtils.debugf(this, "ReparentingFlag set for %s", getHostAddress());
// Convert new nodeId to integer value
int newNodeId = -1;
try {
newNodeId = Integer.parseInt(m_updates.getReparentNewNodeId());
} catch (final NumberFormatException nfE) {
LogUtils.warnf(this, nfE, "Unable to convert new nodeId value to an int while processing reparenting update: %s", m_updates.getReparentNewNodeId());
}
// Set this collector's nodeId to the value of the interface's
// new parent nodeid.
m_nodeId = newNodeId;
// We must now reinitialize the thresholder for this interface,
// in order to update the NodeInfo object to reflect changes
// to the interface's parent node among other things.
//
try {
LogUtils.debugf(this, "Reinitializing SNMP thresholder for %s", getHostAddress());
m_thresholder.release(this);
m_thresholder.initialize(this, this.getPropertyMap());
LogUtils.debugf(this, "Completed reinitializing SNMP thresholder for %s", getHostAddress());
} catch (final RuntimeException rE) {
LogUtils.warnf(this, rE, "Unable to initialize %s for %s thresholding.", getHostAddress(), m_service.getName());
} catch (final Throwable t) {
LogUtils.errorf(this, t, "Uncaught exception, failed to initialize interface %s for %s thresholding.", getHostAddress(), m_service.getName());
}
}
// Updates have been applied. Reset ThresholderUpdates object.
// .
m_updates.reset();
} // end synchronized
return !ABORT_THRESHOLD_CHECK;
}
}