summaryrefslogtreecommitdiffstats
path: root/tests/topotests/lib/bmp_collector/bmpserver.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/topotests/lib/bmp_collector/bmpserver.py')
-rwxr-xr-xtests/topotests/lib/bmp_collector/bmpserver.py185
1 files changed, 185 insertions, 0 deletions
diff --git a/tests/topotests/lib/bmp_collector/bmpserver.py b/tests/topotests/lib/bmp_collector/bmpserver.py
new file mode 100755
index 000000000..c42c38756
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bmpserver.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import argparse
+import errno
+import logging
+
+# XXX: something more reliable should be used "Twisted" a great choice.
+import os
+import signal
+import socket
+import sys
+
+from datetime import datetime
+
+from bmp import BMPMsg
+
+BGP_MAX_SIZE = 4096
+
+# Global variable to track shutdown signal
+shutdown = False
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-a", "--address", type=str, default="0.0.0.0")
+parser.add_argument("-p", "--port", type=int, default=1789)
+parser.add_argument("-l", "--logfile", type=str, default="/var/log/bmp.log")
+parser.add_argument("-r", "--pidfile", type=str, default="/var/run/bmp.pid")
+
+
+def handle_signal(signum, frame):
+ global shutdown
+ timestamp_print(f"Received signal {signum}, shutting down.")
+ shutdown = True
+
+
+def timestamp_print(message, file=sys.stderr):
+ """Helper function to timestamp_print messages with timestamps."""
+
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ print(f"[{current_time}] {message}", file=file)
+
+
+def check_pid(pid):
+ if pid < 0: # user input error
+ return False
+ if pid == 0: # all processes
+ return False
+ try:
+ os.kill(pid, 0)
+ return True
+ except OSError as err:
+ if err.errno == errno.EPERM: # a process we were denied access to
+ return True
+ if err.errno == errno.ESRCH: # No such process
+ return False
+ # should never happen
+ return False
+
+
+def savepid():
+ ownid = os.getpid()
+
+ flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
+ mode = ((os.R_OK | os.W_OK) << 6) | (os.R_OK << 3) | os.R_OK
+
+ try:
+ fd = os.open(pid_file, flags, mode)
+ except OSError:
+ try:
+ pid = open(pid_file, "r").readline().strip()
+ if check_pid(int(pid)):
+ timestamp_print(
+ "PID file already exists and program still running %s\n" % pid_file
+ )
+ return False
+ else:
+ # If pid is not running, reopen file without O_EXCL
+ fd = os.open(pid_file, flags ^ os.O_EXCL, mode)
+ except (OSError, IOError, ValueError):
+ timestamp_print(
+ "issue accessing PID file %s (most likely permission or ownership)\n"
+ % pid_file
+ )
+ return False
+
+ try:
+ f = os.fdopen(fd, "w")
+ line = "%d\n" % ownid
+ f.write(line)
+ f.close()
+ saved_pid = True
+ except IOError:
+ timestamp_print("Can not create PID file %s\n" % pid_file)
+ return False
+ timestamp_print("Created PID file %s with value %d\n" % (pid_file, ownid))
+ return True
+
+
+def removepid():
+ try:
+ os.remove(pid_file)
+ except OSError as exc:
+ if exc.errno == errno.ENOENT:
+ pass
+ else:
+ timestamp_print("Can not remove PID file %s\n" % pid_file)
+ return
+ timestamp_print("Removed PID file %s\n" % pid_file)
+
+
+def main():
+ global shutdown
+
+ # Set up signal handling for SIGTERM and SIGINT
+ signal.signal(signal.SIGTERM, handle_signal)
+ signal.signal(signal.SIGINT, handle_signal)
+
+ args = parser.parse_args()
+ ADDRESS, PORT = args.address, args.port
+ LOG_FILE = args.logfile
+
+ global pid_file
+ pid_file = args.pidfile
+
+ timestamp_print(f"Starting bmpserver on {args.address}:{args.port}")
+
+ savepid()
+
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ try:
+ s.bind((ADDRESS, PORT))
+ s.listen()
+ timestamp_print(f"Listening on TCP {args.address}:{args.port}")
+
+ connection, client_address = s.accept()
+ timestamp_print(f"TCP session opened from {client_address}")
+
+ try:
+ while not shutdown: # Check for shutdown signal
+ data = connection.recv(BGP_MAX_SIZE)
+ if shutdown:
+ break
+
+ if not data:
+ # connection closed
+ break
+
+ timestamp_print(
+ f"Data received from {client_address}: length {len(data)}"
+ )
+
+ while len(data) > BMPMsg.MIN_LEN:
+ data = BMPMsg.dissect(data, log_file=LOG_FILE)
+
+ timestamp_print(f"Finished dissecting data from {client_address}")
+
+ except Exception as e:
+ timestamp_print(f"{e}")
+ pass
+ except KeyboardInterrupt:
+ timestamp_print(f"Got Keyboard Interrupt.")
+ pass
+ finally:
+ timestamp_print(f"TCP session closed with {client_address}")
+ connection.close()
+ except socket.error as sock_err:
+ timestamp_print(f"Socket error: {e}")
+ except Exception as e:
+ timestamp_print(f"{e}")
+ finally:
+ timestamp_print(f"Server shutting down on {ADDRESS}:{PORT}")
+ removepid()
+
+
+if __name__ == "__main__":
+ try:
+ sys.exit(main())
+ except KeyboardInterrupt:
+ logging.info("BMP server was interrupted and is shutting down.")
+ removepid()
+ sys.exit(0)