Getting Started with IOS-XE Guestshell

This post has been on my backlog for a while... I've been intermittently trying out the IOS-XE guestshell feature for a few different projects, and never feeling like any of them were good enough to write about.

So why not just write about guestshell itself? It's such a nifty tool.

What is Guestshell?

Now that Cisco's Catalyst platform has standardized on IOS-XE, we can take advantage of some of the features of the Linux-based platform. One of which being Linux Containers (LXC).

Most of the latest Catalyst routers, switches, and access points support what Cisco calls Application Hosting - which is a fancy way of just saying it can run containers on-box. Another acronym to be aware of is IOx - which is what Cisco calls their IOS-XE/Linux appliction framework.

Guestshell is a specific feature within IOS-XE, where a dedicated pre-built container is enabled for on-box access to a Linux shell.

What Hardware Supports Guestshell?

Okay - so for this example, I'll be using the new Catalyst 8000V platform (a virtual Catalyst router). I do also have a Catalyst 9200L on hand, but unfortunately it's one of the few Catalyst platforms that does not support Guestshell (due to lower-end HW specs).

You can also use the Catalyst 8000V to test this in a lab, or you could use most of the Catalyst 8000 / 9000 series platforms if you had them available (or the CSRv & some ISR routers!).

You can find a list of supported platforms & other hardware requirements here.

All that being said - let's get started!

Enabling IOx

So before we can actually use the guestshell feature, there is some pre-configuration to be done.

First we'll need to ensure that the IOx application framework is enabled & running. We can check this by using the show iox command:

0x8KV01# show iox

IOx Infrastructure Summary:
---------------------------
IOx service (CAF)              : Not Running
IOx service (HA)               : Not Supported
IOx service (IOxman)           : Not Running
IOx service (Sec storage)      : Not Supported
Libvirtd 5.5.0                 : Running

And as we see above - most components are listed as "Not Running" or "Not Supported".

So we'll enter config mode, and use the iox command to enable these services:

0x8KV01# conf t
Enter configuration commands, one per line.  End with CNTL/Z.
0x8KV01(config)# iox 
0x8KV01(config)#
0x8KV01(config)# exit
0x8KV01# show iox

IOx Infrastructure Summary:
---------------------------
IOx service (CAF)              : Running
IOx service (HA)               : Not Supported
IOx service (IOxman)           : Running
IOx service (Sec storage)      : Not Supported
Libvirtd 5.5.0                 : Running

Configuring Guestshell Networking

There's a good chance we'll want our container to have access to the outside world, so let's take a quick look at the network configuration.

Note: The networking config syntax will differ if you are using a Catalyst router or a Catalyst switch. Since I am using a Catalyst 8000V - we will look at the config for a Catalyst router first.

In order to allow external access to our container, we'll need to configure a gateway interface and NAT translations. Then we can provide a full network configuration to our container:

## Enable NAT on our external interface - in my case, gigabitEthernet1
0x8KV01(config)# interface gigabitEthernet 1
0x8KV01(config-if)# ip nat outside

## Create a virtual interface for the guestshell container
0x8KV01(config-if)# int virtualportgroup0
0x8KV01(config-if)# ip address 192.168.100.1 255.255.255.0
0x8KV01(config-if)# ip nat inside

## Create an ACL to match traffic from guestshell & Enable NAT
0x8KV01(config)# ip access-list standard IOX_NAT
0x8KV01(config-std-nacl)# permit 192.168.100.0 0.0.0.255
0x8KV01(config)# ip nat inside source list IOX_NAT interface gigabitEthernet 1 overload

## Assign network configuration to our guestshell container
0x8KV01(config)#app-hosting appid guestshell
0x8KV01(config-app-hosting)# app-vnic gateway0 virtualportgroup 0 guest-interface 0
0x8KV01(config-app-hosting-gateway0)# guest-ipaddress 192.168.100.5 netmask 255.255.255.0
0x8KV01(config-app-hosting-gateway0)# app-default-gateway 192.168.100.1 guest-interface 0
0x8KV01(config-app-hosting)# name-server0 8.8.8.8

Now, as I mentioned before - this config will look different if you're using a switch vs a router.

So let's take a quick look at what this would look like, if we were on a Catalyst Switch instead:

Cat9k(config)# interface AppGigabitEthernet 1/0/1
Cat9k(config-if)# switchport mode trunk

Cat9k(config)# app-hosting appid name
Cat9k(config-app-hosting)# app-vnic AppGigabitEthernet trunk
Cat9k(config-app-hosting-trunk)# vlan 10 guest-interface 0
Cat9k(config-app-hosting-vlan-access-ip))# guest-ipaddress 192.168.100.1 netmask 255.255.255.0

Cat9k(config-app-hosting)# app-default-gateway 192.168.100.1 guest-interface 0
Cat9k(config)# name-server 8.8.8.8 

Once all that config is done - Let's move on to getting our container started!!

Managing the Guestshell Process

In order to start a container on our Catalyst device, we'll need to go through a process of deploying the app, activating it, then starting it.

For guestshell, there is a shortcut command that will perform all of these steps for you - and make managing the guestshell process much easier.

So let's go ahead and spin up our guestshell container with the command guestshell enable:

0x8KV01# guestshell enable
Interface will be selected if configured in app-hosting
Please wait for completion
guestshell installed successfully
Current state is: DEPLOYED
guestshell activated successfully
Current state is: ACTIVATED
guestshell started successfully
Current state is: RUNNING
Guestshell enabled successfully

0x8KV01# show app-hosting list
App id                                   State
---------------------------------------------------------
guestshell                               RUNNING

And as you can see above, everything gets automatically deployed & started. We can verify using the show app-hosting list command to see that our container has been started.

If we need to, we can also stop & deactivate our container using the command guestshell disable.

We could also use the commands below if we wanted to perform these steps manually:

0x8KV01# app-hosting activate appid guestshell
0x8KV01# app-hosting start appid guestshell

0x8KV01# app-hosting stop appid guestshell
0x8KV01# app-hosting deactivate appid guestshell

Before we move onto accessing the guestshell interface, I also wanted to show where you can get more detail about the guestshell container.

Using the show app-hosting detail appid guestshell command, we can see details around the container version, current MAC/IP config, and resource consumption:

0x8KV01# show app-hosting detail appid guestshell
App id                 : guestshell
Owner                  : iox
State                  : RUNNING
Application
  Type                 : lxc
  Name                 : GuestShell
  Version              : 3.2.0
  Description          : Cisco Systems Guest Shell XE for x86_64
  Path                 : /guestshell/:guestshell.tar
  URL Path             :
Activated profile name : custom

Resource reservation
  Memory               : 256 MB
  Disk                 : 1 MB
  CPU                  : 800 units
  CPU-percent          : 3 %
  VCPU                 : 1

Attached devices
  Type              Name               Alias
  ---------------------------------------------
  serial/shell     iox_console_shell   serial0
  serial/aux       iox_console_aux     serial1
  serial/syslog    iox_syslog          serial2
  serial/trace     iox_trace           serial3

Network interfaces
   ---------------------------------------
eth0:
   MAC address         : 52:54:dd:d:bf:da
   IPv4 address        : 192.168.100.5
   IPv6 address        : ::
   Network name        : VPG0

Port forwarding
  Table-entry  Service  Source-port  Destination-port
  ---------------------------------------------------

Accessing the Guestshell CLI

Now for the fun part! Let's get into our guestshell container.

We'll just need to use the guestshell command, and we'll be dropped into the Linux shell:

0x8KV01# guestshell
[[email protected] ~]$
[[email protected] ~]$ uname -a
Linux guestshell 5.4.40 #1 SMP Tue Oct 6 17:57:00 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Let's also make sure our network & NAT configuration worked:

[[email protected] ~]$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=64 time=3.57 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=64 time=2.64 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=64 time=2.54 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 2.543/2.916/3.567/0.465 ms

If needed, we can also run commands on our Catalyst device CLI without leaving the guestshell interface by using the dohost utility:

[[email protected] ~]$ dohost "show app-hosting list"
App id                                   State
---------------------------------------------------------
guestshell                               RUNNING

[[email protected] ~]$

Just a note, the reverse is also possible. Using the guestshell run command from our Catalyst CLI, we can run commands in our container:

0x8KV01# guestshell run uname -a
Linux guestshell 5.4.40 #1 SMP Tue Oct 6 17:57:00 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

0x8KV01#

It's also worth reiterating, that the guestshell container is a full Linux container (It runs CentOS). So if we need to install additional Linux packages, we can use our Linux package manager (YUM) to do so.

Anyways - Let's wrap this up by looking at the built-in Python modules & capabilities

Using Python in Guestshell

One of the more appealing use cases for guestshell is the ability to run native Python scripts & code directly on your Catalyst device.

A couple of possible use cases could be:

  • Troubleshoot intermittent issues - Automatically collect device state (routes, MAC changes, etc) on a regular basis to log locally or send via email
  • Automated resolution - If a certain type of state change is observed, we could run a pre-set list of commands to try and automatically fix the issue
  • Local monitoring / troubleshooting - We could run a series of ping, web page load tests, etc from a python script to help monitor performance from the switch's perspective
  • Change management / notification - A script could be written to monitor the device configuration for any changes made, then send an automated email when something was detected
  • Have a risky change that might sever your connection to a remote device? A script could execute the change for you, and if unsuccessful, revert to a working configuration (without doing a reload after)

These are just a handful of ideas that came to mind while writing this blog post... But there are many more possibilities!

Within the guestshell container, there is a special cli python module that comes pre-loaded. This module allows us to run CLI commands on our catalyst device directly from a python script & receive the command output for parsing.

Let's look at a quick example using the Python interactive interpreter to save the device configuration:

[[email protected] ~]$ python3
Python 3.6.8 (default, Aug 24 2020, 17:57:11)
[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> import cli
>>> status = cli.execute("write memory")
>>> if "OK" in status: print("Device configuration was saved!")
...
Device configuration was saved!

The example above uses the cli.execute Python function, which will run commands in Cisco's exec mode.

What if we wanted to make configuration changes? In that case we would use the cli.configure function:

>>> config_data = """interface loopback200
... ip address 172.16.99.10 255.255.255.0
... """
>>>
>>> cli.configure(config_data)
'Line 1 SUCCESS:  interface loopback200\nLine 2 SUCCESS:  ip address 172.16.99.10 255.255.255.0\n'
>>>
>>> cli.execute("show run interface loopback200")
'Building configuration...\nCurrent configuration : 68 bytes\n!\ninterface Loopback200\n ip address 172.16.99.10 255.255.255.0\nend\n'

In the above example, we used a multi-line comment to provide the list of commands to enter. The cli.configure function also supports a Python list - so we could provide the commands in that format as well.

You'll notice that the Python interpreter returns a status for each command that we attempted to run. This should allow us to error-check & ensure that all of our intended configuration was accepted.

Lastly, we used the cli.execute command again to validate that our running configuration looks correct.

There's a bit more functionality built into the cli Python module, but I just wanted to cover some of the common use cases. More details can be found here


A Few Common Questions...

Does this give me access to the underlying IOS-XE Linux shell?

Nope, anything running in guestshell is running in a container - which runs on-top of the IOS-XE image.

What about resource conflicts?

The linux containers running on the IOS-XE platform are given their own dedicated resources. Any resource constraints that affect the containers will not impact normal network functions.