Skip to content

Commit

Permalink
tests: fix bmp test parallelization
Browse files Browse the repository at this point in the history
Multiple BMP tests can run in parallel but, when one instance ends,
it kills the BMP server process of all BMP tests.

Save the PID of a BMP server and only kill it at the end.

Link: FRRouting#17465
Fixes: 875511c ("topotests: add basic bmp collector")
Signed-off-by: Louis Scalbert <[email protected]>
  • Loading branch information
louis-6wind authored and pguibert6WIND committed Nov 25, 2024
1 parent 64bb46d commit 5eb4c2b
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 4 deletions.
78 changes: 77 additions & 1 deletion tests/topotests/lib/bmp_collector/bmpserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import argparse

# XXX: something more reliable should be used "Twisted" a great choice.
import os
import signal
import socket
import sys
Expand All @@ -20,11 +21,11 @@
# 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):
Expand All @@ -40,6 +41,74 @@ def timestamp_print(message, file=sys.stderr):
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

Expand All @@ -51,8 +120,13 @@ def main():
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:
Expand Down Expand Up @@ -97,11 +171,13 @@ def main():
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)
7 changes: 4 additions & 3 deletions tests/topotests/lib/topogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1293,18 +1293,19 @@ def start(self, log_file=None):
log_err = os.path.join(log_dir, "bmpserver.log")

log_arg = "-l {}".format(log_file) if log_file else ""
self.pid_file = os.path.join(log_dir, "bmpserver.pid")

with open(log_err, "w") as err:
self.run(
"{}/bmp_collector/bmpserver.py -a {} -p {} {}&".format(
CWD, self.ip, self.port, log_arg
"{}/bmp_collector/bmpserver.py -a {} -p {} -r {} {}&".format(
CWD, self.ip, self.port, self.pid_file, log_arg
),
stdout=None,
stderr=err,
)

def stop(self):
self.run("pkill -f bmpserver.py")
self.run(f"kill $(cat {self.pid_file}")
return ""


Expand Down

0 comments on commit 5eb4c2b

Please sign in to comment.