fix(config): drop the uptime line from get_config()

`show running-config` is preceded by a header block, and one of its lines
reports the system uptime. That value necessarily differs between any two
reads, so every caller comparing consecutive configs sees a change each
time.

Measured on a GS110TPv3 under NetOrk: 876 of 894 stored config snapshots
marked as changed, one git commit and one config_changed warning per poll,
while every other device at the same installation sat between 2 and 18. The
history was worthless for that switch — a genuine change would have been
invisible among hundreds of uptime diffs.

Only the uptime line is removed. Model, firmware version, serial and MAC are
stable and belong in a config backup; a changed firmware version is exactly
the kind of change worth recording.

netgear_plus is unaffected — its get_config() returns empty strings.

The first read after this lands reports one real change, since the line
disappears from the stored config. That is unavoidable and happens once.
This commit is contained in:
Christian Manivong
2026-08-18 10:30:11 +07:00
parent 20460e7a5a
commit 5317af0be1
2 changed files with 93 additions and 2 deletions
+29 -2
View File
@@ -821,6 +821,29 @@ class NetgearSmartDriver(ConfigLifecycleMixin, SwitchDriver):
return interfaces_ip
_RUNTIME_STATE_LINE = re.compile(r"^\s*!\s*System Up Time:", re.IGNORECASE)
@classmethod
def _strip_runtime_state(cls, config: str) -> str:
"""Drop lines describing state rather than configuration.
``show running-config`` is preceded by a header block, and one of its
lines reports the system uptime. That value necessarily differs
between any two reads, so a caller diffing consecutive configs sees a
change every single time — on a GS110TPv3 that marked 876 of 894
stored snapshots as changed and made the history useless.
Only the uptime line goes. The rest of the header (model, firmware,
serial, MAC) is stable, and a changed firmware version is precisely
the kind of change a config backup should record.
"""
if not config:
return config
kept = [
ln for ln in config.splitlines(keepends=True) if not cls._RUNTIME_STATE_LINE.match(ln)
]
return "".join(kept)
def get_config(
self,
retrieve: str = "all",
@@ -836,10 +859,14 @@ class NetgearSmartDriver(ConfigLifecycleMixin, SwitchDriver):
configs = {"running": "", "startup": "", "candidate": ""}
if retrieve in ("all", "running"):
configs["running"] = self._send_paged_command("show running-config")
configs["running"] = self._strip_runtime_state(
self._send_paged_command("show running-config")
)
if retrieve in ("all", "startup"):
configs["startup"] = self._send_paged_command("show startup-config")
configs["startup"] = self._strip_runtime_state(
self._send_paged_command("show startup-config")
)
if sanitized:
configs = napalm_helpers.sanitize_configs(configs, C.CISCO_SANITIZE_FILTERS)