A while ago Doug asked whether anyone knew a utility to read XML and send SNMP traps. I mentioned that it would be easy to do with Groovy but did not have time to give any details. I’ve been meaning to write a post about how we use Groovy in RapidInsight and thought this would be a good excercise.
First I have state once again that I’m not a developer nor play one on Youtube. I can however, put together scripts, especially if there are examples, but writing java code is not my cup of tea. Groovy makes this stuff easy enough to deal with for people like me (system integrators, admins, etc.)
In this example, we’ll read the RSS feed (which is XML) from Doug’s blog and take action if the feed title includes the word BSM in it
Groovy includes powerful XML utility called xmlslurper that makes reading XML a breeze.
def url = "http://dougmcclure.net/blog/feed/"
def feed = new XmlSlurper().parse(url)
feed.channel.item.each {post ->
if (post.title.toString().matches(".*BSM.*")) {
println "found BSM post!: " + post.title + " – " post.link.toString()
}
}
There it is, we already have a Groovy script to retrieve the RSS feed, iterate through the posts and determine whether the title includes BSM keyword. One can take a look at how xmlSlurper works and use it to get the information she needs.
RapidInsight makes it even easier for the script developers to work with the external interfaces without having to learn the intricacies of each interface. In RapidInsight, we can create a class called say RssFeed, store data related to the RSS feed as object properties.Here is how our class may look like:
<model name="RssFeed">
<properties>
<property name="name" type="string">
<property name="url" type="string">
<property name="type" type="string">
</property>
</property>
The class can be used to store the information related to the Rss feed as objects. We can create a UI for the user to enter this information, or simply use a script to create the instances. Creating an object in RapidInsight repository is straight forward, pass the properties as name value pairs to add operation:
def props = [:]
props.name = "Doug McClure BSM Blog"
props.url = "http://dougmcclure.net/blog/feed/"
props.type = "Rss 2.0"
RssFeed.add[props]
// I could have written this as a single line as well
// RssFeed.add[url:"http://dougmcclure.net/blog/feed/", type = "Rss 2.0"]
By storing the url, etc. as object properties, we can now have a more generic script that would work with any RSS feed. Next we can add “operations” to this class to deal with the Rss feeds, hiding the complexities of the external interface from the script developer. An operation can read the feed and return name value pairs in a map to the user.
This means the script developer does not have to understand the structure of the RSS or how to use XmlSlurper. The operation code can be modified to add support for different RSS formats (v1, v2), use ROME library to parse RSS instead of XmlSlurper, etc., scripts would continue to work as before and script developers like me would not have to learn something different. All it matters is what is passed to us, which is a List of Map (name value pairs), and this is same for all external interfaces. (Take a look a this post to see more on how RapidInsight uses name value pairs when working with external interfaces)
The operation may be something like this:
//readFeed operation returns blog posts and links as a list of maps
def readFeed() {
def feed = new XmlSlurper().parse(url)
def posts = []
feed.channel.item.each {
posts << [title:it.title.toString(), link:it.link.toString()]
}
return posts
}
Now that we have removed both the data (like url) and the external interface interaction out, our script can be simpler:
def dougsFeed = RssFeed.get("Doug McClure BSM Blog")
if (dougsFeed.title.matches(".*BSM.*))
println "found BSM post!: " + dougsFeed.title + " – " + dougsFeed.link
}
Or we can extend it to do the same for each RssFeed object we may have defined:
// iterate through each RssFeed object
RssFeed.each { blog ->
def feed = blog.readFeed()
if (feed.title.matches(".*BSM.*))
println "found BSM post!: blog.name <a href=${blog.link}>${blog.link}</a>"
}
}
Let’s move to the second part of the requirement: taking an action like sending an SNMP trap. Groovy can use any java library and it seems like there is an open source java library for pretty much anything you can think of!
We use SNMP4J in RapidInsight. The java library can be used directly in the groovy script, however SNMP is more complicated than RSS, hence figuring out how to use the library is difficult for a non java developer like myself. Here again, RapidInsight helps by providing infrastructure to make it easier to work with SNMP and hide the complexity even further.
SnmpTrap class can be used to store common data such where (ip/port) to send the traps, which snmp version, etc. SnmpTrap class has an operation called send() that takes name value pairs and sends the trap. Usage is very easy:
def trap = SnmpTrap.
get(name:
"myTrapDestination")
def props = [:]
props.enterprise = ".1.3.6.1.4.1.88888.12"
props.generic = 6
props.specific = 1
props.varbinds = []
trap.send(props)
// or in a single line
// trap.send(enterprise:".1.3.6.1.4.1.88888.12",specific:1,generic:6,varbinds:[])
If I only have a few different kind of SNMP traps to send, like up, down etc., I can move the trap definition into the operations to make the script itself even simpler.
//operations
def sendDownTrap
() {
send
(enterprise:
".1.3.6.1.4.1.88888.12",specific:
1,generic:
6,varbinds:
[])
}
def sendUpTrap
() {
send
(enterprise:
".1.3.6.1.4.1.88888.12",specific:
2,generic:
6,varbinds:
[])
}
// script:
def trap = SnmpTrap.get(name:"myTrapDestination")
trap.sendDownTrap()
The functionality described in this post is not difficult to implement in perl etc. for a good developer; there are libraries available and the whole thing can be implemented in a single script. RapidInsight provides functionality at every step to make not only implementation but maintenance and operations support easier. Scripts can be executed from a web based UI, scheduled to run periodically, only available to subset of authorized users, logging, notifications, etc. instead of collection of scripts running as cron jobs that can get out of control rapidly.