I have added a multicast page to the web app that demonstrates the ability to pull in details for multicast. I originally planned to use this with the Nexus multicast lab, however as TextFSM or Genie do not support the parsing of the command “show ip mroute”. Side note Genie does support the command “show ip mroute vrf all” accoriding to the documentation.
Anyway what I ended up doing was creating my own TextFSM template to parse the Nexus output for “show ip mroute”. I created a post to explain how.
To see the full code please see my GitHub, version 3 of the project.
This is a demonstration of what can be collected and displayed quite easily with just entering IP addresses into a web form.
Below are screnshots of the final implementation. This is pulling quite basic information from the “show ip mroute” table. Nothing is stored in a database, when the user enters the IP address(s) the script will go off and run the command “show ip mroute”.
First Basic Test
I have added this in a basic form to my Flask views.py page. This will be the first test to see if I can get the multicast routing table parsed for each Nexus switch.
This test is just me submitting a form and then letting the script refer to a background process to perform the Cisco commands and parsing then redirecting to the inventory page.
Excerpt from my views.py file.
0 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 |
@devices_blueprint.route('/multicast', methods = ['GET', 'POST']) def multicast(): form = DeviceForm() # Stolen from info page, may need to change this to specific multicast if form.validate_on_submit(): return redirect(url_for('devices.bg_get_mroute')) else: return render_template('multicast.html', form=form) @devices_blueprint.route('/bg_get_mroute', methods = ['GET', 'POST']) def bg_get_mroute(): """ Gets the multicast routing table and parses it """ ips = ["172.16.1.115","172.16.1.116","172.16.1.117","172.16.1.118"] # Perform show ip mroute get_detailed = CiscoCommands() genie_list, error_ips = get_detailed.create_threads(ips, "show_mroute") for device in genie_list: print(device) return redirect(url_for('devices.inventory')) |
Output is slightly out of order due to the multithreading. This will be tidied up as the data is extracted.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
host: Scrapli Driver 172.16.1.116:22, OS: nxos I am Nexus and running Nexus command [{'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '232.0.0.0/8', 'up_time': '3d23h', 'incoming_interface': 'Null', 'reverse_path_forwarding_neighbour_ip': '0.0.0.0', 'outgoing_interface_count': ['0'], 'outgoing_interface': [], 'outgoing_multicast_up_time': []}, {'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '239.0.1.2/32', 'up_time': '22:58:00', 'incoming_interface': 'loopback0', 'reverse_path_forwarding_neighbour_ip': '10.1.10.1', 'outgoing_interface_count': ['1'], 'outgoing_interface': ['Ethernet1/2'], 'outgoing_multicast_up_time': ['22:58:00,']}, {'vrf_name': 'default', 'multicast_source_ip': '172.16.2.200/32', 'multicast_group_ip': '239.0.1.2/32', 'up_time': '22:58:03', 'incoming_interface': 'Ethernet1/1', 'reverse_path_forwarding_neighbour_ip': '10.1.1.1', 'outgoing_interface_count': ['0'], 'outgoing_interface': [], 'outgoing_multicast_up_time': []}, {'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '239.255.255.250/32', 'up_time': '2d07h', 'incoming_interface': 'loopback0', 'reverse_path_forwarding_neighbour_ip': '10.1.10.1', 'outgoing_interface_count': ['2'], 'outgoing_interface': ['Ethernet1/2', 'Ethernet1/1'], 'outgoing_multicast_up_time': ['2d07h,', '2d07h,']}] host: Scrapli Driver 172.16.1.117:22, OS: nxos I am Nexus and running Nexus command host: Scrapli Driver 172.16.1.115:22, OS: nxos I am Nexus and running Nexus command [{'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '232.0.0.0/8', 'up_time': '3d23h', 'incoming_interface': 'Null', 'reverse_path_forwarding_neighbour_ip': '0.0.0.0', 'outgoing_interface_count': ['0'], 'outgoing_interface': [], 'outgoing_multicast_up_time': []}] [{'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '232.0.0.0/8', 'up_time': '3d23h', 'incoming_interface': 'Null', 'reverse_path_forwarding_neighbour_ip': '0.0.0.0', 'outgoing_interface_count': ['0'], 'outgoing_interface': [], 'outgoing_multicast_up_time': []}, {'vrf_name': 'default', 'multicast_source_ip': '172.16.2.200/32', 'multicast_group_ip': '239.0.1.2/32', 'up_time': '22:58:03', 'incoming_interface': 'Vlan20', 'reverse_path_forwarding_neighbour_ip': '172.16.2.200', 'outgoing_interface_count': ['1'], 'outgoing_interface': ['Vlan16'], 'outgoing_multicast_up_time': ['00:30:19,']}, {'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '239.255.255.250/32', 'up_time': '2d07h', 'incoming_interface': 'Ethernet1/1', 'reverse_path_forwarding_neighbour_ip': '10.1.1.2', 'outgoing_interface_count': ['1'], 'outgoing_interface': ['Vlan20'], 'outgoing_multicast_up_time': ['2d07h,']}] host: Scrapli Driver 172.16.1.118:22, OS: nxos I am Nexus and running Nexus command [{'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '232.0.0.0/8', 'up_time': '3d23h', 'incoming_interface': 'Null', 'reverse_path_forwarding_neighbour_ip': '0.0.0.0', 'outgoing_interface_count': ['0'], 'outgoing_interface': [], 'outgoing_multicast_up_time': []}, {'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '239.0.1.2/32', 'up_time': '22:58:01', 'incoming_interface': 'Ethernet1/2', 'reverse_path_forwarding_neighbour_ip': '10.1.2.1', 'outgoing_interface_count': ['1'], 'outgoing_interface': ['Vlan50'], 'outgoing_multicast_up_time': ['22:58:01,']}, {'vrf_name': 'default', 'multicast_source_ip': '172.16.2.200/32', 'multicast_group_ip': '239.0.1.2/32', 'up_time': '22:58:00', 'incoming_interface': 'Vlan16', 'reverse_path_forwarding_neighbour_ip': '10.1.6.1', 'outgoing_interface_count': ['1'], 'outgoing_interface': ['Vlan50'], 'outgoing_multicast_up_time': ['22:58:00,']}, {'vrf_name': 'default', 'multicast_source_ip': '*', 'multicast_group_ip': '239.255.255.250/32', 'up_time': '2d07h', 'incoming_interface': 'Ethernet1/2', 'reverse_path_forwarding_neighbour_ip': '10.1.2.1', 'outgoing_interface_count': ['1'], 'outgoing_interface': ['Vlan50'], 'outgoing_multicast_up_time': ['2d07h,']}] |
A video to show it working. Look at the output in the VScode terminal.
WTForm Variables Passing Into Show Script
I usually use a second web page and then JavaScript file to send the commands. Not this time, I skipped second page and the JavaScript button to use the “Submit” button of the form.
Having the ability to pass the IP addreses from the form into the next page without the use of a page in the middle helped. The post is here to read more, mehod 2.
0 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 33 34 35 36 37 38 39 40 41 42 43 |
@devices_blueprint.route('/multicast', methods = ['GET', 'POST']) def multicast(): form = MulticastForm() if form.validate_on_submit(): form_IPs = {'mcast_routers':[form.data]} # Gather IPs from form to pass into another page session["form_IPs"]=form_IPs return redirect(url_for('devices.bg_get_mroute')) else: return render_template('multicast.html', form=form) @devices_blueprint.route('/bg_get_mroute', methods = ['GET', 'POST']) def bg_get_mroute(): """ Gets the multicast routing table and parses it """ # Get form values form_IPs = session.get("form_IPs", None) ips = form_IPs['mcast_routers'][0]['ip'].replace(" ", "").replace("\r\n\r\n", "").replace("\r\n", "").split(',') # Perform show ip mroute get_detailed = CiscoCommands() genie_list, error_ips = get_detailed.create_threads(ips, "show_mroute") show_output_dict = {} parsed_output = [] for device in genie_list: for key, value in device.items(): if key != "show_output": print("\n") print(f"Hostname: {key}") show_output_dict.update({key:re.split('\n\n|\n|\n\n\n',device['show_output'])}) parsed_output.append({key:value}) return render_template('bg_get_mroute.html', show_output_dict=show_output_dict, parsed_output=parsed_output) |
Manipulating HTML Template with Jinja
I used Jinja templating more on this html template with nested for loops. I have created a separate post to explain my use in more detail.
0 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
{% extends "base.html" %} {% block content %} <div class="jumbotron"> <h1>Multicast Details</h1> </div> <br> <br> <div class="container"> <div class="jumbotron"> <ul class="nav nav-tabs"> <li class="nav-item"> <a class="nav-link active" data-toggle="tab" href="#quickstats">Multicast Basics</a> </li> <li class="nav-item"> <a class="nav-link" data-toggle="tab" href="#portdetail">Multicast Details</a> </li> <li class="nav-item"> <a class="nav-link" data-toggle="tab" href="#rawoutput">"Show ip mroute" Raw Output</a> </li> </ul> <div id="myTabContent" class="tab-content"> <div class="tab-pane active" id="quickstats"> <div class="row"> <div class="col-lg-6"> <br> {% for dict_item in parsed_output %} {% for hostname, value in dict_item.items() %} <br> <p><strong>Hostname: </strong> {{ hostname }}</p> {% for i in dict_item[hostname] %} <p><strong>Multicast Groups: </strong> {{ i['multicast_group_ip'] }}</p> {% endfor %} {% endfor %} {% endfor %} </div> <div class="col-lg-6"> <br> </div> </div> </div> <div class="tab-pane" id="portdetail"> <div class="row"> <div class="col-lg-6"> <br> {% for dict_item in parsed_output %} {% for hostname, value in dict_item.items() %} <br> <p><strong>Hostname: </strong> {{ hostname }}</p> {% for i in dict_item[hostname] %} <p><strong>Multicast Groups: </strong> {{ i['multicast_group_ip'] }} <br><strong>Uptime: </strong> {{ i['up_time'] }} <br><strong>Incoming Interface: </strong> {{ i['incoming_interface'] }} <br><strong>RPF Neighbour IP: </strong> {{ i['reverse_path_forwarding_neighbour_ip'] }} {% if i['outgoing_interface'] == [] %} <br><strong>Outgoing Interface: </strong> No Interfaces {% else %} <br><strong>Outgoing Interface: </strong> {{ i['outgoing_interface'] }} {% endif %} </p> {% endfor %} {% endfor %} {% endfor %} </div> </div> </div> <div class="tab-pane" id="rawoutput"> <br> {% for key,value in show_output_dict.items() %} <br><strong>{{ key }}</strong><br> <code> {% for element in value %} {{ element }}<br> {% endfor %} </code> {% endfor %} <br> </div> </div> </div> </div> {% endblock %} |