Skip to content

Commit e131846

Browse files
committed
cli: add show snmp
Give the agent the same read-out the other services have: whether it is enabled, which versions and addresses it answers on, and the configured communities. Version and listen have no default in the model, so mark the values that apply when they are unset rather than print "none". The source column reports what the agent actually does, not what the configuration says. A community whose target-tag matches no target is dropped rather than widened to any source, so show that instead of an unrestricted community that is not being served. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
1 parent b878506 commit e131846

6 files changed

Lines changed: 130 additions & 0 deletions

File tree

‎package/klish-plugin-infix/klish-plugin-infix.mk‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ else
2323
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-containers
2424
endif
2525

26+
ifeq ($(BR2_PACKAGE_NETSNMP),y)
27+
KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-snmp
28+
else
29+
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-snmp
30+
endif
31+
2632
ifeq ($(BR2_PACKAGE_KLISH_PLUGIN_INFIX_SHELL),y)
2733
KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-shell
2834
else

‎src/bin/show/__init__.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,19 @@ def mdns(args: List[str]) -> None:
544544
cli_pretty(data, "show-mdns")
545545

546546

547+
def snmp(args: List[str]) -> None:
548+
data = get_json("/ietf-snmp:snmp", "running")
549+
if not data:
550+
print("SNMP not configured.")
551+
return
552+
553+
if RAW_OUTPUT:
554+
print(json.dumps(data, indent=2))
555+
return
556+
557+
cli_pretty(data, "show-snmp")
558+
559+
547560
def system(args: List[str]) -> None:
548561
"""Get system state from sysrepo"""
549562
data = get_json("/ietf-system:system-state")
@@ -757,6 +770,7 @@ def execute_command(command: str, args: List[str]):
757770
'rip': rip,
758771
'routes': routes,
759772
'services': services,
773+
'snmp': snmp,
760774
'software': software,
761775
'stp': stp,
762776
'system': system

‎src/klish-plugin-infix/configure.ac‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ AC_ARG_ENABLE(shell,
2626
AS_HELP_STRING([--enable-shell], [Enable shell access from CLI]),,[
2727
enable_shell=no])
2828

29+
AC_ARG_ENABLE(snmp,
30+
AS_HELP_STRING([--enable-snmp], [Enable SNMP agent commands in CLI]),,[
31+
enable_snmp=no])
32+
2933
AC_ARG_WITH(shell,
3034
AS_HELP_STRING([--with-shell=shell], [Path to shell to use, default: /bin/sh]),
3135
[shell=$withval], [shell=yes])
@@ -40,6 +44,7 @@ AC_CHECK_LIB([klish], [kplugin_new],, AC_MSG_ERROR([Klish not found]))
4044
# Control build with automake flags
4145
AM_CONDITIONAL(CONTAINERS, [test "x$enable_containers" != "xno"])
4246
AM_CONDITIONAL(SHELL, [test "x$enable_shell" != "xno"])
47+
AM_CONDITIONAL(SNMP, [test "x$enable_snmp" != "xno"])
4348

4449
AS_IF([test "x$with_shell" != "xno"], [
4550
AS_IF([test "x$shell" = "xyes"], [shell=/bin/sh])
@@ -70,6 +75,7 @@ cat <<EOF
7075

7176
Optional features:
7277
Container support ....: $enable_containers
78+
SNMP support .........: $enable_snmp
7379
Allow shell access ...: $enable_shell
7480
Shell to use .........: $with_shell
7581

‎src/klish-plugin-infix/xml/Makefile.am‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ endif
88
if SHELL
99
klishconf_DATA += shell.xml
1010
endif
11+
12+
if SNMP
13+
klishconf_DATA += snmp.xml
14+
endif
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<KLISH
3+
xmlns="https://klish.libcode.org/klish3"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="https://src.libcode.org/pkun/klish/src/master/klish.xsd">
6+
7+
<VIEW name="main">
8+
9+
<COMMAND name="show" help="Show system status and configuration files" mode="switch">
10+
11+
<COMMAND name="snmp" help="Show SNMP agent configuration">
12+
<ACTION sym="script">show snmp</ACTION>
13+
</COMMAND>
14+
15+
</COMMAND>
16+
17+
</VIEW>
18+
</KLISH>

‎src/statd/python/cli_pretty/cli_pretty.py‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3946,6 +3946,84 @@ def _mdns_svc_name(stype):
39463946
return stype.lstrip("_").split("._")[0]
39473947

39483948

3949+
def _snmp_source(snmp, tag):
3950+
"""Where a community is answered from, as the agent sees it.
3951+
3952+
An unset tag means any source. A tag no target carries is the
3953+
operator asking for a restriction that does not exist, and the agent
3954+
drops the community rather than widen it, so say so here too.
3955+
"""
3956+
if tag is None:
3957+
return "any"
3958+
3959+
out = []
3960+
for target in snmp.get("target", []):
3961+
if tag not in target.get("tag", []):
3962+
continue
3963+
udp = target.get("udp", {})
3964+
addr = udp.get("ip")
3965+
if not addr:
3966+
continue
3967+
plen = udp.get("prefix-length")
3968+
out.append(f"{addr}/{plen}" if plen is not None else addr)
3969+
3970+
return ", ".join(out) if out else f"none, no target tagged {tag}"
3971+
3972+
3973+
def show_snmp(json):
3974+
snmp = json.get("ietf-snmp:snmp", {})
3975+
if not snmp:
3976+
print("SNMP not configured.")
3977+
return
3978+
3979+
engine = snmp.get("engine", {})
3980+
3981+
enabled = engine.get("enabled")
3982+
print(f"{'Enabled':<16}: {'yes' if enabled else 'no'}")
3983+
3984+
# Neither leaf has a YANG default; unset means the agent serves
3985+
# every version it supports, and listens on every address.
3986+
versions = [v for v in ("v1", "v2c") if v in engine.get("version", {})]
3987+
print(f"{'Versions':<16}: {', '.join(versions or ['v1', 'v2c'])}"
3988+
f"{'' if versions else ' (default)'}")
3989+
3990+
listen = []
3991+
for entry in engine.get("listen", []):
3992+
udp = entry.get("udp", {})
3993+
addr = udp.get("ip")
3994+
if not addr:
3995+
continue
3996+
port = udp.get("port", 161)
3997+
listen.append(f"[{addr}]:{port}" if ":" in addr else f"{addr}:{port}")
3998+
print(f"{'Listen':<16}: {', '.join(listen or ['0.0.0.0:161', '[::]:161'])}"
3999+
f"{'' if listen else ' (default)'}")
4000+
4001+
if engine.get("engine-id"):
4002+
print(f"{'Engine ID':<16}: {engine['engine-id']}")
4003+
4004+
communities = snmp.get("community", [])
4005+
if not communities:
4006+
print("\nNo communities, the agent answers nothing.")
4007+
return
4008+
4009+
print()
4010+
table = SimpleTable([
4011+
Column("COMMUNITY", flexible=True),
4012+
Column("SECURITY NAME"),
4013+
Column("SOURCE"),
4014+
])
4015+
for entry in communities:
4016+
secname = entry.get("security-name", "")
4017+
# An unset name means the agent uses the security name as
4018+
# the community, see RFC 7407. A binary name has octets
4019+
# that do not survive being printed.
4020+
name = entry.get("text-name")
4021+
if name is None:
4022+
name = "<binary>" if entry.get("binary-name") else secname
4023+
table.row(name, secname, _snmp_source(snmp, entry.get("target-tag")))
4024+
table.print()
4025+
4026+
39494027
def show_mdns(json):
39504028
mdns = json.get("infix-services:mdns", {})
39514029
if not mdns:
@@ -6110,6 +6188,8 @@ def main():
61106188

61116189
subparsers.add_parser('show-mdns', help='Show mDNS configuration and neighbors')
61126190

6191+
subparsers.add_parser('show-snmp', help='Show SNMP agent configuration')
6192+
61136193
subparsers.add_parser('show-firewall', help='Show firewall overview')
61146194
subparsers.add_parser('show-firewall-matrix', help='Show firewall matrix')
61156195
subparsers.add_parser('show-firewall-zone', help='Show firewall zones') \
@@ -6187,6 +6267,8 @@ def main():
61876267
show_lldp(json_data)
61886268
elif args.command == "show-mdns":
61896269
show_mdns(json_data)
6270+
elif args.command == "show-snmp":
6271+
show_snmp(json_data)
61906272
elif args.command == "show-firewall":
61916273
show_firewall(json_data)
61926274
elif args.command == "show-firewall-matrix":

0 commit comments

Comments
 (0)