Skip to content

Commit 7f6fa2a

Browse files
authored
Merge pull request FRRouting#20359 from LabNConsulting/chopps/20356-backport
backport of 20536: Fix mgmtd abort (core) during exit with in-progress config change
2 parents f4d3e5f + d12e84b commit 7f6fa2a

File tree

3 files changed

+100
-7
lines changed

3 files changed

+100
-7
lines changed

lib/vty.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4154,11 +4154,6 @@ void vty_terminate(void)
41544154
{
41554155
struct vty *vty;
41564156

4157-
if (mgmt_fe_client) {
4158-
mgmt_fe_client_destroy(mgmt_fe_client);
4159-
mgmt_fe_client = NULL;
4160-
}
4161-
41624157
memset(vty_cwd, 0x00, sizeof(vty_cwd));
41634158

41644159
vty_reset();
@@ -4178,5 +4173,10 @@ void vty_terminate(void)
41784173
vtys_fini(vtysh_sessions);
41794174
vtys_init(vtysh_sessions);
41804175

4176+
if (mgmt_fe_client) {
4177+
mgmt_fe_client_destroy(mgmt_fe_client);
4178+
mgmt_fe_client = NULL;
4179+
}
4180+
41814181
vty_serv_stop();
41824182
}

tests/topotests/conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from lib.common_config import generate_support_bundle
2121
from lib.topogen import diagnose_env, get_topogen
2222
from lib.topolog import get_test_logdir, logger
23-
from lib.topotest import json_cmp_result, gdb_core
23+
from lib.topotest import gdb_core, json_cmp_result
2424
from munet import cli
2525
from munet.base import BaseMunet, Commander, proc_error
2626
from munet.cleanup import cleanup_current, cleanup_previous
@@ -357,7 +357,9 @@ def check_for_core_dumps():
357357
tgen.existing_core_files = set()
358358
existing = tgen.existing_core_files
359359

360-
cores = glob.glob(os.path.join(tgen.logdir, "*/*.dmp"))
360+
cores = glob.glob(f"{tgen.logdir}/**/*.dmp", recursive=True)
361+
if cores:
362+
logging.info("Found core dumps: %s", cores)
361363
latest = {x for x in cores if x not in existing}
362364
if latest:
363365
existing |= latest
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# SPDX-License-Identifier: ISC
2+
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
3+
#
4+
# December 31 2025, Christian Hopps <[email protected]>
5+
#
6+
# Copyright (c) 2025, LabN Consulting, L.L.C.
7+
#
8+
"""
9+
Test static route functionality
10+
"""
11+
import datetime
12+
import logging
13+
import os
14+
import re
15+
import time
16+
17+
import pytest
18+
from lib.common_config import step
19+
from lib.topogen import Topogen
20+
from munet.base import Timeout
21+
from munet.watchlog import WatchLog
22+
23+
pytestmark = [pytest.mark.staticd]
24+
25+
26+
@pytest.fixture(scope="function")
27+
def tgen(request):
28+
"Setup/Teardown the environment and provide tgen argument to tests"
29+
30+
topodef = {
31+
"s1": ("r1",),
32+
}
33+
34+
tgen = Topogen(topodef, request.module.__name__)
35+
tgen.start_topology()
36+
37+
for router in tgen.routers().values():
38+
router.load_frr_config("frr.conf")
39+
40+
tgen.start_router()
41+
yield tgen
42+
tgen.stop_topology()
43+
44+
45+
def scan_for_match(wl, regex, timeout=30):
46+
regex = re.compile(regex)
47+
to = Timeout(timeout)
48+
logging.debug("scanning %s for %s", wl.path, regex)
49+
while to:
50+
content = wl.snapshot_refresh()
51+
if m := regex.search(content):
52+
logging.debug("found '%s' in %s", m.group(0), wl.path)
53+
return m
54+
time.sleep(0.5)
55+
raise TimeoutError(f"timeout waiting for {regex} in {wl.path}")
56+
57+
def test_quit_during_config(tgen):
58+
if tgen.routers_have_failure():
59+
pytest.skip(tgen.errors)
60+
61+
r1g = tgen.gears["r1"]
62+
r1 = r1g.net
63+
wl = WatchLog(r1.rundir / "mgmtd.log")
64+
65+
# Get a config file with `count` static IPv4 routes
66+
count = 10 * 1024
67+
config_file = os.path.join(r1.logdir, "bigconfig.conf")
68+
with open(config_file, "w") as cfile:
69+
for i in range((1 << 24), (1 << 24) + count * 4, 4):
70+
dq0 = (i >> 24) & 0xff
71+
dq1 = (i >> 16) & 0xff
72+
dq2 = (i >> 8) & 0xff
73+
dq3 = i & 0xff
74+
cfile.write(f"ip route {dq0}.{dq1}.{dq2}.{dq3}/30 101.0.0.2\n")
75+
76+
step(f"add {count} static routes", reset=True)
77+
load_command = 'vtysh -f "{}"'.format(config_file)
78+
79+
wl.snapshot()
80+
config_proc = r1.popen(load_command)
81+
try:
82+
83+
# Wait for part of the configuration to start being applied
84+
scan_for_match(wl, re.escape(r"ip route 1.0.1.0/30 101.0.0.2"))
85+
logging.info("partial config applied, waiting for completion")
86+
87+
# Now stop the router to see if we get any core files
88+
r1.stopRouter(False)
89+
finally:
90+
if config_proc:
91+
config_proc.kill()

0 commit comments

Comments
 (0)