/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2007-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.mib2events;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.percederberg.mibble.Mib;
import net.percederberg.mibble.MibLoader;
import net.percederberg.mibble.MibLoaderException;
import net.percederberg.mibble.MibSymbol;
import net.percederberg.mibble.MibValue;
import net.percederberg.mibble.MibValueSymbol;
import net.percederberg.mibble.snmp.SnmpNotificationType;
import net.percederberg.mibble.snmp.SnmpObjectType;
import net.percederberg.mibble.snmp.SnmpTrapType;
import net.percederberg.mibble.snmp.SnmpType;
import net.percederberg.mibble.type.IntegerType;
import net.percederberg.mibble.value.ObjectIdentifierValue;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.opennms.netmgt.xml.eventconf.AlarmData;
import org.opennms.netmgt.xml.eventconf.Decode;
import org.opennms.netmgt.xml.eventconf.Event;
import org.opennms.netmgt.xml.eventconf.Events;
import org.opennms.netmgt.xml.eventconf.Logmsg;
import org.opennms.netmgt.xml.eventconf.Mask;
import org.opennms.netmgt.xml.eventconf.Maskelement;
import org.opennms.netmgt.xml.eventconf.Varbindsdecode;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class Mib2Events {
private static final String DEFAULT_UEIBASE = "uei.opennms.org/mib2events/";
private static final String MIB2OPENNMS_UEIBASE = "uei.opennms.org/mib2opennms/";
/** Command-line help. */
private static final String COMMAND_HELP =
"Reads a MIB definition, creating from its traps (if any) a set of\n" +
"event definitions suitable for loading into OpenNMS. This program\n" +
"comes with ABSOLUTELY NO WARRANTY; for details, see the LICENSE.txt\n" +
"file.\n" +
"\n" +
"Syntax: java -jar mib2events.jar [--ueibase <base UEI>] [--compat] \\\n" +
" --mib <file or URL>\n" +
"\n" +
" --mib The pathname or URL of a MIB to load\n" +
" --ueibase The base UEI for resulting event definitions\n" +
" (default: uei.opennms.org/mib2events/)\n" +
" --compat Turn on compatability mode to create output similar to\n" +
" that of mib2opennms\n" +
"\n" +
"EXAMPLES\n" +
"\n" +
"Create events from the OSPF-TRAP-MIB, putting the events' UEI into an\n" +
"IETF namespace:\n" +
"\n" +
"java -jar mib2events.jar --mib OSPF-TRAP-MIB.my --ueibase uei.opennms.org/vendors/ietf/\n";
private String m_mibLocation;
private String m_ueiBase = null;
private MibLoader m_loader;
private Mib m_mib;
private boolean m_compat = false;
private static final Pattern TRAP_OID_PATTERN = Pattern.compile("(.*)\\.(\\d+)$");
public static void main(String[] args) throws FileNotFoundException {
BasicConfigurator.configure();
Logger.getRootLogger().setLevel(Level.WARN);
Mib2Events convertor = new Mib2Events();
convertor.parseCli(args);
try {
convertor.convert();
} catch (FileNotFoundException e) {
printError(convertor.getMibLocation(), e);
System.exit(1);
} catch (IOException e) {
printError(convertor.getMibLocation(), e);
} catch (MibLoaderException e) {
e.getLog().printTo(System.err);
System.exit(1);
}
if (convertor.getMib().getLog().warningCount() > 0) {
convertor.getMib().getLog().printTo(System.err);
}
PrintStream out = System.out;
out.println("<!-- Start of auto generated data from MIB: " + convertor.getMib().getName() + " -->");
try {
convertor.printEvents(out);
} catch (Throwable e) {
printError(convertor.getMibLocation(), e);
System.exit(1);
}
out.println("<!-- End of auto generated data from MIB: " + convertor.getMib().getName() + " -->");
}
public String getMibLocation() {
return m_mibLocation;
}
public void convert() throws IOException, MibLoaderException {
m_loader = new MibLoader();
URL url;
try {
url = new URL(m_mibLocation);
} catch (MalformedURLException e) {
url = null;
}
if (url == null) {
File file = new File(m_mibLocation);
m_loader.addDir(file.getParentFile());
m_mib = m_loader.load(file);
} else {
m_mib = m_loader.load(url);
}
}
private void printEvents(PrintStream out) throws MarshalException, ValidationException, ParserConfigurationException, SAXException, IOException {
if (m_loader == null) {
throw new IllegalStateException("convert() must be called first");
}
for (Mib mib : m_loader.getAllMibs()) {
if (!mib.isLoaded()) {
continue;
}
Events events = convertMibToEvents(mib, getEffectiveUeiBase());
if (events.getEventCount() < 1) {
System.err.println("No trap or notification definitions found in this MIB (" + mib.getName() + "), exiting");
System.exit(0);
}
if (!m_compat) {
StringWriter writer = new StringWriter();
events.marshal(writer);
stripXmlNameSpace(writer.toString(), out);
} else {
for (Event event : events.getEventCollection()) {
StringWriter writer = new StringWriter();
event.marshal(writer);
ByteArrayOutputStream formattedXml = new ByteArrayOutputStream();
stripXmlNameSpace(writer.toString(), formattedXml);
String noXmlProcessingInstruction = formattedXml.toString().replaceAll("(?m)<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>\n", "");
out.print(noXmlProcessingInstruction.replaceAll("dest=\"logndisplay\"", "dest='logndisplay'"));
}
}
}
}
private void stripXmlNameSpace(String xml, OutputStream out) throws ParserConfigurationException, SAXException, IOException {
String noNameSpace = xml.replaceAll(" xmlns=\"[^\"]*\"", "");
prettyPrintXML(new ByteArrayInputStream(noNameSpace.getBytes()), out);
}
private String getUeiBase() {
return m_ueiBase;
}
private String getEffectiveUeiBase() {
if (getUeiBase() != null) {
return getUeiBase();
}
if (m_compat) {
return MIB2OPENNMS_UEIBASE;
} else {
return DEFAULT_UEIBASE;
}
}
public void parseCli(String[] argv) {
Options opts = new Options();
opts.addOption("m", "mib", true, "Pathname or URL of MIB file to scan for traps");
opts.addOption("b", "ueibase", true, "Base UEI for resulting events");
opts.addOption("c", "compat", false, "Turn on compatibility mode to create output as similar to mib2opennms as possible");
CommandLineParser parser = new GnuParser();
try {
CommandLine cmd = parser.parse(opts, argv);
if (cmd.hasOption('m')) {
m_mibLocation = cmd.getOptionValue('m');
} else {
printHelp("You must specify a MIB file pathname or URL");
System.exit(1);
}
if (cmd.hasOption("b")) {
m_ueiBase = cmd.getOptionValue('b');
}
if (cmd.hasOption("c")) {
m_compat = true;
}
} catch (ParseException e) {
printHelp("Failed to parse command line options");
System.exit(1);
}
}
public static void printHelp(String msg) {
System.out.println("Error: " + msg + "\n\n");
System.out.println(COMMAND_HELP);
}
public static void printError(String msg) {
System.err.print("Error: ");
System.err.println(msg);
}
public static void printError(String file, FileNotFoundException e) {
StringBuffer buf = new StringBuffer();
buf.append("Could not open file:\n\t");
buf.append(file);
printError(buf.toString());
}
public static void printError(String url, IOException e) {
StringBuffer buf = new StringBuffer();
buf.append("Could not open URL:\n\t");
buf.append(url);
printError(buf.toString());
}
public static void printError(String msg, Throwable e) {
StringBuffer buf = new StringBuffer();
buf.append("Error: ");
buf.append(msg);
printError(buf.toString());
}
public static String getTrapEnterprise(MibValueSymbol trapValueSymbol) {
return getMatcherForOid(getTrapOid(trapValueSymbol)).group(1);
}
public static String getTrapSpecificType(MibValueSymbol trapValueSymbol) {
return getMatcherForOid(getTrapOid(trapValueSymbol)).group(2);
}
public static Matcher getMatcherForOid(String trapOid) {
Matcher m = TRAP_OID_PATTERN.matcher(trapOid);
if (!m.matches()) {
throw new IllegalStateException("Could not match the trap OID '" + trapOid + "' against '" + m.pattern().pattern() + "'");
}
return m;
}
private static String getTrapOid(MibValueSymbol trapValueSymbol) {
if (trapValueSymbol.getType() instanceof SnmpNotificationType) {
return "." + trapValueSymbol.getValue().toString();
} else if (trapValueSymbol.getType() instanceof SnmpTrapType) {
SnmpTrapType v1trap = (SnmpTrapType) trapValueSymbol.getType();
return "." + v1trap.getEnterprise().toString() + "." + trapValueSymbol.getValue().toString();
} else {
throw new IllegalStateException("Trying to get trap information from an object that's not a trap and not a notification");
}
}
public static String getTrapEventLabel(MibValueSymbol trapValueSymbol) {
StringBuffer buf = new StringBuffer();
buf.append(trapValueSymbol.getMib());
buf.append(" defined trap event: ");
buf.append(trapValueSymbol.getName());
return buf.toString();
}
public static String getTrapEventUEI(MibValueSymbol trapValueSymbol, String ueibase) {
StringBuffer buf = new StringBuffer(ueibase);
if (! ueibase.endsWith("/")) {
buf.append("/");
}
buf.append(trapValueSymbol.getName());
return buf.toString();
}
public static List<MibValue> getTrapVars(MibValueSymbol trapValueSymbol) {
if (trapValueSymbol.getType() instanceof SnmpNotificationType) {
SnmpNotificationType v2notif = (SnmpNotificationType) trapValueSymbol.getType();
return getV2NotificationObjects(v2notif);
} else if (trapValueSymbol.getType() instanceof SnmpTrapType) {
SnmpTrapType v1trap = (SnmpTrapType) trapValueSymbol.getType();
return getV1TrapVariables(v1trap);
} else {
throw new IllegalStateException("trap type is not an SNMP v1 Trap or v2 Notification");
}
}
@SuppressWarnings("unchecked")
private static List<MibValue> getV1TrapVariables(SnmpTrapType v1trap) {
return v1trap.getVariables();
}
@SuppressWarnings("unchecked")
private static List<MibValue> getV2NotificationObjects(SnmpNotificationType v2notif) {
return v2notif.getObjects();
}
public Logmsg getTrapEventLogmsg(MibValueSymbol trapValueSymbol) {
Logmsg msg = new Logmsg();
msg.setDest("logndisplay");
final StringBuffer dbuf = new StringBuffer();
dbuf.append("<p>");
dbuf.append("\n");
dbuf.append("\t").append(trapValueSymbol.getName()).append(" trap received\n");
int vbNum = 1;
for (MibValue vb : getTrapVars(trapValueSymbol)) {
dbuf.append("\t").append(vb.getName()).append("=%parm[#").append(vbNum).append("]%\n");
vbNum++;
}
if (dbuf.charAt(dbuf.length() - 1) == '\n') {
dbuf.deleteCharAt(dbuf.length() - 1); // delete the \n at the end
}
dbuf.append("</p>\n\t");
msg.setContent(dbuf.toString());
return msg;
}
public static String getTrapEventDescr(MibValueSymbol trapValueSymbol) {
String description = ((SnmpType) trapValueSymbol.getType()).getDescription();
// FIXME There a lot of detail here (like removing the last \n) that can go away when we don't need to match mib2opennms exactly
final String descrStartingNewlines = description.replaceAll("^", "\n<p>");
final String descrEndingNewlines = descrStartingNewlines.replaceAll("$", "</p>\n");
//final String descrTabbed = descrEndingNewlines.replaceAll("(?m)^", "\t");
//final StringBuffer dbuf = new StringBuffer(descrTabbed);
final StringBuffer dbuf = new StringBuffer(descrEndingNewlines);
if (dbuf.charAt(dbuf.length() - 1) == '\n') {
dbuf.deleteCharAt(dbuf.length() - 1); // delete the \n at the end
}
//if (dbuf.lastIndexOf("\n") != -1) {
// dbuf.insert(dbuf.lastIndexOf("\n") + 1, '\t');
//}
//final StringBuffer dbuf = new StringBuffer(descrEndingNewlines);
//dbuf.append("\n");
dbuf.append("<table>");
dbuf.append("\n");
int vbNum = 1;
for (MibValue vb : getTrapVars(trapValueSymbol)) {
dbuf.append("\t<tr><td><b>\n\n\t").append(vb.getName());
dbuf.append("</b></td><td>\n\t%parm[#").append(vbNum).append("]%;</td><td><p>");
SnmpObjectType snmpObjectType = ((SnmpObjectType) ((ObjectIdentifierValue) vb).getSymbol().getType());
if (snmpObjectType.getSyntax().getClass().equals(IntegerType.class)) {
IntegerType integerType = (IntegerType) snmpObjectType.getSyntax();
if (integerType.getAllSymbols().length > 0) {
SortedMap<Integer, String> map = new TreeMap<Integer, String>();
for (MibValueSymbol sym : integerType.getAllSymbols()) {
map.put(new Integer(sym.getValue().toString()), sym.getName());
}
dbuf.append("\n");
for (Entry<Integer, String> entry : map.entrySet()) {
dbuf.append("\t\t").append(entry.getValue()).append("(").append(entry.getKey()).append(")\n");
}
dbuf.append("\t");
}
}
dbuf.append("</p></td></tr>\n");
vbNum++;
}
if (dbuf.charAt(dbuf.length() - 1) == '\n') {
dbuf.deleteCharAt(dbuf.length() - 1); // delete the \n at the end
}
dbuf.append("</table>\n\t");
return dbuf.toString();
}
public static AlarmData getTrapEventAlarmData() {
AlarmData ad = new AlarmData();
// FIXME This is incorrect most of the time...
ad.setReductionKey("%uei%:%dpname%:%nodeid%:%interface%");
ad.setAlarmType(1);
ad.setAutoClean(false);
return ad;
}
public Event getTrapEvent(MibValueSymbol trapValueSymbol, String ueibase) {
Event evt = new Event();
// Set the event's UEI, event-label, logmsg, severity, and descr
evt.setUei(getTrapEventUEI(trapValueSymbol, ueibase));
evt.setEventLabel(getTrapEventLabel(trapValueSymbol));
evt.setLogmsg(getTrapEventLogmsg(trapValueSymbol));
evt.setSeverity("Indeterminate");
evt.setDescr(getTrapEventDescr(trapValueSymbol));
if (!m_compat) {
//evt.setAlarmData(getTrapEventAlarmData());
}
if (!m_compat) {
List<Varbindsdecode> decode = getTrapVarbindsDecode(trapValueSymbol);
if (!decode.isEmpty()) {
evt.setVarbindsdecode(decode);
}
}
evt.setMask(new Mask());
// The "ID" mask element (trap enterprise)
addMaskElement(evt, "id", getTrapEnterprise(trapValueSymbol));
// The "generic" mask element: hard-wired to enterprise-specific(6)
addMaskElement(evt, "generic", "6");
// The "specific" mask element (trap specific-type)
addMaskElement(evt, "specific", getTrapSpecificType(trapValueSymbol));
return evt;
}
private void addMaskElement(Event event, String name, String value) {
if (event.getMask() == null) {
throw new IllegalStateException("Event mask is null, must have been set before this method was called");
}
Maskelement me = new Maskelement();
me.setMename(name);
me.addMevalue(value);
event.getMask().addMaskelement(me);
}
private static List<Varbindsdecode> getTrapVarbindsDecode(MibValueSymbol trapValueSymbol) {
Map<String, Varbindsdecode> decode = new LinkedHashMap<String, Varbindsdecode>();
int vbNum = 1;
for (MibValue vb : getTrapVars(trapValueSymbol)) {
String parmName = "parm[#" + vbNum + "]";
SnmpObjectType snmpObjectType = ((SnmpObjectType) ((ObjectIdentifierValue) vb).getSymbol().getType());
if (snmpObjectType.getSyntax().getClass().equals(IntegerType.class)) {
IntegerType integerType = (IntegerType) snmpObjectType.getSyntax();
if (integerType.getAllSymbols().length > 0) {
SortedMap<Integer, String> map = new TreeMap<Integer, String>();
for (MibValueSymbol sym : integerType.getAllSymbols()) {
map.put(new Integer(sym.getValue().toString()), sym.getName());
}
for (Entry<Integer, String> entry : map.entrySet()) {
if (!decode.containsKey(parmName)) {
Varbindsdecode newVarbind = new Varbindsdecode();
newVarbind.setParmid(parmName);
decode.put(newVarbind.getParmid(), newVarbind);
}
Decode d = new Decode();
d.setVarbinddecodedstring(entry.getValue());
d.setVarbindvalue(entry.getKey().toString());
decode.get(parmName).addDecode(d);
}
}
}
vbNum++;
}
return new ArrayList<Varbindsdecode>(decode.values());
}
private Events convertMibToEvents(Mib mib, String ueibase) {
Events events = new Events();
for (MibSymbol sym : getAllSymbolsFromMib(mib)) {
if (!(sym instanceof MibValueSymbol)) {
continue;
}
MibValueSymbol vsym = (MibValueSymbol) sym;
if ((!(vsym.getType() instanceof SnmpNotificationType)) && (!(vsym.getType() instanceof SnmpTrapType))) {
continue;
}
events.addEvent(getTrapEvent(vsym, ueibase));
}
return events;
}
@SuppressWarnings("unchecked")
private static Collection<MibSymbol> getAllSymbolsFromMib(Mib mib) {
return mib.getAllSymbols();
}
public static void prettyPrintXML(InputStream docStream, OutputStream out) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(docStream);
OutputFormat fmt = new OutputFormat(doc);
fmt.setOmitXMLDeclaration(true);
fmt.setLineWidth(72);
fmt.setIndenting(true);
fmt.setIndent(2);
XMLSerializer ser = new XMLSerializer(out, fmt);
ser.serialize(doc);
}
public Mib getMib() {
return m_mib;
}
}