Netmiko4 Feature: send_multiline()

for handling additional prompts

Author: Kirk Byers
Date: August 3rd, 2021
Netmiko Logo

Introduction

In an earlier article I introduced the send_multiline_timing() feature. In this article let's talk about a closely related feature namely: send_multiline(). Both of these features are in the current Netmiko develop branch and should be released some time in early 2022.

The Problem

What is the problem we are trying to solve with send_multiline()?

Basically, network automation has situations where we execute a command on a network device and then we are prompted for additional information. For example:

cisco3#delete flash:/my_file.txt
Delete filename [my_file.txt]?
Delete bootflash:/my_file.txt? [confirm]y
cisco3#

And similarly, an extended ping on Cisco IOS-XE would look like the following:

cisco3#ping
Protocol [ip]:
Target IP address: 8.8.8.8
Repeat count [5]: 30
Datagram size [100]:
Timeout in seconds [2]:
Extended commands [n]:
Sweep range of sizes [n]:
Type escape sequence to abort.
Sending 30, 100-byte ICMP Echos to 8.8.8.8, timeout is 2 seconds:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Success rate is 100 percent (30/30), round-trip min/avg/max = 1/2/4 ms
cisco3#

A Pattern-Based Solution

Now, in the previous article on send_multiline_timing(), I showed you a way to solve this problem using an entirely timing based solution. In this article instead of using a timing based solution, we will use a pattern-based solution.

In other words, what if we sent a command and then searched for a pattern in the output. Once the pattern was detected, then we would send the next command and search for the next pattern. We would repeat this command + pattern behavior until we have properly handled all the additional prompting.

Thus if we are trying to delete a file named "myfile.txt", we would do the following:

send: "del flash:/myfile.txt"

And the router would respond back with:

response: "Delete filename [myfile.txt]?"

Once we saw this response, we would then send an <enter>:

send: "\n"

And then the router responds back with:

response: "Delete bootflash:/myfile.txt? [confirm]"

Basically, the router is asking us, 'do you really want to do this' and we send a 'y' for yes.

send: "y"

Now at this point the next response back would be the router's prompt (i.e. we are completely done handling the special prompts).

In my test environment this router prompt is: "cisco3#". Consequently, we could search for that entire prompt ("cisco3#"), or for part of that prompt ("#"). As an alternate solution, with send_multiline() if using a null-string as a pattern, then Netmiko will automatically search for the trailing prompt.

So representing all of the above into Python code, we would end up with the following:

with ConnectHandler(**device) as net_connect:
    filename = "myfile.txt"
    cmd_list = [
        [f"del flash:/{filename}", r"Delete filename"],
        ["\n", r"confirm"],
        ["y", ""],
    ]

    output = net_connect.send_multiline(cmd_list)
    print(output)

Note, that cmd_list is a list of lists with each element of the inner list being the command to send and the pattern to search for, so our first element is the following:

        [f"del flash:/{filename}", r"Delete filename"]

Here the command is "del flash:/myfile.txt" and the pattern that we are searching for is the string "Delete filename".

Note, the patterns are all regular expression patterns so special care must be taken to avoid special regex characters (unless you really mean to use them). You can observe that with the "confirm" pattern that I intentionally avoided using the square brackets in the pattern (as I knew these bracket characters are special regex characters).

If you want to use a string literal that contains special characters and you want all of the characters to be just the actual characters (and not regex characters), then you can use re.escape() to accomplish this(it will automatically escape any special characters).

For example:

In [2]: re.escape("[confirm]")
Out[2]: '\\\[confirm\\\]'

Also note, in my final [cmd, pattern] list, I send the following:

        ["y", ""],

Here I send the "y" character indicating yes and I use a null-string to indicate that I want to automatically search for the trailing prompt as the pattern.

And then finally, we feed all of these commands and patterns into the send_multiline() method, and it does its magic of sending the commands, searching for the patterns, and returning the output:

    output = net_connect.send_multiline(cmd_list)
    print(output)

Here is the output of executing this script (the name of the file I deleted was different):

$ python delete_file.py 
Password: 
del flash:/test9-dp.txt
Delete filename [test9-dp.txt]? 
Delete bootflash:/test9-dp.txt? [confirm]y
cisco3#
cisco3#

Reference code used in this article is here

Kirk Byers

@kirkbyers

You might also be interested in: