frr/tests/topotests/lib/topolog.py
Daniel Baumann 3124f89aed
Adding upstream version 10.1.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-05 10:03:58 +01:00

161 lines
4.4 KiB
Python

# SPDX-License-Identifier: ISC
#
# topolog.py
# Library of helper functions for NetDEF Topology Tests
#
# Copyright (c) 2017 by
# Network Device Education Foundation, Inc. ("NetDEF")
#
"""
Logging utilities for topology tests.
This file defines our logging abstraction.
"""
import logging
import os
try:
from xdist import is_xdist_controller
except ImportError:
def is_xdist_controller():
return False
# Helper dictionary to convert Topogen logging levels to Python's logging.
DEBUG_TOPO2LOGGING = {
"debug": logging.DEBUG,
"info": logging.INFO,
"output": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR,
"critical": logging.CRITICAL,
}
FORMAT = "%(asctime)s %(levelname)s: %(name)s: %(message)s"
handlers = {}
logger = logging.getLogger("topo")
# Remove this and use munet version when we move to pytest_asyncio
def get_test_logdir(nodeid=None, module=False):
"""Get log directory relative pathname."""
xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "")
mode = os.getenv("PYTEST_XDIST_MODE", "no")
# nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running
# may be missing "::testname" if module is True
if not nodeid:
nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
cur_test = nodeid.replace("[", "_").replace("]", "_")
if module:
idx = cur_test.rfind("::")
path = cur_test if idx == -1 else cur_test[:idx]
testname = ""
else:
path, testname = cur_test.split("::")
testname = testname.replace("/", ".")
path = path[:-3].replace("/", ".")
# We use different logdir paths based on how xdist is running.
if mode == "each":
if module:
return os.path.join(path, "worker-logs", xdist_worker)
return os.path.join(path, testname, xdist_worker)
assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}"
return path if module else os.path.join(path, testname)
def set_handler(lg, target=None):
if target is None:
h = logging.NullHandler()
else:
if isinstance(target, str):
h = logging.FileHandler(filename=target, mode="w")
else:
h = logging.StreamHandler(stream=target)
h.setFormatter(logging.Formatter(fmt=FORMAT))
# Don't filter anything at the handler level
h.setLevel(logging.DEBUG)
lg.addHandler(h)
return h
def set_log_level(lg, level):
"Set the logging level."
# Messages sent to this logger only are created if this level or above.
log_level = DEBUG_TOPO2LOGGING.get(level, level)
lg.setLevel(log_level)
def reset_logger(lg):
while lg.handlers:
x = lg.handlers.pop()
x.close()
lg.removeHandler(x)
def get_logger(name, log_level=None, target=None, reset=True):
lg = logging.getLogger(name)
if reset:
reset_logger(lg)
if log_level is not None:
set_log_level(lg, log_level)
if target is not None:
set_handler(lg, target)
return lg
def logstart(nodeid, logpath):
"""Called from pytest before module setup."""
worker = os.getenv("PYTEST_TOPOTEST_WORKER", "")
wstr = f" on worker {worker}" if worker else ""
handler_id = nodeid + worker
logpath = logpath.absolute()
logging.debug("logstart: adding logging for %s%s at %s", nodeid, wstr, logpath)
root_logger = logging.getLogger()
handler = logging.FileHandler(logpath, mode="w")
handler.setFormatter(logging.Formatter(FORMAT))
root_logger.addHandler(handler)
handlers[handler_id] = handler
logging.debug("logstart: added logging for %s%s at %s", nodeid, wstr, logpath)
return handler
def logfinish(nodeid, logpath):
"""Called from pytest after module teardown."""
worker = os.getenv("PYTEST_TOPOTEST_WORKER", "")
wstr = f" on worker {worker}" if worker else ""
root_logger = logging.getLogger()
handler_id = nodeid + worker
if handler_id not in handlers:
logging.critical("can't find log handler to remove")
else:
logging.debug(
"logfinish: removing logging for %s%s at %s", nodeid, wstr, logpath
)
h = handlers[handler_id]
root_logger.removeHandler(h)
h.flush()
h.close()
del handlers[handler_id]
logging.debug(
"logfinish: removed logging for %s%s at %s", nodeid, wstr, logpath
)
console_handler = set_handler(logger, None)
set_log_level(logger, "debug")