Programming


16
May 11

JSONBOT plugin

Afternoon y’all!

Last weekend I decided to give observium a try. Observium is a network monitoring system and fully snmp-based. There are also alternatives like Zabbix and/or Nagios but Observium differs that it’s very very easy to set-up.

I’ve spent like half-an hour installing observium on a clean debian server and after those 30 minutes it was already graphing 3 devices in my network. I’ve spent a couple of hours after that toying around, discovering what’s possible and what not.

Something observium does is when it discovers a problem, for example a device that got disconnected, it sends an e-mail to the snmp-syscontact (or if overridden another email address).

I usually also have an irc window open when I’m sitting behind my computer like many of you I suppose. And I almost immediately had the wish that the irc-bot (jsonbot) that I have running would be connected somehow to observium and inform me of those messages. A couple of friends also use observium (one of them has been pushing me around a lot when o when I would start using observium :P ) and they also liked the idea.

So today I got up around 8:30 in the morning brewed a fresh pot of coffee and opened my editor. Now some hours and I don’t know how many cups of coffee later I’d like to present to you my observium-jsonbot-plugin!

What it does is check an imap e-mail box for new messages and inform you about it. The features it provides are:

  • Present a list of unread e-mails
  • Display the contents of a given message
  • Automatically check for new messages (by default every 60 secs. when enabled)

Here’s how it works! First of all you need to configure your bot with the options for the observium imapbox. The following options can be set:

imap-host (default: mail.example.com)
imap-username (default: observ@example.com)
imap-password (default: password)
imap-folder (default: INBOX)
imap-ssl (default: True)
imap-interval (default: 60)
watcher-enabled (default: False)

These options can be set using:

!observium-cfg

#Example
!observium-cfg imap-host mail.yourdomain.com

# Don't forget to save and reload when you're done using:
!observium-cfgsave
#and
!reload observium

When you’ve configured the plugin these are the commands available to you:

# retrieve all unread message-subjects from the configured imap-folder
!observ

#example output:
[#3] Device Up: host.yourdomain.tld
[#2] Device Down: host.yourdomain.tld

# retrieve the contents of a specific message
!observ-msg 3

#example output:
[Subject]: Device Up: host.yourdomain.tld
[Message]: Device up: host.yourdomain.tld at 16-05-2011 10:45:56

# if you've enabled the watcher in the config you can enable it for the current channel using:
!observ-watch-enable

#or disable it using:
!observ-watch-disable

#or list the channels for which it has been enabled:
!observ-watch-list

If you’ve enabled the watcher for a certain channel the bot will check the mailbox every 60 seconds (unless you’ve changed the interval) and if new messages have been found it will post a list of subjects in the channel.

Of course you’ve been waiting for the downloadlink: well here it is: Download

You should put this file in <yourbotdirectory>/jsb/plugs/socket/
Then restart your bot and enable the plugin using:

!plug-enable observium

I hope you have fun with it and if you’ve got any comments and/or find bugs, please let me know :)

Kind regards,

Lammert


8
Jul 10

Python and net-snmp

Some of you may know that i have a 19″ rack at home with some interesting hardware in it. Some of this hardware also provides a couple of hundreds of gigabytes of storage. But that system takes about 3 maybe 4 amps when it’s idling. Taken into account that 1 amp 24/7 costs me about 20 euros per month. So you can imagine that i don’t have all my equipment running 24/7.

Once in a while i use this system to put some files onto it and then i turn it off again. To be able to also do this remote i’ve bought myself an APC PDU (model AP9212) a couple of months ago. Since that time i always had an idea to automate a backup job which would perform the following steps for me:

  1. Turn on my storage system
  2. Mount the backup share
  3. Transfer the backup onto the share
  4. Unmount the backup share
  5. Send a shutdown command to my storage system
  6. Turn of my storage system

The day before yesterday i finally put my hands on this idea and started looking how i could get and set data to and from my PDU using SNMP. A while ago i already figured out the specific OID’s needed for this job.

.1.3.6.1.4.1.318.1.1.4.4.2.1.4.1 for the name of the first outlet (change the last digit to the corresponding port)

and

.1.3.6.1.4.1.318.1.1.4.4.2.1.3.1 for the status of the first outlet (change the last digit to the corresponding port)

So i started searching and reading and found that the net-snmp package comes with python bindings. The small example for an snmpget looked simple enough so i started programming. Within the hour i had written a small script that reads the names and statuses of all 8 ports. (see example code at the bottom of this post)

Unfortunately my hopes were up and this was all the easy part and i was stupid enough to think that an snmpset wouldn’t be much harder, ok in the end maybe it wasn’t but getting there…..

Documentation for the net-snmp python bindings isn’t widely available i discovered you pretty much have to rely on the README provided with the net-snmp package. Searching for code examples is a quest without suitable results (it returns a lot of snmpget examples which i didn’t need).

And while trying different ways to get my code working and actually controlling a port on my PDU did take me several hours. I ran into all sorts of errors for example when i used the following piece of code trying to get it to work:

1
2
infovar = netsnmp.Varbind(".1.3.6.1.4.1.318.1.1.4.4.2.1.3.5", "1")
netsnmp.snmpset(infovar, Version = 1, DestHost='192.168.2.3', Community='writecommunity')

i got this error:

error: set: no type found for objectNone

ok so diving into the README once again i changed the first line to:

1
infovar = netsnmp.Varbind(".1.3.6.1.4.1.318.1.1.4.4.2.1.3.5", "0", "1", "INTEGER")

now my code actually ran but nothing happened on my PDU… i thought. I started inspecting my network traffic because i was curious what actually was being sent to my PDU.  I made a pcap file with tcpdump and inspected it with wireshark and that showed me the response from the PDU:

error-status: noSuchName (2)

Ok, so it was clear that somehow snmp couldn’t find this OID and wasn’t able to write a value to it. But how is that possible? i’m certain that my OID is right. So a lot of trying, searching, cursing and more cursing later i started digging in the net-snmp package.

In this package i found a unittest for the python snmp bindings so i’ve decided to enable the snmp daemon on my server and run this unittest and to my big suprise this worked including the snmpset method. And when i dug through the unittest code i found nothing really different from my code.

After careful reading the code over and over again i suddenly realised that they put a value in sysDescription.0 but their varbind looked like this:

1
netsnmp.Varbind("sysDescription", "0", "a new description")

and i realised that they split up sysDescription.0 in the varbind call so what if i do the same thing? so i started an snmpwalk to look at the values again and i made my varbind look like this:

1
netsnmp.Varbind("enterprises", "318.1.1.4.4.2.1.3.5", "1", "INTEGER")

and when i ran my code again i saw that port 5 which i was testing on suddenly turned on i started cheering because this was the thing i was looking for, my code was working. (i added INTEGER into the call because without it it was still producing an error).

I hope that with this post i save some of you the same trouble i had to undergo. I can now continue writing my backupscript, i can now perform the first and last task now the ones that lie in between :P

The SNMP Get example (reading the names and statuses of port 1 through 8)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import netsnmp

HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'

for i in range(1,9):
    info = ".1.3.6.1.4.1.318.1.1.4.4.2.1.4.%i" % (i)
    control = ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i" % (i)
    infovar = netsnmp.Varbind(info)
    infores = netsnmp.snmpget(infovar, Version = 1, DestHost = '192.168.2.3', Community='readcommunity')

    controlvar = netsnmp.Varbind(control)
    controlres = netsnmp.snmpget(controlvar, Version = 1, DestHost = '192.168.2.3', Community='readcommunity')

    if controlres[0] == '1':
        print HEADER + infores[0] + ' ... ' + OKGREEN + 'On' + ENDC
    elif controlres[0] == '2':
        print HEADER + infores[0] + ' ... ' + OKBLUE + 'Off' + ENDC
    elif controlres[0] == '3':
        print HEADER + infores[0] + ' ... ' + WARNING + 'Rebooting' + ENDC
    else:
        print HEADER + infores[0] + ' ... ' + FAIL + 'Error' + ENDC

The SNMP set example (setting port 5 to status On)

1
2
3
4
5
6
7
8
9
10
11
import netsnmp

HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'

var = netsnmp.Varbind('enterprises', '318.1.1.4.4.2.1.3.5', '1', 'INTEGER')
res = netsnmp.snmpset(var, Version = 1, DestHost='192.168.2.3', Community='writecommunity')

Use code at your own risk, i only posted it to provide an example of how it can be done. Also the ip address of my PDU and community string have been changed, change to your needs if you want to try it. If you have any questions, comments or compliments please leave a comment below.