Skip to content

Lab 12 - Exploring Netmiko

In this lab, we will explore using Netmiko for network automation. Recall that Netmiko is an SSH client for Python and in this lab you'll be SSHing and automating Cisco IOS devices. Even in a world where there are only APIs on network devices, you may still need to automate turning on the API. And to do that, Netmiko is stil a great choice.

Task 1 - Use Netmiko in Exec Mode

This task will show you how to establish an interactive SSH session to a Cisco router running IOS using Netmiko.

Note: More specifically, this lab uses the CSR1000v running IOS-XE.

Step 1-1

Make sure you can ping csr1 from the Linux terminal.

ntc@ntc-training:python$ ping csr1 -c 3
PING csr1 (172.18.0.6) 56(84) bytes of data.
64 bytes from csr1.ntc-training (172.18.0.6): icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from csr1.ntc-training (172.18.0.6): icmp_seq=2 ttl=64 time=0.093 ms
64 bytes from csr1.ntc-training (172.18.0.6): icmp_seq=3 ttl=64 time=0.090 ms

--- csr1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 46ms
rtt min/avg/max/mdev = 0.079/0.087/0.093/0.009 ms

Note: The IP address you have might be different from the above and that is OK.

Step 1-2

Navigate to the /home/ntc/files directory and enter the Python Interactive Interpreter.

ntc@ntc-training:~$ cd /home/ntc/files
ntc@ntc-training:files$ python
Python 3.8.12 (default, Oct 13 2021, 09:22:51)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Step 1-3

Import the netmiko ConnectHandler function and establish an SSH session to the Cloud Services Router device with the following details:

  • Hostname: csr1
  • Username: ntc
  • Password: ntc123
  • SSH port: 22
>>> from netmiko import ConnectHandler
>>>
>>> platform = 'cisco_ios'
>>> host = 'csr1'
>>> username = 'ntc'
>>> password = 'ntc123'
>>>
>>> device = ConnectHandler(device_type=platform, ip=host, username=username, password=password)
>>>

Step 1-4

Issue a dir() on device to see available methods that can be called.

>>> dir(device)
['RESPONSE_RETURN', 'RETURN', 'TELNET_RETURN', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_autodetect_fs', '_build_ssh_client', '_config_mode', '_connect_params_dict', '_first_line_handler', '_legacy_mode', '_lock_netmiko_session', '_modify_connection_params', '_open', '_read_channel', '_read_channel_expect', '_read_channel_timing', '_sanitize_output', '_session_locker', '_session_log_close', '_session_log_fin', '_test_channel_read', '_timeout_exceeded', '_try_session_preparation', '_unlock_netmiko_session', '_use_ssh_config', '_write_channel', '_write_session_log', 'allow_agent', 'allow_auto_change', 'alt_host_keys', 'alt_key_file', 'ansi_escape_codes', 'auth_timeout', 'banner_timeout', 'base_prompt', 'blocking_timeout', 'check_config_mode', 'check_enable_mode', 'cleanup', 'clear_buffer', 'close_session_log', 'commit', 'config_mode', 'conn_timeout', 'device_type', 'disable_paging', 'disconnect', 'enable', 'encoding', 'establish_connection', 'exit_config_mode', 'exit_enable_mode', 'fast_cli', 'find_prompt', 'global_cmd_verify', 'global_delay_factor', 'host', 'is_alive', 'keepalive', 'key_file', 'key_policy', 'normalize_cmd', 'normalize_linefeeds', 'open_session_log', 'paramiko_cleanup', 'passphrase', 'password', 'pkey', 'port', 'protocol', 'read_channel', 'read_until_pattern', 'read_until_prompt', 'read_until_prompt_or_pattern', 'remote_conn', 'remote_conn_pre', 'run_ttp', 'save_config', 'secret', 'select_delay_factor', 'send_command', 'send_command_expect', 'send_command_timing', 'send_config_from_file', 'send_config_set', 'serial_login', 'serial_settings', 'session_log', 'session_log_record_writes', 'session_preparation', 'session_timeout', 'set_base_prompt', 'set_terminal_width', 'sock', 'special_login_handler', 'ssh_config_file', 'strip_ansi_escape_codes', 'strip_backspaces', 'strip_command', 'strip_prompt', 'system_host_keys', 'telnet_login', 'timeout', 'use_keys', 'username', 'verbose', 'write_channel']
>>>

Step 1-5

Verify there is an active connection to the device. Verify it is alive.

>>> device.is_alive()
True
>>>

You could also view the help menu for this method:

>>> help(device.is_alive)
Help on method is_alive in module netmiko.base_connection:

is_alive() method of netmiko.cisco.cisco_ios.CiscoIosSSH instance
    Returns a boolean flag with the state of the connection.
(END)

Step 1-6

Now we can send a few "show" or "exec" level commands. To do this, use the send_command method.

Execute the command show version and save the response as a variable:

>>> output = device.send_command('show version')
>>>
>>> print(output)
Cisco IOS XE Software, Version 17.01.01
Cisco IOS Software [Amsterdam], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.1.1, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2019 by Cisco Systems, Inc.
Compiled Fri 22-Nov-19 03:39 by mcpre


Cisco IOS-XE software, Copyright (c) 2005-2019 by cisco Systems, Inc.
All rights reserved.  Certain components of Cisco IOS-XE software are
licensed under the GNU General Public License ("GPL") Version 2.0.  The
software code licensed under GPL Version 2.0 is free software that comes
with ABSOLUTELY NO WARRANTY.  You can redistribute and/or modify such
GPL code under the terms of GPL Version 2.0.  For more details, see the
documentation or "License Notice" file accompanying the IOS-XE software,
or the applicable URL provided on the flyer accompanying the IOS-XE
software.


ROM: IOS-XE ROMMON

csr1 uptime is 5 days, 7 hours, 18 minutes
Uptime for this control processor is 5 days, 7 hours, 19 minutes
System returned to ROM by reload
System image file is "bootflash:packages.conf"
Last reload reason: reload



This product contains cryptographic features and is subject to United
States and local country laws governing import, export, transfer and
use. Delivery of Cisco cryptographic products does not imply
third-party authority to import, export, distribute or use encryption.
Importers, exporters, distributors and users are responsible for
compliance with U.S. and local country laws. By using this product you
agree to comply with applicable laws and regulations. If you are unable
to comply with U.S. and local laws, return this product immediately.

A summary of U.S. laws governing Cisco cryptographic products may be found at:
http://www.cisco.com/wwl/export/crypto/tool/stqrg.html

If you require further assistance please contact us by sending email to
export@cisco.com.

License Level: ax
License Type: N/A(Smart License Enabled)
Next reload license Level: ax


Smart Licensing Status: UNREGISTERED/No Licenses in Use

cisco CSR1000V (VXE) processor (revision VXE) with 2078006K/3075K bytes of memory.
Processor board ID 9SAGBHTUEE9
9 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
3978444K bytes of physical memory.
6188032K bytes of virtual hard disk at bootflash:.

Configuration register is 0x2102

>>>

Step 1-7

Re-issue the same command using a pipe include to only return the Configuration register:

>>> output = device.send_command('show version | include register')
>>>
>>> print(output)
Configuration register is 0x2102
>>>

Step 1-8

If you want to ensure all config registers are correct, you can use the in containment keyword we covered in the booleans lab like so:

>>> '0x2102' in output
True
>>>

Maybe it's more important that the device just doesn't boot into ROMMON mode:

>>> '0x2142' not in output
True
>>>

Step 1-9

Save the configuration using the wr mem command.

>>> output = device.send_command('wr mem')
>>>
>>> print(output)
Building configuration...
[OK]
>>>

You can always view the output coming back from the device by saving it to a variable.

Step 1-10

Let's try checking connectivity to the IP address 10.0.0.15.

>>> output = device.send_command('ping 10.0.0.15')
>>>
>>> print(output)
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.0.15, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/9 ms
>>>

Task 2 - Issue Configuration Commands with Netmiko

In this task, we'll explore two methods to send configuration commands to the device.

Option 1: - send a list of commands to the device.

Option 2: - send commands from a file.

While you can use the send_command method, it's much cleaner to use the methods shown in this task, e.g. send_config_set and send_config_from_file.

Step 2-1

Add a new loopback to the device by first creating a list of commands you want to send:

>>> commands = ['interface Loopback100', 'ip address 10.200.1.20 255.255.255.0']
>>>

Step 2-2

Use the send_config_set method takes a list as a parameter:

>>> output = device.send_config_set(commands)
>>>

You can optionally print the response seeing what Netmiko is doing under the covers. Notice how it is automatically going into and exiting configuration mode.

>>> print(output)
configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
csr1(config)#interface Loopback100
csr1(config-if)#ip address 10.200.1.20 255.255.255.0
csr1(config-if)#end
csr1#
>>>

Step 2-3

You can also use help() to learn more about each method just like you saw with the built-in Python data types.

For example, here is the help on send_config_set:

>>> help(device.send_config_set)

Help on method send_config_set in module netmiko.base_connection:

send_config_set(config_commands=None, exit_config_mode=True, delay_factor=1, max_loops=150, strip_prompt=False, strip_command=False, config_mode_command=None, cmd_verify=True, enter_config_mode=True, error_pattern='') method of netmiko.cisco.cisco_ios.CiscoIosSSH instance
    Send configuration commands down the SSH channel.

    config_commands is an iterable containing all of the configuration commands.
    The commands will be executed one after the other.

    Automatically exits/enters configuration mode.
--- OUTPUT SNIPPED ---

Note: Remember you can press q to exit the help function.

Step 2-4

Add two community strings and verify they're configured.

>>> snmp_commands = ['snmp-server community ntclab RO', 'snmp-server community ntcrw RW']
>>>
>>> response = device.send_config_set(snmp_commands)
>>>
>>> verify = device.send_command('show run | inc snmp-server community')
>>>
>>> print(verify)
snmp-server community ntc-public RO
snmp-server community ntc-private RW
snmp-server community ntclab RO
snmp-server community ntcrw RW
>>>

Step 2-5

Let's try to send commands from a file now.

The file called config.txt should exist on your pod in the /home/ntc/files/ directory. You can use the os module to send Linux commands to the shell. Verify that the file exists:

>>> import os
>>>
>>> os.system('ls /home/ntc/files/config.txt')
/home/ntc/files/config.txt
0

Step 2-6

Make sure that you can see the following commands in config.txt:

>>> os.system('cat /home/ntc/files/config.txt')
!
snmp-server community supersecret RW
snmp-server community notprivate RO
!
interface Loopback101
 ip address 10.9.88.1 255.255.255.0
!
0

Step 2-7

Deploy the commands from the config file using the Netmiko method called send_config_from_file:

>>> output = device.send_config_from_file('/home/ntc/files/config.txt')
>>>

Step 2-8

Verify output has the commands without any errors being received from the device:

>>> print(output)
configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
csr1(config)#!
csr1(config)#snmp-server community supersecret RW
csr1(config)#snmp-server community notprivate RO
csr1(config)#!
csr1(config)#interface Loopback101
csr1(config-if)# ip address 10.9.88.1 255.255.255.0
csr1(config-if)#!
csr1(config-if)#end
csr1#
>>>

Task 3 - Use Other Built-in Methods

In this task, you'll get to see a few more built-in methods.

Step 3-1

Enter Config Mode:

>>> device.config_mode()
'configure terminal\r\nEnter configuration commands, one per line.  End with CNTL/Z.\r\ncsr1(config)#'
>>>

Step 3-2

By default send_command waits for the device "prompt string" to return to what it was. If you're choosing to send non-global config commands using send_command, the prompt will change. Therefore, you should be aware of send_command_timing:

>>> data = device.config_mode()
>>> data = device.send_command_timing('interface Gigabit3')
>>>

However, to keep things simple, you should use send_config_set and send_config_from_file when sending configuration commmands.

Step 3-3

You can always view your prompt string:

>>> print(device.find_prompt())
csr1(config-if)#
>>>

Step 3-4

Exit configuration mode:

>>> device.exit_config_mode()
'end\r\ncsr1#'
>>>

Step 3-5

Disconnect from the device

>>> device.disconnect()
>>>

Step 3-6

Validate the SSH connection is no longer active:

>>> device.is_alive()
False
>>>

Step 3-7

Re-establish connection back to the device:

>>> device.establish_connection()
''
>>>

Step 3-8

Finally, disconnect one final time.

>>> device.disconnect()
>>>

Note: Your scripts should always try to close sessions gracefully, otherwise you might use up all available VTYs on the device with open sessions when testing, especially if your scripts error out or crash.