Network automation can take many forms depending on the technologies and environments. Sometimes though, we have to dive deep into low-level automation, where we can’t use REST API or other more-convenient technologies. In this article, we will take a brief look at the pexpect python library, which can be used for low-level automation.
As an example, we will automate the ssh connection to the Cisco router. The whole workflow will look like this:
- Login to the Cisco router via ssh
- Execute show platform command
- Print output of the show command to the console
Bear in mind, that there are more user-friendly libraries to handle ssh connections to the Cisco routers, for example Netmiko.
Workflow overview
First, let’s take a look at the console output from the workflow we want to automate.
garzum@server:~$ ssh admin@10.0.1.2
Password:
Router#show platform
Chassis type: C1113-8PMLTEEA
Slot Type State Insert time (ago)
--------- ------------------- --------------------- -----------------
0 C1113-8PMLTEEA ok 2w0d
0/0 C1113-1x1GE ok 2w0d
0/1 C1113-ES-8 ok 2w0d
0/2 C1113-LTE ok 2w0d
0/3 C1113-GFAST-M ok 2w0d
R0 C1113-8PMLTEEA ok, active 2w0d
F0 C1113-8PMLTEEA ok, active 2w0d
P0 PWR-12V ok 2w0d
Slot CPLD Version Firmware Version
--------- ------------------- ---------------------------------------
0 17100501 17.5(1r)
R0 17100501 17.5(1r)
F0 17100501 17.5(1r)
Router#
Since we know what the output looks like, let’s start coding!
Establishing connection
In the beginning, we need to import pexpect library.
import pexpect
For convenience and code readability, let’s create variables with login data.
username = "admin"
password = "admin"
ip = "10.0.1.2"
The last variable before the connection will have a string with a router prompt assigned to it.
prompt = "Router#"
Now, we’re ready to establish a connection!
child = pexpect.spawn(f"ssh {username}@{ip}")
child.expect("Password:")
child.sendline(password)
child.expect(prompt)
To establish a connection we need to use the spawn method from pexpect library. As an argument, we’re passing a command which initiates it. In our case, it’s ssh admin@10.0.1.2. After that, we can interact with the router using the child variable.
If you take a look at the workflow output, right after executing ssh command, the router asks us for the password. So in our code, we’re expecting, that router(child) will send us a line with Password: string.
To send password, we’re using the sendline method. As an argument, we’re passing a variable password, which contains a string.
What happens next? Router displays its prompt – Router#. The following expect waits for it.
But what if we don’t know what’s the hostname of the router? Fortunately, you can pass a string with regex as an argument to the expect method, for example “.+#”.
Now, we’re ready to display router platform information.
Show command execution
child.sendline("show platform")
child.expect(prompt)
After sending show platform command to the router, we’re again expecting a prompt. That way we know that the execution of show command is over.
“But where is the output?” – You may ask. In pexpect, we have to pull it out of the child object. The whole interaction with the router is not printed directly to the console.
output = child.before.decode()
print(output)
The output of the show platform command is stored in the before variable, that’s in the child object. We can refer to it with child.before. In the same line, we’re using a decode method before assignment to the output variable. It’s because child.before is a bytes type. The decode method converts bytes to the str, which is printed to the console in the next line.
The complete code is as follows.
import pexpect
username = "admin"
password = "admin"
ip = "10.0.1.2"
prompt = "Router#"
child = pexpect.spawn(f"ssh {username}@{ip}")
child.expect("Password:")
child.sendline(password)
child.expect(prompt)
child.sendline("show platform")
child.expect(prompt)
output = child.before.decode()
print(output)
You can find the code in my Pexpect Examples repository.
It produces the following output.
show platform
Chassis type: C1113-8PMLTEEA
Slot Type State Insert time (ago)
--------- ------------------- --------------------- -----------------
0 C1113-8PMLTEEA ok 2w1d
0/0 C1113-1x1GE ok 2w1d
0/1 C1113-ES-8 ok 2w1d
0/2 C1113-LTE ok 2w1d
0/3 C1113-GFAST-M ok 2w1d
R0 C1113-8PMLTEEA ok, active 2w1d
F0 C1113-8PMLTEEA ok, active 2w1d
P0 PWR-12V ok 2w1d
Slot CPLD Version Firmware Version
--------- ------------------- ---------------------------------------
0 17100501 17.5(1r)
R0 17100501 17.5(1r)
F0 17100501 17.5(1r)
Summary
That’s for the basics of the pexpect library. In this article, the ssh connection was used as an example but personally, I like to use pexpect more when I’m interacting with devices via the console connection.
I hope that you’ve gained a basic overview of how it works. In the next articles, I’ll show you more complicated examples as we will dive deeper into the pexpect abyss.
One thought on “Basic Cisco router automation workflow with pexpect”