4 minute read

Short and simple script to automate bouncing a port on IOS-XE.

Port bounce CLI with IOS-XE scripts

Some time ago I worked on a team that wanted a single command to disable and re-enable an interface on network devices (switch, router, etc.). We call this “bouncing” an interface. At the time, I used Tcl on IOS and IOS-XE to implement interface bouncing. I recently saw a request for the same thing and thought it worth an hour to see if the guest shell with Python on IOS-XE added any interesting capabilities to make this kind of macro different.

Guest shell uses the IOx App Hosting infrastructure. It runs in a container using the host system’s Linux kernel.

Some clear use cases for this capability are bouncing an interface that you are managing the device through and bouncing an interface the device uses for TACACS+ authorization. In both of these instances, you ability to manage the device after shutting down an interface is affected. If the command encompassed both disabling and re-enabling, it wouldn’t matter if the management session was cut off via connectivity or command authorization.

I did not spend a lot of time on these examples. This is a basic exploration of naive methods to address the use cases above.

Neither of these methods address these use cases. I thought they might work, but they don’t. If you run either of these macros and terminate the management session or connectivity to a TACACS+ server for command authorization is impeded, the second half of the macro (no shutdown) doesn’t work. The result is the interface stays disabled.

The use case of having a single command for bouncing an interface is served by these methods. Likely not worth the effort, though.

These macros are basically failed attempts at solving the two use cases above, but I thought I’d post them as examples any way. I suspect functionality like this will have to be implemented deeper in the CLI stack so re-enabling the interface command can survive loss of the management session or command authorization server.

Tcl Script

set interface [lindex $argv 0]
set seconds [lindex $argv 1]
set seconds_sum [expr $seconds *1000]
puts "Interface $interface will be bounced with a $seconds delay. Are you sure? (y/n)"
set answer [gets stdin]
if {$answer == "y"} {
    ios_config "interface $interface" "shutdown"
    after $seconds_sum
    ios_config "interface $interface" "no shutdown"
}

Guest Shell with Python Script

Here we’re enalbing IOx, App Hosting, and Guest shell.

The command tests if Python3 runs in the Guest shell.

conf t
iox
app-hosting appid guestshell
app-vnic management guest-interface 0
end

guestshell enable
guestshell run python3

This is the Python script to grab the arguments (interface name and delay in seconds), disable the interface, sleep, and re-enable the interface.

import sys
import time
from cli import configure

interface = sys.argv[1]
seconds = int(sys.argv[2])
cli.configure([interface, "shutdown", "end"])
time.sleep(seconds)
cli.configure([interface, "no shutdown", "end"])

Alias to give the bounce CLI command:

alias exec bounce guestshell run python3 /flash/guest-share/bounce.py

Usage:

bounce Loopback4 2

Scaled deployment

I’ve included some examples of how these scripts could be deployed to multiple devices using Nornir, Scrapli, and Netmiko.

Deployment of the Tcl script and alias

This script uses Nornir as the framework, Netmiko for file transfer, and Scrapli for modifying the running configuration to add an alias.

# Configuration and commands
alias_cfg = [
    'alias exec bounce tclsh bounce.tcl'
]

# Initialize Nornir
nr = InitNornir(config_file='config.yaml')

# Copy script file to device flash
script_file_result = nr.run(
    task=netmiko_file_transfer,
    source_file='bounce.tcl',
    dest_file='bounce.tcl',
    overwrite_file=True,
    name='Copy bounce.tcl script to network device.'
)
print_result(script_file_result)

# Create alias for bounce
alias_result = nr.run(
    task=send_configs,
    configs=alias_cfg,
    name='Create CLI alias.'
)
print_result(alias_result)

Deployment of the guest shell Python script and alias

We’re using the same tools as before (Nornir, Netmiko, Scrapli), but we’re enabling the IOS-XE App Hosting capability to use the guest shell.

In this case, I used Nornir’s tasks grouping feature. It’s a very nice option to group up tasks into a single function to pass to the task runner. I grouped all of our file operation tasks into a single task.

def put_script_file(task: Task, actions: dict) -> Result:
    task.run(
        task=send_commands,
        commands=actions['del_script_exec'],
        name='Delete previous script.'
    )
    task.run(
        task=netmiko_file_transfer,
        source_file='bounce.py',
        dest_file='bounce.py',
        overwrite_file=True,
        name='Copy script to network device.'
    )
    task.run(
        task=send_interactive,
        interact_events=actions['copy_interact'],
        name='Copy script to guest-share directory.'
    )
    task.run(
        task=send_commands,
        commands=actions['del_root_script_exec'],
        name='Delete script from root directory on flash.'
    )
    return Result(host=task.host,
        result='Successfully copied script file to device.'
    )

def main():
    # Configuration and commands
    guestshell_cfg = [
        'iox',
        'app-hosting appid guestshell',
        'app-vnic management guest-interface 0'
    ]

    guestshell_exec = [
        'guestshell enable',
        'guestshell run python3 -V'
    ]

    script_file_actions = {
        'copy_interact': [
            ('copy flash: flash:', 'Source filename []?', False),
            ('bounce.py', 'Destination filename [bounce.py]?', False),
            ('guest-share/bounce.py', '#', False)
        ],
        'del_script_exec': [
            'delete /force flash:guest-share/bounce.py',
            'delete /force flash:bounce.py'
        ],
        'del_root_script_exec': [
            'delete /force flash:bounce.py'
        ]
    }

    alias_cfg = [
        'alias exec bounce guestshell run python3 /flash/guest-share/bounce.py'
    ]

    # Initialize Nornir
    nr = InitNornir(config_file='config.yaml')

    # Configure iox and app hosting for guestshell
    guestshell_config_results = nr.run(
        task=send_configs,
        configs=guestshell_cfg,
        name='Configure IOx and app hosting.',
        stop_on_failed=True
        )
    print_result(guestshell_config_results)

    # Sleep because it can take 1-2 minutes for IOx to start
    # https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/175/b_175_programmability_cg/m_175_prog_guestshell.html
    sleep(60)

    # Enable guestshell
    guestshell_enable_results = nr.run(
        task=send_commands,
        commands=guestshell_exec,
        name='Enable guestshell.',
        stop_on_failed=True
    )
    print_result(guestshell_enable_results)

    # Copy script file to device flash
    script_file_result = nr.run(
        task=put_script_file,
        actions=script_file_actions,
        name='Copy script file to device.'
    )
    print_result(script_file_result)

    # Create alias for bounce
    alias_result = nr.run(
        task=send_configs,
        configs=alias_cfg,
        name='Create CLI alias.'
    )
    print_result(alias_result)

Quit()

These scripts have dubious value and I did not spend more than a few hours on these examples. On the off chance they’re helpful to my future self or someone else, here they are!

Resources

bounce-cli-iosxe Github Repo
Guest Shell on IOS-XE
Nornir
Scrapli
Netmiko

Updated: