cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
40340
Views
36
Helpful
32
Comments
hapresto
Cisco Employee
Cisco Employee

python_onbox_iosxe.png

Introduction

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 https://cisco.com/go/software 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.

guestshell.png

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(config)#iox

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

dev4431-1#

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 208.67.222.222" | sudo tee --append /etc/resolv.conf

nameserver 208.67.222.222

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

nameserver 208.67.222.222


# 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.


Success!

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   10.192.81.2     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       10.192.81.91    YES DHCP   up                    up

VirtualPortGroup0      192.168.35.1    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

shutdown

media-type rj45

negotiation auto

end

>>> 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 https://centos7.iuscommunity.org/ius-release.rpm

[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 https://developer.ciscospark.com, I create a new bot account.  I grab the Authentication Token because I’ll need that for my script.

spark_bot_setup.png

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

    parser.add_argument(

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

    )

    # Retrieve the Spark Token and Destination Email

    parser.add_argument(

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

    )

    args = parser.parse_args()

    token = args.token

    email = args.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/spark_checkin.py

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

    parser.add_argument(

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

    )

    # Retrieve the Spark Token and Destination Email

    parser.add_argument(

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

    )

    args = parser.parse_args()

    token = args.token

    email = args.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/spark_checkin.py -t OTJjNGM1MGItZjUUyZjM0Y2ViYzc5Mjg3MGUzNTQtYTM0 -e hapresto@cisco.com


And I do indeed get the message in Spark!

spark_checkin_msg.png

Step 3: Connecting to EEM

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

[guestshell@guestshell ~]$ exit

exit

dev4431-1#

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/spark_checkin.py -t OTJjNGM1MGItZjUUyZjM0Y2ViYzc5Mjg3MGUzNTQtYTM0 -e hapresto@cisco.com"

dev4431-1(config-applet)#exit

dev4431-1(config)#exit

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 spark_checkin.py code a little closer you can find it, along with many other Python samples posted in on GitHub in the https://github.com/CiscoDevNet/python_code_samples_network 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

32 Comments
hapresto
Cisco Employee
Cisco Employee

What platform and software version are you running?  Happy to help you get this connected and resolved. 

useridcisco
Level 1
Level 1

Could you extend your article to address Everest and networking between lxc and host CSR1000v? I got it running but without external connection and couldn't find the proper documentation in Cisco's haystack.

This is what you get on the CSR.

r1#guestshell enable

Management Interface will be selected if configured

Please wait for completion

Management Port is not supported on this platform.

Use VPG to enable guestshell.

Example: guestshell enable virtualPortGroup <vpg-num> guest-ip <guest-ip> name-server <name-server>

r1#

hapresto
Cisco Employee
Cisco Employee

With the current version of Guest Shell there is no support for creating network linkages "within the box", rather all network connectivity would go from the Guest Shell container -> out of the Router through the Management Port (on a switch) or the Virtual Port-Group configuration on a router. 

This means to network a Guest Shell to another LXC container running on the box, you would configure both Guest Shell and LXC for external network access as defined for each service.  Then as long as the two containers are on networks that are reachable, you should be good to go. 

useridcisco
Level 1
Level 1

I configured a CSR1000v to get its IP address via DHCP and the VPG to use it with ip unnumbered, then enabled the guest shell to use that same IP, but I have external connectivity from the router not from the LXC. Did you actually try it yourself? Could you point me to a tech/config guide? For the life of me, I couldn't find one.

Huyen Duong
Cisco Employee
Cisco Employee

Hi useridcisco

To make it work, you need to do NAT on gigabit interface (for example gigabit 1). Gigabit 1 will be use to get out of your internal network. VirtualPortGroup0 might be use in different subnet which is NATed to gigabit 1.

Use this sample:

iox

!

interface VirtualPortGroup0

ip address 192.168.35.1 255.255.255.0

ip nat inside

!        

interface GigabitEthernet1

ip address 10.138.159.69 255.255.255.224

ip nat outside

!

ip nat inside source list NAT_ACL interface GigabitEthernet1 overload

!

ip route 0.0.0.0 0.0.0.0 10.138.159.65

!

+ To activate guestshell:

guestshell enable VirtualPortGroup 0 guest-ip 192.168.35.2

Cheers

useridcisco
Level 1
Level 1

Thanks, Huyen Duong! That worked. I misinterpreted Harold's post and wrongly configured the VirtualPortGroup. Still a tech/config guide would be much useful, that explains features and caveats of network connectivity between LXC and NOS.

Also, step 3 I didn't do and used instead

guestshell enable VirtualPortGroup 1 guest-ip 10.0.0.2 name-server 8.8.8.8

hapresto
Cisco Employee
Cisco Employee

Thanks huyeduon

I didn't see the last comment, so sorry for the delay. 

As for a config guide, here is the official guide for Guest Shell!  It includes samples such as what was shared above. 

https://www.cisco.com/c/en/us/td/docs/switches/lan/catalyst9300/software/release/16-5/configuration_guide/prog/b_165_pro…

useridcisco
Level 1
Level 1

That guide doesn't mention the CSR though. And it'd also be nice to have something more in-depth that explains how actually network connectivity is implemented for the LXCs or that defines a VPG.

Huyen Duong
Cisco Employee
Cisco Employee

This is good news,

Btw, by default CSR1000V, Everest code will be using Python 2.7.5, in which you might get some SNIMissing error when trying to install ciscosparkapi with pip.

One workaround to install ciscosparkapi is using wget to download ciscosparkapi-0.7.tar.gz to guestshell then manually install ciscosparkapi.

Then follow Harold's detail guide, if you still find issue when running the spark_check_in script, you might need to do: sudo yum upgrade in guestshell.

Good luck

hapresto
Cisco Employee
Cisco Employee

Correct.. Though IOS XE 16.6 for the CSR did bring Guest Shell support to the release, it is in a "preview" functionality at this time and not fully supported or recommended for production use yet.  This does mean that full docs aren't quite ready yet, however the functionality of Guest Shell on CSR is very similar to on ISR and Catalyst Platforms so the guides from those should be useful and relevant. 

useridcisco
Level 1
Level 1

I haven't gotten that error. I pip install'ed it and then it imported just fine in python.

useridcisco
Level 1
Level 1

authorized_keys are not persistent between reloads (same guest shell instance) in the CSR, any workaround?

useridcisco
Level 1
Level 1

I still haven't found a workaround for this. Any ideas?

taoli
Cisco Employee
Cisco Employee

不错。好好学习一下。谢谢。

comtechvs
Level 1
Level 1

Hi Harold,

What cli version were you testing with?  In __version__ = '1.2.0' something seems broken. IOS isr4300-universalk9.16.06.02.SPA.bin

[guestshell@guestshell ~]$ python --version

Python 2.7.5

[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.

>>> import cli

>>> cli.execute("show version")

Traceback (most recent call last):

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

  File "/usr/lib/python2.7/site-packages/cli/__init__.py", line 94, in execute

    response = pnp.listener.cli_exec_request(command)

  File "/usr/lib/python2.7/site-packages/pnp/_pnp.py", line 502, in cli_exec_request

    raise PnPParseError(str(perr))

pnp._pnp.PnPParseError: ParseError: Response is not xml parsable. mismatched tag: line 6, column 2

>>> cli.executep("show version")

ParseError: Response is not xml parsable. mismatched tag: line 6, column 2

REQUEST:

    <?xml version="1.0" encoding="UTF-8"?>

     <pnp xmlns="urn:cisco:pnp" version="1.0" sid="0C44501E87335D9D82ABDC10CDE75558230A2F39857308182751434EF434D810B9200BE48FA1913F499E0BB585831B17A8F98CCA0D9D3AECA58FE8961F3C9D0D" udi="PID:ISR4321/K9,VID:V01,SN:XXXXXXXX">

          <request correlator="python_cli_api@1514850280.16" xmlns="urn:cisco:pnp:cli-exec">

        <execCLI maxWait="PT3600S" xsd="false">

            <cmd>show version</cmd>

        </execCLI>

      </request>

     </pnp>

RESPONSE:

<html>

<head><title>502 Bad Gateway</title></head>

<body bgcolor="white">

<center><h1>502 Bad Gateway</h1></center>

<hr><center>nginx</center>

</body>

</html>

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: