Cisco Guest Shell is a Linux-based container environment that is separate from the host device (IOS-XE) and can automatically control IOS-XE settings using Python or the like. This time, I built an API server on Guest Shell using the Python web framework "Flask", got the show command result from Postman, and changed the interface settings.
IOS-XE
A virtual interface VirtualPortGroup0
(192.168.30.1) is created on the IOS-XE side and associated with the Guest Shell address (192.168.30.2) to enable external access to the Guest Shell.
I will omit it because it depends on the environment, but for Internet access, I also set PAT to convert the Guest Shell address to the Gigabit Ethernet1 address.
Config Config
Router(config)#iox
Router(config)#ip http server
Router(config)#interface GigabitEthernet1
Router(config-if)# ip address 192.168.100.196 255.255.255.0
Router(config-if)# exit
Router(config)#interface VirtualPortGroup0
Router(config-if)# ip address 192.168.30.1 255.255.255.0
Router(config-if)# exit
Router(config)#app-hosting appid guestshell
Router(config-app-hosting)# app-vnic gateway0 virtualportgroup 0 guest-interface 0
Router(config-app-hosting-gateway0)# guest-ipaddress 192.168.30.2 netmask 255.255.255.0
Router(config-app-hosting-gateway0)# app-default-gateway 192.168.30.1 guest-interface 0
Router(config-app-hosting)# name-server0 192.168.100.1
Router(config-app-hosting)# end
Router#guestshell enable
Confirmation command
Router#show iox-service
IOx Infrastructure Summary:
---------------------------
IOx service (CAF) 1.10.0.1 : Running
IOx service (HA) : Not Supported
IOx service (IOxman) : Running
IOx service (Sec storage) : Not Supported
Libvirtd 1.3.4 : Running
Router#show app-hosting list
App id State
---------------------------------------------------------
guestshell RUNNING
Guest Shell Use the default settings as they are.
Router#guestshell
[guestshell@guestshell ~]$ sudo ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.30.2 netmask 255.255.255.0 broadcast 192.168.30.255
(abridgement)
[guestshell@guestshell ~]$ netstat -nr
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.30.1 0.0.0.0 UG 0 0 0 eth0
192.168.30.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
I used the pre-installed Python 2.7.5 to install Flask and the show command parser template ntc_templates.
[guestshell@guestshell ~]$ python -V
Python 2.7.5
[guestshell@guestshell ~]$ pip -V
pip 20.2.3 from /usr/lib/python2.7/site-packages/pip (python 2.7)
[guestshell@guestshell ~]$ pip install flask
[guestshell@guestshell ~]$ pip install ntc_templates
I created api.py directly under the home directory of Guest Shell.
[guestshell@guestshell ~]$ pwd
/home/guestshell
[guestshell@guestshell ~]$ touch api.py
The general flow of processing is as follows.
--The execution trigger is HTTP GET from the API client to the URI http: // <Guest Shell IP address> / show / <command name connected with _>
--Execute the function getCommand ()
--Convert'_'at the end of URI to''(space) and generate show command
--Run the show command on the pre-installed Cisco CLI Python module
--Parse the output result with NTC-templates / TextFSM and return it as JSON
--If it fails in the middle, an error message is returned in JSON
Also, in order to access from the outside, host = '0.0.0.0'
is specified as an argument of ʻapp.run ()`.
api.py
from flask import Flask, jsonify, request
from cli import configurep, cli
from ntc_templates.parse import parse_output
app = Flask(__name__)
@app.route("/show/<command>", methods=["GET"])
def getCommand(command):
cmd = "show " + command.replace("_", " ")
try:
sh_output = cli(cmd)
sh_output_parsed = parse_output(platform="cisco_ios", command=cmd, data=sh_output)
return jsonify(sh_output_parsed)
except:
return jsonify([{"result": "Fail to parse the output"}])
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=8080)
[guestshell@guestshell ~]$ python api.py
* Serving Flask app "api" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
Here is an example of getting the result of the show command show ip interface brief
from Postman.
Enter the URI as follows in GET. Although it is abbreviated as ʻip_int_brief` at the end, it can be parsed without any problem if it is an abbreviation allowed by NTC-templates.
I added the code to the above api.py. The general flow of processing is as follows.
--The execution is triggered by HTTP POST from the API client to the URI http: // <Guest Shell IP address> / set / interface
. Describe the interface setting parameters in JSON format in Body.
--Execute the function setInterface ()
--Generate CLI command according to setting parameters
--Change settings and save settings with Cisco CLI Python module
--If successful, return a Success message in JSON
After editing, execute Python / start the API server again.
api.py(Additions)
@app.route("/set/interface", methods=["POST"])
def setInterface():
interface_cmd = "interface " + request.json['interface']
if 'state' in request.json:
if request.json['state'] == "enabled":
state_cmd = "no shutdown"
elif request.json['state'] == "disabled":
state_cmd = "shutdown"
else:
state_cmd = ""
if 'description' in request.json:
description_cmd = "description " + request.json['description']
else:
description_cmd = ""
if 'address' in request.json:
address_cmd = "ip address " + request.json['address'] + " " + request.json['netmask']
else:
address_cmd = ""
configurep([interface_cmd, state_cmd, description_cmd, address_cmd, "end"])
cli("write")
return jsonify([{"result": "Success"}])
This is an example of setting GigabitEthernet2
Description, IP address, and opening a port from Postman.
Enter the URI as shown below in POST, and specify Content-Type in ʻapplication / json` in Header.
Describe the setting parameters in Body. Of the following, the interface name ʻinterface is required, but the other open / closed
state, Description
description, and IP address ʻaddress / netmask
are optional.
Click the Send button, and if successful, the following message will be displayed.
You can also check the setting status from the terminal screen while api.py is running.
192.168.100.100 - - [27/Sep/2020 05:59:20] "POST /set/interface HTTP/1.1" 200 -
Line 1 SUCCESS: interface GigabitEthernet2
Line 2 SUCCESS: no shutdown
Line 3 SUCCESS: description TEST
Line 4 SUCCESS: ip address 10.1.1.1 255.255.255.0
Line 5 SUCCESS: end
Settings were changed and saved on the IOS-XE side without any problems.
Router#sh conf | begin interface GigabitEthernet2
interface GigabitEthernet2
description TEST
ip address 10.1.1.1 255.255.255.0
negotiation auto
no mop enabled
no mop sysid
As a simple example, I was able to successfully build an API server with Guest Shell. However, Guest Shell can execute show commands / change settings of devices without authentication, so there may be issues from the viewpoint of security and trail management. Also, for this application, it may be better to control with Off-Box using an external tool like Ansible instead of On-Box format like Guest Shell.
Recommended Posts