Showing results for 
Search instead for 
Did you mean: 

Introducing Python and Guest Shell on IOS-XE 16.5

Cisco Employee



I’ve been long waiting for the latest version of IOS-XE to ship, and on April 13th IOS-XE 16.5 “Everest” posted to for the ISR, ASR, and CSR routing platforms (still to come are the Catalyst Switching lines).  The “Everest" release of IOS-XE is jam packed with new programmability features that I think everyone will agree are excellent additions as network programmability is becoming a mainstream topic for us all.  Some of the highlights of this release include

  • A modular approach to YANG Data Models for Cisco IOS-XE Features (ISR, CSR, ASR)
  • Support for Zero Touch Provisioning (ZTP) for Day 0 infrastructure setup (ISR)
  • New Application Hosting capabilities provided by a Linux Guest Shell (ISR)
  • Ability to develop and execute Python scripts On-Box (ISR)

Note: Not all IOS-XE platforms support all features in release 16.5.  Future software releases will extend features to other platforms. 

In future blog posts I’ll dive deeper into all of these features, but today we’re going to look at the new Python execution capabilities.

Python and GuestShell

The ability to execute Python code directly on an end device is a part of the Application Hosting capabilities provided by GuestShell.  We’ll go deeper into GuestShell on another day, but for now it is important to understand just some of the basics.  GuestShell is a containerized Linux runtime that can be enabled on your IOS-XE device.  On the ISR 4000 platforms, Guest Shell provides a CentOS 7 environment in which you can install and run applications, such as Python scripts.  From within Guest Shell you and your applications have access to the networks of the host platform, bootflash, and IOS CLI.  Guest Shell is isolated from the underlying host software to prevent interference of the core network functions of the device.


And I can’t go any further without mentioning that though a new feature to IOS-XE, Open NX-OS has offered Guest Shell and Python for awhile now.  In fact the feature within IOS-XE is modeled after how it works within NX-OS so much of what we’ll look at here applies to NX-OS as well!

Guest Shell Quick Start

Enough talk… let’s get into some config and code!  For this blog post I am using an ISR 4431 running 16.5.1b.  The router was recently upgraded to 16.5 and has little other configuration in place.  Before we can jump into doing something interesting with Python, we need to get Guest Shell up and running.

Step 1: Enable IOX

IOX is the manager that handles guest shell and other 3rd party applications in IOS-XE.

dev4431-1#conf t

Enter configuration commands, one per line.  End with CNTL/Z.


dev4431-1#show iox

Virtual Service Global State and Virtualization Limits:

Infrastructure version : 1.7

Total virtual services installed : 0

Total virtual services activated : 0

Machine types supported   : KVM, LXC

Machine types disabled    : none

Maximum VCPUs per virtual service : 2

Resource virtualization limits:

Name                         Quota     Committed     Available


system CPU (%)                  75             0            75

memory (MB)                    512             0           512

bootflash (MB)                1000             0          1000

IOx Infrastructure Summary:


IOx service (CAF)    : Running

IOx service (HA)     : Not Running

IOx service (IOxman) : Running

Libvirtd             : Running

It should take only a few minutes for IOX start up.  The HA service may not start on all platforms.

Step 2: Enable Guest Shell

From enable mode, you can now start the Guest Shell.  It will take a few minutes to finish the startup procedure.

dev4431-1#guestshell enable

Management Interface will be selected if configured

Please wait for completion

dev4431-1#show app-hosting list

App id                           State


guestshell                       RUNNING


Step 3: Setup Guest Shell for Network Access - DNS

To do anything of real interest, you need to tell Guest Shell what DNS servers to use.  We do this by setting them within “/etc/resolv.conf” just like a typical Linux OS.

! Log into Guest Shell from the router

dev4431-1#guestshell run bash

[guestshell@guestshell ~]$

# Use echo to add your DNS Server to the file.  Here I use Open DNS. 

[guestshell@guestshell ~]$ echo "nameserver" | sudo tee --append /etc/resolv.conf


[guestshell@guestshell ~]$ cat /etc/resolv.conf


# Test to see if things are working by using pip to install “requests”

[guestshell@guestshell ~]$ sudo -E pip install requests

Collecting requests

  Downloading requests-2.13.0-py2.py3-none-any.whl (584kB)

    100% |################################| 593kB 610kB/s

Installing collected packages: requests

Successfully installed requests-2.13.0

Note… if your environment requires Proxy Servers to access the internet, you will need to configure them within Guest Shell as well as DNS.


Alright, we are all set and ready for some Python Magic!

Quick Exploration of Python on IOS-XE

As Guest Shell is based on Cent OS 7, it comes with Python 2.7.5 pre-installed.

[guestshell@guestshell ~]$ python --version

Python 2.7.5

Along with Guest Shell, a few Python libraries are included for interacting with the underlying IOS platform.  Below I give a show how we can use them send commands to IOS and process the results.

[guestshell@guestshell ~]$ python

Python 2.7.5 (default, Jun 17 2014, 18:11:42)

[GCC 4.8.2 20140120 (Red Hat 4.8.2-16)] on linux2

Type "help", "copyright", "credits" or "license" for more information.

>>> from cli import *


>>> ios_version = cli("show version")

>>> print(ios_version)

Cisco IOS XE Software, Version 16.05.01b

>>> ip_interfaces = cli("show ip int bri")

>>> print(ip_interfaces)

Interface              IP-Address      OK? Method Status                Protocol

GigabitEthernet0/0/0     YES DHCP   up                    up

GigabitEthernet0/0/1   unassigned      YES NVRAM  down                  down

GigabitEthernet0/0/2   unassigned      YES NVRAM  down                  down

GigabitEthernet0/0/3   unassigned      YES NVRAM  down                  down

GigabitEthernet0    YES DHCP   up                    up

VirtualPortGroup0    YES manual up                    up

>>> # Now I’ll configure the description on an interface

>>> conf_snippet = '''interface GigabitEthernet0/0/2

...                     desc Configured by Python'''

>>> config_result = configure(conf_snippet)

>>> config_result

[ConfigResult(success=True, command='interface GigabitEthernet0/0/2', line=1, output='', notes=None), ConfigResult(success=True, command='   desc Configured by Python', line=2, output='', notes=None)]

>>> # The clip function runs a command and prints the results

>>> clip("show run int GigabitEthernet0/0/2")

Building configuration...

Current configuration : 132 bytes


interface GigabitEthernet0/0/2

description Configured by Python

no ip address


media-type rj45

negotiation auto


>>> exit()

If you prefer Python 3, you can install it like you would on any Cent OS 7 device.

[guestshell@guestshell ~]$sudo -E yum -y install

[guestshell@guestshell ~]$sudo -E yum -y install python35u-3.5.3

[guestshell@guestshell ~]$python3.5 --version

Python 3.5.3

Let’s do something interesting

Okay, running live Python commands on a router is interesting, but not very powerful all by itself.  Let’s take a look at how we can combine this new feature with EEM for new and interesting options.  With the addition of Python and Guest Shell, you can now have EEM execute a Python Script as an action.  This provides the power of Python, with the event driven actions of EEM… Awesome!   For this example we’ll take a step into “Chat Ops” and our Router send a Spark message every time the configuration is changed.

Step 1:  Create a Spark Bot Account for our Router

In order to send a message with Spark, our router will need an account to use.  Rather than create a full account for every device, I’ll just create a Bot under my own Spark Account for this router.  From, I create a new bot account.  I grab the Authentication Token because I’ll need that for my script.


Step 2:  The Python Code

For this demo, I created a very simple Python Script that can be used to send a message to someone.  Here is the script.

from ciscosparkapi import CiscoSparkAPI

if __name__=='__main__':

    # Use ArgParse to retrieve command line parameters.

    from argparse import ArgumentParser

    parser = ArgumentParser("Spark Check In")

    # Retrieve the Spark Token and Destination Email


        "-t", "--token", help="Spark Authentication Token", required=True


    # Retrieve the Spark Token and Destination Email


        "-e", "--email", help="Email to Send to", required=True


    args = parser.parse_args()

    token = args.token

    email =

    message = "**Alert:** Config Changed"

    api = CiscoSparkAPI(access_token=token)

    api.messages.create(toPersonEmail=email, markdown=message)

I am using the very handy CiscoSparkAPI Python module to send the message, so we’ll need to pip install that first.

[guestshell@guestshell ~]$ sudo -E pip install ciscosparkapi

Now I create  “scripts” folder in the bootflash to store this and other future scripts.

[guestshell@guestshell ~]$ mkdir /bootflash/scripts

Because this directory is on bootflash, I could create the script from my local machine and upload it using any available method to get it onto the box.  For now, I’m just going to use “vi” from within Guest Shell to create the script.

[guestshell@guestshell ~]$ cat /bootflash/scripts/

from ciscosparkapi import CiscoSparkAPI

if __name__=='__main__':

    # Use ArgParse to retrieve command line parameters.

    from argparse import ArgumentParser

    parser = ArgumentParser("Spark Check In")

    # Retrieve the Spark Token and Destination Email


        "-t", "--token", help="Spark Authentication Token", required=True


    # Retrieve the Spark Token and Destination Email


        "-e", "--email", help="Email to Send to", required=True


    args = parser.parse_args()

    token = args.token

    email =

    message = "**Alert:** Config Changed"

    api = CiscoSparkAPI(access_token=token)

    api.messages.create(toPersonEmail=email, markdown=message)

Before I tie it into EEM, let’s test it real quick to make sure it works.  The script uses the common “argparse” module for Python to take in command line arguments for the token, email to send to, and message to send.

[guestshell@guestshell ~]$ python /bootflash/scripts/ -t OTJjNGM1MGItZjUUyZjM0Y2ViYzc5Mjg3MGUzNTQtYTM0 -e

And I do indeed get the message in Spark!


Step 3: Connecting to EEM

First I drop out of guest shell and get back into IOS.

[guestshell@guestshell ~]$ exit



Next I configure the EEM Applet to use the script.

dev4431-1#config t

dev4431-1(config)#event manager applet GUESTSHELL-CONFIG-CHANGE-TO-SPARK

dev4431-1(config-applet)#event syslog pattern "%SYS-5-CONFIG_I: Configured from"

dev4431-1(config-applet)#action 0.0 cli command "en"

dev4431-1(config-applet)#action 1.0 cli command "guestshell run python /bootflash/scripts/ -t OTJjNGM1MGItZjUUyZjM0Y2ViYzc5Mjg3MGUzNTQtYTM0 -e"



Now each time I make a configuration change, I receive the notification in Cisco Spark.  This is a very basic implementation of the “Chat Ops” idea, but highlights how quick and easy this type of thing can be leveraged with very little time, or programming skill needed.

With the power of Python, I could use ncclient and NETCONF to leverage the model drive programmability options under the hood as an alternative to the CLI options we looked at earlier this post.

Summing Up!

Wow... I hope you found this article enjoyable and got you thinking about how you might leverage this new capability of IOS-XE.  If you’d like to test out the code a little closer you can find it, along with many other Python samples posted in on GitHub in the repository.

If this kind of thing has you interested, be sure to join us at Cisco Live 2017 in Vegas.  We've several sessions in DEVNET discussing the new programmability features of IOS-XE and all our platforms.  A few suggestions to take a look at include:

DEVNET-1694 - Supercharge the Network with Python On IOS-XE

DEVNET-2556 - DevNet Workshop - Dive into Leveraging Python on IOS-XE

DEVNET-1695 - Application Hosting in IOS-XE

DEVNET-2557 - DevNet Workshop - Hand On - Application Hosting in IOS-XE

DEVNET-2589 - DevNet Workshop - Hands-On - Solving Problems with Application Hosting and Guestshell

DEVNET-2102 - DevNet Workshop - Network Programmability with NXOS Using Guest Shell

Also, we've got new Learning Labs and Sandboxes planned to provide more great hands on options and info for you at anytime.

What will you use this new feature for?  Leave a note in the comments, or tweet me @hfpreston


Nice article, but on 9300 running Fuji, importing the ciscosparkapi doesn't work anymore:

[guestshell@guestshell ~]$  sudo -E pip install ciscosparkapi

Collecting ciscosparkapi

  Using cached

    Complete output from command python egg_info:

    Traceback (most recent call last):

      File "<string>", line 1, in <module>

      File "/var/volatile/tmp/pip-install-KxTH5e/ciscosparkapi/", line 62, in <module>

        packages=find_packages(include=['ciscosparkapi', 'ciscosparkapi.*']),

    TypeError: find_packages() got an unexpected keyword argument 'include'

Command "python egg_info" failed with error code 1 in /var/volatile/tmp/pip-install-KxTH5e/ciscosparkapi/

so what now ?


ok, installing version 0.7 works:

sudo -E pip install ciscosparkapi==0.7

Content for Community-Ad