Desired state, read before write & idempotency

Make changes idempotent

Lesson 11 of 20 · 15 min · Module 3: Change the network

Running a script twice should not change the network twice. Read what is there, compare it with what should be there, and send only the difference.

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…

  • Describe the desired state as data
  • Send only the difference
  • Report what changed

The idea

A change is idempotent when running it again leaves the network as it is. The pattern: describe the desired state as data, read the actual state from the device, and send only what is missing. Return what you changed, so a report can say which devices were touched, and save only when something changed. On Huawei, the text that display vlan shows for a VLAN is set with description in the VLAN view.

DESIRED = {10: "USERS", 30: "GUESTS"}
present = {1, 10}
print(sorted(set(DESIRED) - present))

01Desired minus actual

Set difference gives what is missing. Sorting it makes the result, and the order of the commands, predictable.

DESIRED = {10: "USERS", 20: "VOICE", 30: "GUESTS", 99: "MGMT"}
print(sorted(set(DESIRED) - {1, 10, 20, 99}))
Output
[30]

02Lines only for what is missing

Build the configuration from the difference. When the difference is empty, the list is empty and nothing needs to be sent.

lines = []
for vid in [30]:
    lines += [f"vlan {vid}", f"name {DESIRED[vid]}"]
print(lines)
print([f"vlan {vid}" for vid in []])
Output
['vlan 30', 'name GUESTS']
[]

03Huawei describes a VLAN

In the VLAN view, Huawei takes description for the text that display vlan shows. The same data gives each vendor its own lines.

import os
from netmiko import ConnectHandler

USERNAME = os.environ["NET_USERNAME"]
PASSWORD = os.environ["NET_PASSWORD"]

with ConnectHandler(device_type="huawei", host="10.0.0.13",
                    username=USERNAME, password=PASSWORD) as conn:
    print(conn.send_config_set(["vlan 50", "description LAB"]))
Output
system-view
Enter system view, return user view with return command.
[sw3]vlan 50
[sw3-vlan50]description LAB
[sw3-vlan50]return
<sw3>

Your turn

Write ensure_vlans(conn, platform): read the switch's VLANs, create the VLANs from DESIRED that are missing (with their names), save only if something changed, and return the sorted list of VLAN IDs it added. Run it on sw1, sw2 and sw3 and store the results in first, then run it on all three again and store those results in second.

What the checks look for

  • first shows what each switch needed
  • second shows that nothing was left to do
  • VLAN 30 is named GUESTS on every switch
  • sw2 received no configuration and no save
  • Each VLAN was created only once
  • Changed switches are saved
Try it in the editor →

Take this with you

Read, compare, change only the difference, and report what changed. A second run must report nothing.

Go a little further

Also fix VLANs that exist with a different name, and return them separately as renamed.

Learn Network Automation for free

20 short lessons with real code and instant checks.

See the whole course →