Jump to chapter (9)
Welcome: what you will learn and the big picture
What you will learn in this module. You will learn what network automation is, why engineers use it, and how a tiny Python script can log in to many routers for you, run commands and report back. By the end you will have run two real scripts in the lab: one that reads the hostname and uptime of every router, and one that pushes the same NTP setting to five routers and checks the result. This is the first module of the CCNP Automation (350-901 AUTOCOR) track and also the start of the "Python for Network Engineers" track.
Prerequisites. You should know what a router is, what an IP address is, and how to type show commands on a Cisco-style CLI. That is all. You do not need to know any programming. We assume you have never written a line of code, and we explain every line we show.
Analogy: a recipe card and a tireless helper
Think of a recipe card for tea: boil water, add leaves, add milk, wait two minutes, pour. A new cook can follow it exactly and get the same tea every time. A script is a recipe card for a computer. You write the steps once, in a text file, in a language the computer understands (we use Python). The computer then follows the steps exactly, as many times as you ask, without getting tired or bored. Your helper never skips step 3 because it is 2 a.m.
The picture for the whole module: you write a script, a host runs it, SSH carries it to the routers.
The lab world you will work in
Every lab in this module uses an automation host called AUTO. It is a small Linux-like machine on the management network 10.99.0.0/24 (AUTO is 10.99.0.100). It is joined through a switch to routers such as R1 (10.99.0.1) and R2 (10.99.0.2). The routers accept SSH logins for a lab user named netops. The password in the lab is the placeholder NK@2026; never reuse a lab password in real life. On AUTO you will use these commands:
AUTO> ls first_script.py
ls lists the files on the host. Here the only file is first_script.py, left by a senior engineer. You will open it in chapter 3 and run it in chapter 4.
The nine chapters
- Big picture (this chapter)
- Why automate, and the automation ladder
- Python and your first script file
- Your first script, line by line
- Lists and loops: one idea, many routers
- Pushing configuration safely
- Reading errors without panic
- Verifying and safe habits
- Summary and checklist
Worked example. Your manager asks for a new NTP server on 5 branch routers. By hand: five logins, five conf t sessions, five write memory. With a script: one command, python3 ntp_push.py, and a printed report. Chapter 6 does exactly this.
Common mistake. Believing "automation means I do not need networking knowledge". The script only types what you tell it to type. If you do not know the right command, a script just types the wrong command faster, on more devices.
Exam trap. The 350-901 AUTOCOR exam tests automation concepts, Python reading skills, APIs, data formats and tools. You are not asked to write long programs from nothing, but you must read short Python and spot what it does or why it fails. This module builds that reading habit.
The 2 a.m. typo
A network team changed a banner on 40 switches by hand over a weekend. On switch 27 a tired engineer typed a wrong VLAN number in a pasted block, and nobody noticed until Monday. After that, the team wrote one small script holding the exact lines. They reviewed the lines once, in daylight, and then ran the script on all 40.
Lesson: automation moves the careful thinking to one place (the file) and the typing to the computer.
"Why would a network engineer learn Python?"
Strong answer: to remove repetitive, error-prone manual work (the same change on many devices), to get consistent results with a record of what was done, and to be able to read and write the scripts and API calls that modern tools are built on. Add that automation does not replace networking knowledge; it multiplies it.
Key takeaways
- A script is a recipe card: steps written once, followed exactly many times.
- Python is the language we use; the lab host is called AUTO.
- The script reaches routers over SSH using the lab user netops.
- You need networking knowledge first; automation multiplies it.
Why automate, and the automation ladder
Imagine a hospital where every nurse writes the same patient form by hand, 50 times a day. Sooner or later a nurse writes a wrong dose. The hospital does not fix this by telling nurses to "be more careful". It fixes it with a printed form and a checklist. Network automation is the printed form for network changes: the steps are fixed in a file, reviewed once, and then repeated by a computer.
The arithmetic of repetition
Suppose a change takes about 3 minutes per router by hand: log in, conf t, type, end, write memory, check, log out. That is an estimate for illustration, not a measurement. For 5 routers it is 15 minutes. For 50 routers it is 150 minutes, a whole afternoon, and each router is a new chance for a typo. A script does the same steps in the time it takes the SSH sessions to open, and it never mistypes line 2 because it was typed correctly once.
| Question | By hand | With a script |
|---|---|---|
| Speed | Minutes per device | Seconds per device |
| Consistency | Depends on the person | Identical every time |
| Evidence | Your memory | The script prints a report |
| Repeating next month | Start again from scratch | Run the same file |
The automation ladder
Teams climb four steps, one at a time. This course teaches all four, and this module is step 2.
The automation ladder. Each step builds on the one before. You do not need step 4 to benefit from step 2.
- Manual CLI: you type on one device at a time. Fine for 3 devices.
- Scripts over SSH: a program types the CLI for you. Works on almost every device that has SSH today.
- APIs: an API (Application Programming Interface) is a door made for programs. Instead of screen text, the device returns clean structured data (for example JSON). You meet RESTCONF and NETCONF later in the track.
- Pipelines: the wanted configuration lives in files kept in Git (a history of every change). When a file changes, an automatic pipeline tests it and pushes it. The files become the single source of truth.
Automation also multiplies mistakes
A script is fast in both directions. A wrong line typed by hand breaks one router. The same wrong line in a script breaks fifty routers in a minute. That is why good teams review the script, run it first on one test device, and verify afterwards. Chapters 6 to 8 build these habits.
Worked example. Task: "Which routers are running for more than 90 days?" Manual: open 40 SSH sessions, read 40 show version outputs. Scripted (step 2): one loop, one filter, one list printed. Step 3 would ask each router for the uptime as a number through an API. Same question, three levels.
Common mistake. Starting with the fanciest tool. Many beginners jump to pipelines and give up. Start with one small read-only script (show commands only). Nothing can break, and you learn the rhythm.
Exam trap. Know the vocabulary: script, API, source of truth, pipeline. A common question asks which approach gives structured data for programs (an API) and which approach parses screen text (a CLI script).
The audit nobody wanted
An auditor asked a company for proof that all 120 routers had the same login banner. The team spent two days opening sessions. The next quarter they wrote a read-only script that logged in, read the banner, compared it to the approved text and printed OK or DIFFERENT for each router. The audit took ten minutes.
Lesson: read-only scripts are the safest first automation, and they produce evidence.
"What is the difference between a CLI script and an API?"
A CLI script automates what a human types and then has to read the text answer back, so it must parse screen output. An API gives a defined way to ask for data or send a change, and the answer comes back as structured data such as JSON. APIs are easier for programs to read and less fragile, but many older devices only offer the CLI, so both skills matter.
Key takeaways
- Automation gives speed, consistency and evidence.
- The ladder: manual CLI, scripts, APIs, pipelines.
- An API gives programs structured data; the CLI gives screen text.
- A script repeats mistakes quickly, so review, test on one device and verify.
- Start with read-only scripts.
Python and your first script file
Python is a programming language: a way to write instructions using words that look a little like English. Network engineers like it because it is easy to read and because other people have already written ready-made code for networking. Ready-made code that you can reuse is called a library (or module). Loading one into your script is called importing it.
A Python script is simply a text file whose name ends in .py. The computer reads it from the top, one line at a time, and does what each line says. To run it you type python3 and the file name. Nothing is installed on your own laptop for this module: everything happens on the lab host AUTO.
Your first line of Python
You can run one line without making a file by using python3 -c followed by the code in quotes. The function print() shows whatever is inside the brackets on the screen:
AUTO> python3 -c "print('Hello, network')"
Hello, network
AUTO> python3 -c "print(10 + 5)" 15
AUTO> python3 -c "print('R1', 'is up')"
R1 is up
- Text goes inside quotes (single or double). Text in quotes is called a string.
- Numbers have no quotes, and Python can do maths on them:
10 + 5printed15. - Several things separated by commas are printed with one space between them.
The host commands you will use
Because AUTO is a lab machine, it has a few small helper commands for working with files. You will use them in every chapter:
| Command | What it does |
|---|---|
ls | List the files |
cat FILE | Show the text of a file |
nl FILE | Show the file with line numbers |
python3 FILE | Run the script |
sed -i 's/old/new/' FILE | Replace text inside a file |
edit FILE N TEXT | Replace the whole of line N |
insert FILE N TEXT | Add a new line before line N |
Here are the first eight lines of the senior engineer's file, shown with line numbers by nl. Lines that start with # are comments: notes for humans that Python skips.
AUTO> nl first_script.py
1 # first_script.py - your first automation script
2 # Lines that start with # are COMMENTS: notes for humans, Python skips them.
3
4 # Step 1: load the SSH tool. nkmiko works like the popular "netmiko" library.
5 from nkmiko import ConnectHandler
6
7 # Step 2: the LIST of routers to visit (their management IP addresses).
8 ROUTERS = ["10.99.0.1", "10.99.0.2"]
Two rules that save you hours
- Indentation matters. Lines inside a block (for example a loop) are pushed right by 4 spaces. Python uses that gap to know which lines belong together. Mixing it up is an error.
- Spelling and case matter.
Printis notprint. A missing quote or bracket is a SyntaxError.
If you name a file that does not exist, the host says so plainly:
AUTO> python3 nofile.py python3: can't open file '/home/lab/nofile.py': [Errno 2] No such file or directory
Worked example. You want to check a one-line idea quickly: python3 -c "print(len('PUN-CORE-01'))". You are asking Python to count the characters of the hostname. Small experiments like this are the fastest way to learn.
Common mistake. Typing Python code straight at the host prompt (for example print("hi") without python3 -c). The host prompt understands host commands, not Python. Start Python first, or use python3 -c.
Exam trap. Remember: a comment starts with # and is ignored. A block is marked by a colon at the end of a line and by indentation, not by curly brackets as in many other languages.
The file that would not run
A new engineer typed python3 first_script and got "No such file". The file was called first_script.py. The fix was the full name including .py. Running ls first would have shown the exact name.
Lesson: run ls to see the real file names before you run a script.
"What is a Python script, and how do you run it?"
A script is a plain text file of Python statements, normally ending in .py. You run it with python3 filename.py, and Python reads it top to bottom. Mention comments, indentation and that libraries are loaded with import.
Key takeaways
- Python is a readable language; a script is a text file ending in .py.
- Run a script with python3 FILE; run one line with python3 -c.
- print() shows output; text goes in quotes; numbers do not.
- Comments start with #; indentation groups lines.
Your first script, line by line
Do not try to memorise a script. Read it like a recipe: top to bottom, one step at a time. Here is the whole of first_script.py in four steps. We take each step and explain every word.
Step 1: load the SSH tool
from nkmiko import ConnectHandler
nkmiko is the lab's SSH library. It works like the popular netmiko library used in real networks. The line says: "from the library nkmiko, bring in the tool called ConnectHandler". ConnectHandler is the tool that logs in to a device. Without this line Python would not know the word ConnectHandler.
Step 2: the list of routers
ROUTERS = ["10.99.0.1", "10.99.0.2"]
This creates a variable named ROUTERS. A variable is a labelled box that holds a value. The value here is a list: items inside square brackets, separated by commas. Each item is a string (text in quotes). Writing the name in capitals is only a habit that means "this is a setting I may edit".
Step 3: visit each router
for ip in ROUTERS:
print("Connecting to", ip)
conn = ConnectHandler(device_type="nk_ios", host=ip, username="netops", password="NK@2026")
print(" Hostname:", conn.find_prompt().replace("#", ""))
for ip in ROUTERS:is a loop. It takes the items of the list one by one, puts the current one in the variableip, and runs the indented lines below. The colon at the end opens the block.ConnectHandler(...)logs in over SSH. The words inside the brackets are arguments: the settings you pass in.device_typesays what kind of CLI to expect,hostis the IP, then the username and password. The result, a live session, is stored inconn.conn.find_prompt()asks the router for its prompt, for examplePUN-CORE-01#..replace("#", "")swaps the # for nothing, leaving the plain hostname.
What happens for each router in the loop. When the list runs out, the loop ends.
Step 4: ask a question and pick out the answer
output = conn.send_command("show version")
for line in output.splitlines():
if "uptime" in line:
print(" " + line)
conn.disconnect()
send_command("show version") types the command and gives back everything the router printed as one long string. We store it in output. splitlines() cuts that text into a list of lines. The inner loop looks at each line; if "uptime" in line asks "does this line contain the word uptime?"; only then it prints. disconnect() logs out. Notice the nesting: a loop inside a loop, each indented 4 more spaces.
Here is a tiny script that shows what send_command really returns. The full output has 21 lines, so we keep only the uptime line:
from nkmiko import ConnectHandler
conn = ConnectHandler(device_type="nk_ios", host="10.99.0.1", username="netops", password="NK@2026")
text = conn.send_command("show version")
print(type(text))
print("lines returned:", len(text.splitlines()))
for line in text.splitlines():
if "uptime" in line:
print("kept:", line)
conn.disconnect()
AUTO> python3 peek.py
<class 'str'>
lines returned: 21
kept: PUN-CORE-01 uptime is 0 minutes
Running the real script
AUTO> python3 first_script.py Connecting to 10.99.0.1 Hostname: PUN-CORE-01 PUN-CORE-01 uptime is 0 minutes Connecting to 10.99.0.2 Hostname: BLR-EDGE-02 BLR-EDGE-02 uptime is 0 minutes Done: 2 routers checked
Read the output like the script: for each IP it prints "Connecting to", the hostname, the uptime line, and at the end the count. The uptime shows 0 minutes because the simulated routers have just started; that is a feature of the lab, not a bug in the script.
Worked example. Lab 1 question: "What is the hostname of the router at 10.99.0.2?" Find the line Connecting to 10.99.0.2 in the output; the next line shows Hostname: BLR-EDGE-02. The script gave you the answer without you logging in anywhere.
Common mistake. Forgetting the indentation or the colon after for. If the lines under for are not pushed right by 4 spaces, Python raises an IndentationError. If the colon is missing, a SyntaxError.
Exam trap. find_prompt(), send_command() and disconnect() are methods of the connection object. send_command is for show commands (it returns a string); configuration uses a different method, which chapter 6 covers.
Which router is it?
An engineer got an alert about "10.99.0.2" but did not know which site it was. Instead of logging in, he ran the senior's script, which printed the hostname BLR-EDGE-02 for that IP. He knew immediately it was the Bengaluru edge router and called the right team.
Lesson: even a read-only script is useful: it turns IP addresses into names.
"Walk me through a simple script that connects to a router and runs a show command."
Import the connection tool, build a list of device IPs, loop over the list, open a session with ConnectHandler (device type, host, username, password), run send_command, process the returned text, and disconnect. Mention that credentials should not be hard-coded in real scripts.
Key takeaways
- ConnectHandler logs in; send_command returns the show output as one string.
- A list holds many IPs; a for loop visits each one.
- splitlines() turns text into a list of lines; "word" in line tests a line.
- Always disconnect when you are done.
Lists and loops: one idea, many routers
The whole power of automation is one idea: do the same thing for every item in a list. On a switch you already know a version of it. The command interface range gigabitEthernet 0/1 - 4 applies the same commands to four ports. In Python the list is the range and the for loop is the "apply to each".
A list in detail
A list keeps its items in order. Each item has a position number called an index, and Python starts counting at 0, like Gi0/0 being the first port. len() tells you how many items there are.
routers = ["10.99.0.1", "10.99.0.2", "10.99.0.3"]
count = 0
for ip in routers:
print("Visiting", ip)
count = count + 1
print("Visited", count, "routers")
print("The list has", len(routers), "items")
print("First item:", routers[0])
AUTO> python3 loop_demo.py
Visiting 10.99.0.1
Visiting 10.99.0.2
Visiting 10.99.0.3
Visited 3 routers
The list has 3 items
First item: 10.99.0.1
count = 0creates a counter that starts at zero.count = count + 1reads "take the current value, add 1, store it back". It runs once per router, so after three routers the counter is 3.routers[0]is the first item. The lineprint("Visited", ...)is not indented, so it runs once, after the loop ends.
You can also loop over numbers with range(). range(1, 4) gives 1, 2, 3: the end number is not included. This is handy for building repeated config lines:
for n in range(1, 4):
print("interface Loopback" + str(n))
AUTO> python3 loop_demo2.py interface Loopback1 interface Loopback2 interface Loopback3
str(n) turns the number into text so it can be joined to other text with +. Chapter-wise Python lessons cover this in the next module.
Teaching the script about a third router
In Lab 1 a new router arrives at 10.99.0.3. You change one line, the list, and nothing else. Line 8 before the change:
ROUTERS = ["10.99.0.1", "10.99.0.2"]
AUTO> sed -i 's/"10.99.0.2"/"10.99.0.2", "10.99.0.3"/' first_script.py
AUTO> nl first_script.py (line 8 only)
8 ROUTERS = ["10.99.0.1", "10.99.0.2", "10.99.0.3"]
AUTO> python3 first_script.py Connecting to 10.99.0.1 Hostname: PUN-CORE-01 PUN-CORE-01 uptime is 0 minutes Connecting to 10.99.0.2 Hostname: BLR-EDGE-02 BLR-EDGE-02 uptime is 0 minutes Connecting to 10.99.0.3 Hostname: HYD-EDGE-03 HYD-EDGE-03 uptime is 0 minutes Done: 3 routers checked
The loop visited one more router, and the counter at the end, len(ROUTERS), now says 3. You did not copy-paste any login code. That is the magic of lists and loops.
Worked example. Lab 1 question: "What is the hostname of the new router (10.99.0.3)?" The second run prints Hostname: HYD-EDGE-03 under Connecting to 10.99.0.3. Add an IP to the list, and the script reports one more device.
Common mistake. Putting a line at the wrong indentation. If print("Visited", ...) were indented, it would run inside the loop and print three times. Also, forgetting a comma between two IPs in the list joins them into one wrong string.
Exam trap. Indexes start at 0. range(1, 4) excludes 4. len(list) counts items. Expect questions that ask what a short loop prints.
Forty routers, one list
A team kept router IPs in a text note and copied them into a script's list. When two sites merged, they added eleven IPs to the list and re-ran the same script. The next report included all 51 devices, and nobody wrote new login code.
Lesson: data (the list) and logic (the loop) are separate, so growth is a one-line change.
"What is a for loop and why is it central to automation?"
A for loop repeats a block of code once for each item in a sequence such as a list of device IPs. It lets one piece of code handle ten or a thousand devices. Mention indentation, the colon, and that the loop variable takes each value in turn.
Key takeaways
- A list holds many values in order; indexes start at 0.
- A for loop runs its indented lines once per item.
- A counter starts at 0 and adds 1 inside the loop.
- To add a device, add one IP to the list.
Pushing configuration to many routers
So far your scripts only read. Now you will change something. A change needs a different tool from send_command, because configuration commands must be typed in configuration mode. The method is send_config_set(). It does by itself what you do by hand: configure terminal, your lines, then end.
One router first
Always try a change on a single device first. This script sends one line, ntp server 10.99.0.200, to R1 and prints what the method returns. The return value is the "screen" of the configuration session:
from nkmiko import ConnectHandler conn = ConnectHandler(device_type="nk_ios", host="10.99.0.1", username="netops", password="NK@2026") result = conn.send_config_set(["ntp server 10.99.0.200"]) print(result) print(conn.save_config()) conn.disconnect()
AUTO> python3 ntp_one.py configure terminal Enter configuration commands, one per line. End with CNTL/Z. KF-DEL-R1(config)#ntp server 10.99.0.200 KF-DEL-R1(config)#end KF-DEL-R1# write mem Building configuration... [OK] KF-DEL-R1#
- The argument is a list of config lines, even for one line. A list lets you send ten lines the same way.
- The returned text shows the prompt changing to
(config)#, the line being accepted, andend. No error text means the router accepted the line. save_config()is the same aswrite memory. A change that is not saved is lost at the next reload.
Now all five routers
Lab 2 gives you ntp_push.py. It is the same loop you know, with the config step inside:
# ntp_push.py - send the same NTP setting to every router from nkmiko import ConnectHandler # the company time server (NTP = Network Time Protocol) NTP_SERVER = "10.99.0.200" # the routers to change - one management IP per router ROUTERS = ["10.99.0.1", "10.99.0.2", "10.99.0.3", "10.99.0.40", "10.99.0.5"] # the config lines to send: the same list for every router CONFIG = ["ntp server " + NTP_SERVER] changed = 0 # a counter that starts at zero for ip in ROUTERS: print("Working on", ip) conn = ConnectHandler(device_type="nk_ios", host=ip, username="netops", password="NK@2026") conn.send_config_set(CONFIG) # conf t, send the lines, end conn.save_config() # same as: write memory conn.disconnect() changed = changed + 1 print(" NTP set, config saved") print("Routers changed:", changed)
Three new ideas. NTP_SERVER is a variable used inside a bigger string: "ntp server " + NTP_SERVER joins two pieces of text with +. CONFIG is a list of lines, ready to grow. changed is a counter that goes up by one after each successful router.
After the router list typo is fixed (chapter 7 shows how you find it), this is the real run:
AUTO> python3 ntp_push.py
Working on 10.99.0.1
NTP set, config saved
Working on 10.99.0.2
NTP set, config saved
Working on 10.99.0.3
NTP set, config saved
Working on 10.99.0.4
NTP set, config saved
Working on 10.99.0.5
NTP set, config saved
Routers changed: 5
Do not trust only the script
A script printing "NTP set" proves that your code ran. It does not prove the router holds the line. Log in to one router that you did not look at before, R4, and check its running configuration:
KF-KOL-R4# show running-config | include ntp
ntp server 10.99.0.200
Worked example. To also set a timezone, you would not write new login code. You only grow the list: CONFIG = ["ntp server " + NTP_SERVER, "clock timezone IST 5 30"]. The same loop now sends two lines to every router.
Common mistake. Sending a configuration line with send_command. It tries to run it in the wrong mode and fails. Use send_config_set for config, send_command for show. Also do not forget save_config().
Exam trap. send_config_set enters and leaves configuration mode for you and takes a list of strings. save_config saves the running configuration to the startup configuration.
The NTP rollout
Kaveri Foods bought a time server so all five branch routers would log events with the same time. One engineer scripted the change for the five routers. The script ran in under a minute and printed "Routers changed: 5". He then logged in to one router, ran the include check, and attached that output to the change ticket as proof.
Lesson: the script does the work, and an independent check from the router proves it.
"How do you push configuration to many devices with a script?"
Keep the device list and the config lines as data, loop over the devices, open a session, send the lines with the config method (send_config_set in netmiko), save, disconnect, and print a result per device. Test on one device first and verify afterwards on the device itself.
Key takeaways
- send_config_set takes a list of lines and handles configuration mode for you.
- save_config() is write memory.
- Try one device first, then loop over all.
- Verify on a router; do not trust only the script's own message.
Reading errors without panic
Every programmer sees errors every day. An error is not a failure; it is Python telling you exactly what went wrong and on which line. When a script stops, Python prints a traceback: a short report of the path it took before it fell. Learning to read it calmly is the most useful skill in this module, and Lab 2 trains it on purpose.
The Lab 2 failure
Kaveri Foods' ntp_push.py has a typo in its router list: the fourth address is written 10.99.0.40 but the router is 10.99.0.4. Run it and watch:
AUTO> python3 ntp_push.py Working on 10.99.0.1 NTP set, config saved Working on 10.99.0.2 NTP set, config saved Working on 10.99.0.3 NTP set, config saved Working on 10.99.0.40 Traceback (most recent call last): File "ntp_push.py", line 16, in <module> conn = ConnectHandler(device_type="nk_ios", host=ip, username="netops", password="NK@2026") nkmiko.exceptions.NetmikoTimeoutException: TCP connection to device failed. Common causes of this problem are: 1. Incorrect hostname or IP address. 2. Wrong TCP port. 3. Intermediate firewall blocking access. Device settings: nk_ios 10.99.0.40:22 (connection timed out)
Four steps to read it
Read a traceback from the bottom up, then connect it to what the script printed.
- Go to the last line first. It names the error type,
NetmikoTimeoutException, and a message: the TCP connection to the device failed. - Find the file and line number just above:
ntp_push.py, line 16, the ConnectHandler call. - Look at what was printed before the traceback: "Working on 10.99.0.40". That is the device that caused it.
- Think like a network engineer. A timeout means no TCP session on port 22. Wrong IP? Router down? SSH not enabled? Here the IP is a typo.
What state did the failure leave behind?
Python runs line by line. The routers handled before the error keep their change; the router that failed and the ones after it get nothing. A short read-only script, verify_ntp.py, shows the truth (you will write it in chapter 8):
AUTO> python3 verify_ntp.py KF-DEL-R1 OK KF-MUM-R2 OK KF-CHN-R3 OK KF-KOL-R4 MISSING KF-JAI-R5 MISSING
Three routers are done, two are not. This half-done state is exactly why you must verify, not assume.
Fix and run again
Before: 8 ROUTERS = ["10.99.0.1", "10.99.0.2", "10.99.0.3", "10.99.0.40", "10.99.0.5"] After: 8 ROUTERS = ["10.99.0.1", "10.99.0.2", "10.99.0.3", "10.99.0.4", "10.99.0.5"]
AUTO> python3 ntp_push.py
Working on 10.99.0.1
NTP set, config saved
Working on 10.99.0.2
NTP set, config saved
Working on 10.99.0.3
NTP set, config saved
Working on 10.99.0.4
NTP set, config saved
Working on 10.99.0.5
NTP set, config saved
Routers changed: 5
Running the script twice is safe here, because setting the same NTP line again changes nothing. Scripts with this property are called idempotent: running them again gives the same end state.
Other errors you will meet
AUTO> python3 bad_pw.py
Traceback (most recent call last):
File "bad_pw.py", line 2, in <module>
conn = ConnectHandler(device_type="nk_ios", host="10.99.0.1", username="netops", password="not-the-password")
nkmiko.exceptions.NetmikoAuthenticationException: Authentication to device failed.
Common causes of this problem are:
1. Invalid username and password
2. Incorrect SSH-key file
3. Connecting to the wrong device
Device settings: nk_ios 10.99.0.1:22
AUTO> python3 typo.py
Traceback (most recent call last):
File "typo.py", line 2, in <module>
print(router)
NameError: name 'router' is not defined. Did you mean: 'routers'?
| Error | Usually means | First check |
|---|---|---|
NetmikoTimeoutException | No TCP connection to port 22 | IP typo, router down, SSH off |
NetmikoAuthenticationException | Login rejected | Username, password, AAA |
SyntaxError | Python cannot read the text | Quote, bracket, colon |
NameError | A word Python does not know | Spelling of the variable |
Common mistake. Reading from the top of the traceback and getting lost. Start from the last line. Also do not re-run a half-failed script blindly if it is not safe to repeat; check the state first.
Exam trap. The exception name tells the category: timeout = reachability or SSH, authentication = credentials, SyntaxError = typing mistake before anything runs, NameError = unknown name at run time.
The typo that became a lesson
At Kaveri Foods the rollout stopped on the fourth router with a timeout. The engineer read the last line, saw the printed "Working on 10.99.0.40", realised the router is .4, fixed one character, and ran the script again. Total time: about two minutes of thinking, and a full list of five routers verified.
Lesson: the traceback plus the last printed line almost always points to the cause.
"A script stops with a connection timeout on one device. What do you do?"
Read the last line (timeout), find which device was being processed from the output before it, then check IP correctness, reachability (ping), and whether SSH is enabled. Then check which devices were already changed, fix the cause, and re-run, making sure the script is safe to repeat.
Key takeaways
- A traceback is a helpful report: read the last line first.
- The line number and the output printed before it identify the device.
- A stopped script leaves earlier devices changed and later ones untouched.
- Run, read, fix, run again is normal programming.
Verifying and safe habits
A pilot does not trust the plane because the engines sound fine. The pilot reads the checklist. Treat every script the same way: check before, change, check after. This chapter gives you a verification script and six habits that keep automation safe.
A read-only verification script
The script below checks the NTP line on every router and prints OK or MISSING. It uses one new idea, a decision. if runs its indented lines only when a condition is true; else runs when it is not. "text" in output is true when the text appears inside the output.
from nkmiko import ConnectHandler
ROUTERS = ["10.99.0.1", "10.99.0.2", "10.99.0.3", "10.99.0.4", "10.99.0.5"]
for ip in ROUTERS:
conn = ConnectHandler(device_type="nk_ios", host=ip, username="netops", password="NK@2026")
name = conn.find_prompt().replace("#", "")
text = conn.send_command("show running-config | include ntp")
if "ntp server 10.99.0.200" in text:
print(name, "OK")
else:
print(name, "MISSING")
conn.disconnect()
Run it before the change (on freshly started routers) to see the starting point:
AUTO> python3 verify_ntp.py KF-DEL-R1 MISSING KF-MUM-R2 MISSING KF-CHN-R3 MISSING KF-KOL-R4 MISSING KF-JAI-R5 MISSING
After ntp_push.py has finished, run the same script again:
AUTO> python3 verify_ntp.py KF-DEL-R1 OK KF-MUM-R2 OK KF-CHN-R3 OK KF-KOL-R4 OK KF-JAI-R5 OK
Because it only sends a show command, this script is read-only: it cannot break anything. That makes it the best kind of first automation.
Six safe habits
- Read first. Start with show-only scripts. Add config only when you trust the script.
- Test on one device. Put one lab or low-risk router in the list, check it, then add the rest.
- Print what you do. A line per device ("Working on ...") is your audit trail and your debugging clue.
- Verify on the device. The independent check (
show running-config | include ...) is the proof. - Prefer repeatable changes. Scripts you can run twice without harm are safer after a half-failed run.
- Keep secrets out of the file. Passwords typed in a script end up in backups and chat messages. The lab shows the placeholder
NK@2026only because it is a lab.
Keep going when one device fails
In chapter 7 one bad IP stopped the whole run. A try / except block lets the script catch an expected error, note it, and carry on with the next router. This is a preview; later modules teach it fully. The script below deliberately includes a bad IP:
from nkmiko import ConnectHandler
from nkmiko.exceptions import NetmikoTimeoutException
ROUTERS = ["10.99.0.1", "10.99.0.40", "10.99.0.5"]
failed = []
for ip in ROUTERS:
try:
conn = ConnectHandler(device_type="nk_ios", host=ip, username="netops", password="NK@2026")
except NetmikoTimeoutException:
print("SKIPPED", ip, "- no SSH connection")
failed.append(ip)
continue
print("OK", ip, conn.find_prompt())
conn.disconnect()
print("Failed:", failed)
AUTO> python3 safe_loop.py OK 10.99.0.1 KF-DEL-R1# SKIPPED 10.99.0.40 - no SSH connection OK 10.99.0.5 KF-JAI-R5# Failed: ['10.99.0.40']
The bad address was skipped and noted; the other two routers were still visited. continue jumps straight to the next item in the list. At the end the script tells you what to look at: Failed: ['10.99.0.40'].
Reference code (not run in the simulator): in real projects you read the password from the environment instead of typing it, for example password = os.environ["NET_PASS"] after import os. Never commit real passwords to Git.
Common mistake. Treating "no error" as "success". A script can finish without an error and still have changed the wrong thing. Only a check on the device tells you the end state. Also never run a new config script on all devices as your first test.
Exam trap. Idempotent means that running the same operation again leaves the same end state. Expect this word, and expect "verify after change" as the right answer to workflow questions.
Green script, red network
A script printed "done" for 30 switches, but a wrong VLAN number sat in its config list. Because the team ran it first on a single test switch and verified, they caught the wrong VLAN there. Nothing reached production. The verify script (read-only) was reused for every later change.
Lesson: a test on one device plus an independent check catches mistakes while they are cheap.
"How do you make a network automation script safe to run in production?"
Review the script, run read-only checks first, test on one non-critical device, print a log line per device, handle expected errors so one failure does not leave the rollout half-done unnoticed, keep credentials out of the code, make the change repeatable, and verify the end state on the devices afterwards. A rollback plan or saved backup config is the final safeguard.
Key takeaways
- Check before, change, check after.
- Read-only scripts are the safest and the best place to start.
- if / else lets a script decide; try / except lets it carry on after an expected error.
- Idempotent scripts are safe to run twice.
- Keep secrets out of scripts and out of Git.
Summary and exam checklist
You started this module never having run a script. You now know why engineers automate, what Python and a script are, how a loop visits many routers, how to push and verify a change, and how to read a traceback. Use this page as your revision sheet.
Can-do checklist
- Explain in one minute why a script beats typing on 50 routers.
- Name the four steps of the automation ladder.
- Run a script with
python3 FILEand view it withnl. - Read a loop over a list of IPs and say what it does.
- Add a router to a script by editing the list only.
- Send config with
send_config_setand save it withsave_config. - Read a traceback from the last line up, and name the cause of a timeout.
- Verify the result on a device with a show command or a read-only script.
Mini glossary
- Script
- A text file of Python steps (.py).
- Variable
- A named box that holds a value.
- String
- Text inside quotes.
- List
- Several values in order, in square brackets.
- Loop (for)
- Repeats a block once per item.
- Library / module
- Ready-made code you import.
- ConnectHandler
- The tool that opens an SSH session to a device.
- Traceback
- Python's report of where and why a script stopped.
- API
- A door made for programs, returning structured data.
- Idempotent
- Safe to run again with the same end state.
Command and code cheat-sheet
| You want to... | Use |
|---|---|
| See files | ls |
| Read a file with line numbers | nl FILE |
| Run a script | python3 FILE.py |
| Open an SSH session | ConnectHandler(device_type=..., host=..., username=..., password=...) |
| Run a show command | conn.send_command("show ...") |
| Send config lines | conn.send_config_set([...]) |
| Save config | conn.save_config() |
| Log out | conn.disconnect() |
Most tested facts
- A comment starts with
#; indentation (4 spaces) and a colon mark a block. - Lists start at index 0;
range(1, 4)gives 1, 2, 3. send_commandis for show output;send_config_setis for configuration.- A timeout exception means no TCP connection; an authentication exception means a login problem.
- A script that stops halfway leaves earlier devices changed; always verify.
- An API returns structured data; a CLI script parses screen text.
Next: the module "Python 1" teaches print, variables, strings and f-strings properly, starting from zero. Then "Python 2" covers lists, dictionaries, loops and decisions in depth.
From fear to first script
A support engineer with CCNA-level knowledge avoided scripts for two years. After running read-only scripts in a lab, she moved to a real inventory check: a 12-line script that listed each router's hostname. It saved her team an hour every Monday, and she added a config step six weeks later.
Lesson: start small and read-only; confidence follows.
"Describe a first network automation project you would suggest to a team."
A read-only inventory or compliance check: loop over a device list, log in, run a show command, compare with an expected value, and print OK or DIFFERENT per device. It is low risk, produces evidence, and builds the skills (loops, parsing, error handling) needed for later config changes.
Key takeaways
- Automation = a script plus a list plus a loop, with verification.
- Read the last line of a traceback first.
- Start read-only, test on one device, verify on the device.
- Next up: Python 1, then Python 2.