NetAuto Academy

Netmiko Fundamentals

Running Show Commands with Netmiko

Beginner14 min read

What you'll learn

  • How send_command() runs a show command and returns its output
  • Why real output is raw text, not a ready-made fact like the Phase 2.6 mock returned
  • How to write a small, reliable function that parses one specific fact out of real CLI text
  • How to do the same for a second, different command's output
  • Why this parsing step is the piece that genuinely didn't exist before this module
🎓 Teach This Lesson

Listen to a guided explanation of this lesson.

Spoken narration isn't supported in this browser.

Running a command

Once a connection is open, send_command() runs a single command and returns whatever the device prints back — as plain text, exactly as it would appear on your screen in a real SSH session:

run_command.py
output = connection.send_command("show ntp status")
print(output)

No parsing, no interpretation — output is the device's raw response, character for character, the same as if you'd typed the command yourself.

That's the real difference this lesson is about. simulate_connection(), back in "Your First Network Automation," handed back a ready-made fact: {"ntp_synced": True}. A real device doesn't do that — it hands back text a person can read, and you have to decide what that text means.

This is the piece that's genuinely new

Everything else in this module's connection code — the parameters, ConnectHandler, the try/except — closely mirrors what "Your First Network Automation" already taught. Parsing raw text into a fact is the one skill that module's mock version never needed, because it made that decision up front for you.

What the output actually looks like

Here's realistic example output for show ntp status on a Cisco IOS device — synced and unsynced, shown in the same format a real device returns:

show ntp status — synced
Clock is synchronized, stratum 4, reference is 192.168.1.1
nominal freq is 250.0000 Hz, actual freq is 249.9990 Hz, precision is 2**18
reference time is DEADBEEF.12345678 (12:34:56.123 UTC Wed Jan 1 2025)
clock offset is 0.0123 msec, root delay is 12.34 msec
root dispersion is 5.67 msec, peer dispersion is 2.34 msec

The word to look for is right at the start: 'Clock is synchronized.'

show ntp status — not synced
Clock is unsynchronized, stratum 16, no reference clock
nominal freq is 250.0000 Hz, actual freq is 250.0000 Hz, precision is 2**18
reference time is 00000000.00000000 (00:00:00.000 UTC Mon Jan 1 1900)
clock offset is 0.0000 msec, root delay is 0.00 msec
root dispersion is 0.00 msec, peer dispersion is 0.00 msec

Same command, same format — but the first line reads 'unsynchronized' instead.

Quick checkConcept check
What does send_command('show ntp status') actually return?

Turning text into a fact

The parsing itself doesn't need anything exotic — checking whether the output starts with a specific phrase is enough:

parse_ntp.py
def is_ntp_synced(show_ntp_output):
  """Check whether 'show ntp status' output indicates the clock is synchronized."""
  first_line = show_ntp_output.strip().splitlines()[0] if show_ntp_output.strip() else ""
  return first_line.startswith("Clock is synchronized")

.strip() removes any leading/trailing blank lines Netmiko sometimes includes; .splitlines()[0] takes just the first line, since that's the only one that matters here; .startswith(...) checks the exact phrase this depends on.

Quick checkPredict output
Given the two example outputs above, what does is_ntp_synced() return for each?

This function is genuinely testable without any device at all — it's just a string check, run against sample text:

test_parsing.py
synced_output = "Clock is synchronized, stratum 4, reference is 192.168.1.1\n..."
unsynced_output = "Clock is unsynchronized, stratum 16, no reference clock\n..."

print(is_ntp_synced(synced_output))    # True
print(is_ntp_synced(unsynced_output))  # False

This is exactly how this function was checked while writing this lesson — no device required. Parsing logic like this is worth testing this way before it's ever pointed at a real connection.

A second command, the same pattern

show vlan brief
VLAN Name                             Status    Ports
---- -------------------------------- --------- -------------------------------
1    default                          active    Gi0/1, Gi0/2, Gi0/3
10   MANAGEMENT                       active    Gi0/4
20   USERS                            active    Gi0/5, Gi0/6

Realistic 'show vlan brief' output — a header, a separator line, then one line per VLAN, starting with its ID.

parse_vlan.py
def is_vlan_present(show_vlan_output, vlan_id):
  """Check whether a given VLAN ID appears in 'show vlan brief' output."""
  for line in show_vlan_output.splitlines():
      stripped = line.strip()
      first_word = stripped.split(" ")[0] if stripped else ""
      if first_word == str(vlan_id):
          return True
  return False

Checking the exact first word of each line — not just whether '20' appears anywhere — matters here: a naive search for '1' would also match inside '10', '100', or a port name. Comparing the whole first word avoids that.

Quick checkIdentify the correct code
Why does is_vlan_present() compare the exact first word of each line, instead of just checking if str(vlan_id) appears anywhere in the output?
Quick checkScenario
Using the sample 'show vlan brief' output above, what does is_vlan_present(output, 99) return?

Key takeaways

  • send_command() runs a command and returns the device's raw text response — no parsing done for you.
  • This is the real difference from the Phase 2.6 mock, which handed back a ready-made fact instead of text to interpret.
  • A small, focused parsing function — checking a specific phrase or an exact line's first word — is usually enough; no advanced text-processing tools are needed.
  • Parsing functions are pure logic, testable against sample CLI text with no device required — worth verifying this way before pointing them at a real connection.
  • Comparing an exact word, not a loose substring, avoids the classic '1 matches inside 10' parsing mistake.