From ffe5ed1e32cdfdd1b1b8c78cc50f53d4f7769ad3 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 3 May 2026 22:45:28 +0200 Subject: [PATCH] statd, show: fix several operational data reporting bugs Three bugs found while testing modem support on hardware: ietf_system.py: "interface": null written to DNS server entries when no interface is associated, causing LY_EVALID in the ietf-system schema validator. Only include the key when the interface name is non-empty. ietf_system.py: empty list inserts (user [], server [], options [], search []) overwrote configured data in the operational store instead of leaving it intact. Guard each insert so operational data only shadows configured data when there is something to report. show system: CPU temperature reported only the first sensor named exactly "cpu", "soc", or "core". Platforms with multiple thermal zones (e.g. BPI-R4 exposes cpu and cpu1) always showed only one reading. Collect all sensors whose name matches a CPU/SoC prefix and report the maximum. statd.c: yanger parse errors logged without model name or libyang error string, making failures hard to diagnose. Include both. Signed-off-by: Joachim Wiberg --- src/bin/show/__init__.py | 15 +++++++-------- src/statd/python/yanger/ietf_system.py | 21 ++++++++++++--------- src/statd/statd.c | 5 +++-- test/case/statd/system/ietf-system.json | 4 +--- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/bin/show/__init__.py b/src/bin/show/__init__.py index 14785d9c..e4b050b3 100755 --- a/src/bin/show/__init__.py +++ b/src/bin/show/__init__.py @@ -558,8 +558,9 @@ def system(args: List[str]) -> None: runtime = {} # Extract CPU temperature and fan speed from hardware components - cpu_temp = None + cpu_temps = [] fan_rpm = None + _cpu_prefixes = ("cpu", "soc", "core") if hardware_data and "ietf-hardware:hardware" in hardware_data: components = hardware_data.get("ietf-hardware:hardware", {}).get("component", []) for component in components: @@ -570,18 +571,16 @@ def system(args: List[str]) -> None: name = component.get("name", "") value_type = sensor_data.get("value-type") - # Only capture CPU/SoC temperature (ignore phy, sfp, etc.) - # Different platforms use different names: cpu, soc, core, etc. - if value_type == "celsius" and name in ("cpu", "soc", "core") and cpu_temp is None: - temp_millidegrees = sensor_data.get("value", 0) - cpu_temp = temp_millidegrees / 1000.0 + # Capture all CPU/SoC temperatures (handles cpu, cpu1, soc, core0, etc.) + if value_type == "celsius" and any(name == p or name.startswith(p) for p in _cpu_prefixes): + cpu_temps.append(sensor_data.get("value", 0) / 1000.0) # Capture fan speed if available elif value_type == "rpm" and fan_rpm is None: fan_rpm = sensor_data.get("value", 0) - if cpu_temp is not None: - runtime["cpu_temp"] = cpu_temp + if cpu_temps: + runtime["cpu_temp"] = max(cpu_temps) if fan_rpm is not None: runtime["fan_rpm"] = fan_rpm diff --git a/src/statd/python/yanger/ietf_system.py b/src/statd/python/yanger/ietf_system.py index 60c7c6c0..f096c982 100644 --- a/src/statd/python/yanger/ietf_system.py +++ b/src/statd/python/yanger/ietf_system.py @@ -134,11 +134,10 @@ def add_dns(out): try: ipaddress.ip_address(ip) - servers.append({ - "address": ip, - "origin": "dhcp", - "interface": iface - }) + entry = {"address": ip, "origin": "dhcp"} + if iface: + entry["interface"] = iface + servers.append(entry) except ValueError: continue @@ -146,9 +145,12 @@ def add_dns(out): parts = line.split('#', 1) search.extend(parts[0].split()[1:]) - insert(out, "infix-system:dns-resolver", "options", options) - insert(out, "infix-system:dns-resolver", "server", servers) - insert(out, "infix-system:dns-resolver", "search", search) + if options: + insert(out, "infix-system:dns-resolver", "options", options) + if servers: + insert(out, "infix-system:dns-resolver", "server", servers) + if search: + insert(out, "infix-system:dns-resolver", "search", search) def add_software_slots(out, data): slots = [] @@ -357,7 +359,8 @@ def add_users(out): users.append(user) - insert(out, "authentication", "user", users) + if users: + insert(out, "authentication", "user", users) def add_clock(out): clock = {} diff --git a/src/statd/statd.c b/src/statd/statd.c index dac05583..ad7c78c1 100644 --- a/src/statd/statd.c +++ b/src/statd/statd.c @@ -112,7 +112,8 @@ static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent err = lyd_parse_data_fd(ctx, fd, LYD_JSON, LYD_PARSE_ONLY, 0, parent); if (err) - ERROR("Error, parsing yanger data (%d): %s", err, ly_errmsg(ctx)); + ERROR("Error, parsing yanger data (%d) for model '%s': %s", err, + yanger_args[1] ?: "?", ly_errmsg(ctx)); fclose(stream); /* Note: fclose() already closes the underlying fd from fdopen() */ @@ -227,7 +228,7 @@ static int sr_generic_cb(sr_session_ctx_t *session, uint32_t, const char *model, err = ly_add_yanger_data(ctx, parent, yanger_args); if (err) - ERROR("Error adding yanger data"); + ERROR("Error adding yanger data for xpath: %s", xpath); sr_release_context(con); diff --git a/test/case/statd/system/ietf-system.json b/test/case/statd/system/ietf-system.json index a8dde70b..edd8c11e 100644 --- a/test/case/statd/system/ietf-system.json +++ b/test/case/statd/system/ietf-system.json @@ -110,15 +110,13 @@ } }, "infix-system:dns-resolver": { - "options": {}, "server": [ { "address": "192.168.2.1", "origin": "dhcp", "interface": "e7" } - ], - "search": [] + ] }, "clock": { "boot-datetime": "2026-01-02T20:42:11+00:00",