Your First Network Automation
Validating Results and Building the Report
What you'll learn
- Why a successful connection doesn't automatically mean a device is compliant
- How to turn raw NTP/VLAN state into a single, plain-language status
- How to categorize every device's result, including ones that couldn't be reached
- How to collect results into the shape this project's JSON report needs
- How this stage fits between the simulated connection and the finished report
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
Connected successfully still isn't the same as compliant
The last lesson's loop printed a clean line for SW2 — no exception, no error. It's tempting to read that as "SW2 passed." It didn't: simulate_connection() succeeding only means the (simulated) connection worked. SW2's actual state came back with ntp_synced: False. This is the exact distinction "Validation, Safety & Failure Handling" made two modules ago, now sitting directly in this project's own code.
Turning state into a status
def check_compliance(state):
"""Turn a device's raw state into a plain-language compliance status."""
if state["ntp_synced"]:
if state["vlan_compliant"]:
return "compliant"
else:
return "vlan non-compliant"
else:
return "ntp non-compliant"Two checks, nested: a device only counts as fully 'compliant' if it passes both. This is the same if/else you've used all module — just one level deeper, checking a second thing only once the first has already passed.
Reading it top to bottom: if NTP isn't synced, the device is "ntp non-compliant" — full stop, the VLAN check never even runs, because a device that isn't synced doesn't need a second reason to need attention. If NTP is synced, only then does the function look at VLAN compliance, returning "compliant" or "vlan non-compliant" accordingly.
Categorizing every result — including failures
A real report needs an entry for every device, not just the ones that connected successfully. This is where the try/except from the last lesson and this lesson's compliance check come together:
results = []
for device in valid_devices:
try:
state = simulate_connection(device)
except ConnectionError:
results.append({
"hostname": device["hostname"],
"ip": device["ip"],
"status": "unreachable",
})
continue
status = check_compliance(state)
results.append({
"hostname": device["hostname"],
"ip": device["ip"],
"status": status,
})Every device gets exactly one entry in results — 'unreachable' if the connection failed, or a real compliance status if it succeeded. Nothing is silently skipped, and nothing needs to succeed for the loop to keep going.
Writing the report
The last step this lesson covers: saving results as JSON — exactly the pattern from "Working with JSON in Python":
import json
with open("compliance_report.json", "w") as f:
json.dump(results, f, indent=4)json.dump writes the whole results list out as a readable JSON file — a saved record, not just something printed once and gone.
Key takeaways
- A successful (simulated) connection only means the device responded — its actual compliance still has to be checked separately.
- check_compliance() turns raw NTP/VLAN state into one plain-language status, using a nested if/else that checks the second condition only if the first already passed.
- Every device gets exactly one entry in the results list, whether it was compliant, non-compliant, or unreachable — nothing gets silently dropped.
- json.dump(results, f, indent=4) saves the collected results as a real report file, not just terminal output that disappears.
- This lesson's three pieces — compliance checking, result collection, and report writing — are the last building blocks before Lesson 5 assembles the complete project.