├── .travis.yml ├── BackEndModeules ├── GetRibData.py ├── api_routes.py ├── device_call_backup.py ├── devicecalls.py ├── requirements.txt └── wsgi.py ├── LICENSE ├── Python ├── README.rst ├── arps.py ├── dp_neighbors.py ├── get_cpu.py ├── get_environment.py ├── get_interfaces.py ├── get_l2_ports.py ├── get_ospf.py ├── get_vlans.py ├── mac_address_table.py └── requirements.txt ├── README.rst ├── build ├── asset-manifest.json ├── favicon.ico ├── index.html ├── manifest.json ├── robots.txt └── static │ ├── css │ ├── 2.f7c5f18f.chunk.css │ ├── 2.f7c5f18f.chunk.css.map │ ├── main.f05ca24e.chunk.css │ └── main.f05ca24e.chunk.css.map │ ├── js │ ├── 2.fa43104b.chunk.js │ ├── 2.fa43104b.chunk.js.LICENSE.txt │ ├── 2.fa43104b.chunk.js.map │ ├── main.07de7263.chunk.js │ ├── main.07de7263.chunk.js.map │ ├── main.b227f7a6.chunk.js │ ├── main.b227f7a6.chunk.js.map │ ├── runtime-main.32e9b272.js │ └── runtime-main.32e9b272.js.map │ └── media │ └── router.cd099654.png ├── images ├── Layer2.png ├── iosxeops-int.PNG ├── mem-cpu.PNG ├── xeoprestprod.PNG ├── xeopslaprod.PNG ├── xeopsloading.PNG ├── xeopsprod-env.PNG └── xeopsprodinterfaces.PNG ├── init_app_api.sh ├── install_dependencies.sh ├── package.json ├── public ├── favicon.ico ├── index.html ├── manifest.json └── robots.txt ├── server.js ├── src ├── App.css ├── App.js ├── App.test.js ├── Components │ ├── Config │ │ └── config.js │ ├── DMVPN │ │ ├── Dmvpn-Parent.js │ │ ├── dmvpnData.js │ │ └── topology.js │ ├── Environment │ │ ├── Env-Parent.js │ │ ├── cpuUsages.js │ │ ├── env.js │ │ ├── poe.js │ │ ├── sensors.js │ │ ├── transceivers.js │ │ └── transieverInventory.js │ ├── Forms │ │ ├── bgpNeighborForm.js │ │ ├── interfaceForm.js │ │ └── ospfNeighborForm.js │ ├── IPSlas │ │ ├── Sla-Parent.js │ │ ├── ipslastats.js │ │ ├── slatopologies.js │ │ └── topology.js │ ├── Images │ │ └── router.png │ ├── Index │ │ ├── Index-Parent.js │ │ ├── InterfacesTable.js │ │ ├── arps.js │ │ ├── hsrp.js │ │ ├── interfaceCard.js │ │ ├── qos.js │ │ ├── qosCharts.js │ │ └── topology.js │ ├── InterfaceGraphs │ │ └── liveInterface.js │ ├── LayerThree │ │ ├── Routing-Parent.js │ │ ├── bgp.js │ │ ├── ospf.js │ │ ├── ospfFormOverlay.js │ │ ├── routing.js │ │ └── topology.js │ ├── LayerTwo │ │ ├── accessPorts.js │ │ ├── layerTwo-Parent.js │ │ ├── layerTwo.js │ │ ├── macAddress.js │ │ ├── spanTree.js │ │ ├── trunks.js │ │ └── vlans.js │ ├── Modals │ │ ├── bgpFormModal.js │ │ ├── configQueryModal.js │ │ ├── configSendModal.js │ │ ├── interfaceModal.js │ │ └── loadingModal.js │ ├── Other │ │ ├── axiosRequests.js │ │ ├── bandwidthFunctions.js │ │ ├── chartConfigs.js │ │ ├── configNavbar.js │ │ ├── data.js │ │ ├── dp_neighbors.js │ │ ├── errorBoundry.js │ │ ├── errorComponent.js │ │ ├── errorData.js │ │ ├── interfaceOverlay.js │ │ ├── jsxCard.js │ │ ├── login.js │ │ ├── navbar.js │ │ ├── navbarError.js │ │ ├── pageLoader.js │ │ ├── promises.js │ │ └── tables.js │ └── RibStatus │ │ ├── RIB-Parent.js │ │ ├── RibMain.js │ │ ├── getRibs.js │ │ ├── promises.js │ │ ├── protocols.js │ │ └── ribDiff.js ├── holder.rst ├── index.css ├── index.js ├── reportWebVitals.js └── setupTests.js └── test ├── GetRibData.py ├── ansible.cfg ├── ansible_config.py ├── ansible_config_new.py ├── api_routes.py ├── bgp_config.j2 ├── bgp_config.yml ├── bgp_config_script.py ├── device_call_backup.py ├── devicecalls.py ├── intf_config.j2 ├── intf_config.j2.bak ├── intf_config_vars.yml ├── intf_playbook.yml ├── inventory.yml ├── my_file.out ├── ospf_config.j2 ├── ospf_config.py ├── ospf_config.yml ├── requirements.txt ├── show_version.yml ├── test.rst ├── wsgi.py └── yang_conversions.py /.travis.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | include: 3 | before_script: 4 | -cd BackEndModeules 5 | - language: python 6 | python: 3.8 7 | install: 8 | - pip install -r requirements.txt 9 | "script": [ 10 | "echo \"skipping tests\"" 11 | ] 12 | 13 | - language: node_js 14 | node_js: 15 | - "stable" 16 | script: 17 | - npm test 18 | -------------------------------------------------------------------------------- /BackEndModeules/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | flask 3 | flask_jwt_extended 4 | netmiko 5 | ipapi 6 | SQLAlchemy 7 | gunicorn 8 | 9 | -------------------------------------------------------------------------------- /BackEndModeules/wsgi.py: -------------------------------------------------------------------------------- 1 | from api_routes import app 2 | import os 3 | import ssl 4 | 5 | ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) 6 | ctx.load_cert_chain(f'{os.getcwd()}/domainame.crt', f'{os.getcwd()}/domainame.key') 7 | 8 | if __name__ == "__main__": 9 | app.run(ssl_context=ctx) 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 cober2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Python/README.rst: -------------------------------------------------------------------------------- 1 | IOS-XE Rest Functions 2 | --------------------- 3 | 4 | These are a collection of functions which I'm currently using with the IOS-XE React App. I've converted these to print the data along with return for use elsewhere. 5 | 6 | Enjoy! 7 | -------------------------------------------------------------------------------- /Python/arps.py: -------------------------------------------------------------------------------- 1 | """Set of functions that gets and prints arp entries""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import warnings 7 | 8 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 9 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 10 | 11 | def _check_api_error(response) -> bool: 12 | """Check for API Errors""" 13 | 14 | is_error = False 15 | 16 | if list(response.keys())[0] == 'errors': 17 | is_error = True 18 | 19 | return is_error 20 | 21 | def _print_arps(table_detail, entry) -> None: 22 | """Print Arp entries""" 23 | 24 | try: 25 | print(f"{entry.get('address', {}):<25}{entry.get('enctype', {}):<30} {entry.get('hardware', {}):<30}{entry.get('mode'):<30}{entry.get('time', {}):<40}{entry.get('type'):<30}{table_detail.get('vrf'):<20}") 26 | except TypeError: 27 | pass 28 | 29 | def get_arps(ip, port, username, password) -> list: 30 | """Collects arp for the matching""" 31 | 32 | entries = [] 33 | 34 | try: 35 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-arp-oper:arp-data/arp-vrf" 36 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 37 | arp_entries = json.loads(response.text, strict=False) 38 | 39 | check_error = _check_api_error(arp_entries) 40 | 41 | if check_error: 42 | raise AttributeError 43 | 44 | try: 45 | print(f"{'Address':<30} {'Encap':<30} {'Mac':<30}{'Mode':<40}{'Time':<25} {'Type':<30}{'VRF':<20}") 46 | print("-" * 200) 47 | for i in arp_entries.get('Cisco-IOS-XE-arp-oper:arp-vrf'): 48 | [ _print_arps(i, entry) for entry in i.get('arp-oper')] 49 | except (TypeError, AttributeError): 50 | pass 51 | 52 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL, AttributeError): 53 | pass 54 | 55 | return entries 56 | 57 | 58 | if __name__ == '__main__': 59 | 60 | try: 61 | get_arps() 62 | except TypeError: 63 | input('Input credentials') 64 | -------------------------------------------------------------------------------- /Python/dp_neighbors.py: -------------------------------------------------------------------------------- 1 | """ Set of fuctions which collects CDP and LLDP neighbor info""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import time 7 | import os 8 | import warnings 9 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 10 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 11 | 12 | def get_dp_neighbors(ip, port, username, password) -> list: 13 | """Gets device components restconf/data/Cisco-IOS-XE-cdp-oper:cdp-neighbor-details""" 14 | 15 | data = [] 16 | 17 | try: 18 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-cdp-oper:cdp-neighbor-details" 19 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 20 | converted_json = json.loads(response.text) 21 | data.append(converted_json) 22 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 23 | data.append({}) 24 | 25 | try: 26 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-lldp-oper:lldp-entries" 27 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 28 | converted_json = json.loads(response.text) 29 | data.append(converted_json) 30 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 31 | data.append({}) 32 | 33 | _print_dp_neighbors(data) 34 | 35 | return data 36 | 37 | def _print_dp_neighbors(data): 38 | 39 | print(f"CDP {'Device':<50} {'Local Int':<25} {'Remote-Port':<20}{'Capability':<25}{'Duplex':<30}{'Platform':<25}{'Mgmt IP':<20}{'IP':<20}") 40 | print("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------") 41 | if data[0].get('Cisco-IOS-XE-cdp-oper:cdp-neighbor-details', {}).get('cdp-neighbor-detail', {}): 42 | for i in data: 43 | if isinstance(i.get('Cisco-IOS-XE-cdp-oper:cdp-neighbor-details', {}).get('cdp-neighbor-detail', {}), list): 44 | for a in i['Cisco-IOS-XE-cdp-oper:cdp-neighbor-details']['cdp-neighbor-detail']: 45 | print(f"{a['device-name']:<45}{a['local-intf-name']:<25}{a['port-id']:<25}{a['capability']:<25}{a['duplex']:<30}{a['platform-name']:<25}{a['mgmt-address']:<18}{a['ip-address']}") 46 | else: 47 | print('No CDP Neighbors or CDP isnt Enabled\n') 48 | 49 | print(f"\nLLDP {'Device':<38} {'Local Int':<30} {'Remote-Port':<33}{'Capability':<25}") 50 | print("-------------------------------------------------------------------------------------------------------------------------------------------------------------------") 51 | if data[1].get('Cisco-IOS-XE-lldp-oper:lldp-entries', {}).get('lldp-entry'): 52 | for i in data: 53 | if isinstance(i.get('Cisco-IOS-XE-lldp-oper:lldp-entries', {}).get('lldp-entry'), list): 54 | for a in i['Cisco-IOS-XE-lldp-oper:lldp-entries']['lldp-entry']: 55 | print(f"{a['device-id']:<40}{a.get('local-interface'):<30}{a.get('connecting-interface'):<33}{', '.join(list(dict.fromkeys(a.get('capabilities', {})))):<25}") 56 | else: 57 | print('No LLDP Neighbors or LLDP isnt Enabled\n') 58 | 59 | if __name__ == '__main__': 60 | 61 | try: 62 | get_dp_neighbors() 63 | except TypeError: 64 | input('Input credentials') 65 | -------------------------------------------------------------------------------- /Python/get_cpu.py: -------------------------------------------------------------------------------- 1 | """Function for getting CPU data from a device""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import time 7 | import os 8 | import warnings 9 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 10 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 11 | 12 | 13 | def get_cpu_usages(ip, port, username, password, show_proccess=False) -> tuple: 14 | """Gets real time CPU statistics using restconf/data/Cisco-IOS-XE-process-cpu-oper:cpu-usage""" 15 | 16 | cpu_stats = {} 17 | memory_stats = {} 18 | 19 | try: 20 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-process-cpu-oper:cpu-usage/cpu-utilization" 21 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 22 | cpu_stats = json.loads(response.text) 23 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 24 | pass 25 | 26 | try: 27 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-platform-software-oper:cisco-platform-software/control-processes/control-process" 28 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 29 | converted_json = json.loads(response.text) 30 | get_keys = dict.fromkeys(converted_json) 31 | parent_key = list(get_keys.keys())[0] 32 | memory_stats = converted_json[parent_key] 33 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 34 | pass 35 | 36 | if cpu_stats: 37 | print("{0:<15}{1:<15}\n{2:<15}{3:<16}\n{4:<15}{5:<5}".format('Five Seconds:', cpu_stats['Cisco-IOS-XE-process-cpu-oper:cpu-utilization']['five-seconds'], 38 | 'One Minute:',cpu_stats['Cisco-IOS-XE-process-cpu-oper:cpu-utilization']['one-minute'], 39 | 'Five Minutes:', cpu_stats['Cisco-IOS-XE-process-cpu-oper:cpu-utilization']['five-minutes'])) 40 | if show_proccess: 41 | print("{0:<5}{1:<55}{2:<20}{3:<10}{4:<10}{5:<10}{6:<10}".format('PID', 'Name', 'TTY', 'Avg-Run', 'Five Sec.', 'One Min.', 'Five Min.')) 42 | print('-' * 130) 43 | for i in cpu_stats['Cisco-IOS-XE-process-cpu-oper:cpu-utilization']['cpu-usage-processes']['cpu-usage-process']: 44 | print("{0:<5}{1:<55}{2:<20}{3:<10}{4:<10}{5:<10}{6:<10}".format(i.get('pid'), i.get('name'), i.get('tty'), i.get('avg-run-time'), 45 | i.get('five-seconds'), i.get('one-minute'), i.get('five-minutes'))) 46 | return cpu_stats, memory_stats 47 | 48 | if __name__ == '__main__': 49 | 50 | try: 51 | get_dp_neighbors() 52 | except TypeError: 53 | input('Input credentials') 54 | -------------------------------------------------------------------------------- /Python/get_environment.py: -------------------------------------------------------------------------------- 1 | """Function that gets device enviromental stats""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import time 7 | import os 8 | import warnings 9 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 10 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 11 | 12 | 13 | def get_envirmoment(ip, port, username, password) -> dict: 14 | """Gets real time enviroment statistics using restconf/data/Cisco-IOS-XE-environment-oper:environment-sensors""" 15 | 16 | data = {} 17 | 18 | try: 19 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-environment-oper:environment-sensors" 20 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 21 | data = json.loads(response.text) 22 | 23 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 24 | pass 25 | 26 | if data: 27 | print("{0:<17}{1:<13}{2:<18}{3:<10}{4:<5}".format('Name', 'Location', 'State', 'Current', 'Measurement')) 28 | print("-" * 70 ) 29 | for i in test['Cisco-IOS-XE-environment-oper:environment-sensors']['environment-sensor']: 30 | print("{0:<20}{1:<10}{2:<20}{3:<10}{4:<5}".format(i.get('name'), i.get('location'), i.get('state'), i.get('current-reading'), i.get('sensor-units'))) 31 | 32 | return data 33 | 34 | if __name__ == '__main__': 35 | 36 | try: 37 | get_envirmoment() 38 | except TypeError: 39 | input('Input credentials') 40 | -------------------------------------------------------------------------------- /Python/get_interfaces.py: -------------------------------------------------------------------------------- 1 | """Set of functions to collect interface stats""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import time 7 | import os 8 | import warnings 9 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 10 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 11 | 12 | def get_interfaces(ip, port, username, password, ex_down=None): 13 | """Gets real time interface statistics using IOS-XE\n 14 | Cisco-IOS-XE-interfaces-oper:interfaces and live arp data via Cisco-IOS-XE-arp-oper:arp-data/arp-vrf""" 15 | 16 | data = {} 17 | interface_data = {} 18 | 19 | try: 20 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-interfaces-oper:interfaces" 21 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 22 | converted_json = json.loads(response.text) 23 | interface_data = converted_json.get('Cisco-IOS-XE-interfaces-oper:interfaces').get('interface') 24 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 25 | pass 26 | 27 | if interface_data: 28 | try: 29 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-arp-oper:arp-data/arp-vrf" 30 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 31 | 32 | converted_json = json.loads(response.text, strict=False) 33 | get_keys = dict.fromkeys(converted_json) 34 | parent_key = list(get_keys.keys())[0] 35 | 36 | for interface in interface_data: 37 | convert_bandwidth = _convert_to_mbps(interface) 38 | entries = [_get_arps(interface, i) for i in converted_json[parent_key]][0] 39 | data[interface.get('name')] = {'interface': interface.get('name'), 'data': convert_bandwidth, 'arps': entries} 40 | 41 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError): 42 | for interface in interface_data: 43 | convert_bandwidth = _convert_to_mbps(interface) 44 | data[interface.get('name')] = {'interface': interface.get('name'), 'data': convert_bandwidth, 'arps': []} 45 | 46 | for v in data.values(): 47 | [_print_interfaces_extended(data, ex_down) for data in v.values() if isinstance(data, dict)] 48 | 49 | return data 50 | 51 | def _print_interfaces_extended(data, ex_down) -> dict: 52 | """Prints interface data""" 53 | 54 | if ex_down == True: 55 | if data.get('oper-status', {}) == 'up': 56 | print(f"{data.get('name', {}):<35}{data.get('oper-status', {}):<15} {int(data.get('statistics', {}).get('rx-kbps')) / 1000:<10}{int(data.get('statistics', {}).get('tx-kbps')) / 1000:<10}{data.get('statistics', {}).get('rx-pps'):<10} {data.get('statistics', {}).get('tx-pps'):<10}{data.get('mtu'):<10}{data.get('ipv4', ''):<20} {data.get('ipv4-subnet-mask', ''):<1}") 57 | else: 58 | print(f"{data.get('name', {}):<35}{data.get('oper-status', {}):<15} {int(data.get('statistics', {}).get('rx-kbps')) / 1000:<10}{int(data.get('statistics', {}).get('tx-kbps')) / 1000:<10}{data.get('statistics', {}).get('rx-pps'):<10} {data.get('statistics', {}).get('tx-pps'):<10}{data.get('mtu'):<10}{data.get('ipv4', ''):<20} {data.get('ipv4-subnet-mask', ''):<1}") 59 | 60 | def _convert_to_mbps(interface): 61 | """Convert Kbps to Mbps""" 62 | 63 | interface['statistics']['tx-kbps'] = int(interface.get('statistics').get('tx-kbps')) / 1000 64 | interface['statistics']['rx-kbps'] = int(interface.get('statistics').get('tx-kbps')) / 1000 65 | if interface['oper-status'] == 'if-oper-state-ready': 66 | interface['oper-status'] = 'up' 67 | else: 68 | interface['oper-status'] = 'down' 69 | 70 | return interface 71 | 72 | def _get_arps(interface, i): 73 | """Collects arp for the matching interface""" 74 | 75 | entries = [] 76 | 77 | try: 78 | for entry in i.get('arp-oper'): 79 | if entry.get('interface') == interface.get('name'): 80 | entry.pop('interface') 81 | entry['time'] = entry.get('time').split('.')[0].strip('T00') 82 | entries.append(entry) 83 | except TypeError: 84 | pass 85 | 86 | return entries 87 | 88 | if __name__ == '__main__': 89 | 90 | try: 91 | get_interfaces() 92 | except TypeError: 93 | input('Input credentials') 94 | -------------------------------------------------------------------------------- /Python/get_l2_ports.py: -------------------------------------------------------------------------------- 1 | """Collection of fuction to get layer 2 ports""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import time 7 | import os 8 | import warnings 9 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 10 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 11 | 12 | def get_switch(ip, port, username, password) -> tuple: 13 | """Gets device components /restconf/data/openconfig-platform:components""" 14 | 15 | data = {} 16 | trunk =[] 17 | access = [] 18 | 19 | try: 20 | interfaces_configs = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-native:native/interface" 21 | interface_status = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-interfaces-oper:interfaces" 22 | config_response = requests.get(interfaces_configs, headers=headers, verify=False, auth=(username, password)) 23 | stats_response = requests.get(interface_status, headers=headers, verify=False, auth=(username, password)) 24 | config_json = json.loads(config_response.text) 25 | stats_json = json.loads(stats_response.text) 26 | 27 | for interface, v in config_json['Cisco-IOS-XE-native:interface'].items(): 28 | if isinstance(v, list): 29 | mapped = [map_switchports(config, interface, stats_json) for config in v] 30 | data[interface] = list(mapped) 31 | 32 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL): 33 | pass 34 | 35 | for v in data.values(): 36 | for i in v: 37 | if i[0].get('mode') == 'trunk': 38 | trunk.append(i[0]) 39 | elif i[0].get('mode') == 'access': 40 | access.append(i[0]) 41 | 42 | if trunk: 43 | print(f"{'Interface':<30} {'Mode':<15} {'Status':<28}{'Mbps In':<20}{'Mbps Out':<17}{'Allow Vlans':<13}") 44 | print("-------------------------------------------------------------------------------------------------------------------------------------------") 45 | [print(f"{i.get('interface', {}):<30}{i.get('mode', {}):<10} {i.get('status', {}):<35}{i.get('mbpsOut'):<20}{i.get('mbpsIn'):<20}{i.get('vlans'):<20}") for i in trunk] 46 | print('\n') 47 | if access: 48 | print(f"{'Interface':<35} {'Status':<28} {'Mbps Out':<19}{'Mbps In':<20}{'Vlans'}") 49 | print("-------------------------------------------------------------------------------------------------------------------------------------------") 50 | [print(f"{i.get('interface', {}):<30}{i.get('status', {}):<35}{i.get('mbpsOut'):<20}{i.get('mbpsIn'):<20}{i.get('vlan'):<20}") for i in access] 51 | 52 | return trunk, access 53 | 54 | def map_switchports(config, interface, interfaces_statuses) -> list: 55 | 56 | complete_interface = f"{interface}{config.get('name')}" 57 | interface_mode = False 58 | data = [] 59 | statistics = next((interface for interface in interfaces_statuses['Cisco-IOS-XE-interfaces-oper:interfaces']['interface'] if interface['name'] == complete_interface), None) 60 | 61 | if config.get('switchport', {}).get('Cisco-IOS-XE-switch:mode', {}): 62 | interface_mode = list(config.get('switchport', {}).get('Cisco-IOS-XE-switch:mode', {}).keys())[0] 63 | 64 | if interface_mode == 'access': 65 | access_vlan = config.get('switchport').get('Cisco-IOS-XE-switch:access').get('vlan').get('vlan') 66 | data.append({'mode': 'access','interface': complete_interface, 'vlan': access_vlan, 'status': statistics['oper-status'], 67 | 'mbpsOut': int(statistics['statistics']['tx-kbps'])/1000, 'mbpsIn': int(statistics['statistics']['rx-kbps'])/1000}) 68 | 69 | elif interface_mode == 'trunk': 70 | if config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("allowed", {}).get("vlan", {}).get("vlans", {}): 71 | trunked_vlans = config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("allowed", {}).get("vlan", {}).get("vlans", {}) 72 | native = config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("native", {}).get("vlan", {}) 73 | elif config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("allowed", {}).get("vlan", {}).get("add", {}): 74 | trunked_vlans = config.get('switchport').get('Cisco-IOS-XE-switch:trunk').get('allowed').get('vlan').get('add').get('vlans') 75 | native = config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("native", {}).get("vlan", {}) 76 | elif config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("allowed", {}).get("vlan", {}).get('vlans', {}): 77 | trunked_vlans = config.get('switchport').get('Cisco-IOS-XE-switch:trunk').get('allowed').get('vlan').get('vlans') 78 | native = config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("native", {}).get("vlan", {}) 79 | else: 80 | trunked_vlans = 'all' 81 | native = config.get("switchport").get("Cisco-IOS-XE-switch:trunk", {}).get("native", {}).get("vlan", {}) 82 | 83 | data.append({'mode': 'trunk', 'interface': complete_interface, 'vlans': trunked_vlans, 'native': native, 'status': statistics['oper-status'], 84 | 'mbpsOut': int(statistics['statistics']['tx-kbps'])/1000, 'mbpsIn': int(statistics['statistics']['rx-kbps'])/1000}) 85 | else: 86 | data.append({'mode': None, 'interface': complete_interface, 'status': statistics['oper-status'], 87 | 'mbpsOut': int(statistics['statistics']['tx-kbps'])/1000, 'mbpsIn': int(statistics['statistics']['rx-kbps'])/1000}) 88 | 89 | return data 90 | 91 | if __name__ == '__main__': 92 | 93 | try: 94 | get_switch() 95 | except TypeError: 96 | input('Input credentials') 97 | -------------------------------------------------------------------------------- /Python/get_ospf.py: -------------------------------------------------------------------------------- 1 | """Set of functions that gets and prints arp entries""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import warnings 7 | 8 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 9 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 10 | 11 | def _check_api_error(response) -> bool: 12 | """Check for API Errors""" 13 | 14 | is_error = False 15 | 16 | if list(response.keys())[0] == 'errors': 17 | is_error = True 18 | 19 | return is_error 20 | 21 | def _print_ospf_neigh(ospf_neighbors=None, ospf_interfaces=None) -> None: 22 | """Print Arp entries""" 23 | 24 | print(f"{'Neighbor':<25} {'Address':<30} {'DR':<30}{'BDR':<27}{'State':<25}") 25 | print('-' * 125) 26 | try: 27 | if ospf_neighbors: 28 | for neighbor in ospf_neighbors: 29 | print(f"{neighbor.get('neighbor-id', {}):<25}{neighbor.get('address', {}):<30} {neighbor.get('dr', {}):<30}{neighbor.get('bdr', {}):<25}{neighbor.get('state', {}):<30}") 30 | else: 31 | print('No Neighbors') 32 | except TypeError: 33 | pass 34 | 35 | def _print_ospf_ints(ospf_interfaces) -> None: 36 | """Print Arp entries""" 37 | 38 | 39 | print(f"\n{'Neighbor':<25} {'Network Type':<30} {'Area':<10}{'BDR':<10}{'DR':<10}{'Cost':<15} {'Dead Interval':<15} {'Hello Interval':<15}{'Hello Time':<15}{'Priority':<25}") 40 | try: 41 | print('-' * 200) 42 | for interface in ospf_interfaces: 43 | print(f"{interface.get('name', {}):<25}{interface.get('network-type', {}):<30} {interface.get('area', {}):<10}{interface.get('bdr', {}):<10}{interface.get('dr', {}):<15}{interface.get('cost', {}):<15}{interface.get('dead-interval', {}):<15} {interface.get('hello-interval', {}):<15}{interface.get('hello-timer', {}):<15}{interface.get('priority', {}):<30}") 44 | 45 | except TypeError: 46 | pass 47 | 48 | def get_ospf(ip, port, username, password): 49 | """Gets device ospf operational data""" 50 | 51 | ospf_neighbors = [] 52 | ospf_interfaces = [] 53 | 54 | try: 55 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-ospf-oper:ospf-oper-data/ospf-state/ospf-instance?fields=ospf-area/ospf-interface/ospf-neighbor" 56 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 57 | ospf = json.loads(response.text) 58 | 59 | check_error = _check_api_error(ospf) 60 | 61 | if check_error: 62 | raise AttributeError 63 | 64 | for i in ospf.get('Cisco-IOS-XE-ospf-oper:ospf-instance')[0].get('ospf-area'): 65 | [list((ospf_neighbors.append(neighbor) for neighbor in interface.get('ospf-neighbor'))) for interface in i.get('ospf-interface')] 66 | 67 | _print_ospf_neigh(ospf_neighbors) 68 | 69 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError, TypeError): 70 | pass 71 | 72 | try: 73 | 74 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-ospf-oper:ospf-oper-data/ospf-state/ospf-instance" 75 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 76 | ospf = json.loads(response.text) 77 | 78 | check_error = _check_api_error(ospf) 79 | if check_error: 80 | raise AttributeError 81 | 82 | for instance in ospf.get('Cisco-IOS-XE-ospf-oper:ospf-instance', {}): 83 | for area in instance.get('ospf-area', {}): 84 | if isinstance(area.get('ospf-interface', {}), list): 85 | for interface in area.get('ospf-interface', {}): 86 | interface['area'] = area.get('area-id', {}) 87 | for i in interface.get('ospf-neighbor', {}): 88 | ospf_interfaces.append(interface) 89 | 90 | _print_ospf_ints(ospf_interfaces) 91 | 92 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError, TypeError) as e: 93 | pass 94 | 95 | 96 | return ospf_neighbors, ospf_interfaces 97 | 98 | if __name__ == '__main__': 99 | 100 | try: 101 | get_ospf() 102 | except TypeError: 103 | input('Input credentials') 104 | -------------------------------------------------------------------------------- /Python/get_vlans.py: -------------------------------------------------------------------------------- 1 | """Set of fuctions which collects vlan data""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import time 7 | import os 8 | import warnings 9 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 10 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 11 | 12 | def get_vlans(ip, port, username, password) -> list: 13 | """Gets device components /restconf/data/openconfig-platform:components""" 14 | 15 | data = [] 16 | 17 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-vlan-oper:vlans" 18 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 19 | vlans = json.loads(response.text) 20 | 21 | for i in vlans.get('Cisco-IOS-XE-vlan-oper:vlans', {}).get('vlan', {}): 22 | try: 23 | if i.get('vlan-interfaces'): 24 | data.append({"id": i.get('id'), "name": i.get('name'), "status": i.get('status'), "interfaces": ", ".join([interface.get('interface') for interface in i.get('vlan-interfaces')])}) 25 | else: 26 | data.append({"id": i.get('id'), "name": i.get('name'), "status": i.get('status'), "interfaces": ''}) 27 | except TypeError: 28 | pass 29 | 30 | if data: 31 | print(f"\n{'Vlans':<30} {'Name':<39} {'Status':<50}{'Interfaces':<25}") 32 | print(f"--------------------------------------------------------------------------------------") 33 | for vlan_data in data: 34 | print(f"{vlan_data.get('id', {}):<30}{vlan_data.get('name', {}):<40} {vlan_data.get('status', {}):<30}{', '.join(vlan_data.get('interfaces', '').split(',')[0:4]):<20}") 35 | [_print_vlans_extended(index, vlan, vlan_data) for index, vlan in enumerate(vlan_data.get('interfaces').split(',')) if index != 0 and index % 4 == 0] 36 | else: 37 | print('No Vlans') 38 | 39 | return data 40 | 41 | def _print_vlans_extended(index, vlan, vlan_data): 42 | """Prints vlans sideXside""" 43 | 44 | try: 45 | print(" " * 100 + vlan, vlan_data.get('interfaces', '').split(',')[index + 1], vlan_data.get('interfaces', '').split(',')[index + 2], vlan_data.get('interfaces', '').split(',')[index + 4]) 46 | except IndexError: 47 | pass 48 | 49 | if __name__ == '__main__': 50 | 51 | try: 52 | get_vlans() 53 | except TypeError: 54 | input('Input credentials') 55 | -------------------------------------------------------------------------------- /Python/mac_address_table.py: -------------------------------------------------------------------------------- 1 | """Set of functions that get and print mac address table""" 2 | 3 | from json.decoder import JSONDecodeError 4 | import requests 5 | import json 6 | import warnings 7 | 8 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 9 | headers = {"Content-Type": 'application/yang-data+json', 'Accept': 'application/yang-data+json'} 10 | 11 | def _print_macs(entry) -> None: 12 | """Print mac address entries""" 13 | 14 | try: 15 | print(f"{entry.get('vlan-id-number', {}):<20}{entry.get('mac', {}):<20} {entry.get('mat-addr-type', {}):<15}{entry.get('port'):<20}") 16 | except TypeError: 17 | pass 18 | 19 | def get_bridge(ip, port, username, password) -> list: 20 | """Gets device mac-address-table""" 21 | 22 | mac_table = [] 23 | 24 | try: 25 | uri = f"https://{ip}:{port}/restconf/data/Cisco-IOS-XE-matm-oper:matm-oper-data" 26 | response = requests.get(uri, headers=headers, verify=False, auth=(username, password)) 27 | data = json.loads(response.text) 28 | 29 | print(f"{'Vlan':<25} {'Mac':<15} {'Type':<15}{'Port':<20}") 30 | print("--------------------------------------------------------------------") 31 | 32 | for i in data['Cisco-IOS-XE-matm-oper:matm-oper-data']['matm-table']: 33 | if i.get('matm-mac-entry', {}): 34 | [_print_macs(i) for i in i.get('matm-mac-entry', {})] 35 | 36 | except (JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.InvalidURL,UnboundLocalError, AttributeError, KeyError): 37 | print('No macs found. Please check device compatability with this model') 38 | 39 | return mac_table 40 | 41 | if __name__ == '__main__': 42 | 43 | try: 44 | get_bridge() 45 | except TypeError: 46 | input('Input credentials') 47 | -------------------------------------------------------------------------------- /Python/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://app.travis-ci.com/cober2019/react-ios-xe-ops.svg?branch=main 2 | :target: - 3 | .. image:: https://img.shields.io/badge/npm-16.14.12-blue 4 | :target: - 5 | .. image:: https://img.shields.io/badge/Node-stable-blue 6 | :target: - 7 | 8 | XE-Ops 9 | ============ 10 | 11 | View operational and config data from devices running Cisco IOS-XE software. 12 | 13 | NoteS 14 | ============ 15 | 16 | The build folder is the latest build. All other files are for developement and are updated frequently. They've not been tested for prod build yet. 17 | 18 | **Tested Models** 19 | 20 | | -ASR 1000 Series 21 | | -ISR 4000 Series 22 | | -CSR 1000v 23 | | -CAT 3000 Series 24 | 25 | Available Views: 26 | ----------------- 27 | 28 | **Interfaces:** 29 | 30 | | -Layer Two and Layer Three 31 | | -Arps 32 | | -QoS 33 | **Neighbors:** 34 | 35 | | -DP Neighbors 36 | | -OSPF Neighbors 37 | | -BGP Neighbors 38 | 39 | **Routing:** 40 | 41 | | -OSPF 42 | | -BGP 43 | | -Routing Table (Flapping Route Checker) 44 | 45 | **IP SLAs:** 46 | 47 | | -Check current IP SLA statuses 48 | 49 | **Environment:** 50 | 51 | | -CPU/Memory statuse 52 | | -Sensor statuses 53 | | -SFP statuses 54 | | -POE port statuses 55 | 56 | **DMVPN:** 57 | 58 | | -Tunnel statuses 59 | | -Peer statuses 60 | | -Public IP resolution 61 | | -DMVPN topology visualization 62 | 63 | **LayerTwo:** 64 | 65 | | -Vlans 66 | | -Trunks 67 | | -Spanning-tree 68 | 69 | **Rest Viewer:** 70 | 71 | | -View all device data in JSON format 72 | 73 | 74 | **Snapshots:** 75 | ---------------- 76 | | 77 | 78 | .. image:: https://github.com/cober2019/react-ios-xe-ops/blob/main/images/xeopsprodinterfaces.PNG 79 | .. image:: https://github.com/cober2019/react-ios-xe-ops/blob/main/images/xeopsprod-env.PNG 80 | .. image:: https://github.com/cober2019/react-ios-xe-ops/blob/main/images/xeoprestprod.PNG 81 | .. image:: https://github.com/cober2019/react-ios-xe-ops/blob/main/images/xeopslaprod.PNG 82 | .. image:: https://github.com/cober2019/react-ios-xe-ops/blob/main/images/xeopsloading.PNG 83 | 84 | 85 | **Notes:** 86 | | -Switches are slower to poll than routers 87 | | -Some YANG models may not be compatible with your device. If so, data is collected via Netmiko 88 | | -Views are conditionally rendered which means so some views wont display. 89 | | -Sometimes CPU data for CSRs will error. If so, it will be represented with 'Err' 90 | | -Page data will be cached for 5 minutes per device. This means if you switch pages or log into another device, the page will load what was polled last for that device/page. Beats a loading page! 91 | | -Login timeout set to 30 seconds 92 | | -Collecting RIB tables can take some time. Be patient 93 | | 94 | 95 | Requirements: 96 | -------------- 97 | 98 | Check to see if your device is compatible to use this program. Use the following instructions - https://developer.cisco.com/docs/ios-xe/#!enabling-restconf-on-ios-xe 99 | 100 | Install:Linux 101 | -------------- 102 | The following dependencies are required. If you don't have the following dependencies installed, execute: "sudo bash install_dependencies.sh" 103 | | 104 | | -NPM && Node.js https://docs.npmjs.com/downloading-and-installing-node-js-and-npm 105 | | -Python: https://docs.python-guide.org/starting/install3/linux/ 106 | | -Open SSL 107 | 108 | **Once Dependecies Are Installed:** 109 | | 110 | | **Note: If you're using windows 10, you can use built-in Ubuntu** 111 | | 112 | | 1. Clone this repo to you server and navigate to the /react-ios-xe-ops (root) directory. 113 | | 2. Execute command "sudo bash init_app_routes" which will take care of everything below. When running the script, SSL certs will be created for app to api security. TLSv1.3 for transport. 114 | | 115 | | **If you want to manualy install then continue with these steps:** 116 | | 117 | | 2. Located package.json and execute code "npm install package.json" 118 | | 3. Once packages are installed, execute code "node server.js&" 119 | | 4. Navigate to 127.0.0.1:3000 120 | | 5. Create a virtual environment by executing "python3.8 -m venv ios-xe-ops-env" and activate the env using "source ios-xe-ops-env/bin/activate" 121 | | 6. Install python modules using pip "pip install -r requirements.txt" 122 | | 7. Start the API using "Python3 api_routes.py" 123 | | 8. Go back to the web app and login to your device 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "main.css": "/static/css/main.f05ca24e.chunk.css", 4 | "main.js": "/static/js/main.07de7263.chunk.js", 5 | "main.js.map": "/static/js/main.07de7263.chunk.js.map", 6 | "runtime-main.js": "/static/js/runtime-main.32e9b272.js", 7 | "runtime-main.js.map": "/static/js/runtime-main.32e9b272.js.map", 8 | "static/css/2.f7c5f18f.chunk.css": "/static/css/2.f7c5f18f.chunk.css", 9 | "static/js/2.fa43104b.chunk.js": "/static/js/2.fa43104b.chunk.js", 10 | "static/js/2.fa43104b.chunk.js.map": "/static/js/2.fa43104b.chunk.js.map", 11 | "index.html": "/index.html", 12 | "static/css/2.f7c5f18f.chunk.css.map": "/static/css/2.f7c5f18f.chunk.css.map", 13 | "static/css/main.f05ca24e.chunk.css.map": "/static/css/main.f05ca24e.chunk.css.map", 14 | "static/js/2.fa43104b.chunk.js.LICENSE.txt": "/static/js/2.fa43104b.chunk.js.LICENSE.txt", 15 | "static/media/router.cd099654.png": "/static/media/router.cd099654.png" 16 | }, 17 | "entrypoints": [ 18 | "static/js/runtime-main.32e9b272.js", 19 | "static/css/2.f7c5f18f.chunk.css", 20 | "static/js/2.fa43104b.chunk.js", 21 | "static/css/main.f05ca24e.chunk.css", 22 | "static/js/main.07de7263.chunk.js" 23 | ] 24 | } -------------------------------------------------------------------------------- /build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cober2019/react-ios-xe-ops/37476df9ebdcca1cef4e16e68fbf1a5ee6b0456c/build/favicon.ico -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | IOS-XE-Ops
-------------------------------------------------------------------------------- /build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /build/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /build/static/css/main.f05ca24e.chunk.css: -------------------------------------------------------------------------------- 1 | body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}.App{text-align:center}.App-logo{height:40vmin;pointer-events:none}@media (prefers-reduced-motion:no-preference){.App-logo{-webkit-animation:App-logo-spin 20s linear infinite;animation:App-logo-spin 20s linear infinite}}body>iframe{display:none}.App-header{background-color:#282c34;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:calc(10px + 2vmin);color:#fff}.App-link{color:#61dafb}#dpneighbors-tab-cdp,#dpneighbors-tab-lldp,#interfaces-tab-arp,#interfaces-tab-Interfacetable,#spanningtre-tab-globalvlan,#spanningtre-tab-pervlan,#uncontrolled-tab-example-tab-ipv4,card #uncontrolled-tab-example-tab-ipv6{color:#fff;background-color:inherit;font-weight:700}.text-white{color:#adff2f;text-align:left}.text-yellow{background-color:#ff0!important}.bg-green{background-color:#98fb98}.bg-orange{background-color:orange}.green{background-color:#98fb98!important}th,tr{text-align:center}.disabledCursor{cursor:default}.row-text{font-size:5;text-align:left}.row-text,div[class~=dataTables_filter],div[class~=dataTables_info],div[class~=dataTables_length],label{color:#fff}div[class~=card]{border-radius:15px}input[type~=search],td{color:#fff}a:link,div[role=status]{color:#fff!important}option,select{color:#fff;background-color:#000}.env-row-text{color:#adff2f;font-size:5;text-align:center}.env-row-text-warn{color:orange;font-size:5;text-align:center;font-weight:700}.box-text{color:#000;font-size:5}.up{background-color:#adff2f}.dow,.up{height:25px;width:25px;border-radius:50%;display:inline-block}.dow{background-color:red}body{background-color:grey}.fade-in{-webkit-animation:fadeInAnimation 3s ease;animation:fadeInAnimation 3s ease;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes fadeInAnimation{0%{opacity:0}to{opacity:1}}@keyframes fadeInAnimation{0%{opacity:0}to{opacity:1}}.nav-tabs>li.active>a{color:#000;font-size:16px}.nav-tabs>li>a{color:#575757}.export-button{display:block;width:150px;margin-left:10px;color:#fff;background-color:#3498db;border-radius:8px}.h3{margin-bottom:15px}.background{background-color:#dddada}.card-text{margin-top:0;margin-bottom:0;text-align:left}.overlay{position:absolute;top:0;left:0;right:0;bottom:0;background-color:rgba(66,61,61,.9);z-index:2}.center-login{margin-top:200px;margin-left:600px;width:25%;padding:10px}.loading-table{width:100%;height:200px;padding:10px;opacity:.4;background-color:rgba(66,61,61,.5);border-radius:6px;margin:auto}.status{color:#000;text-align:right}.h3{color:#000;text-align:center}.status{float:right;font-size:10}.fadeTables{display:none}.input-text{text-align:center}.submitButton{display:block;max-width:300px;width:150px;margin:auto;color:#fff;background-color:#4c4ce7;border-radius:6px}.loader{border-width:6px 16px;width:80px;height:80px}.loader,.status-loader{border-bottom:16px solid #f3f3f3;border-top:16px solid #f3f3f3;border-color:#3498db #f3f3f3;border-style:solid;border-radius:50%;-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite;margin:auto}.status-loader{border-width:16px;width:10px;height:10px}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.blinking{-webkit-animation:blinkingText 1.5s infinite;animation:blinkingText 1.5s infinite}@-webkit-keyframes blinkingText{0%{color:orange}60%{color:#ff0}99%{color:#ff0}to{color:orange}}.blinking-status{-webkit-animation:blinkingStatusText infinite .5;animation:blinkingStatusText infinite .5}@-webkit-keyframes blinkingStatusText{0%{color:#000}49%{color:#000}60%{color:transparent}99%{color:transparent}to{color:#000}}@keyframes blinkingStatusText{0%{color:#000}49%{color:#000}60%{color:transparent}99%{color:transparent}to{color:#000}}.spinner{height:60px;width:60px;display:block;-webkit-animation:rotation .6s linear infinite;animation:rotation .6s linear infinite;border:6px solid rgba(0,174,239,.15);border-top-color:rgba(0,174,239,.8);border-radius:100%}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@keyframes rotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.spinner-status{height:20px;width:20px;display:flex;-webkit-animation:rotation .6s linear infinite;animation:rotation .6s linear infinite;border:4px solid rgba(0,174,239,.15);border-top-color:rgba(0,174,239,.8);border-radius:100%}.warning-loader{border-bottom:16px solid #f3f3f3;border-top:16px solid #f3f3f3;border-color:orange #f3f3f3;border-style:solid;border-width:16px;border-radius:50%;width:80px;height:80px;-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite;margin:auto}.blinking-loader{-webkit-animation:blinkingText 2s infinite;animation:blinkingText 2s infinite}@keyframes blinkingText{0%{color:#fff}60%{color:grey}99%{color:#fff}to{color:#fff}} 2 | /*# sourceMappingURL=main.f05ca24e.chunk.css.map */ -------------------------------------------------------------------------------- /build/static/js/runtime-main.32e9b272.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,i,l=r[0],p=r[1],f=r[2],c=0,s=[];c" 21 | exit 11 22 | fi 23 | 24 | fail_if_error() { 25 | [ $1 != 0 ] && { 26 | unset PASSPHRASE 27 | exit 10 28 | } 29 | } 30 | 31 | export PASSPHRASE=$(head -c 500 /dev/urandom | tr -dc a-z0-9A-Z | head -c 128; echo) 32 | 33 | subj=" 34 | C=US 35 | ST=MD 36 | O=Blah 37 | localityName=Bal 38 | commonName=$DOMAIN 39 | organizationalUnitName=XEOps 40 | emailAddress=admin@x.com 41 | " 42 | 43 | openssl genrsa -des3 -out $DOMAIN.key -passout env:PASSPHRASE 2048 44 | fail_if_error $? 45 | 46 | openssl req \ 47 | -new \ 48 | -batch \ 49 | -subj "$(echo -n "$subj" | tr "\n" "/")" \ 50 | -key $DOMAIN.key \ 51 | -out $DOMAIN.csr \ 52 | -passin env:PASSPHRASE 53 | fail_if_error $? 54 | cp $DOMAIN.key $DOMAIN.key.org 55 | fail_if_error $? 56 | 57 | openssl rsa -in $DOMAIN.key.org -out $DOMAIN.key -passin env:PASSPHRASE 58 | fail_if_error $? 59 | 60 | openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt 61 | fail_if_error $? 62 | 63 | #CREATE PYTHON VIRTUAL ENVIRONMENT 64 | python3.8 -m venv ios-xe-ops-env 65 | #ACTIVATE ENVIRONMENT 66 | source ios-xe-ops-env/bin/activate 67 | #INSTALL REQUIREMENTS 68 | pip3 install -r requirements.txt 69 | #START API 70 | python3 wsgi.py 71 | -------------------------------------------------------------------------------- /install_dependencies.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | #IF YOU DONT HAVE THE FOLLOWING DEPENDENCIES INSTALLED THEN RUN THIS SCRIPT, sudo bash install_dependencies 5 | sudo apt update 6 | INSTALL_PKGS="python3.10 nodejs npm openssl python3.8-venv pip" 7 | for i in $INSTALL_PKGS; do 8 | sudo apt-get install -y $i 9 | done 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ios-xe-ops", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://127.0.0.1:5000/", 6 | "dependencies": { 7 | "@egjs/hammerjs": "^2.0.17", 8 | "@fortawesome/fontawesome-svg-core": "^1.2.36", 9 | "@material-ui/styles": "^4.11.4", 10 | "@testing-library/jest-dom": "^5.12.0", 11 | "@testing-library/react": "^11.2.7", 12 | "@testing-library/user-event": "^12.8.3", 13 | "axios": "^0.21.4", 14 | "bootstrap": "^5.1.3", 15 | "chart.js": "^3.5.0", 16 | "concurrently": "^6.2.0", 17 | "crypto-js": "^4.1.1", 18 | "datatables.net": "^1.11.3", 19 | "datatables.net-buttons": "^1.7.1", 20 | "datatables.net-buttons-dt": "^1.7.1", 21 | "datatables.net-dt": "^1.10.25", 22 | "electron-builder": "^22.10.5", 23 | "electron-is-dev": "^2.0.0", 24 | "http-proxy-middleware": "^2.0.1", 25 | "jquery": "^3.6.0", 26 | "keycharm": "^0.4.0", 27 | "package.json": "^2.0.1", 28 | "react": "^17.0.2", 29 | "react-bootstrap": "^2.0.1", 30 | "react-d3-speedometer": "^1.0.1", 31 | "react-dom": "^17.0.2", 32 | "react-error-boundary": "^3.1.3", 33 | "react-history": "^0.18.2", 34 | "react-new-window": "^0.1.3", 35 | "react-query": "^3.24.3", 36 | "react-router-bootstrap": "^0.25.0", 37 | "react-router-dom": "^5.2.0", 38 | "react-scripts": "4.0.3", 39 | "react-tabs": "^3.2.2", 40 | "react-web-tabs": "^1.0.1", 41 | "reactjs-popup": "^2.0.4", 42 | "recoil": "^0.4.1", 43 | "vis-data": "^7.1.2", 44 | "vis-network": "^9.1.0", 45 | "vis-util": "^5.0.2", 46 | "wait-on": "^5.3.0", 47 | "web-vitals": "^1.1.2" 48 | }, 49 | "scripts": { 50 | "start": "react-scripts start", 51 | "build": "react-scripts build", 52 | "test": "react-scripts test", 53 | "eject": "react-scripts eject" 54 | }, 55 | "eslintConfig": { 56 | "extends": [ 57 | "react-app", 58 | "react-app/jest" 59 | ] 60 | }, 61 | "browserslist": { 62 | "production": [ 63 | ">0.2%", 64 | "not dead", 65 | "not op_mini all" 66 | ], 67 | "development": [ 68 | "last 1 chrome version", 69 | "last 1 firefox version", 70 | "last 1 safari version" 71 | ] 72 | }, 73 | "devDependencies": { 74 | "@types/datatables.net": "^1.10.20", 75 | "@types/jquery": "^3.5.6" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cober2019/react-ios-xe-ops/37476df9ebdcca1cef4e16e68fbf1a5ee6b0456c/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 17 | IOS-XE-Ops 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff" 8 | } 9 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const { createProxyMiddleware } = require('http-proxy-middleware'); 4 | 5 | const app = express(); 6 | 7 | app.use(express.static(path.join(__dirname, 'build'))); 8 | 9 | app.post('/login', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true, secure: false })); 10 | app.post('/pollIndexPage', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 11 | app.post('/pollEnv', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 12 | app.post('/pollL2Page', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 13 | app.post('/pollRouting', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 14 | app.post('/getDmvpn', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 15 | app.post('/getipsla', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 16 | app.post('/ribStatus', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 17 | app.post('/liveinterfaces', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 18 | app.post('/pollRouting', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 19 | app.post('/getDmvpn', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 20 | app.post('/getipsla', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 21 | app.post('/query', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 22 | app.post('/apistatus', createProxyMiddleware({ target: 'https://127.0.0.1:5000', changeOrigin: true , secure: false })); 23 | 24 | app.get('/*', function (req, res) {res.sendFile(path.join(__dirname, 'build', 'index.html'));}); 25 | 26 | const PORT = process.env.PORT || 3000; 27 | 28 | app.listen(PORT, () => console.log(`Server started on port ${PORT}`)); 29 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import 'bootstrap/dist/css/bootstrap.min.css'; 3 | import { Index } from './Components/Index/Index-Parent.js'; 4 | import { LayerTwo } from './Components/LayerTwo/layerTwo-Parent.js'; 5 | import { Environment } from './Components/Environment/Env-Parent.js'; 6 | import { Routing } from './Components/LayerThree/Routing-Parent.js'; 7 | import { Dmvpn } from './Components/DMVPN/Dmvpn-Parent.js'; 8 | import { RestConfig } from './Components/Config/config.js'; 9 | import { RibIndex } from './Components/RibStatus/RIB-Parent.js'; 10 | import { DeviceAuth } from './Components/Other/login.js'; 11 | import { IpSlas } from './Components/IPSlas/Sla-Parent.js'; 12 | import { LiveInterfaces } from './Components/InterfaceGraphs/liveInterface.js'; 13 | import { RecoilRoot, atom } from 'recoil'; 14 | import { 15 | BrowserRouter as Router, 16 | Switch, 17 | Route, 18 | } from "react-router-dom"; 19 | import { QueryClient, QueryClientProvider } from 'react-query'; 20 | const queryClient = new QueryClient(); 21 | 22 | export const encytpKey = atom({ 23 | key: 'key', 24 | default: 'jdh%):Aap(3>S#', 25 | }); 26 | 27 | export const client = atom({ 28 | key: 'queryClient', 29 | default: CachedQueryClient, 30 | }); 31 | 32 | 33 | function App() { 34 | 35 | return ( 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 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 | 136 | ); 137 | } 138 | 139 | export default App; 140 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | it('xeops', () => { 5 | render(); 6 | }); 7 | -------------------------------------------------------------------------------- /src/Components/DMVPN/Dmvpn-Parent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Container from "react-bootstrap/Container"; 3 | import Row from "react-bootstrap/Row"; 4 | import Col from "react-bootstrap/Col"; 5 | import {useRecoilState} from 'recoil'; 6 | import { ErrorBoundary } from 'react-error-boundary' 7 | import { Ospf } from '../LayerThree/ospf'; 8 | import { Navigation } from '../Other/navbar'; 9 | import { DmvpnData } from './dmvpnData'; 10 | import { useQuery, useQueryClient} from 'react-query'; 11 | import {encytpKey, client} from '../../App' 12 | import { NavigationFallback } from "../Other/navbarError"; 13 | import { IsErrorFallback } from "../Other/errorComponent"; 14 | import { PageLoader } from '../Other/pageLoader'; 15 | import { ApiRequest } from "../Other/axiosRequests"; 16 | 17 | export function Dmvpn(){ 18 | const [decryptKey] = useRecoilState(encytpKey); 19 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'getDmvpn', async () => { 20 | 21 | const response = await ApiRequest(decryptKey, '/getDmvpn') 22 | 23 | return response.data 24 | 25 | }, 26 | { 27 | refetchInterval: 20000 28 | } 29 | ) 30 | 31 | 32 | if (error){ 33 | return
34 | 35 | 36 | 37 |

Error Collecting Data. I'll Keep Trying

38 |
39 |
40 | } 41 | else if (data){ 42 | return 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {data.dmvpn.length > 0 ? :
} 57 | 58 | 59 | 60 | 61 | } 62 | else if (isLoading){ 63 | return
64 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 65 |
66 | } 67 | 68 | 69 | } 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/Components/DMVPN/topology.js: -------------------------------------------------------------------------------- 1 | import router from '../Images/router.png'; 2 | import { Network } from "vis-network"; 3 | 4 | var font = { size: 18, strokeWidth: 0, color: 'white'} 5 | var Hubfont = { size: 18, strokeWidth: 0, color: 'orange'} 6 | var nodeFont = { size: 16, color: "white"} 7 | var options = { 8 | nodes: { 9 | shape: 'image', 10 | image: router, 11 | size: 35, 12 | color: { 13 | border: 'black', 14 | background: '#166F20', 15 | highlight: { 16 | border: '#2B7CE9', 17 | background: '#D2E5FF' 18 | } 19 | } 20 | }, 21 | layout: { 22 | randomSeed: undefined, 23 | improvedLayout:true, 24 | clusterThreshold: 200, 25 | hierarchical: { 26 | enabled:true, 27 | levelSeparation: 500, 28 | treeSpacing: 300, 29 | edgeMinimization: true, 30 | parentCentralization: true, 31 | direction: 'DU', // UD, DU, LR, RL 32 | sortMethod: 'hubsize', // hubsize, directed 33 | shakeTowards: 'roots' // roots, leaves 34 | } 35 | }, 36 | physics:{ 37 | enabled: true, 38 | hierarchicalRepulsion: { 39 | centralGravity: 0.0, 40 | springLength: 0, 41 | springConstant: 0.01, 42 | nodeDistance: 250, 43 | damping: 1, 44 | avoidOverlap: 1 45 | }, 46 | }, 47 | 48 | } 49 | 50 | export function UpdateDmvpnTopology(ref, peers, localIp){ 51 | 52 | Object.entries(peers).forEach((details, i) => { 53 | if (details[1].state !== 'UP'){ 54 | ref.body.data.edges.push({id: details[1].peerNbma, 55 | from: localIp, 56 | to: i, 57 | color: 'yellow', 58 | label: details[1].state + '\n' + details[1].peerTunnel + "\n" + details[1].attrb, 59 | font: font 60 | }); 61 | } 62 | else{ 63 | ref.body.data.edges.push({id: details[1].vlanInt, 64 | from: localIp, 65 | to: i, 66 | color: 'green', 67 | label: details[1].state + '\n' + details[1].peerTunnel + "\n" + details[1].attrb, 68 | font: font 69 | }); 70 | }}) 71 | 72 | return ref 73 | 74 | } 75 | 76 | export function DmvpnTopologyBuild(ref, peers, localIp, hubs){ 77 | 78 | const nodes = [] 79 | const edges = [] 80 | 81 | nodes.push({id: localIp, label: 'Local\n' + localIp, font: nodeFont }) 82 | 83 | Object.entries(peers).forEach((details, i) => { 84 | 85 | if (hubs.some(ip => ip.hubNbma === details[1].peerNbma)){ 86 | const spookeOrHub = details[1].peerNbma + ': Hub' 87 | nodes.push({id: details[0], label: spookeOrHub, font: Hubfont}); 88 | } 89 | else{ 90 | const spookeOrHub = details[1].peerNbma + ': Spoke' 91 | nodes.push({id: details[0], label: spookeOrHub, font: font}); 92 | } 93 | 94 | 95 | }) 96 | 97 | Object.entries(peers).forEach((details, i) => { 98 | 99 | 100 | if (details[1].state !== 'UP'){ 101 | edges.push({id: details[1].peerNbma, 102 | from: localIp, 103 | to: i, 104 | color: 'yellow', 105 | label: details[1].state + '\n' + details[1].peerTunnel + "\n" + details[1].attrb, 106 | font: font 107 | }); 108 | } 109 | else{ 110 | edges.push({id: details[1].peerNbma, 111 | from: localIp, 112 | to: i, 113 | color: 'green', 114 | label: details[1].state + '\n' + details[1].peerTunnel + "\n" + details[1].attrb, 115 | font: font 116 | }); 117 | }}) 118 | 119 | var network = new Network(ref, {nodes: nodes, edges: edges}, options); 120 | 121 | return network 122 | 123 | } -------------------------------------------------------------------------------- /src/Components/Environment/Env-Parent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useQuery } from 'react-query'; 3 | import Row from "react-bootstrap/Row"; 4 | import Container from "react-bootstrap/Container"; 5 | import Col from "react-bootstrap/Col"; 6 | import {useRecoilState} from 'recoil'; 7 | import { CpuUsage} from './cpuUsages' 8 | import { Sensors} from './sensors' 9 | import { ErrorBoundary } from 'react-error-boundary' 10 | import { CreateCard } from '../Other/jsxCard'; 11 | import { Navigation } from '../Other/navbar'; 12 | import { NavigationFallback } from "../Other/navbarError"; 13 | import { ApiRequest } from "../Other/axiosRequests"; 14 | import { IsErrorFallback } from "../Other/errorComponent"; 15 | import { PoeConnections } from './poe' 16 | import { Transceivers } from './transceivers' 17 | import { TransceiversInv } from './transieverInventory' 18 | import { PageLoader } from '../Other/pageLoader'; 19 | import {encytpKey} from '../../App' 20 | 21 | 22 | export function Environment(){ 23 | const [decryptKey] = useRecoilState(encytpKey); 24 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'pollEnv', async () => { 25 | 26 | const response = await ApiRequest(decryptKey, '/pollEnv') 27 | 28 | return response.data 29 | 30 | }, 31 | { 32 | refetchInterval: 10000 33 | } 34 | ) 35 | 36 | 37 | if (error){ 38 | return <> 39 | 40 | 41 | 42 |

Error Collecting Data. I'll Keep Trying

43 |
44 | 45 | } 46 | else if (data){ 47 | return 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {CreateCard(, "SFP Statuses")} 57 | 58 | 59 | {CreateCard(, 'Environmental Stats')} 60 | {CreateCard(, 'Poe Interface')} 61 | {CreateCard(, "SFP Inventory")} 62 | 63 | 64 | 65 | } 66 | else if (isLoading){ 67 | return <> 68 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 69 | 70 | } 71 | 72 | 73 | 74 | } 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/Components/Environment/env.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect } from 'react'; 2 | import { useQuery } from 'react-query'; 3 | import axios from 'axios'; 4 | import { CpuUsage} from './cpuUsages' 5 | import { Sensors} from './sensors' 6 | import { ErrorBoundary } from '../Other/errorBoundry'; 7 | import { EnvData} from '../Other/data'; 8 | import { Navbar } from '../Other/navbar'; 9 | import { Poe , Transiever } from '../Other/data'; 10 | import { PoeConnections } from './poe' 11 | import { Transceivers } from './transceivers' 12 | import {AES, enc}from 'crypto-js'; 13 | 14 | 15 | 16 | export function Environment(props){ 17 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), 'MYKEY4DEMO'); 18 | const password = passwordDecrypt.toString(enc.Utf8); 19 | const { isLoading, error, data, isFetching } = useQuery('pollEnv', async () => { 20 | 21 | const response = await axios.post('/pollEnv', {'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 22 | 'password': password, 'port': localStorage.getItem('port')}) 23 | return response.data 24 | 25 | }, 26 | { 27 | refetchInterval: 10000, cacheTime: 0 28 | } 29 | ) 30 | 31 | 32 | if (error){ 33 | return
34 | 35 |

Error Collecting Data. I'll Keep Trying

36 |
37 |
38 | } 39 | else if (data){ 40 | return
41 | 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 | 55 |
56 | 57 | 58 | 59 |
60 |
61 |
62 | } 63 | else if (isLoading){ 64 | return
65 |

Collecting Data for {localStorage.getItem('ip')}

66 |
67 |
68 | } 69 | 70 | 71 | 72 | } 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/Components/Environment/poe.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { PoeTableHtml } from '../Other/chartConfigs'; 3 | import { PoeData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function PoeConnections(props){ 8 | const poeTableRef = React.createRef() 9 | const poeTable = PoeTableHtml(poeTableRef) 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | 12 | useEffect(() => { 13 | if(poeTableRef.current !== null){ 14 | try{ 15 | $(poeTableRef.current).DataTable().clear() 16 | $(poeTableRef.current).DataTable().rows.add(Object.values(props.poeprops.poe)) 17 | $(poeTableRef.current).DataTable().rows.add(Object.values(props.poe)) 18 | $(poeTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | }, [props.poe]) 23 | 24 | useEffect(() => { 25 | $(poeTableRef.current).DataTable().destroy() 26 | PoeData(poeTableRef.current, props.poe) 27 | }, []) 28 | 29 | return poeTable 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/Components/Environment/sensors.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { EnvTableHtml } from '../Other/chartConfigs'; 3 | import { SensorData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function Sensors(props){ 8 | const envTableRef = React.createRef() 9 | const table = EnvTableHtml(envTableRef) 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | 12 | 13 | useEffect(() => { 14 | if(envTableRef.current !== null){ 15 | try{ 16 | $(envTableRef.current).DataTable().clear() 17 | $(envTableRef.current).DataTable().rows.add(Object.values(props.env)) 18 | $(envTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | }, [props.env]) 23 | 24 | useEffect(() => { 25 | $(envTableRef.current).DataTable().destroy() 26 | SensorData(envTableRef.current, props.env) 27 | 28 | }, []) 29 | 30 | return table 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Components/Environment/transceivers.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { TransceiverTableHtml } from '../Other/chartConfigs'; 3 | import { TransieverData } from '../Other/tables'; 4 | 5 | const $ = require('jquery'); 6 | $.DataTable = require('datatables.net'); 7 | 8 | export function Transceivers(props){ 9 | const transceiverTableRef = React.createRef() 10 | const transceiverOpertable = TransceiverTableHtml(transceiverTableRef) 11 | $.fn.dataTable.ext.errMode = 'none'; 12 | 13 | useEffect(() => { 14 | if(transceiverTableRef.current !== null){ 15 | try{ 16 | $(transceiverTableRef.current).DataTable().clear() 17 | $(transceiverTableRef.current).DataTable().rows.add(props.transceivers) 18 | $(transceiverTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | }, [props.transceivers]) 23 | 24 | useEffect(() => { 25 | 26 | $(transceiverTableRef.current).DataTable().destroy() 27 | TransieverData(transceiverTableRef.current, props.transceivers) 28 | 29 | }, []) 30 | 31 | return transceiverOpertable 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Components/Environment/transieverInventory.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { InventoryTransceiverTableHtml } from '../Other/chartConfigs'; 3 | import { InvTransieverData } from '../Other/tables'; 4 | 5 | const $ = require('jquery'); 6 | $.DataTable = require('datatables.net'); 7 | 8 | 9 | export function TransceiversInv(props){ 10 | const inventoryTransceiverTableRef = React.createRef() 11 | const inventorytable = InventoryTransceiverTableHtml(inventoryTransceiverTableRef) 12 | $.fn.dataTable.ext.errMode = 'none'; 13 | 14 | useEffect(() => { 15 | if(inventoryTransceiverTableRef.current !== null){ 16 | try{ 17 | $(inventoryTransceiverTableRef.current).DataTable().clear() 18 | $(inventoryTransceiverTableRef.current).DataTable().rows.add(props.transceivers) 19 | $(inventoryTransceiverTableRef.current).DataTable().draw(false) 20 | } 21 | catch{} 22 | } 23 | }, [props.transceivers]) 24 | 25 | useEffect(() => { 26 | 27 | $(inventoryTransceiverTableRef.current).DataTable().destroy() 28 | InvTransieverData(inventoryTransceiverTableRef.current, props.transceivers) 29 | 30 | }, []) 31 | 32 | return inventorytable 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Components/Forms/ospfNeighborForm.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import axios from 'axios'; 3 | import Form from 'react-bootstrap/Form' 4 | import { useQuery } from 'react-query' 5 | import {useRecoilState} from 'recoil'; 6 | import Card from "react-bootstrap/Card"; 7 | import Row from "react-bootstrap/Row"; 8 | import Col from "react-bootstrap/Col"; 9 | import { AES, enc }from 'crypto-js'; 10 | import {encytpKey} from '../../App' 11 | 12 | export function ModifyOspfNeighbor(props){ 13 | const bgpForm = useRef(); 14 | const [network, setNetwork] = useState(undefined) 15 | const [wildcard, setWildcard] = useState(undefined) 16 | const [decrypt] = useRecoilState(encytpKey); 17 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), decrypt); 18 | const { isLoading, error, data, isFetching, refetch } = useQuery(localStorage.getItem('ip') + 'modifyOspfNeighbor', async () => { 19 | 20 | const data = await axios.post('/modifyOspfNeighbor', {'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 21 | 'password': passwordDecrypt.toString(enc.Utf8), 'port': localStorage.getItem('port'),'data': {'type': 'ospf', 'network': network, 'wildcard': wildcard}}) 22 | 23 | return data.data 24 | 25 | }, 26 | {enabled: false} ); 27 | 28 | const handleSubmit = (evt) => { 29 | evt.preventDefault(); 30 | refetch()}; 31 | 32 | return 33 | 34 | Add OSPF Network 35 |
36 | 37 | 38 | Network 39 | setNetwork(e.target.value)} placeholder="Network" name="network" style={{textAlign: 'left', fontWeight: 'bold' }}/> 40 | 41 | 42 | Network 43 | setWildcard(e.target.value)} placeholder="Wildcard" name="wildcard" style={{textAlign: 'left', fontWeight: 'bold' }}/> 44 | 45 | 46 | 47 |
48 |
49 |
50 | 51 | 52 | } -------------------------------------------------------------------------------- /src/Components/IPSlas/Sla-Parent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Container from "react-bootstrap/Container"; 3 | import Col from "react-bootstrap/Col"; 4 | import Row from "react-bootstrap/Row"; 5 | import {useRecoilState} from 'recoil'; 6 | import { Navigation } from '../Other/navbar' 7 | import { SlaStats } from './ipslastats' 8 | import { BuildSlaTopologies } from './slatopologies' 9 | import { useQuery } from 'react-query'; 10 | import { ErrorBoundary } from 'react-error-boundary' 11 | import { encytpKey, client} from '../../App' 12 | import { CreateCard } from '../Other/jsxCard'; 13 | import { NavigationFallback } from "../Other/navbarError"; 14 | import { PageLoader } from '../Other/pageLoader'; 15 | import { ApiRequest } from "../Other/axiosRequests"; 16 | 17 | export function IpSlas(){ 18 | const [decryptKey] = useRecoilState(encytpKey); 19 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'getipsla', async () => { 20 | 21 | const response = await ApiRequest(decryptKey, '/getipsla') 22 | 23 | return response.data 24 | 25 | }, 26 | { 27 | refetchInterval: 5000 28 | } 29 | ) 30 | 31 | if (error){ 32 | return <> 33 | 34 | 35 | 36 |

Error Collecting Data. I'll Keep Trying

37 |
38 | 39 | } 40 | else if (data){ 41 | return 42 | 43 | 44 | 45 | 46 | {CreateCard(, "IP SLAs")} 47 | 48 | 49 | { data.slas.map(sla => ( 50 | 51 | 52 | {CreateCard(, "SLA ID: " + sla['oper-id'])} 53 | 54 | 55 | ))} 56 | 57 | 58 | } 59 | else if (isLoading){ 60 | return <> 61 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 62 | 63 | } 64 | 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/Components/IPSlas/ipslastats.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { SlaTableHtml } from '../Other/chartConfigs'; 3 | import { IpSlaData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function SlaStats(props){ 8 | const slaTableRef = React.createRef() 9 | const slaTable = SlaTableHtml(slaTableRef) 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | 12 | 13 | useEffect(() => { 14 | if(slaTableRef.current !== null){ 15 | try{ 16 | $(slaTableRef.current).DataTable().clear() 17 | $(slaTableRef.current).DataTable().rows.add(props.slas) 18 | $(slaTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | 23 | }, [props.slas]) 24 | 25 | 26 | useEffect(() => { 27 | 28 | $(slaTableRef.current).DataTable().destroy() 29 | IpSlaData(slaTableRef.current, props.slas) 30 | 31 | }, []) 32 | 33 | return slaTable 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/Components/IPSlas/slatopologies.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { IpSlaTopologyBuild, UpdateIpSlaTopology } from './topology'; 3 | const $ = require('jquery'); 4 | $.DataTable = require('datatables.net'); 5 | 6 | export function BuildSlaTopologies(props){ 7 | const slaTopologyRef = React.createRef() 8 | const slaTopology = useRef() 9 | $.fn.dataTable.ext.errMode = 'none'; 10 | 11 | useEffect(() => { 12 | try{ 13 | slaTopology.current = UpdateIpSlaTopology(slaTopology.current, props.sla, props.localIp) 14 | slaTopologyRef.current = slaTopology.current 15 | } 16 | catch(e){console.log(e)} 17 | 18 | }, [props.sla]) 19 | 20 | useEffect(() => { 21 | 22 | try{ 23 | slaTopology.current = IpSlaTopologyBuild(slaTopologyRef.current, props.sla, props.localIp) 24 | slaTopologyRef.current = slaTopology.current 25 | } 26 | catch{} 27 | }, []) 28 | 29 | return
30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/Components/IPSlas/topology.js: -------------------------------------------------------------------------------- 1 | 2 | import router from '../Images/router.png'; 3 | import { Network } from "vis-network"; 4 | 5 | var font = { size: 15, strokeWidth: 0, color: 'white'} 6 | var arrowEnabled = {enabled: true} 7 | var nodeFont = { size: 16, color: "white"} 8 | var options = { 9 | nodes: { 10 | shape: 'image', 11 | image: router, 12 | size: 35, 13 | color: { 14 | border: 'black', 15 | background: '#166F20', 16 | highlight: { 17 | border: '#2B7CE9', 18 | background: '#D2E5FF' 19 | } 20 | } 21 | }, 22 | layout: { 23 | randomSeed: undefined, 24 | improvedLayout:true, 25 | clusterThreshold: 150, 26 | hierarchical: { 27 | enabled:true, 28 | levelSeparation: 500, 29 | treeSpacing: 150, 30 | edgeMinimization: true, 31 | parentCentralization: true, 32 | direction: 'RL', // UD, DU, LR, RL 33 | sortMethod: 'hubsize', // hubsize, directed 34 | shakeTowards: 'roots' // roots, leaves 35 | } 36 | }, 37 | physics:{ 38 | enabled: true, 39 | hierarchicalRepulsion: { 40 | centralGravity: 0.0, 41 | springLength: 0, 42 | springConstant: 0.01, 43 | nodeDistance: 200, 44 | damping: 1, 45 | avoidOverlap: 1 46 | }, 47 | }, 48 | 49 | } 50 | 51 | export function UpdateIpSlaTopology(ref, sla, localIp){ 52 | 53 | 54 | if(sla['latest-return-code'] !== 'ret-code-ok'){ 55 | ref.body.data.edges.update({id: 2000, 56 | from: 1, 57 | to: localIp, 58 | color: 'yellow', 59 | label: 'Latest: ' + sla['latest-return-code'] + '\nSuccess: ' +sla['success-count'] + '\nFailures: ' + sla['failure-count'], 60 | }) 61 | } 62 | else{ 63 | ref.body.data.edges.update({id: 2000, 64 | from: 1, 65 | to: localIp, 66 | color: 'green', 67 | label: 'Latest: ' + sla['latest-return-code'] + '\nSuccess: ' +sla['success-count'] + '\nFailures: ' + sla['failure-count'], 68 | }) 69 | } 70 | 71 | return ref 72 | 73 | } 74 | 75 | 76 | 77 | export function IpSlaTopologyBuild(ref, sla, localIp){ 78 | 79 | const nodes = [] 80 | const edges = [] 81 | 82 | nodes.push({id: 1, label: 'Responder', font: nodeFont}) 83 | nodes.push({id: localIp, label: localIp, font: nodeFont}) 84 | 85 | 86 | if(sla['latest-return-code'] !== 'ret-code-ok'){ 87 | edges.push({id: 2000, 88 | from: 1, 89 | to: localIp, 90 | color: 'yellow', 91 | label: 'Latest: ' + sla['latest-return-code'] + '\nSuccess: ' +sla['success-count'] + '\nFailures: ' + sla['failure-count'], 92 | font: font, 93 | arrows:{from: arrowEnabled} 94 | }) 95 | } 96 | else{ 97 | edges.push({id: 2000, 98 | from: 1, 99 | to: localIp, 100 | color: 'green', 101 | label: 'Latest: ' + sla['latest-return-code'] + '\nSuccess: ' +sla['success-count'] + '\nFailures: ' + sla['failure-count'], 102 | font: font, 103 | arrows:{from: arrowEnabled} 104 | }) 105 | } 106 | 107 | 108 | var data = { 109 | nodes: nodes, 110 | edges: edges, 111 | }; 112 | 113 | var network = new Network(ref, data, options); 114 | 115 | return network 116 | 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/Components/Images/router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cober2019/react-ios-xe-ops/37476df9ebdcca1cef4e16e68fbf1a5ee6b0456c/src/Components/Images/router.png -------------------------------------------------------------------------------- /src/Components/Index/Index-Parent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useQuery, useQueryClient } from 'react-query' 3 | import Tabs from "react-bootstrap/Tabs"; 4 | import Tab from "react-bootstrap/Tab"; 5 | import Container from "react-bootstrap/Container"; 6 | import Card from "react-bootstrap/Card"; 7 | import {useRecoilState} from 'recoil'; 8 | import { ErrorBoundary } from 'react-error-boundary' 9 | import { Arps} from './arps' 10 | import { InterfaceTable} from './InterfacesTable' 11 | import { Qos} from './qos' 12 | import { Hsrp} from './hsrp' 13 | import { DpNeighbors} from '../Other/dp_neighbors' 14 | import { CreateCard } from '../Other/jsxCard'; 15 | import { Navigation } from '../Other/navbar'; 16 | import { PageLoader } from '../Other/pageLoader'; 17 | import { NavigationFallback } from "../Other/navbarError"; 18 | import { IsErrorFallback } from "../Other/errorComponent"; 19 | import { ModifyInterface } from '../Forms/interfaceForm'; 20 | import { BandwidthDiff } from '../Other/bandwidthFunctions'; 21 | import { ApiRequest } from '../Other/axiosRequests'; 22 | import {encytpKey, client} from '../../App' 23 | 24 | export function Index(){ 25 | const [decryptKey] = useRecoilState(encytpKey); 26 | const cache = React.useRef(useQueryClient()) 27 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'indexData', async () => { 28 | 29 | const response = await ApiRequest(decryptKey, '/pollIndexPage') 30 | const calulatedBandwdithDiff = BandwidthDiff(cache, response.data) 31 | 32 | return calulatedBandwdithDiff 33 | 34 | }, 35 | { 36 | refetchInterval: 2000 37 | } 38 | ) 39 | 40 | if (error){ 41 | return
42 | 43 | 44 | 45 |

Error Collecting Data. I'll Keep Trying

46 |
47 |
48 | } 49 | else if (data){ 50 | 51 | return 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {CreateCard()} 63 | 64 | 65 | 66 | 67 | 68 | 69 | { Object.values(data.interfaces).map(interfaceDetail => ( 70 | 71 | {interfaceDetail.qos.length !== 0 ? : <>} 72 | 73 | ))} 74 | 75 | 76 | {CreateCard(, "ARPs")} 77 | {CreateCard()} 78 | {data.hsrp.length > 0 ? CreateCard(, "HSRP Topology") :
} 79 | 80 | } 81 | else if (isLoading){ 82 | 83 | return <> 84 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 85 | 86 | } 87 | 88 | 89 | } 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/Components/Index/InterfacesTable.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { InterfacesTableHtml } from '../Other/chartConfigs'; 3 | import { InterfaceData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function InterfaceTable(props){ 8 | const interfacesTableRef = React.createRef() 9 | const interfacestable = InterfacesTableHtml(interfacesTableRef) 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | 12 | useEffect(() => { 13 | if(interfacesTableRef.current !== null){ 14 | try{ 15 | $(interfacesTableRef.current).DataTable().clear() 16 | $(interfacesTableRef.current).DataTable().rows.add(Object.values(props.interfaces)) 17 | $(interfacesTableRef.current).DataTable().draw(false) 18 | } 19 | catch{} 20 | } 21 | 22 | }, [props.interfaces]) 23 | 24 | 25 | useEffect(() => { 26 | $(interfacesTableRef.current).DataTable().destroy() 27 | InterfaceData(interfacesTableRef.current, props.interfaces) 28 | }, []) 29 | 30 | return interfacestable 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Components/Index/arps.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { ArpTableHtml } from '../Other/chartConfigs'; 3 | import { ArpData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | 8 | export function Arps(props){ 9 | const arpTableRef = React.createRef() 10 | const table = ArpTableHtml(arpTableRef) 11 | $.fn.dataTable.ext.errMode = 'none'; 12 | 13 | useEffect(() => { 14 | if(arpTableRef.current !== null){ 15 | try{ 16 | $(arpTableRef.current).DataTable().clear() 17 | $(arpTableRef.current).DataTable().rows.add(props.arps) 18 | $(arpTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | 23 | }, [props.arps]) 24 | 25 | useEffect(() => { 26 | $(arpTableRef.current).DataTable().destroy() 27 | ArpData(arpTableRef.current, props.arps) 28 | 29 | }, []) 30 | 31 | return table 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Components/Index/hsrp.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { HsrpTableHtml } from '../Other/chartConfigs'; 3 | import { HsrpData } from '../Other/tables'; 4 | import { HsrpTopologyBuild, UpdateHsrpTopology } from './topology'; 5 | const $ = require('jquery'); 6 | $.DataTable = require('datatables.net'); 7 | 8 | 9 | export function Hsrp(props){ 10 | const hsrpTableRef = React.createRef() 11 | const table = HsrpTableHtml(hsrpTableRef) 12 | const hsrpTopologyRef = React.createRef() 13 | const hsrpTopology = useRef(null) 14 | $.fn.dataTable.ext.errMode = 'none'; 15 | 16 | useEffect(() => { 17 | if(hsrpTableRef.current !== null){ 18 | try{ 19 | $(hsrpTableRef.current).DataTable().clear() 20 | $(hsrpTableRef.current).DataTable().rows.add(props.hsrp) 21 | $(hsrpTableRef.current).DataTable().draw(false) 22 | } 23 | catch{} 24 | try{ 25 | hsrpTopology.current = UpdateHsrpTopology(hsrpTopology.current, props.hsrp, props.localIp) 26 | hsrpTopologyRef.current = hsrpTopology.current 27 | } 28 | catch{} 29 | } 30 | 31 | }, [props.hsrp]) 32 | 33 | useEffect(() => { 34 | 35 | $(hsrpTableRef.current).DataTable().destroy() 36 | HsrpData(hsrpTableRef.current, props.hsrp) 37 | 38 | try{ 39 | if(props.hsrp.length >= 1){ 40 | hsrpTopology.current = HsrpTopologyBuild(hsrpTopologyRef.current, props.hsrp, props.localIp) 41 | hsrpTopologyRef.current = hsrpTopology.current 42 | } 43 | } 44 | catch{} 45 | 46 | }, []) 47 | 48 | return
49 |
50 |
51 |
52 |
53 | {table} 54 |
55 |
56 | } 57 | -------------------------------------------------------------------------------- /src/Components/Index/interfaceCard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import Col from "react-bootstrap/Col"; 3 | import Row from "react-bootstrap/Row"; 4 | import { InitialChartBuild, UpdateChart } from '../Other/chartConfigs'; 5 | const $ = require('jquery'); 6 | $.DataTable = require('datatables.net'); 7 | 8 | 9 | export function InterfaceCard(props){ 10 | const interfacesChart = useRef(null) 11 | const interfacesRef = React.createRef() 12 | const interfaceNameRef = React.createRef(props.value.name) 13 | $.fn.dataTable.ext.errMode = 'none'; 14 | 15 | useEffect(() => { 16 | if(interfacesChart.current !== null){ 17 | try{ 18 | let updatedChart = UpdateChart(interfacesChart.current, parseInt(props.value['statistics']['tx-kbps']),parseInt(props.value['statistics']['rx-kbps'])); 19 | updatedChart.update() 20 | interfacesChart.current = updatedChart 21 | } 22 | catch{} 23 | } 24 | 25 | }, [props.value]) 26 | 27 | 28 | useEffect(() => { 29 | try{ 30 | let chart = InitialChartBuild(interfacesRef.current.getContext('2d'), parseInt(props.value['statistics']['tx-kbps']), parseInt(props.value['statistics']['rx-kbps'])); 31 | interfacesChart.current = chart 32 | } 33 | catch{} 34 | }, []) 35 | 36 | 37 | return
38 | 39 | 40 | 41 |
42 | 43 | 44 |

Speed:

45 |

Status:

46 |

IP:

47 |

MTU:

48 |

Mbps Out:

49 |

Mbps In:

50 |

Mbps Out Diff:

51 |

Mbps In Diff:

52 | 53 | 54 |

{Math.round(parseInt(props.value.speed) / 1000000000) * 1000 } (Mbps)

55 |

{props.value['oper-status']}

56 | {props.value.ipv4 ?

{props.value.ipv4}

:

n/a

} 57 |

{props.value.mtu}

58 |

{props.value['statistics']['tx-kbps']}

59 |

{props.value['statistics']['rx-kbps']}

60 |

{props.value.outbandwidthDiff}

61 |

{props.value.inbandwidthDiff}

62 | 63 | 64 |

PPs Out:

65 |

PPs In:

66 |

InDis:

67 |

OutDis:

68 |

InErr:

69 |

InDis:

70 |

CRC:

71 |

InDis:

72 | 73 | 74 | 75 |

{props.value['statistics']['rx-pps']}

76 |

{props.value['statistics']['tx-pps']}

77 |

{props.value['statistics']['in-discards']}

78 |

{props.value['statistics']['out-discards']}

79 |

{props.value['statistics']['in-errors']}

80 |

{props.value['statistics']['out-errors']}

81 |

{props.value['statistics']['in-crc-errors']}

82 |

{props.value['statistics']['num-flaps']}

83 | 84 | 85 |
86 | 87 | 88 |

Description:

89 |

LastChange:

90 | 91 | 92 | {props.value.description ?

{props.value.description}

:

n/a

} 93 |

{props.value['statistics']['discontinuity-time'].split('.')[0]}

94 | 95 | 96 |
97 |
98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/Components/Index/qos.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import Row from "react-bootstrap/Row"; 3 | import Col from "react-bootstrap/Col"; 4 | import { ErrorBoundary } from 'react-error-boundary' 5 | import { QosChart } from './qosCharts'; 6 | import { IsErrorFallback } from "../Other/errorComponent"; 7 | import { QosTopologyBuild, UpdateQosTopology } from './topology'; 8 | const $ = require('jquery'); 9 | $.DataTable = require('datatables.net'); 10 | 11 | export function Qos(props){ 12 | const qosTopologyRef = React.createRef() 13 | const qosTopology = useRef(null) 14 | $.fn.dataTable.ext.errMode = 'none'; 15 | 16 | useEffect(() => { 17 | try{ 18 | props.qos.map(queue => { 19 | qosTopology.current = UpdateQosTopology(qosTopology.current, queue, props.interface.data.name) 20 | qosTopologyRef.current = qosTopology.current 21 | }) 22 | } 23 | catch{} 24 | }, [props.qos]) 25 | 26 | useEffect(() => { 27 | 28 | try{ 29 | props.qos.map(queue => { 30 | qosTopology.current = QosTopologyBuild(qosTopologyRef.current, queue, props.interface.data.name) 31 | qosTopologyRef.current = qosTopology.current 32 | }) 33 | } 34 | catch{} 35 | 36 | }, []) 37 | 38 | return 39 | { props.qos.map(queue => ( 40 | <> 41 | 42 |
{props.interface.data.name} - Allocation: {queue.allocation} - Policy: {queue.interface_policy} - Direction: {queue.direction}
43 |
44 | 45 | 46 | { 47 | queue.queues.map(queueDetails => 48 | 49 | 50 | 51 | 52 | 53 | )} 54 | 55 | 56 | ))} 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/Components/Index/qosCharts.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { BarChart, BarChartUpdate } from '../Other/chartConfigs'; 3 | import { CreateCard } from '../Other/jsxCard'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function QosChart(props){ 8 | const qosChartRef = useRef(null) 9 | const chartCanvasRef = React.createRef() 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | 12 | useEffect(() => { 13 | try{ 14 | let updatedChart = BarChartUpdate(qosChartRef.current, [parseInt(props.queue.rate)]); 15 | updatedChart.update() 16 | chartCanvasRef.current = updatedChart 17 | } 18 | catch{} 19 | 20 | }, [props.queue]) 21 | 22 | useEffect(() => { 23 | let qosChart = BarChart(chartCanvasRef.current.getContext('2d'), parseInt(props.queue.rate)); 24 | qosChartRef.current = qosChart 25 | }, []) 26 | 27 | return <>{CreateCard(, '',
{props.queue['queue-name']}
)} 28 | 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/Components/InterfaceGraphs/liveInterface.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useQuery, useQueryClient } from 'react-query' 3 | import Container from "react-bootstrap/Container"; 4 | import Col from "react-bootstrap/Col"; 5 | import Row from "react-bootstrap/Row"; 6 | import {useRecoilState} from 'recoil'; 7 | import { InterfaceCard} from '../Index/interfaceCard' 8 | import { Navigation } from '../Other/navbar'; 9 | import { NavigationFallback } from '../Other/navbarError'; 10 | import { CreateCard } from '../Other/jsxCard'; 11 | import { IsErrorFallback } from "../Other/errorComponent"; 12 | import { ErrorBoundary } from 'react-error-boundary' 13 | import { PageLoader } from '../Other/pageLoader'; 14 | import { BandwidthDiff } from '../Other/bandwidthFunctions'; 15 | import { encytpKey, client} from '../../App' 16 | import { ShowInterface } from '../Modals/interfaceModal'; 17 | import { ModifyInterface } from '../Forms/interfaceForm'; 18 | import { ApiRequest } from "../Other/axiosRequests"; 19 | 20 | export function LiveInterfaces(){ 21 | const [decryptKey] = useRecoilState(encytpKey); 22 | const cache = React.useRef(useQueryClient()) 23 | const [modalShow, setModalShow] = React.useState(false); 24 | const [interfaceShow, setInterfaceShow] = React.useState(false); 25 | const [selectInterface, setSelectInterface] = React.useState(undefined) 26 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'liveinterfaces', async () => { 27 | 28 | const response = await ApiRequest(decryptKey, '/liveinterfaces') 29 | const calulatedBandwdithDiff = BandwidthDiff(cache, response.data) 30 | 31 | if(selectInterface !== undefined){ 32 | Object.values(calulatedBandwdithDiff.interfaces).map(int => { 33 | if(int.data.name === selectInterface.interface && modalShow){ 34 | setSelectInterface(int) 35 | } 36 | }) 37 | } 38 | 39 | return calulatedBandwdithDiff 40 | 41 | }, 42 | { 43 | refetchInterval: 5000 44 | } 45 | ) 46 | 47 | const interfaceFocus = (interfaceDetails) => { 48 | setSelectInterface(interfaceDetails) 49 | setModalShow(true) 50 | } 51 | 52 | const closeInterface = () => { 53 | setSelectInterface(undefined) 54 | setInterfaceShow(false) 55 | setModalShow(false) 56 | } 57 | 58 | if (error){ 59 | return <> 60 | 61 | 62 | 63 |

Error Collecting Data. I'll Keep Trying

64 |
65 | 66 | } 67 | else if (data){ 68 | return 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | { Object.values(data.interfaces).map((value) => ( 77 | 78 | ))} 79 | 80 | 81 | 82 | 83 | { Object.values(data.interfaces).map((value) => ( 84 | 85 | {CreateCard(, value.interface)} 86 | 87 | ))} 88 | 89 | 90 | 91 | {modalShow ? } show={modalShow} onHide={() => closeInterface()}/> 92 | : 93 | <>} 94 | 95 | {interfaceShow ? } show={interfaceShow} onHide={() => closeInterface()}/> 96 | : 97 | <>} 98 | 99 | 100 | 101 | } 102 | else if (isLoading){ 103 | 104 | return <> 105 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 106 | 107 | } 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/Components/LayerThree/Routing-Parent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Container from "react-bootstrap/Container"; 3 | import Row from "react-bootstrap/Row"; 4 | import Col from "react-bootstrap/Col"; 5 | import {useRecoilState} from 'recoil'; 6 | import { Ospf } from './ospf'; 7 | import { Bgp } from './bgp'; 8 | import { Navigation } from '../Other/navbar' 9 | import { useQuery } from 'react-query'; 10 | import { ErrorBoundary } from 'react-error-boundary' 11 | import {encytpKey, client} from '../../App' 12 | import { NavigationFallback } from "../Other/navbarError"; 13 | import { IsErrorFallback } from "../Other/errorComponent"; 14 | import { ApiRequest } from "../Other/axiosRequests"; 15 | import { PageLoader } from '../Other/pageLoader'; 16 | 17 | 18 | export function Routing(){ 19 | const [decryptKey] = useRecoilState(encytpKey); 20 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'pollRouting', async () => { 21 | 22 | const response = await ApiRequest(decryptKey, '/pollRouting') 23 | 24 | return response.data 25 | 26 | }, 27 | { 28 | refetchInterval: 5000, 29 | } 30 | ) 31 | 32 | if (error){ 33 | return <> 34 | 35 | 36 | 37 |

Error Collecting Data. I'll Keep Trying

38 |
39 | 40 | } 41 | else if (data){ 42 | return 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | } 62 | else if (isLoading){ 63 | return <> 64 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 65 | 66 | } 67 | 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/Components/LayerThree/bgp.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import Card from "react-bootstrap/Card"; 3 | import Row from "react-bootstrap/Row"; 4 | import Col from "react-bootstrap/Col"; 5 | import { CreateCard } from '../Other/jsxCard'; 6 | import { BgpTableHtml } from '../Other/chartConfigs'; 7 | import { BgpData } from '../Other/tables'; 8 | import { BgpTopologyBuild, UpdateBgpTopology } from './topology'; 9 | import { ModifyBgpNeighbor } from '../Forms/bgpNeighborForm'; 10 | import { BgpModal } from '../Modals/bgpFormModal'; 11 | const $ = require('jquery'); 12 | $.DataTable = require('datatables.net'); 13 | 14 | export function Bgp(props){ 15 | const [modalShow, setModalShow] = React.useState(false); 16 | const bgpTableRef = React.createRef() 17 | const bgpTopologyRef = React.createRef() 18 | const bgpTopology = useRef(null) 19 | const bgpTable = BgpTableHtml(bgpTableRef) 20 | 21 | useEffect(() => { 22 | 23 | try{ 24 | if(props.neighbors.length <= 10){ 25 | $(bgpTableRef.current).DataTable().destroy() 26 | BgpData(bgpTableRef.current, props.neighbors) 27 | bgpTopology.current = BgpTopologyBuild(bgpTopologyRef.current, props.neighbors, props.details[0], props.details[2], props.details[5], props.topology) 28 | bgpTopologyRef.current = bgpTopology.current 29 | 30 | } 31 | else if(props.neighbors.length > 10){ 32 | $(bgpTableRef.current).DataTable().clear() 33 | $(bgpTableRef.current).DataTable().rows.add(props.neighbors) 34 | $(bgpTableRef.current).DataTable().draw(false) 35 | bgpTopology.current = UpdateBgpTopology(bgpTopology.current, props.neighbors, props.details[2]) 36 | bgpTopologyRef.current = bgpTopology.current 37 | } 38 | 39 | } 40 | catch{} 41 | 42 | }, [props.neighbors]) 43 | 44 | 45 | if(props.neighbors.length !== 0){ 46 | return 47 | 48 | 49 | 50 | Local AS: {props.details[0]} 51 | 52 | 53 |

Vrf-Name:

54 |

Router-Id:

55 |

BGP Table Ver.:

56 |

Routing Table Ver.:

57 |

Total Prefixes:

58 |

Path Entries:

59 |

AS Path Ent.

60 |

Route-Map Ent.

61 |

Filter-List Ent.:

62 |

Total Memory:

63 | 64 | 65 |

{props.details[1]}

66 |

{props.details[2]}

67 |

{props.details[3]}

68 |

{props.details[4]}

69 |

{props.details[5]}

70 |

{props.details[7]}

71 |

{props.details[9]}

72 |

{props.details[11]}

73 |

{props.details[13]}

74 |

{props.details[19]}

75 | 76 |
77 |
78 |
79 | 80 | 81 | 82 | {modalShow ? } show={modalShow} onHide={() => setModalShow(false)}/> :
} 83 |
84 | 85 | {CreateCard(bgpTable, "BGP Neighbors",
)} 86 |
87 | 88 | {CreateCard(
, "BGP Topology")} 89 | 90 | 91 | 92 | 93 | 94 | 95 | } 96 | else{ 97 | return 98 | 99 | BGP Neighbors 100 |
101 | {bgpTable} 102 | {modalShow ? } show={modalShow} onHide={() => setModalShow(false)}/> :
} 103 |
104 |
105 | } 106 | 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/Components/LayerThree/ospf.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import Card from "react-bootstrap/Card"; 3 | import Row from "react-bootstrap/Row"; 4 | import Col from "react-bootstrap/Col"; 5 | import { OpsfIntsTableHtml } from '../Other/chartConfigs'; 6 | import { OspfData} from '../Other/tables'; 7 | import { OspfTopologyBuild, UpdateOspfTopology } from './topology'; 8 | import { ModifyOspfNeighbor } from '../Forms/ospfNeighborForm'; 9 | import { CreateCard } from '../Other/jsxCard'; 10 | import { OspfForm } from './ospfFormOverlay'; 11 | const $ = require('jquery'); 12 | $.DataTable = require('datatables.net'); 13 | 14 | export function Ospf(props){ 15 | const [modalShow, setModalShow] = React.useState(false); 16 | const ospfTableRef = React.createRef() 17 | const ospfIntsTableRef = React.createRef() 18 | const ospfTopologyRef = React.createRef() 19 | const ospfTopology = useRef(null) 20 | const ospfIntTable = OpsfIntsTableHtml(ospfIntsTableRef) 21 | $.fn.dataTable.ext.errMode = 'none'; 22 | 23 | useEffect(() => { 24 | if(ospfTableRef.current !== null){ 25 | 26 | try{ 27 | $(ospfIntsTableRef.current).DataTable().clear() 28 | $(ospfIntsTableRef.current).DataTable().rows.add(Object.values(props.interfaces)) 29 | $(ospfIntsTableRef.current).DataTable().draw(false) 30 | } 31 | catch{} 32 | 33 | try{ 34 | if(props.topology.length >= 1){ 35 | ospfTopology.current = UpdateOspfTopology(ospfTopology.current, props.neighbors, props.topology) 36 | ospfTopologyRef.current = ospfTopology.current 37 | } 38 | } 39 | catch(e){} 40 | } 41 | }, [props.neighbors]) 42 | 43 | useEffect(() => { 44 | 45 | OspfData(ospfIntsTableRef.current, props.interfaces) 46 | 47 | try{ 48 | if(props.interfaces.length >= 1){ 49 | ospfTopology.current = OspfTopologyBuild(ospfTopologyRef.current, props.neighbors, props.topology) 50 | ospfTopologyRef.current = ospfTopology.current 51 | } 52 | } 53 | catch{} 54 | 55 | }, []) 56 | 57 | return <> 58 | { 59 | OSPF Interfaces/Neighbors 60 |
61 | 62 |
63 | 64 | {ospfIntTable} 65 | 66 |
} 67 | 68 | {modalShow ? } show={modalShow} onHide={() =>setModalShow(false)}/> :
} 69 |
70 |
71 | {props.interfaces.length >= 1 ? 72 | <>{
{CreateCard(
, "OSPF Topology")}
} 73 | : 74 | <>} 75 | 76 | 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/Components/LayerThree/ospfFormOverlay.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | import Button from 'react-bootstrap/Button' 3 | import Container from "react-bootstrap/Container"; 4 | import { CreateCard } from '../Other/jsxCard'; 5 | 6 | export function OspfForm(props) { 7 | console.log(props) 8 | return ( 9 | 17 | 18 | 19 | 20 | {CreateCard(props.component, props.interface)} 21 | 22 | 23 | 24 | ); 25 | } -------------------------------------------------------------------------------- /src/Components/LayerThree/routing.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import axios from 'axios'; 3 | import { Ospf } from './ospf'; 4 | import { Bgp } from './bgp'; 5 | import { Navbar } from '../Other/navbar' 6 | import { useQuery, getQueriesData } from 'react-query'; 7 | import { ErrorBoundary } from '../Other/errorBoundry'; 8 | import {AES, enc}from 'crypto-js'; 9 | 10 | 11 | export function Routing(props){ 12 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), 'MYKEY4DEMO'); 13 | const password = passwordDecrypt.toString(enc.Utf8); 14 | const { isLoading, error, data, isFetching } = useQuery('pollRouting', async () => { 15 | 16 | const response = await axios.post('/pollRouting',{'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 17 | 'password': password, 'port': localStorage.getItem('port')}) 18 | 19 | return response.data 20 | 21 | }, 22 | { 23 | refetchInterval: 5000, 24 | } 25 | ) 26 | 27 | if (error){ 28 | return
29 | 30 |

Error Collecting Data. I'll Keep Trying

31 |
32 |
33 | } 34 | else if (data){ 35 | return
36 | 37 |
38 |
39 | 40 | 41 | 42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 |
50 |
51 |
52 | } 53 | else if (isLoading){ 54 | return
55 |

Collecting routing data for {localStorage.getItem('ip')}

56 |
57 |
58 | } 59 | 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/accessPorts.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { AccessTableHtml } from '../Other/chartConfigs'; 3 | import { AccesPortData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function AccessPorts(props){ 8 | const accesssTableRef = React.createRef() 9 | const accessstable = AccessTableHtml(accesssTableRef) 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | 12 | 13 | useEffect(() => { 14 | if(accesssTableRef.current !== null){ 15 | try{ 16 | $(accesssTableRef.current).DataTable().clear() 17 | $(accesssTableRef.current).DataTable().rows.add(Object.values(props.ports)) 18 | $(accesssTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | }, [props.ports]) 23 | 24 | 25 | useEffect(() => { 26 | $(accesssTableRef.current).DataTable().destroy() 27 | AccesPortData(accesssTableRef.current, props.ports) 28 | }, []) 29 | 30 | return accessstable 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/layerTwo-Parent.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react'; 2 | import { useQuery } from 'react-query'; 3 | import Tabs from "react-bootstrap/Tabs"; 4 | import Tab from "react-bootstrap/Tab"; 5 | import Container from "react-bootstrap/Container"; 6 | import Row from "react-bootstrap/Row"; 7 | import Col from "react-bootstrap/Col"; 8 | import {useRecoilState} from 'recoil'; 9 | import { DpNeighbors} from '../Other/dp_neighbors' 10 | import { GlobalSpanTreeHtml} from '../Other/chartConfigs' 11 | import { MacTable} from './macAddress' 12 | import { SpanTable} from './spanTree' 13 | import { Trunks} from './trunks' 14 | import { AccessPorts} from './accessPorts' 15 | import { Vlans} from './vlans' 16 | import { Navigation } from '../Other/navbar'; 17 | import { ErrorBoundary } from 'react-error-boundary' 18 | import {encytpKey, cache} from '../../App' 19 | import { CreateCard } from '../Other/jsxCard'; 20 | import { Card } from 'react-bootstrap'; 21 | import { NavigationFallback } from "../Other/navbarError"; 22 | import { IsErrorFallback } from "../Other/errorComponent"; 23 | import { PageLoader } from '../Other/pageLoader'; 24 | import { ApiRequest } from "../Other/axiosRequests"; 25 | 26 | export function LayerTwo(){ 27 | const bridgeGlobalTble = useRef(false) 28 | const [decryptKey] = useRecoilState(encytpKey); 29 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') +'pollL2Page', async () => { 30 | 31 | const response = await ApiRequest(decryptKey, '/pollL2Page') 32 | 33 | try{ 34 | bridgeGlobalTble.current = GlobalSpanTreeHtml(response.data.globalSpan) 35 | } 36 | catch{} 37 | 38 | return response.data 39 | 40 | }, 41 | { 42 | refetchInterval: 5000 43 | } 44 | ) 45 | 46 | if (error){ 47 | return <> 48 | 49 | 50 | 51 |

Error Collecting Data. I'll Keep Trying

52 |
53 | 54 | } 55 | else if (data){ 56 | 57 | return 58 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | 67 | {bridgeGlobalTble.current} 68 | 69 | {data.span.hasOwnProperty.key ? 70 | { data.span.map(instance => ())} 71 | :
} 72 | 73 | 74 | 75 | 76 | 77 | {CreateCard()} 78 | 79 | 80 | 81 | 82 | {data.vlans.length > 0 ? CreateCard(, "Vlans") :
} 83 | 84 | 85 | 86 | {data.trunks.length > 0 ? CreateCard(, "Trunks") :
} 87 | 88 | 89 | {data.access.length > 0 ? CreateCard(, "Access Ports"):
} 90 | 91 | 92 | {data.mac_addresses.length > 0 ? CreateCard(, "MAC-Addresses") :
} 93 | 94 | 95 | 96 |
97 | 98 | 99 | 100 | } 101 | else if (isLoading){ 102 | return <> 103 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 104 | 105 | } 106 | } 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/layerTwo.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react'; 2 | import { useQuery } from 'react-query'; 3 | import axios from 'axios'; 4 | import { DpNeighbors} from '../Other/dp_neighbors' 5 | import { GlobalSpanTreeHtml} from '../Other/chartConfigs' 6 | import { MacTable} from './macAddress' 7 | import { SpanTable} from './spanTree' 8 | import { Trunks} from './trunks' 9 | import { AccessPorts} from './accessPorts' 10 | import { Vlans} from './vlans' 11 | import { Navbar } from '../Other/navbar'; 12 | import { ErrorBoundary } from '../Other/errorBoundry'; 13 | import { MacData, SpanData, VlansData, TrunkData, AccessData, DpData} from '../Other/data'; 14 | import {AES, enc}from 'crypto-js'; 15 | 16 | 17 | 18 | export function LayerTwo(props){ 19 | const bridgeGlobalTble = useRef(false) 20 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), 'MYKEY4DEMO'); 21 | const password = passwordDecrypt.toString(enc.Utf8); 22 | const { isLoading, error, data, isFetching } = useQuery('pollL2Page', async () => { 23 | 24 | const response = await axios.post('/pollL2Page',{'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 25 | 'password': password, 'port': localStorage.getItem('port')}) 26 | bridgeGlobalTble.current = GlobalSpanTreeHtml(SpanData[1]['Cisco-IOS-XE-spanning-tree-oper:stp-global']) 27 | return response.data 28 | 29 | }, 30 | { 31 | refetchInterval: 5000, 32 | } 33 | ) 34 | 35 | if (error){ 36 | return
37 | 38 |

Error Collecting Data. I'll Keep Trying

39 |
40 |
41 | } 42 | else if (data){ 43 | return
44 | 45 |
46 |
47 |
48 | 56 |
57 |
58 | { data.span[0].map(instance => ( 59 | 60 | 61 | 62 | ))} 63 |
64 |
65 | {bridgeGlobalTble.current} 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 | 95 |
96 |
97 |
98 |
99 | } 100 | else if (isLoading){ 101 | return
102 |

Collecting L2 data for {localStorage.getItem('ip')}

103 |
104 |
105 | } 106 | 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/macAddress.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { MacTableHtml } from '../Other/chartConfigs'; 3 | import { MacAddressData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function MacTable(props){ 8 | const macTableRef = React.createRef() 9 | const macTable = MacTableHtml(macTableRef) 10 | $.fn.dataTable.ext.errMode = 'none'; 11 | console.log(props.macs) 12 | 13 | useEffect(() => { 14 | if(macTableRef.current !== null){ 15 | try{ 16 | $(macTableRef.current).DataTable().clear() 17 | $(macTableRef.current).DataTable().rows.add(Object.values(props.macs)) 18 | $(macTableRef.current).DataTable().draw(false) 19 | } 20 | catch{} 21 | } 22 | 23 | }, [props.macs]) 24 | 25 | 26 | useEffect(() => { 27 | $(macTableRef.current).DataTable().destroy() 28 | MacAddressData(macTableRef.current, props.macs) 29 | }, []) 30 | 31 | return macTable 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/spanTree.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Row from "react-bootstrap/Row"; 3 | import Col from "react-bootstrap/Col"; 4 | import Card from "react-bootstrap/Card"; 5 | import { SpanTreeHtml } from '../Other/chartConfigs'; 6 | import { SpanTableData } from '../Other/tables'; 7 | import { CreateCard } from '../Other/jsxCard'; 8 | const $ = require('jquery'); 9 | $.DataTable = require('datatables.net'); 10 | 11 | 12 | export function SpanTable(props){ 13 | const spanTableRef = React.createRef() 14 | const spanTable = SpanTreeHtml(spanTableRef) 15 | $.fn.dataTable.ext.errMode = 'none'; 16 | 17 | useEffect(() => { 18 | if(spanTableRef.current !== null){ 19 | try{ 20 | $(spanTableRef.current).DataTable().clear() 21 | $(spanTableRef.current).DataTable().rows.add(Object.values(props.span)) 22 | $(spanTableRef.current).DataTable().draw(false) 23 | } 24 | catch{} 25 | } 26 | 27 | }, [props.span]) 28 | 29 | 30 | useEffect(() => { 31 | $(spanTableRef.current).DataTable().destroy() 32 | SpanTableData(spanTableRef.current, props.span) 33 | 34 | }, []) 35 | 36 | return 37 | 38 | 39 | 40 | {props.span.instance} 41 | 42 | 43 | 44 | 45 |

Hello:

46 |

Fwd-Delay:

47 |

Hold Count:

48 |

Bridge Prio.:

49 |

Bridge Add.:

50 |

Des. Root Prio.:

51 |

Des. Root Add.:

52 |

Root Port:

53 |

Root Cost:

54 |

Hold Time:

55 |

Topology Changes:

56 |

Last Change:

57 | 58 | 59 |

{props.span['hello-time']}

60 |

{props.span['forwarding-delay']}

61 |

{props.span['hold-count']}

62 |

{props.span['bridge-priority']}

63 |

{props.span['bridge-address']}

64 |

{props.span['designated-root-priority']}

65 |

{props.span['designated-root-address']}

66 |

{props.span['root-port']}

67 |

{props.span['root-cost']}

68 |

{props.span['hold-time']}

69 |

{props.span['topology-changes']}

70 |

{props.span['time-of-last-topology-change']}

71 | 72 | 73 | {CreateCard(spanTable, "Ports")} 74 | 75 |
76 |
77 |
78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/trunks.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { TrunkTableHtml } from '../Other/chartConfigs'; 3 | import { TrunkData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function Trunks(props){ 8 | const trunksTableRef = React.createRef() 9 | const trunkstable = TrunkTableHtml(trunksTableRef) 10 | console.log(props.ports, 'yes') 11 | 12 | useEffect(() => { 13 | if(trunksTableRef.current !== null){ 14 | try{ 15 | $(trunksTableRef.current).DataTable().clear() 16 | $(trunksTableRef.current).DataTable().rows.add(Object.values(props.ports)) 17 | $(trunksTableRef.current).DataTable().draw(false) 18 | } 19 | catch{} 20 | } 21 | }, [props.ports]) 22 | 23 | useEffect(() => { 24 | $(trunksTableRef.current).DataTable().destroy() 25 | TrunkData(trunksTableRef.current, props.ports) 26 | }, []) 27 | 28 | return trunkstable 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Components/LayerTwo/vlans.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { VlanTableHtml } from '../Other/chartConfigs'; 3 | import { VlanData } from '../Other/tables'; 4 | 5 | const $ = require('jquery'); 6 | $.DataTable = require('datatables.net'); 7 | 8 | export function Vlans(props){ 9 | const vlanTableRef = React.createRef() 10 | const vlantable = VlanTableHtml(vlanTableRef) 11 | 12 | useEffect(() => { 13 | $(vlanTableRef.current).DataTable().destroy() 14 | VlanData(vlanTableRef.current, props.vlans) 15 | 16 | }, []) 17 | 18 | return vlantable 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Components/Modals/bgpFormModal.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | import { CreateCard } from '../Other/jsxCard'; 3 | 4 | export function BgpModal(props) { 5 | 6 | return ( 7 | 16 | 17 | 18 | 19 | {CreateCard(props.component)} 20 | 21 | 22 | 23 | ); 24 | } -------------------------------------------------------------------------------- /src/Components/Modals/configQueryModal.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | import Button from 'react-bootstrap/Button' 3 | import Col from 'react-bootstrap/Row' 4 | 5 | export function RestQueryModal(props) { 6 | 7 | if(props.msg === 'Request Timeout'){ 8 | 9 | return ( 10 | 11 | 12 |

{props.msg}

13 |

The App Did Not Receive A Response From The Device

14 |
15 | 16 | 17 | 18 |
19 | ); 20 | } 21 | else if(props.msg === 'Fetching'){ 22 | return 23 | 24 |

{props.msg}

25 |
26 | 27 | 28 | } 29 | else{ 30 | return 31 | 32 |

{props.msg}

33 |
34 | 35 | 36 | 37 |
38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Components/Modals/configSendModal.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | import Button from 'react-bootstrap/Button' 3 | import Col from 'react-bootstrap/Row' 4 | 5 | export function ConfigurationModal(props) { 6 | 7 | if(props.msg === 'Request Timeout'){ 8 | 9 | return ( 10 | 11 | 12 |

{props.msg}

13 |

The App Did Not Receive A Response From The Device

14 |
15 | 16 | 17 | 18 |
19 | ); 20 | } 21 | else if(props.msg === 'Sending Config'){ 22 | return 23 | 24 |

{props.msg}

25 |
26 | 27 | 28 | } 29 | else{ 30 | return 31 | 32 |

{props.msg}

33 | {props.msg !== 'Configuration Successful' ? <> : } 34 |
35 | 36 | {props.msg === 'Configuration Successful' ? 37 | : 38 | } 39 | 40 |
41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Components/Modals/interfaceModal.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | import { CreateCard } from '../Other/jsxCard'; 3 | 4 | export function ShowInterface(props) { 5 | console.log(props) 6 | return ( 7 | 15 | 16 | 17 | 18 | {CreateCard(props.component, props.interface)} 19 | 20 | 21 | ); 22 | } -------------------------------------------------------------------------------- /src/Components/Modals/loadingModal.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | import Button from 'react-bootstrap/Button' 3 | import Col from 'react-bootstrap/Row' 4 | 5 | export function LoginModal(props) { 6 | 7 | if(props.msg === 'Request Timeout'){ 8 | 9 | return ( 10 | 11 | 12 |

{props.msg}

13 |

The App Did Not Receive A Response From The Device

14 |
15 | 16 | 17 | 18 |
19 | ); 20 | } 21 | else{ 22 | return ( 23 | 24 | 25 |

{props.msg}

26 | {props.msg !== 'Authentication Failed' ?
:

Verify RESTCONF Capabilities and Credentials

} 27 | 28 | 29 | {props.msg === 'Authentication Failed' ? :
} 30 |
31 | 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /src/Components/Other/axiosRequests.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import {AES, enc}from 'crypto-js'; 3 | 4 | const credentials = (decryptKey, customUrl) => { 5 | 6 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), decryptKey); 7 | 8 | if(customUrl === undefined){ 9 | 10 | var credentialsObj = {'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 11 | 'password': passwordDecrypt.toString(enc.Utf8), 'port': localStorage.getItem('port')}; 12 | } 13 | else{ 14 | var credentialsObj = {'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 15 | 'password': passwordDecrypt.toString(enc.Utf8), 'port': localStorage.getItem('port'), 'url': customUrl}; 16 | } 17 | 18 | return credentialsObj 19 | 20 | } 21 | 22 | export const ApiRequest = async (decryptKey, url, customUrl) => { 23 | 24 | const payload = credentials(decryptKey, customUrl) 25 | const response = await axios.post(url, payload).then(response => { 26 | 27 | return response 28 | 29 | }).catch(error => { 30 | console.log(error) 31 | }) 32 | 33 | return response 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Components/Other/bandwidthFunctions.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export const BandwidthDiff = (cachedQuery, data) => { 4 | try{ 5 | const lastQuery = cachedQuery.getQueryCache(localStorage.getItem('ip') + 'indexData').queries[0].state.data.interfaces; 6 | Object.values(lastQuery).map(cacheValue => { 7 | Object.values(data.interfaces).map(newValue => { 8 | if(newValue.data.name === cacheValue.data.name){ 9 | 10 | const diffIn = parseFloat(newValue.data['statistics']['rx-kbps'] - parseFloat(cacheValue.data['statistics']['rx-kbps'])); 11 | const diffOut = parseFloat(newValue.data['statistics']['tx-kbps'] - parseFloat(cacheValue.data['statistics']['tx-kbps'])); 12 | 13 | if(Math.sign(diffIn) === 1){ 14 | newValue.data.inbandwidthDiff = '+' + diffIn.toFixed(2) 15 | } 16 | else if(Math.sign(diffIn) === -1){ 17 | newValue.data.inbandwidthDiff = diffIn.toFixed(2) 18 | } 19 | else{ 20 | newValue.data.inbandwidthDiff = diffIn 21 | }; 22 | 23 | if(Math.sign(diffOut) === 1){ 24 | newValue.data.outbandwidthDiff = '+' + diffOut.toFixed(2) 25 | } 26 | else if(Math.sign(diffOut) === -1){ 27 | newValue.data.outbandwidthDiff = diffOut.toFixed(2) 28 | } 29 | else{ 30 | newValue.data.outbandwidthDiff = diffOut 31 | }; 32 | }; 33 | }); 34 | }); 35 | } 36 | catch{} 37 | 38 | return data 39 | 40 | } -------------------------------------------------------------------------------- /src/Components/Other/configNavbar.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { ApiCheck } from './promises' 3 | import Nav from "react-bootstrap/Nav"; 4 | import Navbar from "react-bootstrap/Navbar"; 5 | import Container from "react-bootstrap/Container"; 6 | import Offcanvas from 'react-bootstrap/Offcanvas' 7 | import Row from "react-bootstrap/Row"; 8 | import { Link } from "react-router-dom"; 9 | 10 | export function RestConfigNavbar(props){ 11 | const [apiStatus, setApiStatus] = useState(true) 12 | 13 | useEffect(() => { 14 | (async () => { 15 | try{ 16 | let apiStatus = await ApiCheck() 17 | if(apiStatus.status === 200) 18 | setApiStatus(true) 19 | else{ 20 | setApiStatus(false) 21 | } 22 | } 23 | catch{ 24 | setApiStatus(false) 25 | } 26 | 27 | })() 28 | }, [props.update]) 29 | 30 | return 31 | 32 | 33 | 34 | 38 | 39 | XE-Ops 40 | 41 |
42 | 43 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/Components/Other/dp_neighbors.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Tabs from "react-bootstrap/Tabs"; 3 | import Tab from "react-bootstrap/Tab"; 4 | import { CdpTableHtml, LldpTableHtml } from './chartConfigs'; 5 | import { CdpTable, LldpTable } from './tables'; 6 | const $ = require('jquery'); 7 | $.DataTable = require('datatables.net'); 8 | 9 | 10 | export function DpNeighbors(props){ 11 | const cdpTableRef = React.createRef() 12 | const cdptable = CdpTableHtml(cdpTableRef) 13 | const lldpTableRef = React.createRef() 14 | const lldptable = LldpTableHtml(lldpTableRef) 15 | $.fn.dataTable.ext.errMode = 'none'; 16 | 17 | useEffect(() => { 18 | if(cdpTableRef.current !== null){ 19 | try{ 20 | $(cdptable.current).DataTable().clear() 21 | $(cdptable.current).DataTable().rows.add(props.dpNeighbors[0]) 22 | $(cdptable.current).DataTable().draw(false) 23 | } 24 | catch{} 25 | } 26 | 27 | 28 | if(lldpTableRef.current !== null){ 29 | try{ 30 | $(lldpTableRef.current).DataTable().clear() 31 | $(lldpTableRef.current).DataTable().rows.add(props.dpNeighbors[1]) 32 | $(lldpTableRef.current).DataTable().draw(false) 33 | } 34 | catch{} 35 | } 36 | }, [props.dpNeighbors]) 37 | 38 | useEffect(() => { 39 | $(lldpTableRef.current).DataTable().destroy() 40 | $(cdptable.current).DataTable().destroy() 41 | CdpTable(cdpTableRef.current, props.dpNeighbors[0]) 42 | LldpTable(lldpTableRef.current, props.dpNeighbors[1]) 43 | }, []) 44 | 45 | return 46 | 47 | {cdptable} 48 | 49 | 50 | {lldptable} 51 | 52 | 53 | 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/Components/Other/errorBoundry.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | 4 | 5 | export classname ErrorBoundary extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { hasError: false, 9 | error: undefined }; 10 | } 11 | 12 | static getDerivedStateFromError(error) { 13 | console.log(error) 14 | // Update state so the next render will show the fallback UI. 15 | return { hasError: true, error: error }; 16 | } 17 | 18 | 19 | render() { 20 | if (this.state.hasError) { 21 | // You can render any custom fallback UI 22 | return
!Something Went Wrong!
23 | } 24 | else{ 25 | // You can render any custom fallback UI 26 | return this.props.children; 27 | } 28 | 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /src/Components/Other/errorComponent.js: -------------------------------------------------------------------------------- 1 | 2 | import Card from "react-bootstrap/Card"; 3 | import Col from "react-bootstrap/Col"; 4 | 5 | 6 | export function IsErrorFallback({error}){ 7 | const reportError = String(error.message) 8 | return 9 | 10 |

!Something Went Wrong. Please Refresh! Please Report the Issue

11 | 12 | Click Here to Submit Issue 13 | 14 |
Report your current page/location in the app. Also a brief explanation on your task that errored
15 |
{reportError}
16 |
17 |
18 | } -------------------------------------------------------------------------------- /src/Components/Other/interfaceOverlay.js: -------------------------------------------------------------------------------- 1 | import Modal from 'react-bootstrap/Modal' 2 | 3 | function InterfaceOverlay(props) { 4 | return ( 5 | 11 | 12 | 13 | Modal heading 14 | 15 | 16 | 17 |

Centered Modal

18 |

19 | Cras mattis consectetur purus sit amet fermentum. Cras justo odio, 20 | dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac 21 | consectetur ac, vestibulum at eros. 22 |

23 |
24 | 25 | 26 | 27 |
28 | ); 29 | } -------------------------------------------------------------------------------- /src/Components/Other/jsxCard.js: -------------------------------------------------------------------------------- 1 | import { ErrorBoundary } from 'react-error-boundary' 2 | import Card from "react-bootstrap/Card"; 3 | import { IsErrorFallback } from "../Other/errorComponent"; 4 | 5 | export function CreateCard(component, title, extra){ 6 | 7 | const card = 8 | 9 | {title} 10 | {extra} 11 | 12 | {component} 13 | 14 | 15 | 16 | 17 | return card 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/Components/Other/login.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Redirect } from "react-router-dom"; 3 | import { useQuery } from 'react-query' 4 | import axios from 'axios'; 5 | import Container from "react-bootstrap/Container"; 6 | import Card from "react-bootstrap/Card"; 7 | import Row from "react-bootstrap/Row"; 8 | import Col from "react-bootstrap/Col"; 9 | import Form from 'react-bootstrap/Form' 10 | import {useRecoilState} from 'recoil'; 11 | import { LoginModal } from '../Modals/loadingModal' 12 | import { AES }from 'crypto-js'; 13 | import {encytpKey} from '../../App' 14 | 15 | export function DeviceAuth(){ 16 | const [encrypt] = useRecoilState(encytpKey); 17 | const [ip, setIp] = useState('') 18 | const [username, setUserNaMe] = useState('') 19 | const [password, setPassword] = useState('') 20 | const [port, setPort] = useState(443) 21 | const [isAuth, setAuth] = useState(false) 22 | const [modalShow, setModalShow] = React.useState(false); 23 | const [msg, setMsg] = useState('Autheniticating') 24 | const requestSession = axios.create(); 25 | requestSession.defaults.timeout = 30000; 26 | const {refetch } = useQuery(ip + 'login', async () => { 27 | 28 | const response = await requestSession.post('/login', {'ip': ip, 'username': username, 'password': password, 'port': port}).then(response =>{ 29 | 30 | if(response.data.status === 200){ 31 | const encryptPassword = AES.encrypt(password, encrypt); 32 | localStorage.setItem('ip', ip); 33 | localStorage.setItem('port', 443); 34 | localStorage.setItem('username', username); 35 | localStorage.setItem('password', encryptPassword.toString()); 36 | localStorage.setItem('model', response.data.model); 37 | localStorage.setItem('serial', response.data.serial); 38 | localStorage.setItem('uptime', response.data.uptime); 39 | localStorage.setItem('software', response.data.software) 40 | setModalShow(false) 41 | setAuth(true) 42 | return response.data 43 | 44 | } 45 | else{ 46 | setMsg('Authentication Failed') 47 | } 48 | }).catch(() => {setMsg('Request Timeout')}) 49 | 50 | return response 51 | 52 | }, 53 | { 54 | enabled: false, cacheTime: Infinity 55 | }) 56 | 57 | 58 | const handleSubmit = (evt) => { 59 | evt.preventDefault(); 60 | refetch(); 61 | 62 | } 63 | 64 | const resetPageStatus = () => { 65 | setMsg('Autheniticating') 66 | setModalShow(false) 67 | } 68 | 69 | useEffect(() => { 70 | localStorage.clear() 71 | }, []) 72 | 73 | 74 | if(!isAuth){ 75 | return ( 76 | 77 | 78 | 79 | 80 | 81 | 82 | IOS-XE Login 83 |
84 | setIp(e.target.value)} placeholder="IP Address" name="ipAddress" required/> 85 | setUserNaMe(e.target.value)} placeholder="Username" name="username" required/> 86 | setPassword(e.target.value)} placeholder="Password" name="password" required/> 87 | setPort(e.target.value)} placeholder="Default 443" name="restconfPort" required/> 88 | setModalShow(true)} type="submit" value="Login" className="btn btn-success"/> 89 | 90 | {modalShow ? resetPageStatus()}/> : <>} 91 |
92 |
93 | 94 | 95 |
96 |
97 | ); 98 | } 99 | else if(isAuth){ 100 | return( 101 | 102 | ) 103 | } 104 | } -------------------------------------------------------------------------------- /src/Components/Other/navbar.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { ApiCheck } from './promises' 3 | import Nav from "react-bootstrap/Nav"; 4 | import Navbar from "react-bootstrap/Navbar"; 5 | import Container from "react-bootstrap/Container"; 6 | import Offcanvas from 'react-bootstrap/Offcanvas' 7 | import Row from "react-bootstrap/Row"; 8 | import { Link } from "react-router-dom"; 9 | 10 | export function Navigation(props){ 11 | const [apiStatus, setApiStatus] = useState(true) 12 | 13 | useEffect(() => { 14 | (async () => { 15 | try{ 16 | let apiStatus = await ApiCheck() 17 | if(apiStatus.status === 200) 18 | setApiStatus(true) 19 | else{ 20 | setApiStatus(false) 21 | } 22 | } 23 | catch{ 24 | setApiStatus(false) 25 | } 26 | 27 | })() 28 | }, [props.update]) 29 | 30 | return 31 | 32 | 33 | 34 | 35 | 39 | 40 | XE-Ops 41 | 42 |
43 | 44 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/Components/Other/navbarError.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { ApiCheck } from './promises' 3 | import Nav from "react-bootstrap/Nav"; 4 | import Navbar from "react-bootstrap/Navbar"; 5 | import Container from "react-bootstrap/Container"; 6 | import Offcanvas from 'react-bootstrap/Offcanvas' 7 | import Row from "react-bootstrap/Row"; 8 | import { Link } from "react-router-dom"; 9 | 10 | export function NavigationFallback(props){ 11 | const [apiStatus, setApiStatus] = useState(true) 12 | 13 | useEffect(() => { 14 | (async () => { 15 | try{ 16 | let apiStatus = await ApiCheck() 17 | if(apiStatus.status === 200) 18 | setApiStatus(true) 19 | else{ 20 | setApiStatus(false) 21 | } 22 | } 23 | catch{ 24 | setApiStatus(false) 25 | } 26 | 27 | })() 28 | }, [props.update]) 29 | 30 | return 31 | 32 | 33 | 34 | 35 | 39 | 40 | XE-Ops 41 | 42 |
43 | 44 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/Components/Other/pageLoader.js: -------------------------------------------------------------------------------- 1 | import Col from "react-bootstrap/Col"; 2 | import Row from "react-bootstrap/Row"; 3 | import Card from "react-bootstrap/Card"; 4 | import Container from "react-bootstrap/Container"; 5 | 6 | export const PageLoader = (ip, serial, model, uptime, software) => { 7 | 8 | const html = 9 | 10 | 11 | 12 | 13 | 14 |

Collecting Data From {ip}

15 |
16 |
Device Model/SN: {model} ({serial})
17 |
Uptime: {uptime}
18 |
Software: ({software})
19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | return html 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Components/Other/promises.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | const instance = axios.create(); 3 | instance.defaults.timeout = 3000; 4 | 5 | export function Login(ip, username, password) { 6 | return new Promise(resolve => { 7 | resolve(axios.post('/login', {'ip': ip, 'username': username, 'password': password, 'port': 443}, { timeout: 30000 }))} 8 | ); 9 | } 10 | 11 | export function Token() { 12 | return new Promise(resolve => { 13 | resolve(axios.post('/token', {'username': 'test', 'password': 'password'}))} 14 | ); 15 | } 16 | 17 | 18 | export function PollInterfaces(ip, username, password, port) { 19 | return new Promise(resolve => { 20 | setTimeout(() => { 21 | resolve(axios.post('/getinterfaces', {'ip': ip, 'username': username, 'password': password, 'port': port})); 22 | }, 2000); 23 | }); 24 | } 25 | 26 | export function GetCpuStatus(ip, username, password, port) { 27 | return new Promise(resolve => { 28 | setTimeout(() => { 29 | resolve(axios.post('/cpustatus', {'ip': ip, 'username': username, 'password': password, 'port': port})); 30 | }, 2000); 31 | }); 32 | } 33 | 34 | export function GetHwdStatus(ip, username, password, port) { 35 | return new Promise(resolve => { 36 | setTimeout(() => { 37 | resolve(axios.post('/hardwarestatus', {'ip': ip, 'username': username, 'password': password, 'port': port})); 38 | }, 5000); 39 | }); 40 | } 41 | 42 | export function GetComponents(ip, username, password, port) { 43 | return new Promise(resolve => { 44 | setTimeout(() => { 45 | resolve(axios.post('/getcomponents', {'ip': ip, 'username': username, 'password': password, 'port': port})); 46 | }, 5000); 47 | }); 48 | } 49 | 50 | export function GetDpNeighbors(ip, username, password, port) { 51 | return new Promise(resolve => { 52 | setTimeout(() => { 53 | resolve(axios.post('/neighbors', {'ip': ip, 'username': username, 'password': password, 'port': port})); 54 | }, 5000); 55 | }); 56 | } 57 | 58 | export function GetVlans(ip, username, password, port) { 59 | return new Promise(resolve => { 60 | setTimeout(() => { 61 | resolve(axios.post('/vlans', {'ip': ip, 'username': username, 'password': password, 'port': port})); 62 | }, 5000); 63 | }); 64 | } 65 | 66 | export function GetLayerTwoInterfaces(ip, username, password, port) { 67 | return new Promise(resolve => { 68 | setTimeout(() => { 69 | resolve(axios.post('/layertwointerfaces', {'ip': ip, 'username': username, 'password': password, 'port': port})); 70 | }, 5000); 71 | }); 72 | } 73 | 74 | export function GetEnvStatus(ip, username, password, port) { 75 | return new Promise(resolve => { 76 | setTimeout(() => { 77 | resolve(axios.post('/getenvirmoment', {'ip': ip, 'username': username, 'password': password, 'port': port})); 78 | }, 5000); 79 | }); 80 | } 81 | 82 | export function GetSfpStatus(ip, username, password, port) { 83 | return new Promise(resolve => { 84 | setTimeout(() => { 85 | resolve(axios.post('/sfpstatus', {'ip': ip, 'username': username, 'password': password, 'port': port})); 86 | }, 5000); 87 | }); 88 | } 89 | 90 | export function GeBgpStatus(ip, username, password, port) { 91 | return new Promise(resolve => { 92 | setTimeout(() => { 93 | resolve(axios.post('/bgpstatus', {'ip': ip, 'username': username, 'password': password, 'port': port})); 94 | }, 5000); 95 | }); 96 | } 97 | 98 | export function PollIndexPage(ip, username, password, port, source) { 99 | return new Promise(resolve => { 100 | setTimeout(() => { 101 | resolve(axios.post('/pollIndexPage', {'ip': ip, 'username': username, 'password': password, 'port': port})); 102 | }, 5000); 103 | }); 104 | } 105 | 106 | export function PollL2Page(ip, username, password, port) { 107 | return new Promise(resolve => { 108 | setTimeout(() => { 109 | resolve(axios.post('/pollL2Page', {'ip': ip, 'username': username, 'password': password, 'port': port})); 110 | }, 5000); 111 | }); 112 | } 113 | 114 | export function ApiCheck() { 115 | return new Promise(resolve => { 116 | setTimeout(() => { 117 | resolve(axios.post('/apistatus')); 118 | }, 5000); 119 | }); 120 | } 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/Components/RibStatus/RIB-Parent.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { useQuery } from 'react-query'; 3 | import Container from "react-bootstrap/Container"; 4 | import Row from "react-bootstrap/Row"; 5 | import Col from "react-bootstrap/Col"; 6 | import {AES, enc} from 'crypto-js'; 7 | import {useRecoilState} from 'recoil'; 8 | import { Navigation } from '../Other/navbar'; 9 | import { RibInfo} from './getRibs'; 10 | import { RibDiff} from './ribDiff'; 11 | import { RoutingProtocols} from './protocols'; 12 | import {encytpKey} from '../../App' 13 | import { CreateCard } from '../Other/jsxCard'; 14 | import { NavigationFallback } from "../Other/navbarError"; 15 | import { ErrorBoundary } from 'react-error-boundary' 16 | import { PageLoader } from '../Other/pageLoader'; 17 | const $ = require('jquery'); 18 | $.DataTable = require('datatables.net'); 19 | 20 | 21 | export function RibIndex(){ 22 | const [decrypt] = useRecoilState(encytpKey); 23 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), decrypt); 24 | const { isLoading, error, data, isFetching } = useQuery(localStorage.getItem('ip') + 'ribStatus', async () => { 25 | 26 | const response = await axios.post('/ribStatus',{'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 27 | 'password': passwordDecrypt.toString(enc.Utf8), 'port': localStorage.getItem('port')}, {'headers': { 'Authorization': 'Bearer ' + localStorage.getItem('token')}}) 28 | 29 | return response.data 30 | 31 | }, 32 | { 33 | refetchInterval: 10000, 34 | } 35 | ) 36 | 37 | if (error){ 38 | return
39 | 40 | 41 | 42 |

Error Collecting Data. I'll Keep Trying

43 |
44 |
45 | } 46 | else if (data){ 47 | return 48 | 49 | 50 | 51 | {CreateCard()} 52 | 53 | 54 | {CreateCard(, "Protocols")} 55 | 56 | 57 | {CreateCard(, 'Flapping Routes')} 58 | 59 | 60 | 61 | } 62 | else if (isLoading){ 63 | return
64 | {PageLoader(localStorage.getItem('ip'), localStorage.getItem('serial'), localStorage.getItem('model'), localStorage.getItem('uptime'), localStorage.getItem('software'))} 65 |
66 | } 67 | 68 | } 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/Components/RibStatus/RibMain.js: -------------------------------------------------------------------------------- 1 | 2 | import { Navbar } from '../Other/navbar' 3 | import { RibInfo} from './getRibs' 4 | import { RibDiff} from './ribDiff' 5 | import { RoutingProtocols} from './protocols' 6 | import axios from 'axios'; 7 | import { useQuery } from 'react-query'; 8 | import {AES, enc}from 'crypto-js'; 9 | const $ = require('jquery'); 10 | $.DataTable = require('datatables.net'); 11 | 12 | 13 | export function RibIndex(){ 14 | const passwordDecrypt = AES.decrypt(localStorage.getItem('password'), 'MYKEY4DEMO'); 15 | const password = passwordDecrypt.toString(enc.Utf8); 16 | const { isLoading, error, data, isFetching } = useQuery('ribStatus', async () => { 17 | 18 | const response = await axios.post('/ribStatus',{'ip': localStorage.getItem('ip'), 'username': localStorage.getItem('username'), 19 | 'password': password, 'port': localStorage.getItem('port')}, {'headers': { 'Authorization': 'Bearer ' + localStorage.getItem('token')}}) 20 | 21 | return response.data 22 | 23 | }, 24 | { 25 | refetchInterval: 10000, 26 | } 27 | ) 28 | 29 | if (error){ 30 | return
31 | 32 |

Error Collecting Data. I'll Keep Trying

33 |
34 |
35 | } 36 | else if (data){ 37 | return
38 | 39 | 40 |
41 | 42 | 43 |
44 | 45 |
46 | } 47 | else if (isLoading){ 48 | return
49 |

Collecting RIB data from {localStorage.getItem('ip')}

50 |
51 |
52 | } 53 | 54 | } 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/Components/RibStatus/getRibs.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Tabs from "react-bootstrap/Tabs"; 3 | import Tab from "react-bootstrap/Tab"; 4 | import { Ipv4RibTableHtml, Ipv6RibTableHtml } from '../Other/chartConfigs'; 5 | import { Ipv4Ribs, Ipv6Ribs } from '../Other/tables'; 6 | const $ = require('jquery'); 7 | $.DataTable = require('datatables.net'); 8 | 9 | export function RibInfo(props){ 10 | const ipv4RibTableRef = React.createRef() 11 | const ipv6RibTableRef = React.createRef() 12 | const ipv4IntTable = Ipv4RibTableHtml(ipv4RibTableRef) 13 | const ipv6IntTable = Ipv6RibTableHtml(ipv6RibTableRef) 14 | $.fn.dataTable.ext.errMode = 'none'; 15 | 16 | useEffect(() => { 17 | 18 | if(ipv4RibTableRef.current !== null){ 19 | try{ 20 | $(ipv4RibTableRef.current).DataTable().clear() 21 | $(ipv4RibTableRef.current).DataTable().rows.add(props.routes['ietf-routing:ipv4']) 22 | $(ipv4RibTableRef.current).DataTable().draw(false) 23 | 24 | $(ipv6RibTableRef.current).DataTable().clear() 25 | $(ipv6RibTableRef.current).DataTable().rows.add(props.routes['ietf-routing:ipv6']) 26 | $(ipv6RibTableRef.current).DataTable().draw(false) 27 | } 28 | catch{} 29 | } 30 | 31 | }, [props.routes]) 32 | 33 | 34 | useEffect(() => { 35 | $(ipv4RibTableRef.current).DataTable().destroy() 36 | Ipv4Ribs(ipv4RibTableRef.current, props.routes['ietf-routing:ipv4']) 37 | $(ipv6RibTableRef.current).DataTable().destroy() 38 | Ipv6Ribs(ipv6RibTableRef.current, props.routes['ietf-routing:ipv6']) 39 | 40 | }, []) 41 | 42 | return 43 | 44 | {ipv4IntTable} 45 | 46 | 47 | {ipv6IntTable} 48 | 49 | 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /src/Components/RibStatus/promises.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export function Login(ip, username, password, port) { 4 | return new Promise(resolve => { 5 | resolve(axios.post('/login', {'ip': ip, 'username': username, 'password': password, 'port': port}))} 6 | ); 7 | } 8 | 9 | export function GetRibData(ip, username, password, port) { 10 | return new Promise(resolve => { 11 | resolve(axios.post('/index', {'ip': ip, 'username': username, 'password': password, 'port': port})); 12 | }); 13 | } 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Components/RibStatus/protocols.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { ProtocolsTableHtml } from '../Other/chartConfigs'; 3 | import { RoutingProtocolData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function RoutingProtocols(props){ 8 | const routingProtocolsRef = React.createRef() 9 | const routingProtocolsTable = ProtocolsTableHtml(routingProtocolsRef) 10 | 11 | useEffect(() => { 12 | $(routingProtocolsRef.current).DataTable().destroy() 13 | RoutingProtocolData(routingProtocolsRef.current, props.protocols) 14 | 15 | }, []) 16 | 17 | return routingProtocolsTable 18 | 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /src/Components/RibStatus/ribDiff.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { PlusRibEntriesTableHtml } from '../Other/chartConfigs'; 3 | import { RibDiffData } from '../Other/tables'; 4 | const $ = require('jquery'); 5 | $.DataTable = require('datatables.net'); 6 | 7 | export function RibDiff(props){ 8 | const ribDiffEntries = React.createRef() 9 | const plusEntriesTable = PlusRibEntriesTableHtml(ribDiffEntries) 10 | console.log(props.ribs) 11 | 12 | useEffect(() => { 13 | 14 | if(ribDiffEntries.current !== null){ 15 | 16 | try{ 17 | $(ribDiffEntries.current).DataTable().clear() 18 | $(ribDiffEntries.current).DataTable().rows.add(props.ribs) 19 | $(ribDiffEntries.current).DataTable().draw(false) 20 | } 21 | catch{} 22 | 23 | } 24 | 25 | }, [props.ribs]) 26 | 27 | 28 | useEffect(() => { 29 | $(ribDiffEntries.current).DataTable().destroy() 30 | RibDiffData(ribDiffEntries.current, props.ribs) 31 | }, []) 32 | 33 | 34 | return plusEntriesTable 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /src/holder.rst: -------------------------------------------------------------------------------- 1 | l 2 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App.js'; 5 | import { RecoilRoot, atom } from 'recoil'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /test/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | 3 | # some basic default values... 4 | 5 | inventory = inventory.yml 6 | forks=10 7 | retry_files_enabled = False 8 | callback_whitelist = profile_tasks 9 | host_key_checking = False 10 | #roles_path = ~/iosxe-ansible/roles/ 11 | collections_paths = ~/.ansible/collections 12 | timeout = 60 13 | #log_path = ~/iosxe-ansible/ansible.log 14 | deprecation_warnings=False 15 | ansible_python_interpreter = /usr/bin/python3 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/ansible_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | from getpass import getpass 5 | from ansible import context 6 | from ansible.parsing.dataloader import DataLoader 7 | from ansible.vars.manager import VariableManager 8 | from ansible.inventory.manager import InventoryManager 9 | from ansible.executor.playbook_executor import PlaybookExecutor 10 | from ansible.module_utils.common.collections import ImmutableDict 11 | 12 | loader = DataLoader() 13 | inventory = InventoryManager(loader=loader, sources='/home/appdeveloper/react-xe-ops-testing/BackEndModeules/AnsiblePlaybooks/inventory.yml') 14 | variable_manager = VariableManager(loader=loader, inventory=inventory) 15 | 16 | def interface_configurations(config_data) -> str: 17 | 18 | variable_manager._extra_vars = {"ip_interfaces": [ 19 | { 20 | "intf_name": config_data.get('name'), 21 | "intf_description": config_data.get('description'), 22 | "intf_ipv4": config_data.get('ip'), 23 | "intf_ipv4_mask": config_data.get('mask'), 24 | "intf_speed": config_data.get('speed'), 25 | "port_status": config_data.get('portStatus') 26 | } 27 | ] 28 | } 29 | playbook_path = '/home/appdeveloper/react-xe-ops-testing/BackEndModeules/AnsiblePlaybooks/intf_playbook.yml' 30 | 31 | if not os.path.exists(playbook_path): 32 | print('[ERROR] The playbook does not exist') 33 | sys.exit() 34 | 35 | context.CLIARGS = ImmutableDict(tags={}, 36 | listtags=False, 37 | listtasks=False, 38 | listhosts=False, 39 | syntax=False, 40 | connection='smart', 41 | module_path=None, 42 | forks=100, 43 | #remote_user='developer', 44 | # remote_pass=password, 45 | private_key_file=None, 46 | ssh_common_args=None, 47 | ssh_extra_args=None, 48 | sftp_extra_args=None, 49 | scp_extra_args=None, 50 | become=False, # 'True' 51 | become_method=None, # 'sudo' 52 | become_user=None, # 'root' 53 | verbosity=True, 54 | check=False, 55 | start_at_task=None) 56 | 57 | results = execute_ansible_playbook(playbook_path, variable_manager) 58 | 59 | return results 60 | 61 | def execute_ansible_playbook(playbook_path, variable_manager) -> str: 62 | play_ex = PlaybookExecutor(playbooks=[playbook_path], inventory=inventory, variable_manager=variable_manager, loader=loader, passwords={}) 63 | results = play_ex.run() 64 | 65 | return results 66 | -------------------------------------------------------------------------------- /test/ansible_config_new.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import ansible_runner 4 | import json 5 | 6 | def playbook_runner(config_data) -> dict: 7 | """ 8 | Run the playbook defined in the function, 9 | extravars will come via config_date dict 10 | variable. 11 | """ 12 | runner = ansible_runner.run( 13 | private_data_dir='/home/appdeveloper/react-xe-ops-testing/BackEndModeules/AnsiblePlaybooks/', 14 | playbook='intf_playbook.yml', 15 | inventory='inventory.yml', 16 | envvars='', 17 | extravars= {"ip_interfaces": [ 18 | { 19 | "intf_name": config_data.get('name'), 20 | "intf_description": config_data.get('description'), 21 | "intf_ipv4": config_data.get('ip'), 22 | "intf_ipv4_mask": config_data.get('mask'), 23 | "intf_speed": config_data.get('speed'), 24 | "port_status": config_data.get('portStatus') 25 | } 26 | ] 27 | } 28 | ) 29 | 30 | stdout = runner.stdout.read() 31 | stdout_lines = runner.stdout.readlines() 32 | events = list(runner.events) 33 | stats = runner.stats 34 | job_status = runner.status 35 | 36 | # print(json.dumps(stats, indent=4)) 37 | # print(job_status) 38 | 39 | for event in runner.events: 40 | event_data = event['event_data'] 41 | if 'host' in event_data: 42 | host = event_data['host'] 43 | 44 | for event in runner.events: 45 | event_data = event['event_data'] 46 | if 'res' in event_data: 47 | resp = event_data['res'] 48 | if 'intf_config.updates' in resp: 49 | updates = resp['intf_config.updates'] 50 | msg_data = json.dumps(updates, indent=4) ## green 51 | if 'msg' in resp: 52 | updates = resp['msg'] 53 | msg_data = json.dumps(updates, indent=4) ## red 54 | 55 | if job_status == 'successful': 56 | status = f"{host} has been configured successfully." # green 57 | 58 | if job_status == 'failed': 59 | status = f"[Error]: {host} changes are failed" 60 | 61 | return {"config_data": updates, "status": job_status} 62 | 63 | -------------------------------------------------------------------------------- /test/bgp_config.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.local_as }} 2 | bgp router-id {{ bgp.router_id }} 3 | neighbor {{ bgp.neighbor }} remote-as {{ bgp.remote_as }} 4 | address-family ipv4 unicast 5 | {% if bgp.route_map is not none and 'routeMap' in bgp.route_map_key %} 6 | neighbor {{ bgp.neighbor }} route-map {{ bgp.route_map }} {{ bgp.direction }} 7 | {% elif bgp.prefix_list is not none and 'prefixList' in bgp.prefix_list_key %} 8 | neighbor {{ bgp.neighbor }} prefix-list {{ bgp.prefix_list }} {{ bgp.direction }} 9 | {% endif %} 10 | exit-address-family 11 | -------------------------------------------------------------------------------- /test/bgp_config.yml: -------------------------------------------------------------------------------- 1 | - name: IOSXE CONFIGURATION CONFIG VIA JINJA 2 | hosts: sandbox-iosxe-latest-1.cisco.com 3 | gather_facts: false 4 | connection: network_cli 5 | 6 | tasks: 7 | - name: Configure BGP on IOS-XE via jinja2 tempalte 8 | ios_config: 9 | backup: no 10 | src: bgp_config.j2 11 | when: 12 | - ansible_network_os == 'ios' 13 | register: bgp_config 14 | 15 | - name: Getting the interface configuration 16 | debug: 17 | var: bgp_config.updates 18 | -------------------------------------------------------------------------------- /test/bgp_config_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import ansible_runner 4 | import json 5 | 6 | def playbook_runner(config_data) -> dict: 7 | """ 8 | Run the playbook defined in the function, 9 | extravars will come via config_date dict 10 | variable. 11 | """ 12 | 13 | runner = ansible_runner.interface.run( 14 | private_data_dir='/home/appdeveloper/react-xe-ops-testing/BackEndModeules/AnsiblePlaybooks/', 15 | playbook='bgp_config.yml', 16 | inventory='inventory.yml', 17 | extravars = {"bgp": 18 | { 19 | "local_as": config_data.get('localAs'), 20 | "router_id": config_data.get('routerId'), 21 | "neighbor": config_data.get('neighbor'), 22 | "remote_as": config_data.get('remoteAs'), 23 | "prefix_list": config_data.get('policyType').split(':')[0], 24 | "prefix_list_key": config_data.get('policyType').split(':')[1], 25 | "route_map": config_data.get('policyType').split(':')[0], 26 | "route_map_key": config_data.get('policyType').split(':')[1], 27 | "direction": config_data.get('policyDirection') 28 | } 29 | } 30 | ) 31 | 32 | 33 | stdout = runner.stdout.read() 34 | stdout_lines = runner.stdout.readlines() 35 | events = list(runner.events) 36 | stats = runner.stats 37 | job_status = runner.status 38 | 39 | # print(json.dumps(stats, indent=4)) 40 | # print(job_status) 41 | 42 | for event in runner.events: 43 | event_data = event['event_data'] 44 | if 'host' in event_data: 45 | host = event_data['host'] 46 | 47 | for event in runner.events: 48 | event_data = event['event_data'] 49 | if 'res' in event_data: 50 | resp = event_data['res'] 51 | if 'bgp_config.updates' in resp: 52 | updates = resp['bgp_config.updates'] 53 | msg_data = json.dumps(updates, indent=4) 54 | if 'msg' in resp: 55 | updates = resp['msg'] 56 | msg_data = json.dumps(updates, indent=4) 57 | 58 | if job_status == 'successful': 59 | status = f"{host} has been configured successfully." 60 | 61 | if job_status == 'failed': 62 | status = f"[Error]: {host} changes are failed" 63 | 64 | return {"config_data": updates, "status": job_status} 65 | -------------------------------------------------------------------------------- /test/intf_config.j2: -------------------------------------------------------------------------------- 1 | {% for item in ip_interfaces %} 2 | interface {{ item.intf_name }} 3 | {% if item.intf_description is not none %} 4 | description {{ item.intf_description }} 5 | {% endif %} 6 | {% if item.intf_ipv4 is not none %} 7 | ip address {{ item.intf_ipv4 }} {{ item.intf_ipv4_mask }} 8 | {% else %} 9 | no ip address 10 | {% endif %} 11 | {% if item.intf_speed is not none %} 12 | no negotiation auto 13 | speed {{ item.intf_speed }} 14 | {% endif %} 15 | {% if item.port_status == "up" %} 16 | no shutdown 17 | {% elif item.port_status == "down" %} 18 | shutdown 19 | {% endif %} 20 | exit 21 | {% endfor %} 22 | 23 | -------------------------------------------------------------------------------- /test/intf_config.j2.bak: -------------------------------------------------------------------------------- 1 | {% for item in ip_interfaces %} 2 | interface {{ item.intf_name }} 3 | {% if item.intf_description is not none %} 4 | description {{ item.intf_description }} 5 | {% endif %} 6 | {% if item.intf_ipv4 is not none %} 7 | ip address {{ item.intf_ipv4 }} {{ item.intf_ipv4_mask }} 8 | {% endif %} 9 | {% if item.intf_speed is not none %} 10 | speed {{ item.intf_speed }} 11 | {% endif %} 12 | {% if item.port_status == "up" %} 13 | no shutdown 14 | {% elif item.port_status == "down" %} 15 | shutdown 16 | {% endif %} 17 | exit 18 | {% endfor %} 19 | -------------------------------------------------------------------------------- /test/intf_config_vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ip_interfaces: 3 | - intf_name: 4 | intf_description: 5 | intf_ipv4: 6 | intf_ipv4_mask: 7 | intf_speed: 8 | port_status: 9 | -------------------------------------------------------------------------------- /test/intf_playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: IOSXE CONFIGURATION CONFIG VIA JINJA 3 | hosts: IOSXE 4 | connection: local 5 | gather_facts: no 6 | # vars_files: 7 | # - intf_config_vars.yml 8 | 9 | # ansible-playbook -i inventory.yml intf_playbook.yml 10 | # 11 | tasks: 12 | #- name: Generate IOS-XE Interfaces Config 13 | # template: 14 | # src: "{{ item.src }}" 15 | # dest: "{{ item.dest }}" 16 | # mode: 0777 17 | # with_items: 18 | # - {src: 'intf_config.j2', dest: 'intf_config_output.cfg'} 19 | # register: output 20 | 21 | - name: Configure IOS-XE device vi a Jinja2 template 22 | ios_config: 23 | # backup: yes 24 | # src: intf_config_output.cfg 25 | src: intf_config.j2 26 | when: 27 | - ansible_network_os == 'ios' 28 | register: intf_config 29 | 30 | - name: Getting the interface configuration 31 | debug: 32 | var: intf_config.updates 33 | 34 | -------------------------------------------------------------------------------- /test/inventory.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | IOSXE: 5 | hosts: 6 | sandbox-iosxe-latest-1.cisco.com: 7 | ansible_host: 131.226.217.143 8 | ansible_network_os: ios 9 | ansible_password: C1sco12345 10 | ansible_user: developer 11 | ungrouped: {} 12 | -------------------------------------------------------------------------------- /test/my_file.out: -------------------------------------------------------------------------------- 1 | 2 | Welcome to the DevNet Sandbox for CSR1000v and IOS XE 3 | 4 | The following programmability features are already enabled: 5 | - NETCONF 6 | - RESTCONF 7 | 8 | Thanks for stopping by. 9 | 10 | 11 | csr1000v-1#terminal width 511 12 | csr1000v-1#terminal length 0 13 | csr1000v-1# 14 | csr1000v-1# 15 | csr1000v-1#show standby brief | ex Interface 16 | 17 | csr1000v-1# 18 | csr1000v-1#exit 19 | -------------------------------------------------------------------------------- /test/ospf_config.j2: -------------------------------------------------------------------------------- 1 | {% if ospf.vrf is not none %} 2 | router ospfv3 {{ ospf.process_id }} 3 | address-faimily ipv4 unicast vrf {ospf.vrf} 4 | {% if ospf.network is not none %} 5 | network {{ ospf.network }} {{ ospf.wildcard }} area {{ ospf.area }} 6 | {%endif%} 7 | {% if ospf.area_type is not none %} 8 | area {{ ospf.area }} {{ ospf.area_type }} 9 | {%endif%} 10 | 11 | 12 | {%else%} 13 | router ospf {{ ospf.process_id }} 14 | {%endif%} 15 | {% if ospf.network is not none %} 16 | network {{ ospf.network }} {{ ospf.wildcard }} area {{ ospf.area }} 17 | {%endif%} 18 | {% if ospf.area_type is not none %} 19 | area {{ ospf.area }} {{ ospf.area_type }} 20 | {%endif%} 21 | 22 | -------------------------------------------------------------------------------- /test/ospf_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import ansible_runner 4 | import json 5 | 6 | def playbook_runner(config_data) -> dict: 7 | print(config_data) 8 | """ 9 | Run the playbook defined in the function, 10 | extravars will come via config_date dict 11 | variable. 12 | """ 13 | 14 | runner = ansible_runner.interface.run( 15 | private_data_dir='/home/appdeveloper/react-xe-ops-testing/BackEndModeules/AnsiblePlaybooks/', 16 | playbook='ospf_config.yml', 17 | inventory='inventory.yml', 18 | extravars = {"ospf": 19 | { 20 | "router_id": config_data.get('routerId'), 21 | "process_id": config_data.get('process'), 22 | "network": config_data.get('network'), 23 | "wildcard": config_data.get('wildcard'), 24 | "area": config_data.get('area'), 25 | "area_type": config_data.get('areaType'), 26 | "vrf": config_data.get('vrf'), 27 | 28 | } 29 | } 30 | ) 31 | 32 | 33 | stdout = runner.stdout.read() 34 | stdout_lines = runner.stdout.readlines() 35 | events = list(runner.events) 36 | stats = runner.stats 37 | job_status = runner.status 38 | 39 | # print(json.dumps(stats, indent=4)) 40 | # print(job_status) 41 | 42 | for event in runner.events: 43 | event_data = event['event_data'] 44 | if 'host' in event_data: 45 | host = event_data['host'] 46 | 47 | for event in runner.events: 48 | event_data = event['event_data'] 49 | if 'res' in event_data: 50 | resp = event_data['res'] 51 | if 'ospf_config.updates' in resp: 52 | updates = resp['ospf_config.updates'] 53 | msg_data = json.dumps(updates, indent=4) 54 | if 'msg' in resp: 55 | updates = resp['msg'] 56 | msg_data = json.dumps(updates, indent=4) 57 | 58 | if job_status == 'successful': 59 | status = f"{host} has been configured successfully." 60 | 61 | if job_status == 'failed': 62 | status = f"[Error]: {host} changes are failed" 63 | 64 | return {"config_data": updates, "status": job_status} 65 | -------------------------------------------------------------------------------- /test/ospf_config.yml: -------------------------------------------------------------------------------- 1 | - name: IOSXE CONFIGURATION CONFIG VIA JINJA 2 | hosts: sandbox-iosxe-latest-1.cisco.com 3 | gather_facts: false 4 | connection: network_cli 5 | 6 | tasks: 7 | - name: Configure OSPF on IOS-XE via jinja2 tempalte 8 | ios_config: 9 | backup: no 10 | src: ospf_config.j2 11 | when: 12 | - ansible_network_os == 'ios' 13 | register: ospf_config 14 | 15 | - name: Getting the ospf configuration 16 | debug: 17 | var: ospf_config.updates 18 | -------------------------------------------------------------------------------- /test/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | flask 3 | flask_jwt_extended 4 | netmiko 5 | ipapi 6 | SQLAlchemy 7 | gunicorn 8 | ansible==2.9.16 9 | ansible-runner 10 | -------------------------------------------------------------------------------- /test/show_version.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: SHOW IOSXE VERSION 3 | hosts: "{{ target_hosts|default('localhost') }}" 4 | connection: local 5 | gather_facts: no 6 | 7 | # ansible-playbook -i inventory.yml -e target_hosts=sandbox-iosxe-latest-1.cisco.com show_version.yml 8 | 9 | tasks: 10 | - name: GET THE IOS-XE SOFTWARE VERSION 11 | ios_command: 12 | commands: show version 13 | wait_for: result[0] contains IOS 14 | register: ios_version 15 | 16 | - name: SHOW THE IOS-XE SOFTWARE VERSION 17 | debug: 18 | msg: 19 | - "{{ ios_version }}" 20 | # - "{{ ios_version.stdout_lines[0][0] }}" 21 | -------------------------------------------------------------------------------- /test/test.rst: -------------------------------------------------------------------------------- 1 | ddd 2 | -------------------------------------------------------------------------------- /test/wsgi.py: -------------------------------------------------------------------------------- 1 | from api_routes import app 2 | import os 3 | import ssl 4 | 5 | ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) 6 | ctx.load_cert_chain(f'{os.getcwd()}/domainame.crt', f'{os.getcwd()}/domainame.key') 7 | 8 | if __name__ == "__main__": 9 | app.run(ssl_context=ctx) 10 | -------------------------------------------------------------------------------- /test/yang_conversions.py: -------------------------------------------------------------------------------- 1 | import os, subprocess 2 | import time 3 | 4 | yang_dir = f"{os.getcwd()}/yangFiles" 5 | 6 | def get_yang(): 7 | 8 | global yang_dir 9 | 10 | files = [yang_file for yang_file in os.listdir(yang_dir) if yang_file.endswith(".yang")] 11 | 12 | return files 13 | 14 | 15 | def get_standard_tree(model): 16 | 17 | 18 | get_model = subprocess.run(["pyang", "-f", "tree", f"{yang_dir}/{model}"], capture_output=True) 19 | model_stdout = str(get_model.stdout, 'utf-8') 20 | 21 | return model_stdout 22 | 23 | 24 | def get_dynamic_tree(model): 25 | 26 | process = subprocess.Popen(['pyang', '-f', 'jstree', '-o', f"{yang_dir}/jstree.html" , f'{model}'], shell=False) 27 | 28 | while process.poll() is None: 29 | time.sleep(1) 30 | 31 | return process.poll() 32 | 33 | 34 | def get_yin(model): 35 | 36 | process = subprocess.Popen(['pyang', '-f', 'yin', '-o', f"{yang_dir}/yin.xml" , f'{yang_dir}/{model}'], shell=False) 37 | 38 | while process.poll() is None: 39 | time.sleep(1) 40 | 41 | yin_file = open(f"{yang_dir}/yin.xml") 42 | 43 | return yin_file.read() 44 | 45 | def get_xpaths(model): 46 | 47 | get_model = subprocess.run(["pyang", "-f", "xpath", f"{yang_dir}/{model}"], capture_output=True) 48 | model_stdout = str(get_model.stdout, 'utf-8') 49 | print(model_stdout) 50 | 51 | return model_stdout 52 | --------------------------------------------------------------------------------