This post will describe the exercises and solutions for week two of Kirk Byers Python for Network Engineers.
The next excercise is to work with output from “show ip bgp”:
III. You have the following four lines from 'show ip bgp': entry1 = "* 1.0.192.0/18 157.130.10.233 0 701 38040 9737 i" entry2 = "* 1.1.1.0/24 157.130.10.233 0 701 1299 15169 i" entry3 = "* 1.1.42.0/24 157.130.10.233 0 701 9505 17408 2.1465 i" entry4 = "* 1.0.192.0/19 157.130.10.233 0 701 6762 6762 6762 6762 38040 9737 i" Note, in each case the AS_PATH starts with '701'. Using split() and a list slice, how could you process each of these such that--for each entry, you return an ip_prefix and the AS_PATH (the ip_prefix should be a string; the AS_PATH should be a list): Your output should look like this: ip_prefix as_path 1.0.192.0/18 ['701', '38040', '9737'] 1.1.1.0/24 ['701', '1299', '15169'] 1.1.42.0/24 ['701', '9505', '17408', '2.1465'] 1.0.192.0/19 ['701', '6762', '6762', '6762', '6762', '38040', '9737'] Ideally, your logic should be the same for each entry (I say this because once I teach you for loops, then I want to be able to process all of these in one four loop). If you can't figure this out using a list slice, you could also solve this using pop().
First we will try to solve this exercise without using a For loop and then we can try to be a bit more efficient.
We have four different entries that are output from the “show ip bgp” command. There is a network, a next-hop, a metric and the AS-path. We want to extract the network and the AS path from this long string. First let’s just put the strings into our variables and check the type of our variable.
entry1 = "* 1.0.192.0/18 157.130.10.233 0 701 38040 9737 i" entry2 = "* 1.1.1.0/24 157.130.10.233 0 701 1299 15169 i" entry3 = "* 1.1.42.0/24 157.130.10.233 0 701 9505 17408 2.1465 i" entry4 = "* 1.0.192.0/19 157.130.10.233 0 701 6762 6762 6762 6762 38040 9737 i" print(type(entry1))
This produces the following output:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path.py
So we have a long string with different information in it and with a lot of white space. If we split the spring based on this white space we should get “*”, “1.0.192.0/18”, “157.130.10.233”, “0”, “701”, “38040”, “9737”, “i”. The first string is “*” which would then be at index 0. We should be able to extract the network by using list slicing and accessing index 1. Let’s try this. We use “split()” with no delimiter specified.
entry_split = entry1.split() ip_prefix = entry_split[1] print(ip_prefix)
The output is the following:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path.py 1.0.192.0/18
That looks good so far. Now for the more tricky part. How do we extract the AS path? We currently have a list consisting of all the strings where a lot of the AS numbers are included. If we look at the BGP output we can see that we have “*” at index 0, the network at index 1, the next-hop at index 2 and the metric at index 3. So the AS path actually starts at index 4. This will be our starting point in the list slice. We don’t know where the output ends though since the AS path has a variable length. What we do know is that “i” is the final string in the list and that anything before “i” is part of the AS path up until and including index 4 in the list slice. First let’s print the list and the index to make it a bit more clear where we are coming from. We use a For loop with “enumerate()” to get both an index and the value. For more information on For loops go to my post on For loops.
for index, value in enumerate(entry_split): print(index, value)
This gives us the following output:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path.py 0 * 1 1.0.192.0/18 2 157.130.10.233 3 0 4 701 5 38040 6 9737 7 i
So we want to print the contents of the list from index 4 to the second last string in the list. The second last string will have the index of “-1”. We create a list where the AS path will be stored with the following code and also print it out:
as_path = entry_split[4:-1] print(as_path)
This gives us this result:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path.py ['701', '38040', '9737']
Then all that is left is to print the table and repeat this code for the rest of the entries:
entry_split = entry1.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format("ip_prefix", "as_path")) print("{:<20} {:<50}".format(ip_prefix, as_path)) entry_split = entry2.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format(ip_prefix, as_path)) entry_split = entry3.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format(ip_prefix, as_path)) entry_split = entry4.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format(ip_prefix, as_path))
This produces the following output:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path.py ip_prefix as_path Traceback (most recent call last): File "as_path.py", line 27, inprint("{:<20} {:<50}".format(ip_prefix, as_path)) TypeError: non-empty format string passed to object.__format__
Ooops... We can't used ".format" on the "as_path" because it's a list and not a string. So we modify the code slightly:
entry_split = entry1.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format("ip_prefix", "as_path")) print("{:<20} {:<50}".format(ip_prefix, str(as_path))) entry_split = entry2.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format(ip_prefix, str(as_path))) entry_split = entry3.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format(ip_prefix, str(as_path))) entry_split = entry4.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] print("{:<20} {:<50}".format(ip_prefix, str(as_path)))
This now produces the following output:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path.py ip_prefix as_path 1.0.192.0/18 ['701', '38040', '9737'] 1.1.1.0/24 ['701', '1299', '15169'] 1.1.42.0/24 ['701', '9505', '17408', '2.1465'] 1.0.192.0/19 ['701', '6762', '6762', '6762', '6762', '38040', '9737']
This looks a lot better. The code is as usual available at Github.
However we have manually gone through all of the entries. There is a lot of duplication of code. What if we had 100 entries like the ones above? We want to be able to parse the text for any number of entries which means we need to find a way to loop through the entries.
So currently we have four lists. Python can loop through lists, that's not a problem. The challenge though is to loop through different lists with a single loop. We could use a For loop with a range but that only works if we know the number of entries beforehand. We shouldn't assume that we do. So this is how I solved the task and It's probably not the best or most elegant way but it works. Running code (IETF reference) and all that.
Let's first create a list consisting of the other lists. We will print the list and show the class type to demonstrate that it's a list.
entries = [entry1, entry2, entry3, entry4] print(entries) print(type(entries))
The output is not very pretty but we see a list consisting of other lists:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path_for.py ['* 1.0.192.0/18 157.130.10.233 0 701 38040 9737 i', '* 1.1.1.0/24 157.130.10.233 0 701 1299 15169 i', '* 1.1.42.0/24 157.130.10.233 0 701 9505 17408 2.1465 i', '* 1.0.192.0/19 157.130.10.233 0 701 6762 6762 6762 6762 38040 9737 i']
What we need to do now is to create a For loop which goes through the list. We've used this before and go back to my Python For loops post if you need more information on For loops.
for entry in entries: entry_split = entry.split() ip_prefix = entry_split[1] as_path = entry_split[4:-1] str_as_path = str(as_path) print("{:<20} {:<50}".format(ip_prefix, str_as_path))
The first part "for entry in entries" tells Python to loop through the "entries" list and the iterator is called "entry" so that is what we refer to when using "split()" etc. The rest of the code is the same as we used previously.
This produces the following result:
daniel@daniel-iperf3:~/python/Week2$ python3 as_path_for.py ip_prefix as_path 1.0.192.0/18 ['701', '38040', '9737'] 1.1.1.0/24 ['701', '1299', '15169'] 1.1.42.0/24 ['701', '9505', '17408', '2.1465'] 1.0.192.0/19 ['701', '6762', '6762', '6762', '6762', '38040', '9737']
I hope this post has been informative. Code is available at Github.
See you next time!