Cisco is Coming out of its Shell

Standard

I got to play with with the upcoming 16.6 CSR release and it finally has guestshell!

Guestshell is a linux shell that we can access from a Cisco device that lets do some interesting things.

Enabling the shell

To enable the feature we simply have to enable iox and then we can enter linux land with guestshell

CSR01(config)#iox

We can fully enter the shell with:

CSR01(config)#do guestshell
 [guestshell@guestshell ~]$

Dohost

The dohost command lets us run IOS commands, let’s take a moment to use bash to create a few loopbacks

[guestshell@guestshell ~]$ for x in {1..5}; do dohost "conf t ; interface l$x ; ip address 10.0.0.$x 255.255.255.255" ; done
 [guestshell@guestshell ~]$ 
 *Jul 4 22:32:39.252: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback1, changed state to up
 *Jul 4 22:32:39.253: %LINK-3-UPDOWN: Interface Loopback1, changed state to up
 *Jul 4 22:32:39.332: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback2, changed state to up
 *Jul 4 22:32:39.332: %LINK-3-UPDOWN: Interface Loopback2, changed state to up
 *Jul 4 22:32:39.415: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback3, changed state to up
 *Jul 4 22:32:39.415: %LINK-3-UPDOWN: Interface Loopback3, changed state to up
 *Jul 4 22:32:39.496: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback4, changed state to up
 *Jul 4 22:32:39.496: %LINK-3-UPDOWN: Interface Loopback4, changed state to up
 *Jul 4 22:32:39.566: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback5, changed state to up
 *Jul 4 22:32:39.567: %LINK-3-UPDOWN: Interface Loopback5, changed state to up

Now that we have some interfaces we can run show commands.

[guestshell@guestshell ~]$ dohost 'show ip route'‚Äč Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2 E1 - OSPF external type 1, E2 - OSPF external type 2 i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2 ia - IS-IS inter area, * - candidate default, U - per-user static route o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP a - application route + - replicated route, % - next hop override, p - overrides from PfR Gateway of last resort is 10.10.20.254 to network 0.0.0.0 S* 0.0.0.0/0 [1/0] via 10.10.20.254 10.0.0.0/8 is variably subnetted, 9 subnets, 2 masks C 10.0.0.1/32 is directly connected, Loopback1 C 10.0.0.2/32 is directly connected, Loopback2 C 10.0.0.3/32 is directly connected, Loopback3 C 10.0.0.4/32 is directly connected, Loopback4 C 10.0.0.5/32 is directly connected, Loopback5 C 10.0.0.6/32 is directly connected, Loopback6 C 10.0.0.7/32 is directly connected, Loopback7 C 10.10.20.0/24 is directly connected, GigabitEthernet1 L 10.10.20.21/32 is directly connected, GigabitEthernet1 192.168.35.0/24 is variably subnetted, 2 subnets, 2 masks C 192.168.35.0/24 is directly connected, VirtualPortGroup0 L 192.168.35.1/32 is directly connected, VirtualPortGroup0

The benefit of this command is that while the terminal shell I talked about ages ago brought some linux utilities into the mix, this allows the full Redhat CLI into the mix. So for example if I wanted to change all the 10 routes in the output (for some reason) I could.

[guestshell@guestshell ~]$ dohost 'show ip route' | sed 's/10/20/g'
 
 Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
 D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
 N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
 E1 - OSPF external type 1, E2 - OSPF external type 2
 i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
 ia - IS-IS inter area, * - candidate default, U - per-user static route
 o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
 a - application route
 + - replicated route, % - next hop override, p - overrides from PfR
 Gateway of last resort is 20.20.20.254 to network 0.0.0.0
 S* 0.0.0.0/0 [1/0] via 20.20.20.254
 20.0.0.0/8 is variably subnetted, 9 subnets, 2 masks
 C 20.0.0.1/32 is directly connected, Loopback1
 C 20.0.0.2/32 is directly connected, Loopback2
 C 20.0.0.3/32 is directly connected, Loopback3
 C 20.0.0.4/32 is directly connected, Loopback4
 C 20.0.0.5/32 is directly connected, Loopback5
 C 20.0.0.6/32 is directly connected, Loopback6
 C 20.0.0.7/32 is directly connected, Loopback7
 C 20.20.20.0/24 is directly connected, GigabitEthernet1
 L 20.20.20.21/32 is directly connected, GigabitEthernet1
 192.168.35.0/24 is variably subnetted, 2 subnets, 2 masks
 C 192.168.35.0/24 is directly connected, VirtualPortGroup0
 L 192.168.35.1/32 is directly connected, VirtualPortGroup0

Or if I wanted to display just the IPs from the show ip route output we could do something like this:

[guestshell@guestshell ~]$ dohost 'show ip route' | awk '{match($0,/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/); ip = substr($0,RSTART,RLENGTH); print ip}' | sort

0.0.0.0
 10.0.0.0
 10.0.0.1
 10.0.0.2
 10.0.0.3
 10.0.0.4
 10.0.0.5
 10.0.0.6
 10.0.0.7
 10.10.20.0
 10.10.20.21
 10.10.20.254
 192.168.35.0
 192.168.35.0
 192.168.35.1

Python on the Box

This also gives us python directly on the box like we have with Nexus.

In addition to the standard python modules, guestshell comes with a cli module that lets us access the router directly. Also since guestshell is linux we can install applications and modules as we need to.

We can use the cli command to run commands.

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

>>> z = cli('show ip int br')
 >>> print z

Interface IP-Address OK? Method Status Protocol
 GigabitEthernet1 10.10.20.21 YES NVRAM up up
 GigabitEthernet2 unassigned YES NVRAM administratively down down
 GigabitEthernet3 unassigned YES NVRAM administratively down down
 Loopback1 10.0.0.1 YES manual up up
 Loopback2 10.0.0.2 YES manual up up
 Loopback3 10.0.0.3 YES manual up up
 Loopback4 10.0.0.4 YES manual up up
 Loopback5 10.0.0.5 YES manual up up
 VirtualPortGroup0 192.168.35.1 YES NVRAM up up

If you just want to view the output you can use the ‘clip’ command to display the standard output without saving any data.

>>> clip('show ip int br')

Interface IP-Address OK? Method Status Protocol
 GigabitEthernet1 10.10.20.21 YES NVRAM up up
 GigabitEthernet2 unassigned YES NVRAM administratively down down
 GigabitEthernet3 unassigned YES NVRAM administratively down down
 Loopback1 10.0.0.1 YES manual up up
 Loopback2 10.0.0.2 YES manual up up
 Loopback3 10.0.0.3 YES manual up up
 Loopback4 10.0.0.4 YES manual up up
 Loopback5 10.0.0.5 YES manual up up
 VirtualPortGroup0 192.168.35.1 YES NVRAM up up

We can use a simple loop to make things like pinging things easier.

>>> for x in range(1,6):
 ... clip('ping 10.0.0.' + str(x))
 ...

Type escape sequence to abort.
 Sending 5, 100-byte ICMP Echos to 10.0.0.1, timeout is 2 seconds:
 !!!!!
 Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms

Type escape sequence to abort.
 Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
 !!!!!
 Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms

Type escape sequence to abort.
 Sending 5, 100-byte ICMP Echos to 10.0.0.3, timeout is 2 seconds:
 !!!!!
 Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms

Type escape sequence to abort.
 Sending 5, 100-byte ICMP Echos to 10.0.0.4, timeout is 2 seconds:
 !!!!!
 Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms

Type escape sequence to abort.
 Sending 5, 100-byte ICMP Echos to 10.0.0.5, timeout is 2 seconds:
 !!!!!
 Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms

Since it is a full python shell we can mix and match modules as needed.

>>> from cli import *
 >>> import re
 >>> for x in range(1,6):
 ... output = cli('ping 10.0.0.' + str(x))
 ... icmp_regex_pattern = r"100 percent"
 ... icmp_success = True if re.search(icmp_regex_pattern, output, re.MULTILINE) else False
 ... if icmp_success:
 ... print "Loopback" + str(x) + " Works!!!"
 ... else:
 ... print "Loopback" + str(x) + " IS DRUNK!!!"
 ...
 Loopback1 Works!!!
 Loopback2 Works!!!
 Loopback3 Works!!!
 Loopback4 Works!!!
 Loopback5 IS DRUNK!!!
The cli command can also string various IOS commands together as well as use variables.

 >>> SLASH32 = '255.255.255.255'
 >>> cli('conf t ; interface l6 ; ip address 10.0.0.6 ' + SLASH32)
 ''
 >>> clip('show run int l6')

Building configuration...
 Current configuration : 64 bytes
 !
 interface Loopback6
 ip address 10.0.0.6 255.255.255.255
 end

We’ll wrap this up by talking about configuration changes, if we are pushing a lot of configuration it can be easier to use the configure or configurep commands which takes a configuration block that is stored in a variable. First we’ll make a variable to that contains the commands needed to add a loopback and enable OSPF on it.

>>> MEOW = '''interface l7
 ... ip address 10.0.0.7 255.255.255.255
 ... description Added by Python
 ... router ospf 1
 ... network 10.0.0.7 0.0.0.0 area 7'''
 >>>
 >>> configurep(MEOW)
 Line 1 SUCCESS: interface l7
 Line 2 SUCCESS: ip address 10.0.0.7 255.255.255.255
 Line 3 SUCCESS: description Added by Python
 Line 4 SUCCESS: router ospf 1
 Line 5 SUCCESS: network 10.0.0.7 0.0.0.0 area 7

Since we are pushing more commands we will want to setup exceptions so the script knows how to handle errors. I’ve edited the MEOW variable to add another loopback with a typo in the IP.

>>> MEOW = '''interface l7
 ... ip address 10.0.0.7 255.255.255.255
 ... description Added by Python
 ... router ospf 1
 ... network 10.0.0.7 0.0.0.0 area 7
 ... interface l8
 ... ip address 10.0.0.0.8 255.255.255.255
 ... description FAILURE!!!'''

Now we can setup an exception that will return any failed commands.

>>> try:
 ... results = configure(MEOW)
 ... print "Success!"
 ... except CLIConfigurationError as e:
 ... print "Failed configurations:"
 ... for failure in e.failed:
 ... print failure
 ...
 Failed configurations:
 Line 7 FAILURE: ip add 10.0.0.0.8 255.255.255.255 (PARSE_ERROR_NOMATCH)
 **CLI Line # 7: ip add 10.0.0.0.8 255.255.255.255
 **CLI Line # 7: ^
 **CLI Line # 7: % Invalid input detected at '^' marker.

Lastly we can run scripts by saving them to a file and either running them from the shell or through the guestshell run command. This lets us have things like EEM call scripts as part of a larger solution.

CSR01#guestshell run cat test.py
 #!/usr/bin/env python
 import cli

cli.cli('conf t ; interface l11 ; ip add 10.0.0.11 255.255.255.255')

CSR01#guestshell run python test.py

CSR01#
 *Jul 4 22:02:32.836: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback11, changed state to up
 CSR01#
 *Jul 4 22:02:32.837: %LINK-3-UPDOWN: Interface Loopback11, changed state to up

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s