Description
When running show CLI commands inside a cSONiC neighbor (docker-sonic-vs container used as a neighbor in the sonic-mgmt KVM testbed), the CLI leaks shell errors to stderr and BGP show commands fail. Root cause: two code paths in sonic-utilities assume the routing stack runs in a separate, nested bgp Docker container (the real-SONiC layout). In docker-sonic-vs, FRR runs in the same container and there is no nested Docker and no rvtysh wrapper, so these calls fail.
This is the sonic-utilities counterpart to the cSONiC neighbor environment gaps tracked in sonic-mgmt (e.g. #22647, #22648).
Steps to reproduce the issue:
- Bring up a KVM testbed with cSONiC neighbors (
./testbed-cli.sh ... -k csonic add-topo ...).
- Exec into any neighbor container, e.g.
docker exec -it csonic_<vmset>_VM0100 bash.
- Run any
show command, e.g.:
show ip --help
show ip route
show ip bgp summary
Describe the results you received:
Every show invocation (even --help) leaks a stderr line, because get_routing_stack() is evaluated at module import time:
root@<container>:/# show ip --help
sudo: docker: command not found
Usage: show ip [OPTIONS] COMMAND [ARGS]...
...
Route/BGP paths leak a second error from the rvtysh wrapper:
root@<container>:/# show ip route
sudo: rvtysh: command not found
In the container, neither binary exists:
# command -v docker -> (nothing) NO_DOCKER
# command -v rvtysh -> (nothing) NO rvtysh in PATH
# command -v vtysh -> /usr/bin/vtysh (only the plain vtysh exists)
Describe the results you expected:
show CLI commands should run cleanly in a docker-sonic-vs neighbor (single-container layout) without leaking sudo: docker: command not found / sudo: rvtysh: command not found, and should fall back to the plain vtysh binary when no nested bgp container / rvtysh wrapper is present.
Root cause / code references (master)
1. get_routing_stack() shells out to docker ps at import time — show/main.py:
-
L92-106 get_routing_stack() runs:
command = "sudo docker ps --format '{{.Image}}\t{{.Names}}' | awk '$2 ~ /^bgp([0-9]+)?$/' | cut -d'-' -f3 | cut -d':' -f1 | head -n 1"
and is invoked at module scope: routing_stack = get_routing_stack() (L106).
Because it's a module-level global, it executes on every show command (including show ... --help). In docker-sonic-vs there is no docker binary, so it emits sudo: docker: command not found to stderr. The try/except does catch the failure and defaults result = 'frr', so the value is fine — but the stderr leak is unconditional. The function's own comment already flags this as undesirable ("To be enhanced ... prevent the continuous execution of this bash oneliner ... collected from a global location (configdb?)").
2. run_bgp_show_command() uses the rvtysh wrapper — utilities_common/bgp_util.py + utilities_common/constants.py:
constants.py L17-18: VTYSH_COMMAND = 'vtysh', RVTYSH_COMMAND = 'rvtysh'
bgp_util.py L264-265:
def run_bgp_show_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, exit_on_fail=True):
output = run_bgp_command(vtysh_cmd, bgp_namespace, constants.RVTYSH_COMMAND, exit_on_fail)
run_bgp_command then builds ['sudo', 'rvtysh', ...] (L251). On real SONiC, rvtysh is the routing-stack-aware wrapper that execs into the bgp container; in docker-sonic-vs it does not exist, so the command fails with sudo: rvtysh: command not found.
Suggested fix
- In
get_routing_stack(), short-circuit when nested Docker is absent (e.g. if shutil.which("docker") is None: return 'frr') and/or suppress stderr, so the bash one-liner doesn't leak on single-container images.
- For the
rvtysh path, fall back to plain vtysh when rvtysh is not present in PATH (single-container / docker-sonic-vs case), or select the wrapper based on detected routing stack/container layout.
Output of show version (or show platform summary):
cSONiC neighbor: docker-sonic-vs image (sonic-mgmt KVM testbed neighbor).
sonic-utilities-data 1.0-1 (Debian python3.11 site-packages). Code paths verified identical on sonic-net/sonic-utilities master (show/main.py L92-106; constants.py L17-18; bgp_util.py L264-265).
Description
When running
showCLI commands inside a cSONiC neighbor (docker-sonic-vscontainer used as a neighbor in the sonic-mgmt KVM testbed), the CLI leaks shell errors to stderr and BGPshowcommands fail. Root cause: two code paths insonic-utilitiesassume the routing stack runs in a separate, nestedbgpDocker container (the real-SONiC layout). Indocker-sonic-vs, FRR runs in the same container and there is no nested Docker and norvtyshwrapper, so these calls fail.This is the
sonic-utilitiescounterpart to the cSONiC neighbor environment gaps tracked in sonic-mgmt (e.g. #22647, #22648).Steps to reproduce the issue:
./testbed-cli.sh ... -k csonic add-topo ...).docker exec -it csonic_<vmset>_VM0100 bash.showcommand, e.g.:Describe the results you received:
Every
showinvocation (even--help) leaks a stderr line, becauseget_routing_stack()is evaluated at module import time:Route/BGP paths leak a second error from the
rvtyshwrapper:In the container, neither binary exists:
Describe the results you expected:
showCLI commands should run cleanly in adocker-sonic-vsneighbor (single-container layout) without leakingsudo: docker: command not found/sudo: rvtysh: command not found, and should fall back to the plainvtyshbinary when no nestedbgpcontainer /rvtyshwrapper is present.Root cause / code references (master)
1.
get_routing_stack()shells out todocker psat import time —show/main.py:L92-106
get_routing_stack()runs:and is invoked at module scope:
routing_stack = get_routing_stack()(L106).Because it's a module-level global, it executes on every
showcommand (includingshow ... --help). Indocker-sonic-vsthere is nodockerbinary, so it emitssudo: docker: command not foundto stderr. Thetry/exceptdoes catch the failure and defaultsresult = 'frr', so the value is fine — but the stderr leak is unconditional. The function's own comment already flags this as undesirable ("To be enhanced ... prevent the continuous execution of this bash oneliner ... collected from a global location (configdb?)").2.
run_bgp_show_command()uses thervtyshwrapper —utilities_common/bgp_util.py+utilities_common/constants.py:constants.pyL17-18:VTYSH_COMMAND = 'vtysh',RVTYSH_COMMAND = 'rvtysh'bgp_util.pyL264-265:run_bgp_commandthen builds['sudo', 'rvtysh', ...](L251). On real SONiC,rvtyshis the routing-stack-aware wrapper that execs into thebgpcontainer; indocker-sonic-vsit does not exist, so the command fails withsudo: rvtysh: command not found.Suggested fix
get_routing_stack(), short-circuit when nested Docker is absent (e.g.if shutil.which("docker") is None: return 'frr') and/or suppress stderr, so the bash one-liner doesn't leak on single-container images.rvtyshpath, fall back to plainvtyshwhenrvtyshis not present inPATH(single-container / docker-sonic-vs case), or select the wrapper based on detected routing stack/container layout.Output of
show version(orshow platform summary):cSONiC neighbor:
docker-sonic-vsimage (sonic-mgmt KVM testbed neighbor).sonic-utilities-data1.0-1 (Debian python3.11 site-packages). Code paths verified identical onsonic-net/sonic-utilitiesmaster (show/main.py L92-106; constants.py L17-18; bgp_util.py L264-265).