PageRenderTime 42ms CodeModel.GetById 12ms app.highlight 25ms RepoModel.GetById 2ms app.codeStats 0ms

/opennms-alarms/syslog-northbounder/src/main/java/org/opennms/netmgt/alarmd/northbounder/syslog/SyslogNorthbounder.java

https://github.com/ajakubo1/opennms
Java | 478 lines | 350 code | 78 blank | 50 comment | 36 complexity | f2639572ab18b20b160c88b3513cf496 MD5 | raw file
  1/*******************************************************************************
  2 * This file is part of OpenNMS(R).
  3 *
  4 * Copyright (C) 2013 The OpenNMS Group, Inc.
  5 * OpenNMS(R) is Copyright (C) 1999-2013 The OpenNMS Group, Inc.
  6 *
  7 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
  8 *
  9 * OpenNMS(R) is free software: you can redistribute it and/or modify
 10 * it under the terms of the GNU General Public License as published
 11 * by the Free Software Foundation, either version 3 of the License,
 12 * or (at your option) any later version.
 13 *
 14 * OpenNMS(R) is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with OpenNMS(R).  If not, see:
 21 *      http://www.gnu.org/licenses/
 22 *
 23 * For more information contact:
 24 *     OpenNMS(R) Licensing <license@opennms.org>
 25 *     http://www.opennms.org/
 26 *     http://www.opennms.com/
 27 *******************************************************************************/
 28
 29package org.opennms.netmgt.alarmd.northbounder.syslog;
 30
 31import java.util.HashMap;
 32import java.util.LinkedList;
 33import java.util.List;
 34import java.util.Map;
 35
 36import org.apache.commons.lang.StringUtils;
 37import org.opennms.core.utils.PropertiesUtils;
 38import org.opennms.netmgt.alarmd.api.NorthboundAlarm;
 39import org.opennms.netmgt.alarmd.api.NorthboundAlarm.AlarmType;
 40import org.opennms.netmgt.alarmd.api.NorthboundAlarm.x733ProbableCause;
 41import org.opennms.netmgt.alarmd.api.NorthbounderException;
 42import org.opennms.netmgt.alarmd.api.support.AbstractNorthbounder;
 43import org.opennms.netmgt.alarmd.northbounder.syslog.SyslogDestination.SyslogFacility;
 44import org.opennms.netmgt.alarmd.northbounder.syslog.SyslogDestination.SyslogProtocol;
 45import org.opennms.netmgt.dao.api.NodeDao;
 46import org.opennms.netmgt.model.OnmsSeverity;
 47import org.productivity.java.syslog4j.Syslog;
 48import org.productivity.java.syslog4j.SyslogConfigIF;
 49import org.productivity.java.syslog4j.SyslogConstants;
 50import org.productivity.java.syslog4j.SyslogIF;
 51import org.productivity.java.syslog4j.SyslogRuntimeException;
 52import org.productivity.java.syslog4j.impl.net.tcp.TCPNetSyslogConfig;
 53import org.productivity.java.syslog4j.impl.net.udp.UDPNetSyslogConfig;
 54import org.slf4j.Logger;
 55import org.slf4j.LoggerFactory;
 56import org.springframework.beans.factory.InitializingBean;
 57
 58/**
 59 * Forwards alarms, N, via Syslog.
 60 * 
 61 * @author <a href="mailto:david@opennms.org>David Hustace</a>
 62 */
 63public class SyslogNorthbounder extends AbstractNorthbounder implements InitializingBean {
 64    private static final Logger LOG = LoggerFactory.getLogger(SyslogNorthbounder.class);
 65	
 66	private static final String NBI_NAME = "SyslogNBI" ;
 67
 68    private SyslogNorthbounderConfig m_config;
 69	
 70	private NodeDao m_nodeDao;
 71	
 72	private SyslogDestination m_destination;
 73
 74	public SyslogNorthbounder(SyslogNorthbounderConfig config, SyslogDestination destination) {
 75		super(NBI_NAME+":"+destination);
 76		m_config = config;
 77		m_destination = destination;
 78	}
 79
 80	@Override
 81	public void afterPropertiesSet() throws Exception {
 82		
 83		if (m_config == null) {
 84			
 85			LOG.info("Syslog Northbounder is currently disabled, rejecting alarm.");
 86			
 87			String msg = "Syslog forwarding configuration is not initialized.";
 88			IllegalStateException e = new IllegalStateException(msg);
 89			LOG.error(msg, e);
 90			throw e;
 91		}
 92		
 93		createNorthboundInstance();
 94		setNaglesDelay(m_config.getNaglesDelay());
 95		setMaxBatchSize(m_config.getBatchSize());
 96		setMaxPreservedAlarms(m_config.getQueueSize());
 97	}
 98
 99	/**
100     * The abstraction makes a call here to determine if the alarm should be placed
101     * on the queue of alarms to be sent northerly.
102     * 
103     */
104	@Override
105    public boolean accepts(NorthboundAlarm alarm) {
106		
107		if (!m_config.isEnabled()) {
108			return false;
109		}
110		
111		LOG.debug("Validating UEI of alarm: {}", alarm.getUei());
112		
113        if (getConfig().getUeis() == null || getConfig().getUeis().contains(alarm.getUei())) {
114    		LOG.debug("UEI: {}, accepted.", alarm.getUei());
115            return true;
116        }
117        
118		LOG.debug("UEI: {}, rejected.", alarm.getUei());
119        return false;
120    }
121    
122	/**
123	 * Each implementation of the AbstractNorthbounder has a nice queue (Nagle's algorithmic) and the worker
124	 * thread that processes the queue calls this method to send alarms to the northern NMS.
125	 * 
126	 */
127    @Override
128    public void forwardAlarms(List<NorthboundAlarm> alarms) throws NorthbounderException {
129        
130        if (alarms == null) {
131        	String errorMsg = "No alarms in alarms list for syslog forwarding.";
132			IllegalStateException e = new IllegalStateException(errorMsg);
133        	LOG.error(errorMsg, e);
134			throw e;
135        }
136        
137        LOG.info("Forwarding {} alarms to destination:{}", alarms.size(), m_destination.getName());
138
139    	Map<Integer, Map<String, Object>> alarmMappings = new HashMap<Integer, Map<String, Object>>();    	
140        
141    	SyslogIF instance;
142    	try {
143    		instance = Syslog.getInstance(m_destination.getName());
144    	} catch (SyslogRuntimeException e) {
145    		LOG.error("Could not find Syslog instance for destination: '{}': {}", m_destination.getName(), e);
146    		throw e;
147    	}
148
149    	/*
150    	 * Iterate over the list of alarms to be forwarded N.
151    	 */
152    	for (NorthboundAlarm alarm : alarms) {
153
154    		Integer count = alarm.getCount();
155    		if (count > 1 && m_destination.isFirstOccurrenceOnly()) {
156    			LOG.debug("Destination {} is configured for new alarm instances only.  Alarm has count of {}.", m_destination.getName(), count);
157    			continue;
158    		}
159
160    		LOG.debug("Creating formatted log message for alarm: {}.", alarm.getId());
161
162    		Map<String, Object> mapping = null;
163
164    		String syslogMessage;
165    		int level;
166    		try {
167    			if (alarmMappings != null) {
168    				mapping = alarmMappings.get(alarm.getId());
169    			}
170
171    			if (mapping == null) {
172    				mapping = createMapping(alarmMappings, alarm);
173    			}
174
175    			LOG.debug("Making substitutions for tokens in message format for alarm: {}.", alarm.getId());
176    			syslogMessage = PropertiesUtils.substitute(m_config.getMessageFormat(), mapping);
177
178    			LOG.debug("Determining LOG_LEVEL for alarm: {}", alarm.getId());
179    			level = determineLogLevel(alarm.getSeverity());
180    			
181    			LOG.debug("Forwarding alarm: {} via syslog to destination: {}", alarm.getId(), m_destination.getName());
182    			instance.log(level, syslogMessage);
183    			
184    		} catch (Exception e1) {
185    			LOG.error("Caught exception sending to destination: '{}': {}", m_destination.getName(), e1);
186    		}
187    	}
188    }
189    
190	private Map<String, Object> createMapping(Map<Integer, Map<String, Object>> alarmMappings, NorthboundAlarm alarm) {
191		Map<String, Object> mapping;
192		mapping = new HashMap<String, Object>();
193		mapping.put("ackUser", alarm.getAckUser());
194		mapping.put("appDn", alarm.getAppDn());
195		mapping.put("logMsg", alarm.getLogMsg());
196		mapping.put("objectInstance", alarm.getObjectInstance());
197		mapping.put("objectType", alarm.getObjectType());
198		mapping.put("ossKey", alarm.getOssKey());
199		mapping.put("ossState", alarm.getOssState());
200		mapping.put("ticketId", alarm.getTicketId());
201		mapping.put("alarmUei", alarm.getUei());
202		mapping.put("ackTime", nullSafeToString(alarm.getAckTime(), ""));
203		
204		AlarmType alarmType = alarm.getAlarmType() == null ? AlarmType.NOTIFICATION : alarm.getAlarmType();
205		mapping.put("alarmType", alarmType.name());
206		
207		String count = alarm.getCount() == null ? "1" : alarm.getCount().toString();
208		mapping.put("count", count);
209		
210		mapping.put("firstOccurrence", nullSafeToString(alarm.getFirstOccurrence(), ""));
211		mapping.put("alarmId", alarm.getId().toString());
212		mapping.put("ipAddr", nullSafeToString(alarm.getIpAddr(), ""));
213		mapping.put("lastOccurrence", nullSafeToString(alarm.getLastOccurrence(), ""));
214		
215		
216		if (alarm.getNodeId() != null) {
217			mapping.put("nodeId", alarm.getNodeId().toString());
218			String nodeLabel = m_nodeDao.getLabelForId(alarm.getNodeId());
219			mapping.put("nodeLabel", nodeLabel == null ? "?" : nodeLabel);
220		} else {
221			mapping.put("nodeId", "");
222			mapping.put("nodeLabel", "");
223		}
224		
225		
226		String poller = alarm.getPoller() == null ? "localhost" : alarm.getPoller().getName();
227		mapping.put("distPoller", poller);
228		
229		String service = alarm.getService() == null ? "" : alarm.getService().getName();					
230		mapping.put("ifService", service);
231		
232		mapping.put("severity", nullSafeToString(alarm.getSeverity(), ""));
233		mapping.put("ticketState", nullSafeToString(alarm.getTicketState(), ""));
234		
235		mapping.put("x733AlarmType", alarm.getX733Type());
236		
237		try {
238			mapping.put("x733ProbableCause", nullSafeToString(x733ProbableCause.get(alarm.getX733Cause()), ""));
239		} catch (Exception e) {
240			LOG.info("Exception caught setting X733 Cause: {}", alarm.getX733Cause(), e);
241			mapping.put("x733ProbableCause", "");
242		}
243		
244		buildParmMappings(alarm, mapping);
245		
246		alarmMappings.put(alarm.getId(), mapping);
247		return mapping;
248	}
249
250	protected void buildParmMappings(final NorthboundAlarm alarm, final Map<String, Object> mapping) {
251		List<EventParm<?>> parmCollection = new LinkedList<EventParm<?>>();
252		String parms = alarm.getEventParms();
253		if (parms == null) return;
254
255		char separator = ';';
256		String[] parmArray = StringUtils.split(parms, separator);
257		for (String string : parmArray) {
258			
259			char nameValueDelim = '=';
260			String[] nameValueArray = StringUtils.split(string, nameValueDelim);
261			String parmName = nameValueArray[0];
262			String parmValue = StringUtils.split(nameValueArray[1], '(')[0];
263			
264			EventParm<String> eventParm = new EventParm<String>(parmName, parmValue);
265			parmCollection.add(eventParm);
266		}
267
268		for (int i = 0; i < parmCollection.size(); i++) {
269			EventParm<?> parm = parmCollection.get(i);
270			Integer parmOffset = i +1;
271			mapping.put("parm[name-#"+parmOffset+"]", parm.getParmName());
272			mapping.put("parm[#"+parmOffset+"]", parm.getParmValue().toString());
273			mapping.put("parm["+parm.getParmName()+"]", parm.getParmValue().toString());
274		}
275	}
276	
277	
278	protected static class EventParm<T extends Object> {
279		private String m_parmName;
280		private T m_parmValue;
281		
282		EventParm(String name, T value) {
283			m_parmName = name;
284			m_parmValue = value;
285		}
286		
287		public String getParmName() {
288			return m_parmName;
289		}
290
291		public T getParmValue() {
292			return (T) m_parmValue;
293		}
294	}
295	
296
297	private String nullSafeToString(Object obj, String defaultString) {
298		if (obj != null) {
299			defaultString = obj.toString();
300		}
301		return defaultString;
302	}
303
304    
305    /**
306     * This is here, for now, until it can be properly wired and proper configuration can be created.
307     * This allows generic 127.0.0.1:UDP/514 to work with OpenNMS having no configuration.  This is
308     * trickery in its finest hour.
309     */
310    private void createNorthboundInstance() throws SyslogRuntimeException {
311    	
312    	LOG.info("Creating Syslog Northbound Instance:{}", m_destination.getName());
313    	
314    	String instName = m_destination.getName();
315    	int facility = convertFacility(m_destination.getFacility());
316    	SyslogProtocol protocol = m_destination.getProtocol();
317    	SyslogConfigIF instanceConfiguration = createConfig(m_destination, protocol, facility);
318    	instanceConfiguration.setIdent("OpenNMS");
319    	instanceConfiguration.setCharSet(m_destination.getCharSet());
320    	instanceConfiguration.setMaxMessageLength(m_destination.getMaxMessageLength());
321    	instanceConfiguration.setSendLocalName(m_destination.isSendLocalName());
322    	instanceConfiguration.setSendLocalTimestamp(m_destination.isSendLocalTime());
323    	instanceConfiguration.setTruncateMessage(m_destination.isTruncateMessage());
324    	instanceConfiguration.setUseStructuredData(SyslogConstants.USE_STRUCTURED_DATA_DEFAULT);
325
326    	try {
327    		Syslog.createInstance(instName, instanceConfiguration);
328    	} catch (SyslogRuntimeException e) {
329    		LOG.error("Could not create northbound instance, '{}': {}", instName, e);
330    		throw e;
331    	}
332
333	}
334    
335	private SyslogConfigIF createConfig(final SyslogDestination dest, final SyslogProtocol protocol, int fac) {
336		SyslogConfigIF config;
337		switch (protocol) {
338		case UDP:
339			config = new UDPNetSyslogConfig(fac, dest.getHost(), dest.getPort());
340			break;
341		case TCP:
342			config = new TCPNetSyslogConfig(fac, dest.getHost(), dest.getPort());
343			break;
344		default:
345			config = new UDPNetSyslogConfig(fac, "localhost", 514);
346		}
347		return config;
348	}
349
350	private int convertFacility(final SyslogFacility facility) {
351		int fac;
352		switch (facility) {
353		case KERN:
354			fac = SyslogConstants.FACILITY_KERN;
355			break;
356		case USER:
357			fac = SyslogConstants.FACILITY_USER;
358			break;
359		case MAIL:
360			fac = SyslogConstants.FACILITY_MAIL;
361			break;
362		case DAEMON:
363			fac = SyslogConstants.FACILITY_DAEMON;
364			break;
365		case AUTH:
366			fac = SyslogConstants.FACILITY_AUTH;
367			break;
368		case SYSLOG:
369			fac = SyslogConstants.FACILITY_SYSLOG;
370			break;
371		case LPR:
372			fac = SyslogConstants.FACILITY_LPR;
373			break;
374		case NEWS:
375			fac = SyslogConstants.FACILITY_NEWS;
376			break;
377		case UUCP:
378			fac = SyslogConstants.FACILITY_UUCP;
379			break;
380		case CRON:
381			fac = SyslogConstants.FACILITY_CRON;
382			break;
383		case AUTHPRIV:
384			fac = SyslogConstants.FACILITY_AUTHPRIV;
385			break;
386		case FTP:
387			fac = SyslogConstants.FACILITY_FTP;
388			break;
389		case LOCAL0:
390			fac = SyslogConstants.FACILITY_LOCAL0;
391			break;
392		case LOCAL1:
393			fac = SyslogConstants.FACILITY_LOCAL1;
394			break;
395		case LOCAL2:
396			fac = SyslogConstants.FACILITY_LOCAL2;
397			break;
398		case LOCAL3:
399			fac = SyslogConstants.FACILITY_LOCAL3;
400			break;
401		case LOCAL4:
402			fac = SyslogConstants.FACILITY_LOCAL4;
403			break;
404		case LOCAL5:
405			fac = SyslogConstants.FACILITY_LOCAL5;
406			break;
407		case LOCAL6:
408			fac = SyslogConstants.FACILITY_LOCAL6;
409			break;
410		case LOCAL7:
411			fac = SyslogConstants.FACILITY_LOCAL7;
412			break;
413		default:
414			fac = SyslogConstants.FACILITY_USER;
415		}
416		return fac;
417	}
418
419	private int determineLogLevel(final OnmsSeverity severity) {
420		int level;
421		switch (severity) {
422		case CRITICAL:
423			level = SyslogConstants.LEVEL_CRITICAL;
424			break;
425		case MAJOR:
426			level = SyslogConstants.LEVEL_ERROR;
427			break;
428		case MINOR:
429			level = SyslogConstants.LEVEL_ERROR;
430			break;
431		case WARNING:
432			level = SyslogConstants.LEVEL_WARN;
433			break;
434		case NORMAL:
435			level = SyslogConstants.LEVEL_NOTICE;
436			break;
437		case CLEARED:
438			level = SyslogConstants.LEVEL_INFO;
439			break;
440		case INDETERMINATE:
441			level = SyslogConstants.LEVEL_DEBUG;
442			break;
443		default:
444			level = SyslogConstants.LEVEL_WARN;
445		}
446		return level;
447	}
448
449
450    public SyslogNorthbounderConfig getConfig() {
451    	
452    	if (m_config == null) {
453    	    String errMsg = "Syslog Northbounder configuration is not set.";
454    		LOG.error(errMsg);
455    		throw new IllegalStateException(errMsg);
456    	}
457        return m_config;
458    }
459
460    public void setConfig(final SyslogNorthbounderConfig config) {
461    	
462    	if (config == null) {
463    		String string = "Syslog Northbounder configuration cannot be set null";
464    		LOG.error(string);
465    		throw new IllegalStateException(string);
466    	}
467    	
468    }
469
470	public NodeDao getNodeDao() {
471		return m_nodeDao;
472	}
473
474	public void setNodeDao(final NodeDao nodeDao) {
475		m_nodeDao = nodeDao;
476	}
477
478}