Non-Interactive SSH use case with Python

Sometimes the best way to learn to do something useful with a scripting language is with a starting point and a real world use case. While I don’t consider myself a Python expert, I can usually figure out how to put things together and get a task accomplished. For this article I challenged myself to create a simple script that performs the following:

  • Open a file for a list of devices and credentials
  • Log in to each device in the file using the credentials found
  • Remove the current NTP server (1.1.1.1)
  • Add a new NTP server (2.2.2.2)
  • Save the configuration

I am sharing the script below as an example. Note this Python file uses paramiko. Therefore that library needs to be installed (MAC users – sudo pip install paramiko)

NTPChange.py

import paramiko

####devices.txt format
#### username,password,host
#### username,password,host

qbfile = open("devices.txt", "r")

for aline in qbfile:
    values = aline.split(",")
    myuser = values[0]
    mypass = values[1]
    myhost = values[2].rstrip()
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(myhost, username=myuser, password=mypass)
    channel = ssh.invoke_shell()
    stdin = channel.makefile('wb')
    stdout = channel.makefile('rb')
    stdin.write('''
    conf t
    no ntp server 1.1.1.1
    ntp server 2.2.2.2
    end
    wr
    exit
    ''')
    print stdout.read()
    ssh.close()

qbfile.close()

devices.txt

cisco,cisco,172.16.1.132
cisco,cisco,172.16.1.133
cisco,cisco,172.16.1.134

Running NTPChange.py yields the following output.

csr1000v-1#
csr1000v-1#    conf t
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v-1(config)#    no ntp server 1.1.1.1
csr1000v-1(config)#    ntp server 2.2.2.2
csr1000v-1(config)#    end
csr1000v-1#    wr
Building configuration...
[OK]
csr1000v-1#    exit

csr1000v-2#
csr1000v-2#    conf t
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v-2(config)#    no ntp server 1.1.1.1
csr1000v-2(config)#    ntp server 2.2.2.2
csr1000v-2(config)#    end
csr1000v-2#    wr
Building configuration...
[OK]
csr1000v-2#    exit

csr1000v-3#
csr1000v-3#    conf t
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v-3(config)#    no ntp server 1.1.1.1
csr1000v-3(config)#    ntp server 2.2.2.2
csr1000v-3(config)#    end
csr1000v-3#    wr
Building configuration...
[OK]
csr1000v-3#    exit


Process finished with exit code 0

Spot checking one of the routers shows that the script functioned as expected.

csr1000v-2#show run | sec ntp
ntp server 2.2.2.2
csr1000v-2#

Downloads

The files created for this example can be downloaded here.

Conclusion

While Python can become very complex and produce very complex functions, it doesn’t necessarily have to be. Specifically, this example shows a non-interactive example where the programmer is assuming the next commands to enter. There is no qualification or validation. My recommendation is to start with simple scripts and thoroughly test in a controlled environment prior to rolling out anything to hundreds (or thousands) of devices. Leveraging Python, and other scripting tools, can dramatically increase an engineer’s efficiency. However it is important to go through a thorough validation process before executing at scale.

Disclaimer: This article includes the independent thoughts, opinions, commentary or technical detail of Paul Stewart. This may or may does not reflect the position of past, present or future employers.

About Paul Stewart, CCIE 26009 (Security)

Paul is a Network and Security Engineer, Trainer and Blogger who enjoys understanding how things really work. With over 15 years of experience in the technology industry, Paul has helped many organizations build, maintain and secure their networks and systems.
This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to Non-Interactive SSH use case with Python

  1. reaper81 says:

    Great post, Paul!

    It’s possible to rewrite the opening of the file a bit to the following syntax:

    with open(“devices.txt”, “r”) as qbfile

    The benefit of the syntax above is that the file will automatically be closed for you.

Leave a Reply