Every now and again you see a snippet of complex CLI syntax that gives you pause for thought. Last week I saw the command below in a change procedure. The command was being used to verify baseline BGP neighbor state and re-verify after a policy change.
1 2 3
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' \ | tr -s ' |\n' | tr -s ' ' '\n' | sed 's/^/show ip bgp nei /' \ | sed 's/$/ adver | grep \//' | vsh
I was a little daunted by the complexity of the command at first. I was slightly embarrassed too, as I had no idea what the command did. I learned that this command finds all neighbors which use a named bgp peer-template, and lists the prefixes they advertise. In this post I’ll break down the command and share the love about NXOS CLI and bash scripting.
For some context, I’ve added my simplified and sanitised example NXOS configuration below. It shows an IPv4 unicast BGP configuration with a peer-template definition and individual neighbors inheriting that configuration. All four adjacencies will advertise the same 188.8.131.52/24 prefix. I won’t bore you with the config for the peer routers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
NXOS-r2# sh running-config bgp feature bgp ip route 184.108.40.206/24 Null0 !BGP prefix origination route-map ebgp-outbound permit 0 router bgp 65001 address-family ipv4 unicast template peer eBGP_Peers description eBGP to transit providers address-family ipv4 unicast route-map ebgp-inbound in route-map ebgp-outbound out next-hop-self soft-reconfiguration inbound network 220.127.116.11 mask 255.255.255.0 neighbor 10.0.1.1 remote-as 65501 inherit peer eBGP_Peers description TransitProvider_1 neighbor 10.0.2.2 remote-as 65502 inherit peer eBGP_Peers description TransitProvider_2 no shutdown neighbor 10.0.3.3 remote-as 65503 inherit peer eBGP_Peers description TransitProvider_3 no shutdown neighbor 10.0.4.4 remote-as 65504 inherit peer eBGP_Peers description TransitProvider_4 no shutdown
Break it down
Of course you have the option to just ‘copy and paste’ the command without fully understanding it. If you need to do that then.. ‘okay’. But you’ll always be at the mercy of google if you approach networking that way. I had to strip the command back to it’s core and rebuild it, examining the output at each stage, before I understood what was happening.
Heres the first command output with no refinements. The last line is the one we’re interested in, with a list of all ‘members’ of the peer template:
1 2 3 4 5 6 7 8 9 10 11 12 13
NXOS-r1# sh ip bgp peer-template eBGP_Peers BGP peer-template is eBGP_Peers Description: eBGP to transit providers For address family: IPv4 Unicast Inbound soft reconfiguration allowed Nexthop always set to local peering address, 0.0.0.0 Third-party Nexthop will not be computed. Inbound route-map configured is ebgp-inbound Outbound route-map configured is ebgp-outbound Members of peer-template eBGP_Peers: default:10.0.1.1 10.0.2.2 10.0.3.3 10.0.4.4
We grep for the ‘default:’ string as it only appears on the line we need.
1 2 3
show ip bgp peer-template eBGP_Peers | egrep default default:10.0.1.1 10.0.2.2 10.0.3.3 10.0.4.4
But we only want neighbor IP addresses, so we use sed to replace ‘default:’ with ”. In other words we remove that string.
1 2 3
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' 10.0.1.1 10.0.2.2 10.0.3.3 10.0.4.4
The next step is to ‘squeeze’ any spaces or newlines (\n) by replacing them with a single space. The tr -s command does a translate and squeeze. The command is nicely named as you can see from the compressed output below.
1 2 3 4
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' \ | tr -s ' |\n' 10.0.1.1 10.0.2.2 10.0.3.3 10.0.4.4
We’ll need to have a separate command for each of these neighbors to show the bgp neighbor related output. I love the way this is achieved, replacing each space with a newline. No need for ‘for’ or ‘while’ loops here.
1 2 3 4 5 6 7
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' \ | tr -s ' |\n' | tr -s ' ' '\n' 10.0.1.1 10.0.2.2 10.0.3.3 10.0.4.4
Now the hard work has been done, you prepend each neighbor address with a ‘show ip bgp nei’ command. This is done by replacing the start of line character ‘^’ with the string.
1 2 3 4 5 6 7
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' \ | tr -s ' |\n' | tr -s ' ' '\n' | sed 's/^/show ip bgp nei /' show ip bgp nei 10.0.1.1 show ip bgp nei 10.0.2.2 show ip bgp nei 10.0.3.3 show ip bgp nei 10.0.4.4
Next you append the required arguments to get the advertised routes and grep for a ‘/’ to pull the prefixes from the output. This time we replace the end of line character ‘$’ with the desired string. Beware that the ‘|’ symbol after ‘adver’ is part of the sed replacement string. The ‘\//’ is also bit odd looking. Here the first ‘\’ is escaping the adjacent ‘/’ so that we actually grep for this character. The last ‘/’ tells sed that the replacement string is complete.
1 2 3 4 5 6 7 8
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' \ | tr -s ' |\n' | tr -s ' ' '\n' | sed 's/^/show ip bgp nei /' \ | sed 's/$/ adver | grep \//' show ip bgp nei 10.0.1.1 adver | grep / show ip bgp nei 10.0.2.2 adver | grep / show ip bgp nei 10.0.3.3 adver | grep / show ip bgp nei 10.0.4.4 adver | grep /
If you were expecting to see some bgp output after these commands then you’re not alone. However the the CLI parser treats these commands as display output rather than command inputs. If you want these commands to be interpreted and acted upon you need the ‘vsh’ command. Thankfully it’s pretty easy to use.
1 2 3 4 5 6 7
show ip bgp peer-template eBGP_Peers | egrep default | sed 's/default://' \ | tr -s ' |\n' | tr -s ' ' '\n' | sed 's/^/show ip bgp nei /' \ | sed 's/$/ adver | grep \//' | vsh l18.104.22.168/24 0.0.0.0 100 32768 i l22.214.171.124/24 0.0.0.0 100 32768 i l126.96.36.199/24 0.0.0.0 100 32768 i l188.8.131.52/24 0.0.0.0 100 32768 i
And so you have it. If you just wanted a count of the prefixes you could pipe it into a count function or a file for later comparison.
I love to learn from others work, and wish I could properly attribute it. I also have no doubt that there are more efficient ways of gathering this output, so I’d love to see your improvements in the comments to that I can learn from you too. The bash stalwart tools like egrep, sed, cut, tr, wc and uniq will always be useful. Having access to all of these and the pipe command to tie them together is pretty sweet.
I love simplicity so, in theory, I should hate commands like this. Why not have a series of individual commands pulled from the configuration by eye, instead of this monstrous command? Well, the joy of commands like this is that I don’t need to know the number of peers nor their IP addresses in advance of issuing the command. Therefore the command is easy to re-use. Also I don’t need to rely upon a human scraping the initial list of neighbors or doing a copy and paste for different routers. In my opinion, having humans editing verification commands introduces more fragility than the complex syntax presented here.
As always I’d love to hear your views in the comments.