123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- #!/usr/bin/python
- #
- # Check state of OSPF sessions in Bird Internet Routing Daemon
- #
- # Maximilian Wilhelm <max@rfc2324.org>
- # -- Wed 26 Apr 2017 07:26:48 PM CEST
- #
- import argparse
- import os.path
- import re
- import subprocess
- import sys
- def read_interfaces_from_file (file_path, missing_ok):
- interfaces = []
- # If we shouldn't care, we won't care if it's not there.
- if not os.path.isfile (file_path) and missing_ok:
- return interfaces
- try:
- with open (args.interfaces_down_ok_file, 'r') as ido_fh:
- for iface in ido_fh.readlines ():
- if not iface.startswith ('#'):
- interfaces.append (iface.strip ())
- except IOError as (errno, strerror):
- print "Failed to read interfaces_down_ok from '%s': %s" % (args.interfaces_down_ok_file, strerror)
- sys.exit (1)
- return interfaces
- parser = argparse.ArgumentParser (description = 'check bird OSPF sessions')
- parser.add_argument ('--proto', '-p', help = 'IP protocol version to check', default = '4', choices = ['4', '6'])
- parser.add_argument ('--protocol', '-P', help = 'Bird OSPF protocol instance name to check', default = "")
- parser.add_argument ('--interfaces_down_ok', metavar = "LIST", help = "List of interfaces which are OK to have no OSPF neighbor. Provide a space separated list.")
- parser.add_argument ('--interfaces_down_ok_file', metavar = "FILENAME", help = "List of interfaces which are OK to have no OSPF neighbor. Provide one interfaces per line.")
- parser.add_argument ('--ignore_missing_file', help = "Ignore a possible non-existent file given as --interfaces_down_ok_file", action = 'store_true')
- args = parser.parse_args ()
- # Are some interfaces ok being down?
- interfaces_down_ok = []
- if args.interfaces_down_ok:
- interfaces_down_ok = args.interfaces_down_ok.split ()
- if args.interfaces_down_ok_file:
- interfaces_down_ok.extend (read_interfaces_from_file (args.interfaces_down_ok_file, args.ignore_missing_file))
- ################################################################################
- # Query OSPF protocl information from bird #
- ################################################################################
- cmds = {
- '4' : '/usr/sbin/birdc',
- '6' : '/usr/sbin/birdc6',
- }
- cmd_interfaces = [ "/usr/bin/sudo", cmds[args.proto], "show ospf interface %s" % args.protocol ]
- cmd_neighbors = [ "/usr/bin/sudo", cmds[args.proto], "show ospf neighbors %s" % args.protocol ]
- try:
- interfaces_fh = subprocess.Popen (cmd_interfaces, bufsize = 4194304, stdout = subprocess.PIPE)
- if interfaces_fh.returncode:
- print "Failed to get OSPF interfaces from bird: %s" % str (" ".join ([line.strip () for line in interfaces_fh.stdout.readlines ()]))
- sys.exit (1)
- neighbors_fh = subprocess.Popen (cmd_neighbors, bufsize = 4194304, stdout = subprocess.PIPE)
- if neighbors_fh.returncode:
- print "Failed to get OSPF neighbors from bird: %s" % str (" ".join ([line.strip () for line in neighbors_fh.stdout.readlines ()]))
- sys.exit (1)
- # cmd exited with non-zero code
- except subprocess.CalledProcessError as c:
- print "Failed to get OSPF information from bird: %s" % c.output
- sys.exit (1)
- # This should not have happend.
- except Exception as e:
- print "Unknown error while getting OSPF information from bird: %s" % str (e)
- sys.exit (3)
- ################################################################################
- # Parse interfaces and neighbors #
- ################################################################################
- interfaces = {}
- interface_re = re.compile (r'^Interface (.+) \(')
- state_re = re.compile (r'(Type|State): (.+)$')
- stub_re = re.compile (r'\(stub\)')
- # Parse interfaces
- interface = None
- for line in interfaces_fh.stdout.readlines ():
- line = line.strip ()
- # Create empty interface hash
- match = interface_re.search (line)
- if match:
- interface = match.group (1)
- interfaces[interface] = {}
- continue
- # Store Type and State attributes
- match = state_re.search (line)
- if match:
- interfaces[interface][match.group (1)] = match.group (2)
- # Delete any stub interfaces from our list
- for iface in interfaces.keys ():
- if stub_re.search (interfaces[iface]['State']):
- del interfaces[iface]
- ok = []
- broken = []
- down = []
- neighbor_re = re.compile (r'^([0-9a-fA-F.:]+)\s+(\d+)\s+([\w/-]+)\s+([0-9:]+)\s+([\w.-]+)\s+([\w.:]+)')
- # Read and check all neighbor states
- for line in neighbors_fh.stdout.readlines ():
- line = line.strip ()
- match = neighbor_re.search (line)
- if match:
- peer = match.group (1)
- state = match.group (3)
- ifname = match.group (5)
- interface = interfaces[ifname]
- # Mark interfaces as "up" in bird
- interface['up'] = 1
- # State FULL is awesome.
- if 'Full' in state:
- ok.append ("%s/%s" % (ifname, peer))
- # In broadcast areas there are only two FULL sessions (to the DR and BDR)
- # all other sessions will be 2-Way/Other which is perfectly fine.
- elif state == "2-Way/Other" and interface['Type'] == "broadcast":
- ok.append ("%s/%s" % (ifname, peer))
- # Everything else is considered broken.
- # Likely some ExStart/* etc. pointing to possible MTU troubles.
- else:
- broken.append ("%s/%s:%s" % (ifname, peer, state))
- # Check for any interfaces which should have (at least) an OSPF peer
- # but don't appear in the neighbors list
- for iface in interfaces.keys ():
- if iface in interfaces_down_ok:
- ok.append ("%s (Down/OK)" % iface)
- elif "up" not in interfaces[iface]:
- down.append (iface)
- ################################################################################
- # Prepare output #
- ################################################################################
- ret_code = 0
- # Any down interfaces?
- if len (down) > 0:
- ret_code = 2
- print "DOWN: %s" % ", ".join (sorted (down))
- # Any broken sessions?
- if len (broken) > 0:
- # Issue a warning when there are issues..
- if ret_code < 2:
- ret_code = 1
- print "BROKEN: %s" % ", ".join (sorted (broken))
- # And the good ones
- if len (ok) > 0:
- print "OK: %s" % ", ".join (sorted (ok))
- sys.exit (ret_code)
|