NetAuto Academy

Network Controllers

Desired State vs. Observed State: Controller-Driven Validation

Advanced12 min read

What you'll learn

  • The difference between desired state and observed state
  • Why a completed task status is a claim, not proof
  • How to check observed state independently, as its own request
  • How this generalizes Reliable Automation's post-check to controller architecture
🎓 Teach This Lesson

Listen to a guided explanation of this lesson.

Spoken narration isn't supported in this browser.

Two different things, easy to conflate

Desired state is what you asked for. Observed state is what the controller has actually, separately confirmed is true right now. A task reporting completed is the controller's claim that it did the work — observed state is the independent check of whether that claim holds.

Desired state
  1. What was requestedthe intent submitted in Lesson 5
  2. A target, not a confirmation
Observed state
  1. What the controller has confirmed is actually truequeried separately, GET /devices/{id}/observed-state
  2. The only thing that actually proves a change stuck
Quick checkConcept check
What is 'observed state,' as distinct from 'desired state'?

A task can report completed and still be wrong

check_after_completion.py
# SW5's task from Lesson 4/5's pattern has already resolved:
# {'status': 'completed', 'changed': True}

response = requests.get("http://127.0.0.1:8766/devices/SW5/observed-state", timeout=2)
print(response.json())

A completely separate request from the task poll — this is the point. Nothing about polling a task to 'completed' automatically checks the device's real state; that has to be asked for explicitly.

output
{'vlans': [], 'last_synced': '2026-08-26T09:00:00Z', 'hostname': 'SW5'}

Real, verified output. SW5's task reported completed with changed: True — but its observed state shows an empty VLAN list, no VLAN 10 in sight. The task's own claim and the actual, separately-checked state disagree.

Quick checkPredict output
SW5's task status is 'completed' with changed: True. Does that guarantee GET /devices/SW5/observed-state will show the required VLAN present?

Checking both, in order

validate_change.py
def check_observed_state(hostname, required_vlans):
  response = requests.get(f"http://127.0.0.1:8766/devices/{hostname}/observed-state", timeout=2)
  actual_vlans = response.json()["vlans"]
  missing = [v for v in required_vlans if v not in actual_vlans]
  return len(missing) == 0, missing

verified, missing = check_observed_state("SW5", [{"id": 10, "name": "USERS"}])
print("verified:", verified, "missing:", missing)

Only run once a task has already resolved to completed — checking observed state before that would be premature. This is the same 'check, don't just trust' shape as validate_rendered_config() from Configuration & Template Automation, now checking a controller's reported reality instead of rendered text.

output
verified: False missing: [{'id': 10, 'name': 'USERS'}]

Real, verified output — proving the mismatch programmatically, not just by eye.

Quick checkIdentify the correct code
Which line correctly and independently checks a device's observed state, separate from polling its task?

Generalizing Reliable Automation's post-check

apply_ntp_config() in Reliable Automation read the device's own state again after a change, independently of whether the change call itself reported success — because a change call succeeding was never proof the change actually took effect. Observed state is that exact discipline, now applied to a controller's task claims instead of a direct device response.

Quick checkScenario
A script polls a task to 'completed' and immediately reports the change as successful, without checking observed state. What's the risk, based on SW5's verified behavior?

Key takeaways

  • Desired state is what was requested; observed state is what the controller has separately confirmed is actually true right now.
  • A task's completed status is the controller's own claim — it is not, by itself, proof the change took effect.
  • GET /devices/{id}/observed-state is a genuinely separate request, checked only after a task resolves — verified directly to disagree with a completed task for SW5.
  • This generalizes Reliable Automation's post-check discipline (Phase 2.8) to controller architecture — never trust a completion claim without an independent check.

Done with this lesson?

Next lesson

Capstone: Automating a Network Through a Controller