/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2010-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.tools.spectrum;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.List;
import org.opennms.core.utils.LogUtils;
import org.springframework.core.io.Resource;
public class EventDispositionReader {
private Resource m_resource;
private Reader m_reader;
private StreamTokenizer m_tokenizer;
private static final String eventCodeExpr = "^0[Xx][0-9A-Fa-f]{1,8}$";
private static final String logEventToken = "E";
private static final String createAlarmToken = "A";
private static final String uniqueAlarmToken = "U";
private static final String notUserClearableToken = "N";
private static final String notPersistentToken = "T";
private static final String clearAlarmToken = "C";
/**
* E A ,,U,N,T
* 0x180000 E 50 A 2, 0x180000, N
*
* This entry states that when an event of type 0x180000 occurs, it will be logged (E) with a severity of 50.
* Also it will generate an alarm (A) of severity Major (2) and of alarm type 0x180000. The alarm is not user
* clearable (N).
*
* The E can be present or absent. If absent the event is not logged to the Archive Manager and will not be
* observed in any client view unless the client is displaying the event history when the event occurs. The
* event severity is not currently used but is logged with the event. If the E is absent the severity should
* also be absent.
*
* The A indicates that when this event occurs an alarm should be generated. Following the A are the alarm
* severity, alarm cause (type) and three qualifiers that can appear in any combination. The alarm type does
* not need to have the same value as the type of the event that generates it, however making them equal
* when possible enhances readability. The possible qualifiers are:
*
* U - A unique alarm should be generated for each occurrence of this event (otherwise no new alarm is
* generated for subsequent occurrences, instead the existing alarm is noted)
* N - The alarm is not user clearable
* T - the alarm is not persistent (it will not be maintained through a SpectroSERVER restart)
*
* In place of the A there can be a C. The C would be followed by an alarm cause value and indicates that
* when the event occurs it should clear an alarm of the type indicated if one exists on the model.
*
* In place of the U one or more event variables can be listed as event discriminators:
* E A ,,,N,T
*
* When discriminators are specified then they are considered together with the alarm cause in determining
* the effective type of the alarm. Consider the following:
*
* 0x180000 E 50 A 2,0x180000, 2,3,N
* 0x180001 E 50 C 0x180000, 2,3
*
* This states that event 0x180000 should generate an alarm of type 0x180000 and that the values for event
* variables 2 and 3 should be saved with the alarm. If event 0x180000 occurs again and there is already
* an alarm present, but the new values of event variables 2 and 3 are not both equal to those stored with
* the alarm, then a new alarm will be generated, even though the unique indicator is not specified. The
* new alarm is considered to be of a different type since the values for the discriminators are different.
* Similarly if event 0x180001 occurs, it will only clear an alarm on this model of type 0x180000 if that
* alarm was generated with the same values of event variables 2 and 3 that were stored when the alarm was
* created. The specification of the discriminators with the clear event is required.
*/
private enum TokenType {
none,
eventCode,
logEvent,
eventSeverity,
createAlarm,
clearAlarm,
alarmSeverity,
alarmSeverityComma,
alarmCause,
alarmCauseComma,
uniqueAlarm,
notUserClearable,
notPersistent
}
public EventDispositionReader(Resource rsrc) throws IOException {
m_resource = rsrc;
m_reader = new BufferedReader(new InputStreamReader(m_resource.getInputStream()));
m_tokenizer = new StreamTokenizer(m_reader);
m_tokenizer.resetSyntax();
m_tokenizer.commentChar('#');
m_tokenizer.eolIsSignificant(true);
m_tokenizer.whitespaceChars('\n', '\n');
m_tokenizer.whitespaceChars('\r', '\r');
m_tokenizer.whitespaceChars(' ', ' ');
m_tokenizer.whitespaceChars('\t', '\t');
m_tokenizer.wordChars('0', '9');
m_tokenizer.wordChars('a', 'z');
m_tokenizer.wordChars('A', 'Z');
}
public List<EventDisposition> getEventDispositions() throws IOException {
List<EventDisposition> eventDispositions = new ArrayList<EventDisposition>();
EventDisposition thisEventDisposition = null;
TokenType lastToken = TokenType.none;
boolean pastAlarmCause = false;
boolean justHitEol = true;
while (m_tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
//System.err.println(m_tokenizer);
if (justHitEol && m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.matches(eventCodeExpr)) {
LogUtils.debugf(this, "Found an event code %s on line %d, creating a new event-disposition", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition = new EventDisposition(m_tokenizer.sval);
lastToken = TokenType.eventCode;
pastAlarmCause = false;
justHitEol = false;
}
if (m_tokenizer.ttype == StreamTokenizer.TT_EOL) {
LogUtils.tracef(this, "Hit EOL on line %d", m_tokenizer.lineno());
if (thisEventDisposition != null) {
LogUtils.tracef(this, "At EOL on line %d, and the working event-disposition is non-null, adding it to the completed pile", m_tokenizer.lineno());
LogUtils.tracef(this, "%s", thisEventDisposition.toString());
eventDispositions.add(thisEventDisposition);
} else {
LogUtils.tracef(this, "At EOL on line %d, but the working event-disposition is null so not adding it", m_tokenizer.lineno());
}
justHitEol = true;
}
if (m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.equals(logEventToken)) {
if (lastToken != TokenType.eventCode) {
LogUtils.errorf(this, "An event-log token [%s] must follow an event code directly, but the one on line %d of %s follows a %s token", logEventToken, m_tokenizer.lineno(), m_resource, lastToken.name());
throw new IllegalArgumentException("Found a log-event token [" + logEventToken + "] in an unexpected place on line " + m_tokenizer.lineno());
}
LogUtils.debugf(this, "Found a log-event token [%s] on line %d, setting log-event true", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setLogEvent(true);
lastToken = TokenType.logEvent;
}
if (lastToken == TokenType.logEvent) {
if (m_tokenizer.nextToken() == StreamTokenizer.TT_WORD && m_tokenizer.sval.matches("^\\d+$")) {
LogUtils.tracef(this, "Found event severity %s for event-code %s on line %d", m_tokenizer.sval, thisEventDisposition.getEventCode(), m_tokenizer.lineno());
thisEventDisposition.setEventSeverity(Integer.valueOf(m_tokenizer.sval));
lastToken = TokenType.eventSeverity;
} else {
LogUtils.errorf(this, "Found a token [%s] following an event-severity token [%s] on line %d of %s that does not appear to be an event severity", m_tokenizer.sval, logEventToken, m_tokenizer.lineno(), m_resource);
throw new IllegalArgumentException("Found an out-of-place token [" + m_tokenizer.sval + "] on line " + m_tokenizer.lineno());
}
}
if (m_tokenizer.ttype == StreamTokenizer.TT_WORD && (m_tokenizer.sval.equals(createAlarmToken) || m_tokenizer.sval.equals(clearAlarmToken))) {
LogUtils.tracef(this, "Found a create-alarm or clear-alarm token [%s] on line %d, checking that it's not out of order", m_tokenizer.sval, m_tokenizer.lineno());
if (lastToken != TokenType.eventCode && lastToken != TokenType.eventSeverity) {
LogUtils.errorf(this, "Found a token [%s] NOT following an event-code [0xNNN...] or event-severity [e.g. 20] token on line %d of %s that does not appear to be an event severity", m_tokenizer.sval, m_tokenizer.lineno(), m_resource);
throw new IllegalArgumentException("Found an out-of-place token [" + m_tokenizer.sval + "] on line "+ m_tokenizer.lineno());
}
}
if (m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.equals(createAlarmToken)) {
LogUtils.debugf(this, "Found a create-alarm token [%s] on line %d", m_tokenizer.sval, m_tokenizer.lineno());
LogUtils.tracef(this, "Found a create-alarm token [%s] on line %d, peeking ahead for the alarm severity", m_tokenizer.sval, m_tokenizer.lineno());
if (m_tokenizer.nextToken() != StreamTokenizer.TT_WORD || ! m_tokenizer.sval.matches("^[01234]$")) {
LogUtils.errorf(this, "Found a create-alarm token [%s] on line %d of %s that's followed by an unexpected token [%s] instead of an alarm severity in range 0-4", createAlarmToken, m_tokenizer.lineno(), m_resource, m_tokenizer.sval);
throw new IllegalArgumentException("Found a create-alarm token [" + createAlarmToken + "] on line [" + m_tokenizer.lineno() + "] followed by unexpected token [" + m_tokenizer.sval + "] instead of an alarm severity in range 0-4");
} else if (m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.matches("^[01234]$")) {
LogUtils.debugf(this, "Found alarm-severity token [%s] on line %d, using it to set alarm severity", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setCreateAlarm(true);
thisEventDisposition.setAlarmSeverity(Integer.valueOf(m_tokenizer.sval));
lastToken = TokenType.alarmSeverity;
}
if (m_tokenizer.nextToken() != ',') {
LogUtils.errorf(this, "The alarm-severity value [%d] on line %d of %s is not followed by a comma", thisEventDisposition.getAlarmSeverity(), m_tokenizer.lineno(), m_resource);
throw new IllegalArgumentException("Alarm-severity [" + thisEventDisposition.getAlarmSeverity() + "] not followed by a comma on line " + m_tokenizer.lineno());
}
lastToken = TokenType.alarmSeverityComma;
continue;
}
if (m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.equals(clearAlarmToken)) {
LogUtils.debugf(this, "Found a clear-alarm token [%s] on line %d, setting clearAlarm to true", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setClearAlarm(true);
lastToken = TokenType.clearAlarm;
}
if (lastToken == TokenType.alarmSeverityComma || lastToken == TokenType.clearAlarm) {
if (m_tokenizer.ttype != StreamTokenizer.TT_WORD) {
LogUtils.errorf(this, "Expecting an alarm-cause token [e.g. 0xNNN...] after a %s on line %d of %s but got a non-word token of type %d instead", lastToken.name(), m_tokenizer.lineno(), m_resource, m_tokenizer.ttype);
throw new IllegalArgumentException("Expected an alarm-cause [e.g. 0xNNN...] after the " + lastToken.name() + " on line " + m_tokenizer.lineno() + " but got a non-word token instead");
} else if (m_tokenizer.sval.matches(eventCodeExpr)) {
if (lastToken == TokenType.alarmSeverityComma) {
LogUtils.debugf(this, "Found alarm-cause of [%s] on line %d, setting accordingly", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setAlarmCause(m_tokenizer.sval);
} else if (lastToken == TokenType.clearAlarm) {
LogUtils.debugf(this, "Found clear-alarm-cause of [%s] on line %d, setting accordingly", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setClearAlarmCause(m_tokenizer.sval);
}
lastToken = TokenType.alarmCause;
}
}
if (lastToken == TokenType.alarmCause && m_tokenizer.ttype == ',') {
lastToken = TokenType.alarmCauseComma;
pastAlarmCause = true;
}
if (lastToken == TokenType.alarmCauseComma && m_tokenizer.ttype != StreamTokenizer.TT_WORD) {
LogUtils.errorf(this, "Found an unexpected non-word token after the comma that follows alarm-cause or clear-alarm-cause [%s] on line %d of %s", thisEventDisposition.getAlarmCause(), m_tokenizer.lineno(), m_resource);
throw new IllegalArgumentException("Unexpected token after the comma following alarm-cause or clear-alarm-cause [" + thisEventDisposition.getAlarmCause() + "] on line " + m_tokenizer.lineno());
}
if (pastAlarmCause && m_tokenizer.ttype == ',') {
LogUtils.tracef(this, "Ignoring a comma in post-(clear)-alarm-cause section of disposition for event-code %s on line %d", thisEventDisposition.getEventCode(), m_tokenizer.lineno());
}
if (pastAlarmCause && m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.matches("^\\d+$")) {
LogUtils.debugf(this, "Found a numeric token [%s] after the (clear)-alarm-cause on line %d, adding as a discriminator", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.addDiscriminator(Integer.valueOf(m_tokenizer.sval));
}
if (pastAlarmCause && m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.equals(uniqueAlarmToken)) {
LogUtils.debugf(this, "Found a unique-alarm token [%s] after the (clear)-alarm-cause on line %d, setting unique-alarm to true", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setUniqueAlarm(true);
}
if (pastAlarmCause && m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.equals(notUserClearableToken)) {
LogUtils.debugf(this, "Found a not-user-clearable token [%s] after the (clear)-alarm-cause on line %d, setting user-clearable to false", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setUserClearable(false);
}
if (pastAlarmCause && m_tokenizer.ttype == StreamTokenizer.TT_WORD && m_tokenizer.sval.equals(notPersistentToken)) {
LogUtils.debugf(this, "Found a not-persistent token [%s] after the (clear)-alarm-cause on line %d, setting persistent to false", m_tokenizer.sval, m_tokenizer.lineno());
thisEventDisposition.setPersistent(false);
}
}
LogUtils.infof(this, "Loaded %d event-dispositions from [%s]", eventDispositions.size(), m_resource);
return eventDispositions;
}
}