/*******************************************************************************
* 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.notifd;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.core.utils.TimeConverter;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.config.DestinationPathManager;
import org.opennms.netmgt.config.GroupManager;
import org.opennms.netmgt.config.NotifdConfigManager;
import org.opennms.netmgt.config.NotificationCommandManager;
import org.opennms.netmgt.config.NotificationManager;
import org.opennms.netmgt.config.PollOutagesConfigManager;
import org.opennms.netmgt.config.UserManager;
import org.opennms.netmgt.config.destinationPaths.Escalate;
import org.opennms.netmgt.config.destinationPaths.Path;
import org.opennms.netmgt.config.destinationPaths.Target;
import org.opennms.netmgt.config.groups.Group;
import org.opennms.netmgt.config.notifd.AutoAcknowledge;
import org.opennms.netmgt.config.notificationCommands.Command;
import org.opennms.netmgt.config.notifications.Notification;
import org.opennms.netmgt.config.users.Contact;
import org.opennms.netmgt.config.users.User;
import org.opennms.netmgt.eventd.EventIpcManager;
import org.opennms.netmgt.eventd.EventIpcManagerFactory;
import org.opennms.netmgt.eventd.EventUtil;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventListener;
import org.opennms.netmgt.model.events.EventUtils;
import org.opennms.netmgt.utils.RowProcessor;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.springframework.util.Assert;
/**
* <p>BroadcastEventProcessor class.</p>
*
* @author <a href="mailto:weave@oculan.com">Brian Weaver </a>
* @author <a href="http://www.opennms.org/">OpenNMS </a>
*/
public final class BroadcastEventProcessor implements EventListener {
/**
*/
private volatile Map<String, NoticeQueue> m_noticeQueues;
private volatile EventIpcManager m_eventManager;
private volatile PollOutagesConfigManager m_pollOutagesConfigManager;
private volatile NotificationManager m_notificationManager;
private volatile NotifdConfigManager m_notifdConfigManager;
private volatile DestinationPathManager m_destinationPathManager;
private volatile UserManager m_userManager;
private volatile GroupManager m_groupManager;
private volatile NotificationCommandManager m_notificationCommandManager;
/**
* A regular expression for matching an expansion parameter delimited by
* percent signs.
*/
private static final String NOTIFD_EXPANSION_PARM = "%(noticeid)%";
private static RE m_expandRE;
/**
* <p>Constructor for BroadcastEventProcessor.</p>
*/
public BroadcastEventProcessor() {
}
/**
* An event listener is created and this instance is setup as the
* endpoint for broadcast events. When a new event arrives it is processed
* and the appropriate action is taken.
*/
protected void init() {
assertPropertiesSet();
initExpandRe();
// start to listen for events
getEventManager().addEventListener(this);
}
private void assertPropertiesSet() {
if (m_noticeQueues == null) {
throw new IllegalStateException("property noticeQueues not set");
}
if (m_eventManager == null) {
throw new IllegalStateException("property eventManager not set");
}
if (m_pollOutagesConfigManager == null) {
throw new IllegalStateException("property pollOutagesConfigManager not set");
}
if (m_notificationManager == null) {
throw new IllegalStateException("property notificationManager not set");
}
if (m_notifdConfigManager == null) {
throw new IllegalStateException("property notifdConfigManager not set");
}
if (m_destinationPathManager == null) {
throw new IllegalStateException("property destinationPathManager not set");
}
if (m_userManager == null) {
throw new IllegalStateException("property userManager not set");
}
if (m_groupManager == null) {
throw new IllegalStateException("property groupManager not set");
}
if (m_notificationCommandManager == null) {
throw new IllegalStateException("property notificationCommandManager not set");
}
}
/**
* Initializes the expansion regular expression. The exception is going to
* be thrown away if the RE can't be compiled, thus the compilation should
* be tested prior to runtime.
*/
protected void initExpandRe() {
try {
m_expandRE = new RE(NOTIFD_EXPANSION_PARM);
} catch (RESyntaxException e) {
// this shouldn't throw an exception, should be tested prior to
// runtime
log().error("failed to compile RE " + NOTIFD_EXPANSION_PARM, e);
// FIXME: wrap this in runtime exception since SOMETIMES we are using
// an incorrect version of regexp pulled from xalan that is doesn't
// extend RuntimeException only Exception. We really need to fix that.
// See Bug# 1736 in Bugzilla.
throw new RuntimeException(e);
}
}
/**
* Unsubscribe from eventd
*/
public void close() {
getEventManager().removeEventListener(this);
}
/**
* {@inheritDoc}
*
* This method is invoked by the EventIpcManager when a new event is
* available for processing.
*/
public void onEvent(Event event) {
if (event == null) return;
if (isReloadConfigEvent(event)) {
log().info("onEvent: handling reload configuration event...");
EventBuilder ebldr = null;
try {
m_userManager.update();
m_groupManager.update();
m_notificationManager.update();
m_destinationPathManager.update();
m_notificationCommandManager.update();
ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_SUCCESSFUL_UEI, getName());
ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Notifd");
} catch (Throwable e) {
log().debug("onEvent: could not reload notifd configuration: "+e, e);
ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, getName());
ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Notifd");
ebldr.addParam(EventConstants.PARM_REASON, e.getLocalizedMessage().substring(0, 128));
}
m_eventManager.sendNow(ebldr.getEvent());
log().info("onEvent: reload configuration event handled.");
return;
}
boolean notifsOn = computeNullSafeStatus();
if (notifsOn && (checkCriticalPath(event, notifsOn))) {
scheduleNoticesForEvent(event);
} else if (!notifsOn) {
if (log().isDebugEnabled()) {
log().debug("discarding event " + event.getUei() + ", notifd status on = " + notifsOn);
}
}
automaticAcknowledge(event, notifsOn);
}
private boolean isReloadConfigEvent(Event event) {
boolean isTarget = false;
if (EventConstants.RELOAD_DAEMON_CONFIG_UEI.equals(event.getUei())) {
List<Parm> parmCollection = event.getParmCollection();
for (Parm parm : parmCollection) {
if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName()) && "Notifd".equalsIgnoreCase(parm.getValue().getContent())) {
isTarget = true;
break;
}
}
log().debug("isReloadConfigEventTarget: Notifd was target of reload event: "+isTarget);
}
return isTarget;
}
/**
* <p>computeNullSafeStatus</p>
*
* @return false if status is not defined in configuration as "on".
*/
public boolean computeNullSafeStatus() {
String notificationStatus = null;
try {
notificationStatus = getNotifdConfigManager().getNotificationStatus();
} catch (MarshalException e) {
log().error("onEvent: problem marshalling configuration", e);
} catch (ValidationException e) {
log().error("onEvent: problem validating marsharled configuraion", e);
} catch (IOException e) {
log().error("onEvent: IO problem marshalling configuration", e);
}
return "on".equalsIgnoreCase(notificationStatus);
}
/**
* @author <a href="mailto:billayers@opennms.org">Bill Ayers</a>
* @param event
* @param notifsOn
* @return boolean representing whether event is not relative to a critical path
*/
private boolean checkCriticalPath(Event event, boolean notifsOn) {
boolean isPathOk = true;
Long nodeid = event.getNodeid();
try {
// If this is a nodeDown event, see if the critical path was down
if (event.getUei().equals(EventConstants.NODE_DOWN_EVENT_UEI)) {
String reason = EventUtils.getParm(event, EventConstants.PARM_LOSTSERVICE_REASON);
if (reason != null && reason.equals(EventConstants.PARM_VALUE_PATHOUTAGE)) {
isPathOk = false;
String cip = EventUtils.getParm(event, EventConstants.PARM_CRITICAL_PATH_IP);
String csvc = EventUtils.getParm(event, EventConstants.PARM_CRITICAL_PATH_SVC);
if (log().isDebugEnabled()) {
log().debug("Critical Path " + cip + " " + csvc + " for nodeId " + nodeid + " did not respond. Checking to see if notice would have been sent...");
}
boolean mapsToNotice = false;
boolean noticeSupressed = false;
Notification[] notifications = null;
mapsToNotice = getNotificationManager().hasUei(event.getUei());
notifications = getNotificationManager().getNotifForEvent(event);
if (notifsOn && mapsToNotice && continueWithNotice(event) && notifications != null) {
noticeSupressed = true;
}
createPathOutageEvent(nodeid.intValue(), EventUtils.getParm(event, EventConstants.PARM_NODE_LABEL), cip, csvc, noticeSupressed);
}
}
} catch (MarshalException e) {
log().error("onEvent: problem marshalling configuration", e);
} catch (ValidationException e) {
log().error("onEvent: problem validating marshalled configuration", e);
} catch (IOException e) {
log().error("onEvent: IO problem marshalling configuration", e);
}
return isPathOk;
}
private ThreadCategory log() {
return ThreadCategory.getInstance(getClass());
}
private void automaticAcknowledge(Event event, boolean notifsOn) {
try {
Collection<AutoAcknowledge> autoAcks = getNotifdConfigManager().getAutoAcknowledges();
// see if this event has an auto acknowledge for a notice
for (AutoAcknowledge curAck : autoAcks) {
if (curAck.getUei().equals(event.getUei())) {
try {
if (log().isDebugEnabled()) {
log().debug("Acknowledging event " + curAck.getAcknowledge() + " " + event.getNodeid() + ":" + event.getInterface() + ":" + event.getService());
}
Collection<Integer> notifIDs = getNotificationManager().acknowledgeNotice(event, curAck.getAcknowledge(), curAck.getMatch());
try {
// only send resolution notifications if notifications are globally turned on
if (curAck.getNotify() && notifsOn) {
sendResolvedNotifications(notifIDs, event, curAck.getAcknowledge(), curAck.getMatch(), curAck.getResolutionPrefix(), getNotifdConfigManager().getConfiguration().isNumericSkipResolutionPrefix());
}
} catch (Throwable e) {
log().error("Failed to send resolution notifications.", e);
}
} catch (SQLException e) {
log().error("Failed to auto acknowledge notice.", e);
}
}
}
} catch (Throwable e) {
log().error("Unable to auto acknowledge notice due to exception.", e);
}
}
private void sendResolvedNotifications(Collection<Integer> notifIDs, Event event, String acknowledge,
String[] match, String resolutionPrefix, boolean skipNumericPrefix) throws Exception {
for (int notifId : notifIDs) {
boolean wa = false;
if(notifId < 0) {
notifId *= -1;
wa = true;
if (log().isDebugEnabled()) {
log().debug("Conditional autoNotify for notifId " + notifId);
}
}
final boolean wasAcked = wa;
final Map<String, String> parmMap = rebuildParameterMap(notifId, resolutionPrefix, skipNumericPrefix);
NotificationManager.expandMapValues(parmMap,
getNotificationManager().getEvent(Integer.parseInt(parmMap.get("eventID"))));
String queueID = getNotificationManager().getQueueForNotification(notifId);
final Map<String, List<String>> userNotifications = new HashMap<String, List<String>>();
RowProcessor ackNotifProcessor = new RowProcessor() {
public void processRow(ResultSet rs) throws SQLException {
String userID = rs.getString("userID");
String contactInfo = rs.getString("contactinfo");
String autoNotifyChar = rs.getString("autonotify");
if(userID.equals("email-address")) {
userID = contactInfo;
}
String cmd = rs.getString("media");
if(autoNotifyChar == null) {
autoNotifyChar = "C";
}
if(autoNotifyChar.equals("Y") || (autoNotifyChar.equals("C") && !wasAcked)) {
List<String> cmdList = userNotifications.get(userID);
if (cmdList == null) {
cmdList = new ArrayList<String>();
userNotifications.put(userID, cmdList);
}
cmdList.add(cmd);
}
}
};
getNotificationManager().forEachUserNotification(notifId, ackNotifProcessor);
for (String userID : userNotifications.keySet()) {
List<String> cmdList = userNotifications.get(userID);
String[] cmds = cmdList.toArray(new String[cmdList.size()]);
if (log().isDebugEnabled()) {
log().debug("Sending " + resolutionPrefix + " notification to userID = " + userID + " for notice ID " + notifId);
}
sendResolvedNotificationsToUser(queueID, userID, cmds, parmMap);
}
}
}
/**
* <p>sendResolvedNotificationsToUser</p>
*
* @param queueID a {@link java.lang.String} object.
* @param targetName a {@link java.lang.String} object.
* @param commands an array of {@link java.lang.String} objects.
* @param params a {@link java.util.Map} object.
* @throws java.lang.Exception if any.
*/
protected void sendResolvedNotificationsToUser(String queueID, String targetName, String[] commands, Map<String, String> params) throws Exception {
int noticeId = -1;
NoticeQueue noticeQueue = null;
if (m_noticeQueues != null) {
synchronized (m_noticeQueues) {
noticeQueue = m_noticeQueues.get(queueID);
}
}
long now = System.currentTimeMillis();
if (getUserManager().hasUser(targetName)) {
NotificationTask newTask = makeUserTask(now, params, noticeId, targetName, commands, null, null);
if (newTask != null) {
noticeQueue.putItem(now, newTask);
}
} else if (targetName.indexOf("@") > -1) {
NotificationTask newTask = makeEmailTask(now, params, noticeId, targetName, commands, null, null);
if (newTask != null) {
synchronized (noticeQueue) {
noticeQueue.putItem(now, newTask);
}
}
} else {
log().warn("Unrecognized target '" + targetName + "' contained in destinationPaths.xml. Please check the configuration.");
}
}
/**
* This method determines if the notice should continue based on the status
* of the notify
*/
private boolean continueWithNotice(Event event) {
String nodeID = event.hasNodeid() ? String.valueOf(event.getNodeid()) : null;
String ipAddr = event.getInterface();
String service = event.getService();
boolean continueNotice = false;
// can't check the database if any of these are null, so let the notice
// continue
if (nodeID == null || ipAddr == null || service == null || ipAddr.equals("0.0.0.0")) {
if (log().isDebugEnabled()) {
log().debug("nodeID=" + nodeID + " ipAddr=" + ipAddr + " service=" + service + ". Not checking DB, continuing...");
}
return true;
}
try {
// check the database to see if notices were turned off for this
// service
String notify = getNotificationManager().getServiceNoticeStatus(nodeID, ipAddr, service);
if ("Y".equals(notify)) {
continueNotice = true;
if (log().isDebugEnabled()) {
log().debug("notify status for service " + service + " on interface/node " + ipAddr + "/" + nodeID + " is 'Y', continuing...");
}
} else {
if (log().isDebugEnabled()) {
log().debug("notify status for service " + service + " on interface/node " + ipAddr + "/" + nodeID + " is " + notify + ", not continuing...");
}
}
} catch (Throwable e) {
continueNotice = true;
log().error("Not able to get notify status for service " + service + " on interface/node " + ipAddr + "/" + nodeID + ". Continuing notice... " + e.getMessage());
}
// in case of a error we will return false
return continueNotice;
}
/**
* Returns true if an auto acknowledgment exists for the specificed event,
* such that the arrival of some second, different event will auto
* acknowledge the event passed as an argument. E.g. if there is an auto ack
* set up to acknowledge a nodeDown when a nodeUp is received, passing
* nodeDown to this method will return true. Should this method be in
* NotifdConfigFactory?
*/
private boolean autoAckExistsForEvent(String eventUei) {
try {
Collection<AutoAcknowledge> autoAcks = getNotifdConfigManager().getAutoAcknowledges();
for (AutoAcknowledge curAck : autoAcks) {
if (curAck.getAcknowledge().equals(eventUei)) {
return true;
}
}
return false;
} catch (Throwable e) {
log().error("Unable to find if an auto acknowledge exists for event " + eventUei + " due to exception.", e);
return false;
}
}
/**
*/
private void scheduleNoticesForEvent(Event event) {
boolean mapsToNotice = false;
try {
mapsToNotice = getNotificationManager().hasUei(event.getUei());
} catch (Throwable e) {
log().error("Couldn't map uei " + event.getUei() + " to a notification entry, not scheduling notice.", e);
return;
}
if (mapsToNotice) {
// check to see if notices are turned on for the interface/service
// in the event
if (continueWithNotice(event)) {
Notification[] notifications = null;
try {
notifications = getNotificationManager().getNotifForEvent(event);
} catch (Throwable e) {
log().error("Couldn't get notification mapping for event " + event.getUei() + ", not scheduling notice.", e);
return;
}
long nodeid = event.getNodeid();
String ipaddr = event.getInterface();
if (notifications != null) {
for (Notification notification : notifications) {
int noticeId = 0;
try {
noticeId = getNotificationManager().getNoticeId();
} catch (Throwable e) {
log().error("Failed to get a unique id # for notification, exiting this notification", e);
continue;
}
Map<String, String> paramMap = buildParameterMap(notification, event, noticeId);
String queueID = (notification.getNoticeQueue() != null ? notification.getNoticeQueue() : "default");
if (log().isDebugEnabled()) {
log().debug("destination : " + notification.getDestinationPath());
log().debug("text message: " + paramMap.get(NotificationManager.PARAM_TEXT_MSG));
log().debug("num message : " + paramMap.get(NotificationManager.PARAM_NUM_MSG));
log().debug("subject : " + paramMap.get(NotificationManager.PARAM_SUBJECT));
log().debug("node : " + paramMap.get(NotificationManager.PARAM_NODE));
log().debug("interface : " + paramMap.get(NotificationManager.PARAM_INTERFACE));
log().debug("service : " + paramMap.get(NotificationManager.PARAM_SERVICE));
}
// get the target and escalation information
Path path = null;
try {
path = getDestinationPathManager().getPath(notification.getDestinationPath());
if (path == null) {
log().warn("Unknown destination path " + notification.getDestinationPath() + ". Please check the <destinationPath> tag for the notification " + notification.getName() + " in the notifications.xml file.");
// changing posted by Wiktor Wodecki
// return;
continue;
}
} catch (Throwable e) {
log().error("Could not get destination path for " + notification.getDestinationPath() + ", please check the destinationPath.xml for errors.", e);
return;
}
String initialDelay = (path.getInitialDelay() == null ? "0s" : path.getInitialDelay());
Target[] targets = path.getTarget();
Escalate[] escalations = path.getEscalate();
// now check to see if any users are to receive the
// notification, if none then generate an event a exit
try {
if (getUserCount(targets, escalations) == 0) {
log().warn("The path " + notification.getDestinationPath() + " assigned to notification " + notification.getName() + " has no targets or escalations specified, not sending notice.");
sendNotifEvent(EventConstants.NOTIFICATION_WITHOUT_USERS, "The path " + notification.getDestinationPath() + " assigned to notification " + notification.getName() + " has no targets or escalations specified.", "The message of the notification is as follows: " + paramMap.get(NotificationManager.PARAM_TEXT_MSG));
return;
}
} catch (Throwable e) {
log().error("Failed to get count of users in destination path " + notification.getDestinationPath() + ", exiting notification.", e);
return;
}
try {
log().info(String.format("Inserting notification #%d into database: %s", noticeId, paramMap.get(NotificationManager.PARAM_SUBJECT)));
getNotificationManager().insertNotice(noticeId, paramMap, queueID, notification);
} catch (SQLException e) {
log().error("Failed to enter notification into database, exiting this notification", e);
return;
}
long startTime = System.currentTimeMillis() + TimeConverter.convertToMillis(initialDelay);
// Find the first outage which applies at this time
String scheduledOutageName = scheduledOutage(nodeid, ipaddr);
if (scheduledOutageName != null) {
// This event occurred during a scheduled outage.
// Must decide what to do
if (autoAckExistsForEvent(event.getUei())) {
// Defer starttime till the given outage ends -
// if the auto ack catches the other event
// before then,
// then the page will not be sent
Calendar endOfOutage = getPollOutagesConfigManager().getEndOfOutage(scheduledOutageName);
startTime = endOfOutage.getTime().getTime();
} else {
// No auto-ack exists - there's no point
// delaying the page, so just drop it (but leave
// the database entry)
continue; // with the next notification (for
// loop)
}
}
List<NotificationTask> targetSiblings = new ArrayList<NotificationTask>();
try {
synchronized(m_noticeQueues) {
NoticeQueue noticeQueue = m_noticeQueues.get(queueID);
processTargets(targets, targetSiblings, noticeQueue, startTime, paramMap, noticeId);
processEscalations(escalations, targetSiblings, noticeQueue, startTime, paramMap, noticeId);
}
} catch (Throwable e) {
log().error("notice not scheduled due to error: ", e);
}
}
} else {
if (log().isDebugEnabled()) {
log().debug("Event doesn't match a notice: " + event.getUei() + " : " + nodeid + " : " + ipaddr + " : " + event.getService());
}
}
}
} else {
if (log().isDebugEnabled()) {
log().debug("No notice match for uei: " + event.getUei());
}
}
}
/**
* Detemines the number of users assigned to a list of Target and Escalate
* lists. Group names may be specified in these objects and the users will
* have to be extracted from those groups
*
* @param targets
* the list of Target objects
* @param escalations
* the list of Escalate objects
* @return the total # of users assigned in each Target and Escalate
* objecst.
*/
private int getUserCount(Target[] targets, Escalate[] escalations) throws IOException, MarshalException, ValidationException {
int totalUsers = 0;
for (int i = 0; i < targets.length; i++) {
totalUsers += getUsersInTarget(targets[i]);
}
for (int j = 0; j < escalations.length; j++) {
Target[] escalationTargets = escalations[j].getTarget();
for (int k = 0; k < escalationTargets.length; k++) {
totalUsers += getUsersInTarget(escalationTargets[k]);
}
}
return totalUsers;
}
/**
*
*/
private int getUsersInTarget(Target target) throws IOException, MarshalException, ValidationException {
int count = 0;
String targetName = target.getName();
if (getGroupManager().hasGroup(targetName)) {
count = getGroupManager().getGroup(targetName).getUserCount();
} else if (getUserManager().hasRole(targetName)) {
count = getUserManager().countUsersWithRole(targetName);
} else if (getUserManager().hasUser(targetName)) {
count = 1;
} else if (targetName.indexOf("@") > -1) {
count = 1;
}
return count;
}
/**
* Sends and event related to a notification
*
* @param uei
* the UEI for the event
*/
private void sendNotifEvent(String uei, String logMessage, String description) {
try {
EventBuilder bldr = new EventBuilder(uei, "notifd");
bldr.setLogMessage(logMessage);
bldr.setDescription(description);
getEventManager().sendNow(bldr.getEvent());
} catch (Throwable t) {
log().error("Could not send event " + uei, t);
}
}
/**
*
*/
static Map<String, String> buildParameterMap(Notification notification, Event event, int noticeId) {
Map<String, String> paramMap = new HashMap<String, String>();
NotificationManager.addNotificationParams(paramMap, notification);
// expand the event parameters for the messages
// call the notif expansion method before the event expansion because
// event expansion will
// throw away any expansion strings it doesn't recognize!
paramMap.put("noticeid", Integer.toString(noticeId));
// Replace the %noticeid% param
String textMessage = expandNotifParms((nullSafeTextMsg(notification)), paramMap);
String numericMessage = expandNotifParms((nullSafeNumerMsg(notification, noticeId)), paramMap);
String subjectLine = expandNotifParms((nullSafeSubj(notification, noticeId)), paramMap);
nullSafeExpandedPut(NotificationManager.PARAM_TEXT_MSG, textMessage, event, paramMap);
nullSafeExpandedPut(NotificationManager.PARAM_NUM_MSG, numericMessage, event, paramMap);
nullSafeExpandedPut(NotificationManager.PARAM_SUBJECT, subjectLine, event, paramMap);
paramMap.put(NotificationManager.PARAM_NODE, event.hasNodeid() ? String.valueOf(event.getNodeid()) : "");
paramMap.put(NotificationManager.PARAM_INTERFACE, event.getInterface());
paramMap.put(NotificationManager.PARAM_SERVICE, event.getService());
paramMap.put("eventID", String.valueOf(event.getDbid()));
paramMap.put("eventUEI", event.getUei());
NotificationManager.expandMapValues(paramMap, event);
return Collections.unmodifiableMap(paramMap);
}
private static void nullSafeExpandedPut(final String key, final String value, final Event event, Map<String, String> paramMap) {
String result = EventUtil.expandParms(value, event);
paramMap.put(key, (result == null ? value : result));
}
private static String nullSafeSubj(Notification notification, int noticeId) {
return notification.getSubject() != null ? notification.getSubject() : "Notice #" + noticeId;
}
private static String nullSafeNumerMsg(Notification notification, int noticeId) {
return notification.getNumericMessage() != null ? notification.getNumericMessage() : "111-" + noticeId;
}
private static String nullSafeTextMsg(Notification notification) {
return notification.getTextMessage() != null ? notification.getTextMessage() : "No text message supplied.";
}
/**
* A parameter expansion algorithm, designed to replace strings delimited by
* percent signs '%' with a value supplied by a Map object.
*
* <p>NOTE: This function only replaces one particular parameter, the
* <code>%noticeid%</code> parameter.</p>
*
* @param input
* the input string
* @param paramMap
* a map that will supply the substitution values
* @return a {@link java.lang.String} object.
*/
public static String expandNotifParms(final String input, final Map<String, String> paramMap) {
String expanded = input;
if (m_expandRE.match(expanded)) {
String key = m_expandRE.getParen(1);
Assert.isTrue("noticeid".equals(key));
String replace = paramMap.get(key);
if (replace != null) {
expanded = m_expandRE.subst(expanded, replace);
}
}
return expanded;
}
/**
*
*/
private void processTargets(Target[] targets, List<NotificationTask> targetSiblings, NoticeQueue noticeQueue, long startTime, Map<String, String> params, int noticeId) throws IOException, MarshalException, ValidationException {
for (int i = 0; i < targets.length; i++) {
String interval = (targets[i].getInterval() == null ? "0s" : targets[i].getInterval());
String targetName = targets[i].getName();
String autoNotify = targets[i].getAutoNotify();
if(autoNotify != null) {
if(autoNotify.equalsIgnoreCase("on")) {
autoNotify = "Y";
} else if(autoNotify.equalsIgnoreCase("off")) {
autoNotify = "N";
} else {
autoNotify = "C";
}
} else {
autoNotify = "C";
}
if (log().isDebugEnabled()) {
log().debug("Processing target " + targetName + ":" + interval);
}
NotificationTask[] tasks = null;
if (getGroupManager().hasGroup((targetName))) {
tasks = makeGroupTasks(startTime, params, noticeId, targetName, targets[i].getCommand(), targetSiblings, autoNotify, TimeConverter.convertToMillis(interval));
} else if (getUserManager().hasRole(targetName)) {
tasks = makeRoleTasks(startTime, params, noticeId, targetName, targets[i].getCommand(), targetSiblings, autoNotify, TimeConverter.convertToMillis(interval));
} else if (getUserManager().hasUser(targetName)) {
NotificationTask[] userTasks = { makeUserTask(startTime, params, noticeId, targetName, targets[i].getCommand(), targetSiblings, autoNotify) };
tasks = userTasks;
} else if (targetName.indexOf("@") > -1) {
// Bug 2027 -- get the command name from the Notifd config instead of using default of "email"
String[] emailCommands = { getNotifdConfigManager().getConfiguration().getEmailAddressCommand() };
NotificationTask[] emailTasks = { makeEmailTask(startTime, params, noticeId, targetName, emailCommands, targetSiblings, autoNotify) };
tasks = emailTasks;
}
if (tasks != null) {
for (int index = 0; index < tasks.length; index++) {
NotificationTask task = tasks[index];
if (task != null) {
synchronized(noticeQueue) {
noticeQueue.putItem(task.getSendTime(), task);
}
targetSiblings.add(task);
}
}
} else {
log().warn("Unrecognized target '" + targetName + "' contained in destinationPaths.xml. Please check the configuration.");
}
}
}
NotificationTask[] makeGroupTasks(long startTime, Map<String, String> params, int noticeId, String targetName, String[] command, List<NotificationTask> targetSiblings, String autoNotify, long interval) throws IOException, MarshalException, ValidationException {
Group group = getGroupManager().getGroup(targetName);
Calendar startCal = Calendar.getInstance();
startCal.setTimeInMillis(startTime);
long next = getGroupManager().groupNextOnDuty(group.getName(), startCal);
// it the group is not on duty
if (next < 0) {
if (log().isDebugEnabled()) {
log().debug("The group " + group.getName() + " is not scheduled to come back on duty. No notification will be sent to this group.");
}
return null;
}
if (log().isDebugEnabled()) {
log().debug("The group " + group.getName() + " is on duty in " + next + " millisec.");
}
String[] users = group.getUser();
// There are no users in the group
if (users == null || users.length == 0) {
if (log().isDebugEnabled()) {
log().debug("Not sending notice, no users specified for group " + group.getName());
}
return null;
}
return constructTasksFromUserList(users, startTime, next, params, noticeId, command, targetSiblings, autoNotify, interval);
}
private NotificationTask[] constructTasksFromUserList(String[] users, long startTime, long offset, Map<String, String> params, int noticeId, String[] command, List<NotificationTask> targetSiblings, String autoNotify, long interval) throws IOException, MarshalException, ValidationException {
List<NotificationTask> taskList = new ArrayList<NotificationTask>(users.length);
long curSendTime = 0;
for (int j = 0; j < users.length; j++) {
NotificationTask newTask = makeUserTask(offset + startTime + curSendTime, params, noticeId, users[j], command, targetSiblings, autoNotify);
if (newTask != null) {
taskList.add(newTask);
curSendTime += interval;
}
}
return taskList.toArray(new NotificationTask[taskList.size()]);
}
NotificationTask[] makeRoleTasks(long startTime, Map<String, String> params, int noticeId, String targetName, String[] command, List<NotificationTask> targetSiblings, String autoNotify, long interval) throws MarshalException, ValidationException, IOException {
String[] users = getUserManager().getUsersScheduledForRole(targetName, new Date(startTime));
// There are no users in the group
if (users == null || users.length == 0) {
if (log().isDebugEnabled()) {
log().debug("Not sending notice, no users scheduled for role " + targetName);
}
return null;
}
return constructTasksFromUserList(users, startTime, 0, params, noticeId, command, targetSiblings, autoNotify, interval);
}
/**
*
*/
private void processEscalations(Escalate[] escalations, List<NotificationTask> targetSiblings, NoticeQueue noticeQueue, long startTime, Map<String, String> params, int noticeId) throws IOException, MarshalException, ValidationException {
for (int i = 0; i < escalations.length; i++) {
Target[] targets = escalations[i].getTarget();
startTime += TimeConverter.convertToMillis(escalations[i].getDelay());
processTargets(targets, targetSiblings, noticeQueue, startTime, params, noticeId);
}
}
/**
*
*/
NotificationTask makeUserTask(long sendTime, Map<String, String> parameters, int noticeId, String targetName, String[] commandList, List<NotificationTask> siblings, String autoNotify) throws IOException, MarshalException, ValidationException {
NotificationTask task = null;
task = new NotificationTask(getNotificationManager(), getUserManager(), sendTime, parameters, siblings, autoNotify);
User user = getUserManager().getUser(targetName);
Command commands[] = new Command[commandList.length];
for (int i = 0; i < commandList.length; i++) {
commands[i] = getNotificationCommandManager().getCommand(commandList[i]);
}
// if either piece of information is missing don't add the task to
// the notifier
if (user == null) {
log().error("user " + targetName + " is not a valid user, not adding this user to escalation thread");
return null;
}
task.setUser(user);
task.setCommands(commands);
task.setNoticeId(noticeId);
task.setAutoNotify(autoNotify);
return task;
}
/**
*
*/
NotificationTask makeEmailTask(long sendTime, Map<String, String> parameters, int noticeId, String address, String[] commandList, List<NotificationTask> siblings, String autoNotify) throws IOException, MarshalException, ValidationException {
NotificationTask task = null;
task = new NotificationTask(getNotificationManager(), getUserManager(), sendTime, parameters, siblings, autoNotify);
User user = new User();
user.setUserId(address);
Contact contact = new Contact();
contact.setType("email");
if (log().isDebugEnabled()) {
log().debug("email address = " + address + ", using contact type " + contact.getType());
}
contact.setInfo(address);
user.addContact(contact);
Command commands[] = new Command[commandList.length];
for (int i = 0; i < commandList.length; i++) {
commands[i] = getNotificationCommandManager().getCommand(commandList[i]);
}
task.setUser(user);
task.setCommands(commands);
task.setNoticeId(noticeId);
task.setAutoNotify(autoNotify);
return task;
}
/**
* Return an id for this event listener
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return "Notifd:BroadcastEventProcessor";
}
/**
* <p>rebuildParameterMap</p>
*
* @param notifId a int.
* @param resolutionPrefix a {@link java.lang.String} object.
* @param skipNumericPrefix a boolean.
* @return a {@link java.util.Map} object.
* @throws java.lang.Exception if any.
*/
public Map<String, String> rebuildParameterMap(final int notifId, final String resolutionPrefix, final boolean skipNumericPrefix) throws Exception {
return getNotificationManager().rebuildParameterMap(notifId, resolutionPrefix, skipNumericPrefix);
}
/**
* Checks the package information for the pollable 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 it's name
* is returned...otherwise null is returned.
*
* @return null if no outage found (indicating a notification may be sent)
* or the outage name, if an applicable outage is found (indicating
* notification should not be sent).
* @throws IOException if any.
* @throws ValidationException if any.
* @throws MarshalException if any.
* @param nodeId a long.
* @param theInterface a {@link java.lang.String} object.
*/
public String scheduledOutage(long nodeId, String theInterface) {
try {
PollOutagesConfigManager outageFactory = getPollOutagesConfigManager();
// Iterate over the outage names
// 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.
//
Collection<String> outageCalendarNames = getNotifdConfigManager().getOutageCalendarNames();
for (String outageName : outageCalendarNames) {
// Does the outage apply to the current time?
if (outageFactory.isCurTimeInOutage(outageName)) {
// Does the outage apply to this interface or node?
if ((outageFactory.isNodeIdInOutage(nodeId, outageName)) || (outageFactory.isInterfaceInOutage(theInterface, outageName)) || (outageFactory.isInterfaceInOutage("match-any", outageName))) {
if (log().isDebugEnabled()) {
log().debug("scheduledOutage: configured outage '" + outageName + "' applies, notification for interface " + theInterface + " on node " + nodeId + " will not be sent");
}
return outageName;
}
}
}
} catch (Throwable e) {
log().error("Error determining current outages", e);
}
return null;
}
/**
* This method is responsible for generating a pathOutage event and
* sending it
*
* @param nodeEntry Entry of node which was rescanned
*/
private void createPathOutageEvent(int nodeid, String nodeLabel, String intfc, String svc, boolean noticeSupressed) {
if (log().isDebugEnabled()) {
log().debug("nodeid = " + nodeid + ", nodeLabel = " + nodeLabel + ", noticeSupressed = " + noticeSupressed);
}
EventBuilder bldr = new EventBuilder(EventConstants.PATH_OUTAGE_EVENT_UEI, "OpenNMS.notifd");
bldr.setNodeid(nodeid);
bldr.addParam(EventConstants.PARM_NODE_LABEL, nodeLabel == null ? "" : nodeLabel);
bldr.addParam(EventConstants.PARM_CRITICAL_PATH_IP, intfc);
bldr.addParam(EventConstants.PARM_CRITICAL_PATH_SVC, svc);
bldr.addParam(EventConstants.PARM_CRITICAL_PATH_NOTICE_SUPRESSED, noticeSupressed);
// Send the event
if (log().isDebugEnabled()) {
log().debug("Creating pathOutageEvent for nodeid: " + nodeid);
}
try {
EventIpcManagerFactory.getIpcManager().sendNow(bldr.getEvent());
} catch (Throwable t) {
log().warn("run: unexpected throwable exception caught during event send", t);
}
}
/**
* <p>getDestinationPathManager</p>
*
* @return a {@link org.opennms.netmgt.config.DestinationPathManager} object.
*/
public DestinationPathManager getDestinationPathManager() {
return m_destinationPathManager;
}
/**
* <p>setDestinationPathManager</p>
*
* @param destinationPathManager a {@link org.opennms.netmgt.config.DestinationPathManager} object.
*/
public void setDestinationPathManager(
DestinationPathManager destinationPathManager) {
m_destinationPathManager = destinationPathManager;
}
/**
* <p>getEventManager</p>
*
* @return a {@link org.opennms.netmgt.eventd.EventIpcManager} object.
*/
public EventIpcManager getEventManager() {
return m_eventManager;
}
/**
* <p>setEventManager</p>
*
* @param eventManager a {@link org.opennms.netmgt.eventd.EventIpcManager} object.
*/
public void setEventManager(EventIpcManager eventManager) {
m_eventManager = eventManager;
}
/**
* <p>getGroupManager</p>
*
* @return a {@link org.opennms.netmgt.config.GroupManager} object.
*/
public GroupManager getGroupManager() {
return m_groupManager;
}
/**
* <p>setGroupManager</p>
*
* @param groupManager a {@link org.opennms.netmgt.config.GroupManager} object.
*/
public void setGroupManager(GroupManager groupManager) {
m_groupManager = groupManager;
}
/**
* <p>getNotifdConfigManager</p>
*
* @return a {@link org.opennms.netmgt.config.NotifdConfigManager} object.
*/
public NotifdConfigManager getNotifdConfigManager() {
return m_notifdConfigManager;
}
/**
* <p>setNotifdConfigManager</p>
*
* @param notifdConfigManager a {@link org.opennms.netmgt.config.NotifdConfigManager} object.
*/
public void setNotifdConfigManager(NotifdConfigManager notifdConfigManager) {
m_notifdConfigManager = notifdConfigManager;
}
/**
* <p>getNotificationCommandManager</p>
*
* @return a {@link org.opennms.netmgt.config.NotificationCommandManager} object.
*/
public NotificationCommandManager getNotificationCommandManager() {
return m_notificationCommandManager;
}
/**
* <p>setNotificationCommandManager</p>
*
* @param notificationCommandManager a {@link org.opennms.netmgt.config.NotificationCommandManager} object.
*/
public void setNotificationCommandManager(
NotificationCommandManager notificationCommandManager) {
m_notificationCommandManager = notificationCommandManager;
}
/**
* <p>getNotificationManager</p>
*
* @return a {@link org.opennms.netmgt.config.NotificationManager} object.
*/
public NotificationManager getNotificationManager() {
return m_notificationManager;
}
/**
* <p>setNotificationManager</p>
*
* @param notificationManager a {@link org.opennms.netmgt.config.NotificationManager} object.
*/
public void setNotificationManager(NotificationManager notificationManager) {
m_notificationManager = notificationManager;
}
/**
* <p>getPollOutagesConfigManager</p>
*
* @return a {@link org.opennms.netmgt.config.PollOutagesConfigManager} object.
*/
public PollOutagesConfigManager getPollOutagesConfigManager() {
return m_pollOutagesConfigManager;
}
/**
* <p>setPollOutagesConfigManager</p>
*
* @param pollOutagesConfigManager a {@link org.opennms.netmgt.config.PollOutagesConfigManager} object.
*/
public void setPollOutagesConfigManager(
PollOutagesConfigManager pollOutagesConfigManager) {
m_pollOutagesConfigManager = pollOutagesConfigManager;
}
/**
* <p>getUserManager</p>
*
* @return a {@link org.opennms.netmgt.config.UserManager} object.
*/
public UserManager getUserManager() {
return m_userManager;
}
/**
* <p>setUserManager</p>
*
* @param userManager a {@link org.opennms.netmgt.config.UserManager} object.
*/
public void setUserManager(UserManager userManager) {
m_userManager = userManager;
}
/**
* <p>getNoticeQueues</p>
*
* @return a {@link java.util.Map} object.
*/
public synchronized Map<String, NoticeQueue> getNoticeQueues() {
return m_noticeQueues;
}
/**
* <p>setNoticeQueues</p>
*
* @param noticeQueues a {@link java.util.Map} object.
*/
public void setNoticeQueues(Map<String, NoticeQueue> noticeQueues) {
m_noticeQueues = noticeQueues;
}
} // end class