Skip to content

Commit 00869c1

Browse files
author
Tobias Hintze
committed
ospf agent implementation
* satisfies check_ospf nagios-plugin * provides attr: ospfNbr{IpAddr,RtrId,Priority,State}
1 parent bc0c14b commit 00869c1

File tree

4 files changed

+313
-6
lines changed

4 files changed

+313
-6
lines changed

bird_bgp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def OnUpdate(ax, axd, state):
7575
"OnSnmpWrite" : OnSnmpWrite,
7676
"OnSnmpRequest" : OnSnmpRequest,
7777
"OnInit" : OnInit,
78-
"OnUpdate" : lambda ax, axd: OnUpdate(ax,axd,bird.getState())
78+
"OnUpdate" : lambda ax, axd: OnUpdate(ax,axd,bird.getBGPState())
7979
}
8080

8181
## initialize agentx module and run main loop

bird_ospf.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/python
2+
#
3+
# Copyright (c) 2016 Travelping GmbH <[email protected]>
4+
# by Tobias Hintze <[email protected]>
5+
#
6+
# This code is inspired and partially copied from
7+
# https://r3blog.nl/index.php/archives/2011/02/24/bgp4-mib-support-for-bird/
8+
# That original code does not clearly declare any license.
9+
#
10+
# This code also uses python-agentx library licensed under GPLv3
11+
# (see agentx.py for details)
12+
#
13+
# So this code is licensed under the GPLv3 (see COPYING.GPLv3).
14+
#
15+
16+
from adv_agentx import AgentX
17+
from adv_agentx import SnmpGauge32, SnmpCounter32, SnmpIpAddress
18+
import time, os
19+
20+
from birdagent import BirdAgent
21+
22+
## handle get and getnext requests
23+
def OnSnmpRead(req, ax, axd):
24+
pass
25+
26+
# handle set requests
27+
def OnSnmpWrite(req, ax, axd):
28+
pass
29+
30+
# handle get, getnext and set requests
31+
def OnSnmpRequest(req, ax, axd):
32+
pass
33+
34+
## initialize any ax and axd dependant code here
35+
def OnInit(ax, axd):
36+
pass
37+
38+
## register some variables
39+
## this function is called when a new snmp request has been received and
40+
## if CacheInterval has expired at that time
41+
def OnUpdate(ax, axd, state):
42+
def state2int(state):
43+
# FIXME i need more samples of `birdc show ospf neighbors`
44+
# for more fine-granular results here...
45+
if state.lower().startswith("full"):
46+
return 8
47+
return 1
48+
print('updated bird-ospf state: {0}'.format(time.time()))
49+
## register variables
50+
axd.RegisterVar('ospf', 0)
51+
52+
for nbrid in sorted(state["ospf-neighbors"].keys(), BirdAgent.ipCompare):
53+
nbr = state["ospf-neighbors"][nbrid]
54+
axd.RegisterVar("ospfNbrIpAddr.%s.0"%nbrid, SnmpIpAddress(nbr["rtrip"]))
55+
axd.RegisterVar("ospfNbrRtrId.%s.0"%nbrid, SnmpIpAddress(nbrid))
56+
axd.RegisterVar("ospfNbrPriority.%s.0"%nbrid, nbr["pri"])
57+
axd.RegisterVar("ospfNbrState.%s.0"%nbrid, state2int(nbr["state"]))
58+
return
59+
60+
61+
62+
# main program
63+
if __name__ == '__main__':
64+
print('bird-ospf AgentX starting')
65+
66+
bird = BirdAgent( \
67+
os.environ.get("BIRDCONF") or "/etc/bird/bird.conf", \
68+
os.environ.get("BIRDCPATH") or "/usr/sbin/birdc", \
69+
os.environ.get("NETSTATCMD") or "netstat -na")
70+
71+
instance = "o_main"
72+
73+
callbacks = {
74+
"OnSnmpRead" : OnSnmpRead,
75+
"OnSnmpWrite" : OnSnmpWrite,
76+
"OnSnmpRequest" : OnSnmpRequest,
77+
"OnInit" : OnInit,
78+
"OnUpdate" : lambda ax, axd: OnUpdate(ax,axd,bird.getOSPFState(instance))
79+
}
80+
81+
## initialize agentx module and run main loop
82+
AgentX(
83+
callbacks,
84+
Name = 'bird-ospf',
85+
#RootOID = '1.3.6.1.2.1.14',
86+
MIBFile = os.environ.get("OSPFMIBFILE") or "/usr/share/bird-snmp/OSPF-MIB.txt",
87+
RootOID = 'OSPF-MIB::ospf',
88+
CacheInterval = int(os.environ.get("AGENTCACHEINTERVAL") or "30")
89+
)
90+
print('bird-ospf AgentX terminating')
91+
92+
# vim:ts=4:sw=4:noexpandtab

birdagent.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ def __init__(self, cfgfile, birdcli, netstatcmd="netstat -na"):
6565
}
6666
_re_birdcli_bgp_end = re.compile("^$")
6767

68+
_re_birdcli_ospf_neighbor = re.compile("^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s+([0-9]+)\s+(\S+)\s+(\S+)\s+(\S+)\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)")
69+
6870
_re_netstat = re.compile("^tcp\s+[0-9]+\s+[0-9]+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)\s+ESTABLISHED")
6971

7072
bgp_keys = [
@@ -144,12 +146,38 @@ def combinedConfigLines(filename):
144146
def bgpKeys():
145147
return BirdAgent.bgp_keys
146148

147-
def getState(self):
149+
def getOSPFState(self, ospf_instance):
150+
"""
151+
fetch OSPF-related state from:
152+
* parsing `birdc show ospf neighbors $ospf` output
153+
"""
154+
155+
# "with"-context-manager for Popen not available in python < 3.2
156+
birdc = subprocess.Popen([self.birdcli, "show", "ospf", "neighbors", ospf_instance], \
157+
stdout=subprocess.PIPE)
158+
output = birdc.communicate()[0]
159+
if birdc.returncode != 0:
160+
print("ERROR: bird-CLI (querying ospf neighbors) %s failed: %i"%(self.birdcli, birdc.returncode))
161+
162+
neighbors = {}
163+
for line in output.split("\n"):
164+
match = self._re_birdcli_ospf_neighbor.search(line)
165+
if match:
166+
rtrid, pri, state, deadtime, iface, rtrip = match.groups()
167+
neighbors[rtrid] = {}
168+
neighbors[rtrid]["pri"] = int(pri)
169+
neighbors[rtrid]["state"] = state
170+
neighbors[rtrid]["deadtime"] = deadtime
171+
neighbors[rtrid]["iface"] = iface
172+
neighbors[rtrid]["rtrip"] = rtrip
173+
return {"ospf-neighbors":neighbors}
174+
175+
def getBGPState(self):
148176
"""
149-
fetch state from:
150-
* configuration file
151-
* some `birdc` output
152-
* some `netstat` output
177+
fetch BGP-related state from:
178+
* parsing configuration file
179+
* parsing `birdc show protocols all` output
180+
* parsing `netstat` output
153181
"""
154182

155183
current_time = int(time.time())

data/check_ospf.0.1.pl

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#!/usr/bin/perl
2+
#
3+
# check_ospf - nagios plugin
4+
#
5+
# Copyright (C) 2006 Larry Low
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License
9+
# as published by the Free Software Foundation; either version 2
10+
# of the License, or (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with this program; if not, write to the Free Software
19+
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20+
#
21+
# Report bugs to: [email protected]
22+
#
23+
# Primary MIB reference - OSPF-MIB
24+
#
25+
use strict;
26+
use warnings;
27+
use lib "/usr/lib/nagios/plugins" ;
28+
use utils qw($TIMEOUT %ERRORS &print_revision &support);
29+
use vars qw($PROGNAME);
30+
31+
# Just in case of problems, let's not hang Nagios
32+
$SIG{'ALRM'} = sub {
33+
print ("ERROR: Plugin took too long to complete (alarm)\n");
34+
exit $ERRORS{"UNKNOWN"};
35+
};
36+
alarm($TIMEOUT);
37+
38+
$PROGNAME = "check_ospf.pl";
39+
sub print_help ();
40+
sub print_usage ();
41+
use POSIX qw(floor);
42+
sub seconds_to_string($);
43+
44+
my ($opt_h,$opt_V);
45+
my $community = "public";
46+
my $snmp_version = 2;
47+
my ($hostname,$peer);;
48+
49+
use Getopt::Long;
50+
&Getopt::Long::config('bundling');
51+
GetOptions(
52+
"V" => \$opt_V, "version" => \$opt_V,
53+
"h" => \$opt_h, "help" => \$opt_h,
54+
"C=s" => \$community, "community=s" => \$community,
55+
"H=s" => \$hostname, "hostname=s" => \$hostname,
56+
"p=s" => \$peer, "peer=s" => \$peer,
57+
"v=i" => \$snmp_version,"snmp_version=i" => \$snmp_version
58+
);
59+
# -h & --help print help
60+
if ($opt_h) { print_help(); exit $ERRORS{'OK'}; }
61+
# -V & --version print version
62+
if ($opt_V) { print_revision($PROGNAME,'$Revision: 0.1 $ '); exit $ERRORS{'OK'}; }
63+
# Invalid hostname print usage
64+
if (!utils::is_hostname($hostname)) { print_usage(); exit $ERRORS{'UNKNOWN'}; }
65+
# No BGP peer specified, print usage
66+
if (!defined($peer)) { print_usage(); exit $ERRORS{'UNKNOWN'}; }
67+
68+
# Setup SNMP object
69+
use Net::SNMP qw(INTEGER OCTET_STRING IPADDRESS OBJECT_IDENTIFIER NULL);
70+
my ($snmp, $snmperror);
71+
if ($snmp_version == 2) {
72+
($snmp, $snmperror) = Net::SNMP->session(
73+
-hostname => $hostname,
74+
-version => 'snmpv2c',
75+
-community => $community
76+
);
77+
} elsif ($snmp_version == 3) {
78+
my ($v3_username,$v3_password,$v3_protocol,$v3_priv_passphrase,$v3_priv_protocol) = split(":",$community);
79+
my @auth = ();
80+
if (defined($v3_password)) { push(@auth,($v3_password =~ /^0x/) ? 'authkey' : 'authpassword',$v3_password); }
81+
if (defined($v3_protocol)) { push(@auth,'authprotocol',$v3_protocol); }
82+
if (defined($v3_priv_passphrase)) { push(@auth,($v3_priv_passphrase =~ /^0x/) ? 'privkey' : 'privpassword',$v3_priv_passphrase); }
83+
if (defined($v3_priv_protocol)) { push(@auth,'privprotocol',$v3_priv_protocol); }
84+
85+
($snmp, $snmperror) = Net::SNMP->session(
86+
-hostname => $hostname,
87+
-version => 'snmpv3',
88+
-username => $v3_username,
89+
@auth
90+
);
91+
} else {
92+
($snmp, $snmperror) = Net::SNMP->session(
93+
-hostname => $hostname,
94+
-version => 'snmpv1',
95+
-community => $community
96+
);
97+
}
98+
99+
if (!defined($snmp)) {
100+
print ("UNKNOWN: SNMP error: $snmperror\n");
101+
exit $ERRORS{'UNKNOWN'};
102+
}
103+
104+
my $state = 'UNKNOWN';
105+
my $output = "$peer status retrieval failed.";
106+
# Begin plugin check code
107+
{
108+
my $ospfNbrRtrId = "1.3.6.1.2.1.14.10.1.3";
109+
my $ospfNbrState = "1.3.6.1.2.1.14.10.1.6";
110+
111+
my %ospfNbrStates = (
112+
1 => 'down(1)',
113+
2 => 'attempt(2)',
114+
3 => 'init(3)',
115+
4 => 'twoWay(4)',
116+
5 => 'exchangeStart(5)',
117+
6 => 'exchange(6)',
118+
7 => 'loading(7)',
119+
8 => 'full(8)'
120+
);
121+
122+
my @snmpoids;
123+
push (@snmpoids,"$ospfNbrRtrId.$peer.0");
124+
push (@snmpoids,"$ospfNbrState.$peer.0");
125+
my $result = $snmp->get_request(
126+
-varbindlist => \@snmpoids
127+
);
128+
if (!defined($result)) {
129+
my $answer = $snmp->error;
130+
$snmp->close;
131+
print ("UNKNOWN: SNMP error: $answer\n");
132+
exit $ERRORS{'UNKNOWN'};
133+
}
134+
135+
$ospfNbrRtrId = $result->{"$ospfNbrRtrId.$peer.0"};
136+
$ospfNbrState = $result->{"$ospfNbrState.$peer.0"};
137+
if ($ospfNbrRtrId eq "noSuchInstance") {
138+
$output = "$peer is not in neighbor table.";
139+
$state = 'CRITICAL';
140+
} else {
141+
$output = "$peer (Router ID $ospfNbrRtrId) state is ".$ospfNbrStates{$ospfNbrState};
142+
143+
if (($ospfNbrState == 4) || ($ospfNbrState == 8)) {
144+
$state = 'OK';
145+
} else {
146+
$state = 'CRITICAL';
147+
}
148+
}
149+
}
150+
print "$state - $output\n";
151+
exit $ERRORS{$state};
152+
153+
sub print_help() {
154+
print_revision($PROGNAME,'$Revision: 0.1 $ ');
155+
print "Copyright (c) 2006 Larry Low\n";
156+
print "This program is licensed under the terms of the\n";
157+
print "GNU General Public License\n(check source code for details)\n";
158+
print "\n";
159+
printf "Check OSPF peer status via SNMP.\n";
160+
print "\n";
161+
print_usage();
162+
print "\n";
163+
print " -H (--hostname) Hostname to query - (required)\n";
164+
print " -C (--community) SNMP read community or v3 auth (defaults to public)\n";
165+
print " (v3 specified as username:authpassword:... )\n";
166+
print " username = SNMPv3 security name\n";
167+
print " authpassword = SNMPv3 authentication pass phrase (or hexidecimal key)\n";
168+
print " authprotocol = SNMPv3 authentication protocol (md5 (default) or sha)\n";
169+
print " privpassword = SNMPv3 privacy pass phrase (or hexidecmal key)\n";
170+
print " privprotocol = SNMPv3 privacy protocol (des (default) or aes)\n";
171+
print " -v (--snmp_version) 1 for SNMP v1\n";
172+
print " 2 for SNMP v2c (default)\n";
173+
print " 3 for SNMP v3\n";
174+
print " -p {--peer} IP of peer\n";
175+
print " -V (--version) Plugin version\n";
176+
print " -h (--help) usage help\n";
177+
print "\n";
178+
support();
179+
}
180+
181+
sub print_usage() {
182+
print "Usage: \n";
183+
print " $PROGNAME -H <HOSTNAME> [-C <community>] -p <peer>\n";
184+
print " $PROGNAME [-h | --help]\n";
185+
print " $PROGNAME [-V | --version]\n";
186+
}
187+

0 commit comments

Comments
 (0)