Search code examples
pythonsshnetmiko

Netmiko read_channel() and Clear_buffer() not printing output


So, I'm trying to push some configuration to my switches automatically but those commands typically return a (y/n) prompt in the switch. As a result, the send_command function will not work as it looks for the prompt, so I'm using the write_channel() function which works perfectly. However, for some reason, neither the read_channel nor the clear_buffer() functions print more than 1 or 2 lines of output after I send a command.

Things I've tried:
Putting the read_channel/buffer in a slow loop.
read_until_prompt/pattern
send_command with cmd_verify=False and auto_find_prompt=False
printing read_channel
and more. . .

send_command can display output no problem and its source code uses read_channel() to push output, so I'm not sure what else to try.

Goal: I need to get the output from a command in a cisco switch (reload in 001:00), so that I can then tell the program how to react to the prompt if there is one.

Here is some basic code I wrote to troubleshoot the problem in my main project:

from netmiko import ConnectHandler
import time

Network_Device = {
                "ip": "10.251.11.38",
                "username": "user",
                "password": "pass",
                "secret": "secretpass",
                "device_type": "cisco_ios",
                "fast_cli": False
                }
        
#Connect = ConnectHandler(**Network_Device)

with ConnectHandler(**Network_Device) as ssh:
    ssh.enable()
    while True:
        x = input(ssh.find_prompt())
        if x == '':
            pass
        elif x == 'exit' or x == 'Exit':
            ssh.disconnect()
        else:
            ssh.write_channel(x) # Writes command to cli

            #Problem lies on the two functions below:  Neither will print out a show command fully.
            ssh.clear_buffer(backoff=True)
            ssh.read_channel()
    

    

Solution

  • Yes, I want to reload dozen's of switches at approximately the same time for a dhcp update.

    It's obvious now you want to reload a bunch of switches. The reload prompts you with Proceed with reload? [confirm]. So you can you use expect_sting argument to either confirm or deny the reload. To do so, it's better to save the configuration first before the reload to avoid any config loss.

    A quick brief of how send_command works. send_command is pattern-based where it checks for the device prompt to know that the command output is done. On the other hand send_command_timing is delay-based meaning it waits for some time and doesn't check for anything to know if the command is done running or not.

    from netmiko import ConnectHandler
    
    device = {
        "device_type": "",
        "ip": "",
        "username": "",
        "password": "",
        "secret": "",
        "conn_timeout": 12,
        "fast_cli": False,
    }
    
    conn = ConnectHandler(**device)
    
    # Check if not logged into enable mode directly
    # and enter enable mode if logged into user mode
    if not conn.check_enable_mode():
        conn.enable()
    
    conn.save_config()  # save configuration first
     
    conn.send_command(command_string="reload", expect_string=r"confirm")
    

    Notice here the expect_string which informs the send_command I am done.

    Now you have one of two options:

    1. send y to confirm the reload (Discouraged)
    2. call conn.disconnect() to both confirm the reload and exit (Preferred)

    Option 1 is discouraged because you will not be able to disconnect after confirming the reload and eventually an OSError or EOFError exception is raised that you will have to handle.

    conn.disconnect()
    

    The above line sends a \n first, then disconnects from the device. This lets you handle other devices reload immediately and you don't have to handle any raised OSError or EOFError excpetions.

    Full example

    from netmiko import ConnectHandler
    
    device = {
        "device_type": "",
        "ip": "",
        "username": "",
        "password": "",
        "secret": "",
        "conn_timeout": 12,
        "fast_cli": False,
    }
    
    conn = ConnectHandler(**device)
    
    if not conn.check_enable_mode():
        conn.enable()
    
    conn.save_config()
    
    # Notice here the `expect_string` which informs 
    # the `send_command` I am done. 
    # I am telling `send_command` function don't look at 
    # the device prompt but look for `"confirm"` to proceed 
    # with the next command.
     
    conn.send_command(command_string="reload", expect_string=r"confirm")
    
    conn.disconnect()
    
    print(f'Reloading {device["ip"]}... Please wait for some time for {device["ip"]} to boot up again')
    

    I didn't use the context manager method on purpose to make use of conn.disconnect() function.

    UPDATE

    As recommended by Kirk Byers (the author of netmiko) in the comments, it's recommended to just add r"confirm" in the expect_string and omit the square brackets.