difflib.unified_diff, noise filtering & changed lines
Detect configuration drift
Lesson 14 of 20 · 15 min · Module 4: Backups, drift and compliance
Someone changed two switches by hand last night and told nobody. Comparing today's configuration with the last backup shows exactly what changed.
Start this lesson →Free. No installation: you write and run the code in your browser, and it is checked automatically.
By the end, you’ll be able to…
- Compare two configurations with difflib
- Ignore the lines that change on their own
- Keep only the added and removed lines
The idea
difflib.unified_diff(old, new, lineterm="") compares two lists of lines. Lines that start with - were removed, lines that start with + were added, and the ---, +++ and @@ lines only describe the comparison. Some lines change on their own, such as the timestamps in comments and the configuration size. Remove them before comparing: lines that start with !, Building configuration, Current configuration, and empty lines.
import difflib
old = ["hostname sw1", "ntp server 10.0.0.250"]
new = ["hostname sw1", "ntp server 10.0.0.251"]
for line in difflib.unified_diff(old, new, lineterm=""):
print(line)
01A unified diff
Context lines start with a space; - and + mark what was removed and added. The first three lines describe the comparison itself.
import difflib
old = ["interface Gi0/7", " switchport access vlan 10"]
new = ["interface Gi0/7", " switchport access vlan 99"]
for line in difflib.unified_diff(old, new, fromfile="backup", tofile="now",
lineterm=""):
print(line)--- backup
+++ now
@@ -1,2 +1,2 @@
interface Gi0/7
- switchport access vlan 10
+ switchport access vlan 9902Noise changes on every run
The size and the timestamps differ as soon as anything changes, even a description. Removing them leaves only real configuration.
text = open("backups/sw1.cfg").read()
print("\n".join(text.splitlines()[:6]))Building configuration...
Current configuration : 2183 bytes
!
! Last configuration change at 02:00:00 UTC Fri Oct 16 2026 by netadmin
! NVRAM config last updated at 02:00:00 UTC Fri Oct 16 2026 by netadmin03startswith takes a tuple
One call checks several prefixes. That keeps the filter short and readable.
NOISE = ("!", "Building configuration", "Current configuration")
print("! NVRAM config last updated".startswith(NOISE))
print("hostname sw1".startswith(NOISE))True
FalseYour turn
backups/ with the device's running configuration now (core1, sw1 and sw2 are Cisco, sw3 is Huawei). Build drift: a dictionary mapping each device name to the list of changed lines, the ones starting with + or -, without the --- and +++ header lines and after removing the noisy lines. A device without changes maps to an empty list.What the checks look for
- core1 shows the new static route
- sw2 shows the changed port
- Unchanged devices have no drift
- Timestamps and sizes are ignored
- clean() removes the noisy lines
Take this with you
Diff against the last backup, ignore the lines that change by themselves, and look at what is left.
Go a little further
Write each non-empty diff to drift/<name>.diff, so it can be attached to a ticket.