diff --git a/napalm_linux/linux.py b/napalm_linux/linux.py index 8f590f0..9fb530f 100644 --- a/napalm_linux/linux.py +++ b/napalm_linux/linux.py @@ -697,13 +697,19 @@ class LinuxDriver(OSDriver): ``valid_lft`` / ``preferred_lft`` fields from DHCP leases are stripped so the output is stable across polls and does not produce false-positive - config-change events in the config-backup feature. + config-change events in the config-backup feature. ``veth*`` interfaces + are stripped entirely — Docker creates/destroys them with a fresh index + and random name on every container restart, which would otherwise flag + a config change on nearly every poll of a Docker host. """ import re running = self._send("ip addr show && ip route show") # Strip volatile DHCP lease timer fields — they decrement every poll running = re.sub(r"\s+valid_lft\s+\S+\s+preferred_lft\s+\S+", "", running) + # Strip veth interface blocks (line + indented sub-lines) — ephemeral + # Docker container network endpoints, not intentional host config + running = re.sub(r"^\d+: veth\S*:.*\n(?:[ \t]+.*\n?)*", "", running, flags=re.MULTILINE) return {"running": running, "startup": "", "candidate": ""} # ------------------------------------------------------------------ diff --git a/tests/test_linux.py b/tests/test_linux.py index aea9688..67572c4 100644 --- a/tests/test_linux.py +++ b/tests/test_linux.py @@ -88,6 +88,46 @@ def test_get_interfaces_parses_state(driver): assert result["eth1"]["is_up"] is False +# --------------------------------------------------------------------------- +# get_config +# --------------------------------------------------------------------------- + + +IP_ADDR_ROUTE_WITH_VETH = """\ +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff + inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0 + valid_lft 3542sec preferred_lft 3542sec +3512: veth5d7e34d@if2: mtu 1500 qdisc noqueue master br-ebb930396f3b state UP group default + link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 +default via 192.168.1.1 dev eth0 +192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10 +""" + + +def test_get_config_strips_dhcp_lease_timers(driver): + with patch.object(driver, "_send", return_value=IP_ADDR_ROUTE_WITH_VETH): + result = driver.get_config() + assert "valid_lft" not in result["running"] + assert "preferred_lft" not in result["running"] + + +def test_get_config_strips_veth_interfaces(driver): + """Docker creates/destroys veth pairs on every container restart — including + them would make get_config() report a false-positive change on every poll.""" + with patch.object(driver, "_send", return_value=IP_ADDR_ROUTE_WITH_VETH): + result = driver.get_config() + assert "veth5d7e34d" not in result["running"] + assert "3512:" not in result["running"] + # Real interfaces and routes must survive the filter + assert "eth0" in result["running"] + assert "default via 192.168.1.1" in result["running"] + + # --------------------------------------------------------------------------- # _parse_uptime # ---------------------------------------------------------------------------