Created
August 2, 2011 22:29
-
-
Save DamianZaremba/1121394 to your computer and use it in GitHub Desktop.
BGPServer written in python - doesn't actually do anything apart from process inotify events right now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import os | |
import logging | |
import sys | |
import signal | |
import socket | |
import pyinotify | |
''' | |
BGPServer - this does the core work of threading out the other classes and | |
sitting around for inotify events. | |
''' | |
class BGPServer: | |
''' | |
Initializes the class and sets up some basic stuff | |
''' | |
def __init__(self, asn, asi, peers_file='peers', routes_file='routes', | |
logging_level=logging.WARNING): | |
self.asn = asn | |
self.asi = asi | |
self.peers_file = None | |
self.routes_file = None | |
logging.basicConfig() | |
self.logger = logging.getLogger('BGPServer') | |
self.logger.setLevel(logging_level) | |
if peers_file[0] != '/': | |
self.logger.debug('Turning relative peers_file path into absolute') | |
self.peers_file = os.path.realpath(peers_file) | |
if routes_file[0] != '/': | |
self.logger.debug('Turning relative routes_file path into absolute') | |
self.routes_file = os.path.realpath(routes_file) | |
if not self.peers_file: | |
self.logger.debug('Seeing peers_file to relative path') | |
self.peers_file = peers_file | |
if not self.routes_file: | |
self.logger.debug('Seeing routes_file to relative path') | |
self.routes_file = routes_file | |
if not self.asn or not self.asi: | |
self.logger.critical('No ASN or ASI specified') | |
self.logger.debug('Setting up watch manager') | |
self.wm = pyinotify.WatchManager() | |
self.notifier = pyinotify.Notifier(self.wm, FileEvent(BGPServer=self)); | |
self.peers = {} | |
self.routes = {} | |
self.running = True | |
self.setup_signals() | |
''' | |
Sets up some signal handlers to ensure we exit as cleanly as possible. | |
''' | |
def setup_signals(self): | |
self.logger.debug('Registering signals') | |
signal.signal(signal.SIGTERM, self.shutdown) | |
signal.signal(signal.SIGINT, self.shutdown) | |
''' | |
Shuts down the server as cleanly as possible. | |
''' | |
def shutdown(self, *args, **kargs): | |
self.logger.debug('Starting shutdown') | |
self.running = False | |
try: | |
self.wm.rm_watch(self.routes_file) | |
self.wm.rm_watch(self.peers_file) | |
except: | |
self.logger.warning("Could not un-register watchers") | |
pass | |
try: | |
self.notifier.stop() | |
except: | |
self.logger.warning("Could not stop notifier cleanly") | |
pass | |
''' | |
Reloads the peers from file. | |
''' | |
def reload_peers(self): | |
self.logger.info("Reload peers running") | |
if os.path.isfile(self.peers_file): | |
fh = open(self.peers_file, 'r') | |
for line in fh.readlines(): | |
line = line.strip() | |
if len(line) == 0: continue | |
if line[0] == "#": continue | |
parts = line.split() | |
if len(parts) == 2: | |
(address, password) = parts | |
else: | |
address = parts[0] | |
password = none | |
aparts = address.split('/') | |
address = aparts[0] | |
port = 179 | |
if len(aparts) == 2: | |
try: | |
port = int(aparts[1]) | |
except ValueError: | |
self.logger.critical("Invalid port specified for %s" % | |
address) | |
next | |
if self.valid_ip(address): | |
peer_id = "%s-%s" % (address, port) | |
self.logger.info("Adding %s to our peers" % peer_id) | |
self.peers[peer_id] = Peer(address, password) | |
else: | |
self.logger.warning("Skipping %s as the address looks dodgy." % peer_id) | |
fh.close() | |
''' | |
Checks if an ip looks valid. | |
''' | |
def valid_ip(self, ip): | |
try: | |
socket.inet_aton(ip) | |
except socket.error: | |
try: | |
socket.inet_pton(socket.AF_INET6, ip) | |
except socket.error: | |
return False | |
else: | |
return True | |
else: | |
return True | |
''' | |
Reloads the routes from file. | |
''' | |
def reload_routes(self): | |
self.logger.info("Reload routes running") | |
pass | |
''' | |
Drops the server into a processing loop until it receives a signal to quit. | |
''' | |
def run(self): | |
self.logger.debug('Starting run') | |
self.logger.debug('Triggering reloads') | |
self.reload_peers() | |
self.reload_routes() | |
self.logger.info('Adding watcher for routes file') | |
self.wm.add_watch(self.routes_file, pyinotify.IN_CLOSE_WRITE) | |
self.logger.info('Adding watcher for peers file') | |
self.wm.add_watch(self.peers_file, pyinotify.IN_CLOSE_WRITE) | |
self.logger.debug('Dropping into while loop') | |
while self.running == True: | |
self.logger.debug('Calling process_events()') | |
self.notifier.process_events() | |
self.logger.debug('Checking if we have any events') | |
if self.notifier.check_events(): | |
self.logger.debug('Reading events') | |
self.notifier.read_events() | |
self.logger.debug('Processing events') | |
self.notifier.process_events() | |
''' | |
Peer - this class represents a single peer | |
''' | |
class Peer: | |
def __init__(self, address, password=None): | |
self.routes = {} | |
''' | |
FileEvent - this class gets called when one of the files changes | |
''' | |
class FileEvent(pyinotify.ProcessEvent): | |
def my_init(self, **kargs): | |
self.BGPServer = kargs.get('BGPServer') | |
def process_IN_CLOSE_WRITE(self, event): | |
if event.pathname == self.BGPServer.peers_file: | |
self.BGPServer.logger.debug("Triggering peers reload event") | |
self.BGPServer.reload_peers() | |
elif event.pathname == self.BGPServer.routes_file: | |
self.BGPServer.logger.debug("Triggering routes reload event") | |
self.BGPServer.reload_routes() | |
else: | |
self.BGPServer.logger.warn("Unknown path '%s'" % event.pathname) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument( | |
'-p', '--peers', dest='peers_file', action='store', default='peers', | |
type=str, help='Path to the peers file', | |
) | |
parser.add_argument( | |
'-r', '--routes', dest='routes_file', action='store', default='routes', | |
type=str, help='Path to the routes file', | |
) | |
parser.add_argument( | |
'-d', '--debug', dest='debug_level', action='store', default='warn', | |
type=str, help='Debug level (critical, warning, info, debug)', | |
) | |
parser.add_argument( | |
'-asn', '--as-number', dest='asn', action='store', type=str, | |
help='AS number', | |
) | |
parser.add_argument( | |
'-asi', '--as-identifier', dest='asi', action='store', type=str, | |
help='AS identifier (your IP address)', | |
) | |
args = parser.parse_args() | |
if args.debug_level.lower() == 'debug': | |
log_level = logging.DEBUG | |
elif args.debug_level.lower() in ('info', 'information'): | |
log_level = logging.INFO | |
elif args.debug_level.lower() in ('warn', 'warning'): | |
log_level = logging.WARNING | |
elif args.debug_level.lower() == 'critical': | |
log_level = logging.CRITICAL | |
else: | |
print("Invalid debug_level supplied") | |
sys.exit(2) | |
if not args.asi: | |
print("No ASI specified") | |
sys.exit(2) | |
if not args.asn: | |
print("No ASN specified") | |
sys.exit(2) | |
server = BGPServer( | |
peers_file = args.peers_file, | |
routes_file = args.routes_file, | |
logging_level = log_level, | |
asn = args.asn, | |
asi = args.asi | |
) | |
server.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment