mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
complete overhaul of configuration module
This commit is contained in:
@@ -1,38 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Module Imports
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import toml
|
import toml
|
||||||
import logging
|
|
||||||
from netaddr import *
|
|
||||||
from logzero import logger
|
from logzero import logger
|
||||||
|
from netaddr import IPNetwork, IPAddress, IPSet
|
||||||
|
|
||||||
# Local imports
|
# Project Imports
|
||||||
from hyperglass import configuration
|
from hyperglass import configuration
|
||||||
|
|
||||||
# Load TOML config file
|
|
||||||
devices = configuration.devices()
|
|
||||||
|
|
||||||
# Load TOML commands file
|
code = configuration.codes()
|
||||||
commands = configuration.commands()
|
|
||||||
|
|
||||||
# Filter config to router list
|
|
||||||
routers_list = devices["router"]
|
|
||||||
|
|
||||||
|
|
||||||
class codes:
|
|
||||||
"""Class for easy calling & recalling of http success/error codes"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
# 200 OK: renders standard display text
|
|
||||||
self.success = 200
|
|
||||||
# 405 Method Not Allowed: Renders Bulma "warning" class notification message with message text
|
|
||||||
self.warning = 405
|
|
||||||
# 415 Unsupported Media Type: Renders Bulma "danger" class notification message with message text
|
|
||||||
self.danger = 415
|
|
||||||
|
|
||||||
|
|
||||||
code = codes()
|
|
||||||
|
|
||||||
|
|
||||||
def frr(cmd, ipprefix, device):
|
def frr(cmd, ipprefix, device):
|
||||||
@@ -138,19 +118,8 @@ def ssh(cmd, ipprefix, device):
|
|||||||
d_type = device["type"]
|
d_type = device["type"]
|
||||||
|
|
||||||
logger.info(f"Constructing {cmd} command for {d_name} to {ipprefix}...")
|
logger.info(f"Constructing {cmd} command for {d_name} to {ipprefix}...")
|
||||||
# Loop through commands config file, set variables for matched commands
|
|
||||||
class command:
|
|
||||||
def __init__(self, type):
|
|
||||||
if type in commands:
|
|
||||||
self.dual = commands[type][0]["dual"]
|
|
||||||
self.ipv4 = commands[type][0]["ipv4"]
|
|
||||||
self.ipv6 = commands[type][0]["ipv6"]
|
|
||||||
else:
|
|
||||||
msg = f"{d_type} is an unsupported network operating system."
|
|
||||||
logger.error(f"{msg}, {code.danger}, {d_name}, {cmd}, {ipprefix}")
|
|
||||||
return (msg, code.danger, d_name, cmd, ipprefix)
|
|
||||||
|
|
||||||
c = command(d_type)
|
c = configuration.command(d_type)
|
||||||
# BGP Community Query
|
# BGP Community Query
|
||||||
if cmd == "bgp_community":
|
if cmd == "bgp_community":
|
||||||
# Extended Communities, new-format
|
# Extended Communities, new-format
|
||||||
|
@@ -1,206 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import requests
|
|
||||||
from netaddr import *
|
|
||||||
from logzero import logger
|
|
||||||
from netmiko import redispatch
|
|
||||||
from netmiko import ConnectHandler
|
|
||||||
from hyperglass import configuration
|
|
||||||
from hyperglass.command import parse
|
|
||||||
from hyperglass.command import construct
|
|
||||||
|
|
||||||
# Load TOML devices file
|
|
||||||
devices = configuration.devices()
|
|
||||||
# Filter config to router list
|
|
||||||
routers_list = devices["router"]
|
|
||||||
# Filter config to credential list
|
|
||||||
credentials_list = devices["credential"]
|
|
||||||
# Filter config to proxy servers
|
|
||||||
proxies_list = devices["proxy"]
|
|
||||||
|
|
||||||
blacklist_config = configuration.blacklist()
|
|
||||||
blacklist = IPSet(blacklist_config["blacklist"])
|
|
||||||
|
|
||||||
general_error = "Error connecting to device."
|
|
||||||
|
|
||||||
|
|
||||||
def execute(lg_data):
|
|
||||||
logger.info(f"Received lookup request for: {lg_data}")
|
|
||||||
# Check POST data from JS, if location matches a configured router's
|
|
||||||
# location, use the router's configured IP address to connect
|
|
||||||
router = lg_data["router"]
|
|
||||||
cmd = lg_data["cmd"]
|
|
||||||
ipprefix = lg_data["ipprefix"]
|
|
||||||
|
|
||||||
for r in routers_list:
|
|
||||||
if r["location"] == router:
|
|
||||||
lg_router_address = r["address"]
|
|
||||||
lg_router_port = r["port"]
|
|
||||||
lg_router_type = r["type"]
|
|
||||||
|
|
||||||
# Check blacklist.toml array for prefixes/IPs and return an error upon a match
|
|
||||||
if cmd in ["bgp_route", "ping", "traceroute"]:
|
|
||||||
try:
|
|
||||||
if IPNetwork(ipprefix).ip in blacklist:
|
|
||||||
msg = f"{ipprefix} is not allowed."
|
|
||||||
code = 405
|
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
|
||||||
return (msg, code, lg_data)
|
|
||||||
# If netaddr library throws an exception, return a user-facing error.
|
|
||||||
except:
|
|
||||||
msg = f"{ipprefix} is not a valid IP Address."
|
|
||||||
code = 415
|
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
|
||||||
return (msg, code, lg_data)
|
|
||||||
|
|
||||||
# Send "clean" request to constructor to build the command that will be sent to the router
|
|
||||||
if lg_router_type == "frr":
|
|
||||||
msg, status, router, query = construct.frr(lg_router_address, cmd, ipprefix)
|
|
||||||
else:
|
|
||||||
msg, status, router, type, command = construct.netmiko(
|
|
||||||
lg_router_address, cmd, ipprefix
|
|
||||||
)
|
|
||||||
nm_host = {
|
|
||||||
"host": router,
|
|
||||||
"device_type": type,
|
|
||||||
"username": returnCred(findCred(router))[0],
|
|
||||||
"password": returnCred(findCred(router))[1],
|
|
||||||
"global_delay_factor": 0.5,
|
|
||||||
}
|
|
||||||
|
|
||||||
def matchProxy(search_proxy):
|
|
||||||
"""Loops through proxy config, matches configured proxy name for each router with a configured proxy. Returns configured proxy parameters for netmiko"""
|
|
||||||
if configured_proxy in proxies_list:
|
|
||||||
proxy_address = proxies_list[search_proxy]["address"]
|
|
||||||
proxy_username = proxies_list[search_proxy]["username"]
|
|
||||||
proxy_password = proxies_list[search_proxy]["password"]
|
|
||||||
proxy_type = proxies_list[search_proxy]["type"]
|
|
||||||
proxy_ssh_command = proxies_list[search_proxy]["ssh_command"]
|
|
||||||
return (
|
|
||||||
proxy_address,
|
|
||||||
proxy_username,
|
|
||||||
proxy_password,
|
|
||||||
proxy_type,
|
|
||||||
proxy_ssh_command,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
msg = "Router does not have a proxy configured."
|
|
||||||
code = 415
|
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
|
||||||
return (msg, code, lg_data)
|
|
||||||
|
|
||||||
def findCred(router):
|
|
||||||
"""Matches router with configured credential"""
|
|
||||||
for r in routers_list:
|
|
||||||
if r["address"] == router:
|
|
||||||
configured_credential = r["credential"]
|
|
||||||
return configured_credential
|
|
||||||
|
|
||||||
def returnCred(configured_credential):
|
|
||||||
"""Matches configured credential with real username/password"""
|
|
||||||
if configured_credential in credentials_list:
|
|
||||||
matched_username = credentials_list[configured_credential]["username"]
|
|
||||||
matched_password = credentials_list[configured_credential]["password"]
|
|
||||||
return matched_username, matched_password
|
|
||||||
else:
|
|
||||||
msg = f"Credential {configured_credential} does not exist"
|
|
||||||
code = 415
|
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
|
||||||
return (general_error, code, lg_data)
|
|
||||||
|
|
||||||
def frr_api_direct(query):
|
|
||||||
"""Sends HTTP POST to router running the hyperglass-frr API"""
|
|
||||||
global lg_router_address
|
|
||||||
global lg_router_port
|
|
||||||
try:
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-API-Key": returnCred(findCred(router))[1],
|
|
||||||
}
|
|
||||||
json_query = json.dumps(query)
|
|
||||||
frr_endpoint = f"http://{router}:{lg_router_port}/frr"
|
|
||||||
frr_output = requests.post(frr_endpoint, headers=headers, data=json_query)
|
|
||||||
return frr_output
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def netmiko_direct():
|
|
||||||
"""Connects to the router via netmiko library, return the command output"""
|
|
||||||
try:
|
|
||||||
nm_connect_direct = ConnectHandler(**nm_host)
|
|
||||||
nm_output_direct = nm_connect_direct.send_command(command)
|
|
||||||
return nm_output_direct
|
|
||||||
except:
|
|
||||||
msg = f"Unable to reach target {router}"
|
|
||||||
code = 415
|
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
|
||||||
return (general_error, code, lg_data)
|
|
||||||
|
|
||||||
def netmiko_proxied(router_proxy):
|
|
||||||
"""Connects to the proxy server via netmiko library, then logs into the router via standard SSH"""
|
|
||||||
nm_proxy = {
|
|
||||||
"host": matchProxy(router_proxy)[0],
|
|
||||||
"username": matchProxy(router_proxy)[1],
|
|
||||||
"password": matchProxy(router_proxy)[2],
|
|
||||||
"device_type": matchProxy(router_proxy)[3],
|
|
||||||
"global_delay_factor": 0.5,
|
|
||||||
}
|
|
||||||
nm_connect_proxied = ConnectHandler(**nm_proxy)
|
|
||||||
nm_ssh_command = matchProxy(router_proxy)[4].format(**nm_host) + "\n"
|
|
||||||
nm_connect_proxied.write_channel(nm_ssh_command)
|
|
||||||
time.sleep(1)
|
|
||||||
proxy_output = nm_connect_proxied.read_channel()
|
|
||||||
try:
|
|
||||||
# Accept SSH key warnings
|
|
||||||
if "Are you sure you want to continue connecting" in proxy_output:
|
|
||||||
nm_connect_proxied.write_channel("yes" + "\n")
|
|
||||||
# time.sleep(1)
|
|
||||||
nm_connect_proxied.write_channel(nm_host["password"] + "\n")
|
|
||||||
# Send password on prompt
|
|
||||||
elif "assword" in proxy_output:
|
|
||||||
nm_connect_proxied.write_channel(nm_host["password"] + "\n")
|
|
||||||
# time.sleep(1)
|
|
||||||
proxy_output += nm_connect_proxied.read_channel()
|
|
||||||
# Reclassify netmiko connection as configured device type
|
|
||||||
redispatch(nm_connect_proxied, nm_host["device_type"])
|
|
||||||
|
|
||||||
host_output = nm_connect_proxied.send_command(command)
|
|
||||||
if host_output:
|
|
||||||
return host_output
|
|
||||||
except:
|
|
||||||
msg = f'Proxy server {nm_proxy["host"]} unable to reach target {nm_host["host"]}'
|
|
||||||
code = 415
|
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
|
||||||
return (general_error, code, lg_data)
|
|
||||||
|
|
||||||
# Loop through router list, determine if proxy exists
|
|
||||||
for r in routers_list:
|
|
||||||
if r["address"] == router:
|
|
||||||
configured_proxy = r["proxy"]
|
|
||||||
if len(configured_proxy) == 0:
|
|
||||||
connection_proxied = False
|
|
||||||
else:
|
|
||||||
connection_proxied = True
|
|
||||||
if status == 200:
|
|
||||||
logger.info(f"Executing {command} on {router}...")
|
|
||||||
try:
|
|
||||||
if connection_proxied is True:
|
|
||||||
output_proxied = netmiko_proxied(configured_proxy)
|
|
||||||
parsed_output = parse.parse(output_proxied, type, cmd)
|
|
||||||
return parsed_output, status, router, type, command
|
|
||||||
elif connection_proxied is False:
|
|
||||||
if type == "frr":
|
|
||||||
output_direct = frr_api_direct(query)
|
|
||||||
parsed_output = parse.parse(output_direct, type, cmd)
|
|
||||||
return parsed_output, status, router, type, command
|
|
||||||
else:
|
|
||||||
output_direct = netmiko_direct()
|
|
||||||
parsed_output = parse.parse(output_direct, type, cmd)
|
|
||||||
return parsed_output, status, router, type, command
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
return msg, status, router, type, command
|
|
@@ -1,67 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Module Imports
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import requests
|
import requests
|
||||||
from netaddr import *
|
|
||||||
from logzero import logger
|
from logzero import logger
|
||||||
from netmiko import redispatch
|
from netmiko import redispatch
|
||||||
from netmiko import ConnectHandler
|
from netmiko import ConnectHandler
|
||||||
|
from netaddr import IPNetwork, IPAddress, IPSet
|
||||||
|
|
||||||
|
# Project Imports
|
||||||
from hyperglass import configuration
|
from hyperglass import configuration
|
||||||
from hyperglass.command import parse
|
from hyperglass.command import parse
|
||||||
from hyperglass.command import construct
|
from hyperglass.command import construct
|
||||||
|
|
||||||
# Load TOML devices file
|
|
||||||
devices = configuration.devices()
|
|
||||||
# Filter config to router list
|
|
||||||
routers_list = devices["router"]
|
|
||||||
# Filter config to credential list
|
|
||||||
credentials_list = devices["credential"]
|
|
||||||
# Filter config to proxy servers
|
|
||||||
proxies_list = devices["proxy"]
|
|
||||||
|
|
||||||
blacklist_config = configuration.blacklist()
|
|
||||||
blacklist = IPSet(blacklist_config["blacklist"])
|
|
||||||
|
|
||||||
general_error = "Error connecting to device."
|
|
||||||
|
|
||||||
|
|
||||||
class device:
|
|
||||||
def __init__(self, lg_router):
|
|
||||||
for r in routers_list:
|
|
||||||
if r["location"] == lg_router:
|
|
||||||
self.address = r["address"]
|
|
||||||
self.asn = r["asn"]
|
|
||||||
self.src_addr_ipv4 = r["src_addr_ipv4"]
|
|
||||||
self.src_addr_ipv6 = r["src_addr_ipv6"]
|
|
||||||
self.credential = r["credential"]
|
|
||||||
self.location = r["location"]
|
|
||||||
self.name = r["name"]
|
|
||||||
self.port = r["port"]
|
|
||||||
self.type = r["type"]
|
|
||||||
self.proxy = r["proxy"]
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
return vars(self)
|
|
||||||
|
|
||||||
|
|
||||||
class credential:
|
|
||||||
def __init__(self, cred):
|
|
||||||
if cred in credentials_list:
|
|
||||||
self.username = credentials_list[cred]["username"]
|
|
||||||
self.password = credentials_list[cred]["password"]
|
|
||||||
|
|
||||||
|
|
||||||
class proxy:
|
|
||||||
def __init__(self, proxy):
|
|
||||||
if proxy in proxies_list:
|
|
||||||
self.address = proxies_list[proxy]["address"]
|
|
||||||
self.username = proxies_list[proxy]["username"]
|
|
||||||
self.password = proxies_list[proxy]["password"]
|
|
||||||
self.type = proxies_list[proxy]["type"]
|
|
||||||
self.ssh_command = proxies_list[proxy]["ssh_command"]
|
|
||||||
|
|
||||||
|
|
||||||
class params:
|
class params:
|
||||||
class http:
|
class http:
|
||||||
@@ -83,7 +36,7 @@ class params:
|
|||||||
return vars(self)
|
return vars(self)
|
||||||
|
|
||||||
def nm_host(self):
|
def nm_host(self):
|
||||||
c = credential(d.credential)
|
c = configuration.credential(d.credential)
|
||||||
attr = {
|
attr = {
|
||||||
"host": self.router,
|
"host": self.router,
|
||||||
"device_type": self.type,
|
"device_type": self.type,
|
||||||
@@ -94,7 +47,7 @@ class params:
|
|||||||
return attr
|
return attr
|
||||||
|
|
||||||
def nm_proxy(self):
|
def nm_proxy(self):
|
||||||
p = proxy(d.proxy)
|
p = configuration.proxy(d.proxy)
|
||||||
attr = {
|
attr = {
|
||||||
"host": p.address,
|
"host": p.address,
|
||||||
"username": p.username,
|
"username": p.username,
|
||||||
@@ -110,7 +63,7 @@ class connect:
|
|||||||
def frr():
|
def frr():
|
||||||
"""Sends HTTP POST to router running the hyperglass-frr API"""
|
"""Sends HTTP POST to router running the hyperglass-frr API"""
|
||||||
http = params().http()
|
http = params().http()
|
||||||
c = credential(d.credential)
|
c = configuration.credential(d.credential)
|
||||||
try:
|
try:
|
||||||
headers = {"Content-Type": "application/json", "X-API-Key": c.password}
|
headers = {"Content-Type": "application/json", "X-API-Key": c.password}
|
||||||
json_query = json.dumps(http.query)
|
json_query = json.dumps(http.query)
|
||||||
@@ -136,7 +89,7 @@ class connect:
|
|||||||
ssh = params().ssh()
|
ssh = params().ssh()
|
||||||
nm_proxy = ssh.nm_proxy()
|
nm_proxy = ssh.nm_proxy()
|
||||||
nm_host = ssh.nm_host()
|
nm_host = ssh.nm_host()
|
||||||
dp = proxy(d.proxy)
|
dp = configuration.proxy(d.proxy)
|
||||||
|
|
||||||
nm_connect_proxied = ConnectHandler(**nm_proxy)
|
nm_connect_proxied = ConnectHandler(**nm_proxy)
|
||||||
nm_ssh_command = dp.ssh_command.format(**nm_host) + "\n"
|
nm_ssh_command = dp.ssh_command.format(**nm_host) + "\n"
|
||||||
@@ -163,10 +116,9 @@ class connect:
|
|||||||
return host_output
|
return host_output
|
||||||
except:
|
except:
|
||||||
msg = f'Proxy server {nm_proxy["host"]} unable to reach target {nm_host["host"]}'
|
msg = f'Proxy server {nm_proxy["host"]} unable to reach target {nm_host["host"]}'
|
||||||
code = 415
|
logger.error(f"{msg}, {code.danger}, {lg_params}")
|
||||||
logger.error(f"{msg}, {code}, {lg_params}")
|
|
||||||
raise
|
raise
|
||||||
return (general_error, code, lg_params)
|
return (general.message_general_error, code.danger, lg_params)
|
||||||
|
|
||||||
|
|
||||||
def execute(lg_data):
|
def execute(lg_data):
|
||||||
@@ -184,28 +136,53 @@ def execute(lg_data):
|
|||||||
global lg_params
|
global lg_params
|
||||||
lg_params = lg_data
|
lg_params = lg_data
|
||||||
|
|
||||||
|
global general
|
||||||
|
general = configuration.general()
|
||||||
|
|
||||||
|
global code
|
||||||
|
code = configuration.codes()
|
||||||
|
|
||||||
# Check blacklist.toml array for prefixes/IPs and return an error upon a match
|
# Check blacklist.toml array for prefixes/IPs and return an error upon a match
|
||||||
if lg_cmd in ["bgp_route", "ping", "traceroute"]:
|
if lg_cmd in ["bgp_route", "ping", "traceroute"]:
|
||||||
try:
|
try:
|
||||||
|
blacklist = IPSet(configuration.blacklist())
|
||||||
if IPNetwork(lg_ipprefix).ip in blacklist:
|
if IPNetwork(lg_ipprefix).ip in blacklist:
|
||||||
msg = f"{lg_ipprefix} is not allowed."
|
msg = f"{lg_ipprefix} is not allowed."
|
||||||
code = 405
|
logger.error(f"{msg}, {code.warning}, {lg_data}")
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
return (msg, code.warning, lg_data)
|
||||||
return (msg, code, lg_data)
|
|
||||||
# If netaddr library throws an exception, return a user-facing error.
|
# If netaddr library throws an exception, return a user-facing error.
|
||||||
except:
|
except:
|
||||||
msg = f"{lg_ipprefix} is not a valid IP Address."
|
msg = f"{lg_ipprefix} is not a valid IP Address."
|
||||||
code = 405
|
logger.error(f"{msg}, {code.danger}, {lg_data}")
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
return (msg, code.danger, lg_data)
|
||||||
return (msg, code, lg_data)
|
if lg_cmd == "bgp_route" and general.enable_max_prefix == True:
|
||||||
|
logger.debug(f"Enable Max Prefix: {general.enable_max_prefix}")
|
||||||
|
logger.debug(f"ipprefix_version: {IPNetwork(lg_ipprefix).version}")
|
||||||
|
logger.debug(f"ipprefix_len: {IPNetwork(lg_ipprefix).prefixlen}")
|
||||||
|
try:
|
||||||
|
if (
|
||||||
|
IPNetwork(lg_ipprefix).version == 4
|
||||||
|
and IPNetwork(lg_ipprefix).prefixlen > general.max_prefix_length_ipv4
|
||||||
|
):
|
||||||
|
msg = f"Prefix length must be smaller than /{general.max_prefix_length_ipv4}. {IPNetwork(lg_ipprefix)} is too specific."
|
||||||
|
logger.error(f"{msg}, {code.warning}, {lg_data}")
|
||||||
|
return (msg, code.warning, lg_data)
|
||||||
|
if (
|
||||||
|
IPNetwork(lg_ipprefix).version == 6
|
||||||
|
and IPNetwork(lg_ipprefix).prefixlen > general.max_prefix_length_ipv6
|
||||||
|
):
|
||||||
|
msg = f"Prefix length must be smaller than /{general.max_prefix_length_ipv4}. {IPNetwork(lg_ipprefix)} is too specific."
|
||||||
|
logger.error(f"{msg}, {code.warning}, {lg_data}")
|
||||||
|
return (msg, code.warning, lg_data)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
elif lg_cmd == "Query Type":
|
elif lg_cmd == "Query Type":
|
||||||
msg = "You must select a query type."
|
msg = "You must select a query type."
|
||||||
code = 405
|
logger.error(f"{msg}, {code.danger}, {lg_data}")
|
||||||
logger.error(f"{msg}, {code}, {lg_data}")
|
return (msg, code.danger, lg_data)
|
||||||
return (msg, code, lg_data)
|
|
||||||
|
|
||||||
global d
|
global d
|
||||||
d = device(lg_router)
|
d = configuration.device(lg_router)
|
||||||
|
|
||||||
if d.type == "frr":
|
if d.type == "frr":
|
||||||
http = params().http()
|
http = params().http()
|
||||||
@@ -217,8 +194,8 @@ def execute(lg_data):
|
|||||||
elif http.status in range(400, 500):
|
elif http.status in range(400, 500):
|
||||||
return http.msg, http.status, http()
|
return http.msg, http.status, http()
|
||||||
else:
|
else:
|
||||||
logger.error(general_error, 500, http())
|
logger.error(general.message_general_error, 500, http())
|
||||||
return general_error, 500, http()
|
return general.message_general_error, 500, http()
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
@@ -236,7 +213,7 @@ def execute(lg_data):
|
|||||||
elif ssh.status in range(400, 500):
|
elif ssh.status in range(400, 500):
|
||||||
return ssh.msg, ssh.status, ssh()
|
return ssh.msg, ssh.status, ssh()
|
||||||
else:
|
else:
|
||||||
logger.error(general_error, 500, ssh())
|
logger.error(general.message_general_error, 500, ssh())
|
||||||
return general_error, 500, ssh()
|
return general.message_general_error, 500, ssh()
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
@@ -1,524 +1,246 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Module Imports
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
|
# Project Imports
|
||||||
import hyperglass
|
import hyperglass
|
||||||
|
|
||||||
|
# Project Directories
|
||||||
dir = os.path.dirname(os.path.abspath(__file__))
|
dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
hyperglass_root = os.path.dirname(hyperglass.__file__)
|
hyperglass_root = os.path.dirname(hyperglass.__file__)
|
||||||
|
# TOML Imports
|
||||||
|
configuration = toml.load(os.path.join(dir, "configuration.toml"))
|
||||||
|
devices = toml.load(os.path.join(dir, "devices.toml"))
|
||||||
|
|
||||||
|
|
||||||
def blacklist():
|
def blacklist():
|
||||||
f = os.path.join(dir, "blacklist.toml")
|
b = toml.load(os.path.join(dir, "blacklist.toml"))
|
||||||
t = toml.load(f)
|
return b["blacklist"]
|
||||||
return t
|
|
||||||
|
|
||||||
|
|
||||||
def commands():
|
def requires_ipv6_cidr(nos):
|
||||||
f = os.path.join(dir, "commands.toml")
|
r = toml.load(os.path.join(dir, "requires_ipv6_cidr.toml"))
|
||||||
t = toml.load(f)
|
nos_list = r["requires_ipv6_cidr"]
|
||||||
return t
|
if nos in nos_list:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def configuration():
|
def networks():
|
||||||
f = os.path.join(dir, "configuration.toml")
|
"""Returns dictionary of ASNs as keys, list of associated locations as values.
|
||||||
t = toml.load(f)
|
Used for populating the /routers/<asn> Flask route."""
|
||||||
return t
|
asn_dict = {}
|
||||||
|
rl = devices["router"]
|
||||||
|
for r in rl.values():
|
||||||
|
asn = r["asn"]
|
||||||
|
if asn in asn_dict:
|
||||||
|
asn_dict[asn].append(r["location"])
|
||||||
|
else:
|
||||||
|
asn_dict[asn] = [r["location"]]
|
||||||
|
return asn_dict
|
||||||
|
|
||||||
|
|
||||||
def devices():
|
def networks_list():
|
||||||
f = os.path.join(dir, "devices.toml")
|
networks_dict = {}
|
||||||
t = toml.load(f)
|
rl = devices["router"]
|
||||||
return t
|
for r in rl.values():
|
||||||
|
asn = r["asn"]
|
||||||
|
if asn in networks_dict:
|
||||||
def requires_ipv6_cidr():
|
networks_dict[asn].append(
|
||||||
f = os.path.join(dir, "requires_ipv6_cidr.toml")
|
dict(
|
||||||
t = toml.load(f)
|
location=r["location"],
|
||||||
return t["requires_ipv6_cidr"]
|
hostname=r["name"],
|
||||||
|
display_name=r["display_name"],
|
||||||
|
requires_ipv6_cidr=requires_ipv6_cidr(r["type"]),
|
||||||
# Filter config to branding variables
|
|
||||||
branding = configuration()["branding"]
|
|
||||||
|
|
||||||
# Filter config to general variables
|
|
||||||
general = configuration()["general"]
|
|
||||||
|
|
||||||
routers_list = devices()["router"]
|
|
||||||
|
|
||||||
|
|
||||||
class dev:
|
|
||||||
"""Functions to import device variables"""
|
|
||||||
|
|
||||||
def networks():
|
|
||||||
asn_dict = dict()
|
|
||||||
for r in routers_list:
|
|
||||||
asn = r["asn"]
|
|
||||||
if asn in asn_dict:
|
|
||||||
asn_dict[asn].append(r["location"])
|
|
||||||
else:
|
|
||||||
asn_dict[asn] = [r["location"]]
|
|
||||||
return asn_dict
|
|
||||||
|
|
||||||
def name():
|
|
||||||
list = []
|
|
||||||
for r in routers_list:
|
|
||||||
list.append(str(r["name"]))
|
|
||||||
return list
|
|
||||||
|
|
||||||
def display_name():
|
|
||||||
list = []
|
|
||||||
for r in routers_list:
|
|
||||||
list.appen(str(r["display_name"]))
|
|
||||||
return list
|
|
||||||
|
|
||||||
|
|
||||||
class gen:
|
|
||||||
"""Functions to import config variables and return default values if undefined"""
|
|
||||||
|
|
||||||
def primary_asn():
|
|
||||||
list = []
|
|
||||||
for g in general:
|
|
||||||
if len(g["primary_asn"]) == 0:
|
|
||||||
return "65000"
|
|
||||||
else:
|
|
||||||
return g["primary_asn"]
|
|
||||||
|
|
||||||
def org_name():
|
|
||||||
list = []
|
|
||||||
for g in general:
|
|
||||||
if len(g["org_name"]) == 0:
|
|
||||||
return "The Company"
|
|
||||||
else:
|
|
||||||
return g["org_name"]
|
|
||||||
|
|
||||||
def debug():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["debug"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def google_analytics():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["google_analytics"]) == 0:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return a["google_analytics"]
|
|
||||||
|
|
||||||
def enable_recaptcha():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["enable_recaptcha"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def message_error():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["message_error"]) == 0:
|
|
||||||
return "{input} is invalid."
|
|
||||||
else:
|
|
||||||
return a["message_error"]
|
|
||||||
|
|
||||||
def message_blacklist():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["message_blacklist"]) == 0:
|
|
||||||
return "{input} is not allowed."
|
|
||||||
else:
|
|
||||||
return a["message_blacklist"]
|
|
||||||
|
|
||||||
def message_rate_limit_query():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["message_rate_limit_query"]) == 0:
|
|
||||||
return "Query limit of {rate_limit_query} per minute reached. Please wait one minute and try again.".format(
|
|
||||||
rate_limit_query=gen.rate_limit_query()
|
|
||||||
)
|
)
|
||||||
else:
|
)
|
||||||
return a["message_rate_limit_query"]
|
else:
|
||||||
|
networks_dict[asn] = [
|
||||||
def enable_bgp_route():
|
dict(
|
||||||
list = []
|
location=r["location"],
|
||||||
for a in general:
|
hostname=r["name"],
|
||||||
try:
|
display_name=r["display_name"],
|
||||||
return a["enable_bgp_route"]
|
requires_ipv6_cidr=requires_ipv6_cidr(r["type"]),
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def enable_bgp_community():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["enable_bgp_community"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def enable_bgp_aspath():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["enable_bgp_aspath"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def enable_ping():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["enable_ping"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def enable_traceroute():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["enable_traceroute"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def rate_limit_query():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["rate_limit_query"]) == 0:
|
|
||||||
return "5"
|
|
||||||
else:
|
|
||||||
return a["rate_limit_query"]
|
|
||||||
|
|
||||||
def rate_limit_site():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["rate_limit_site"]) == 0:
|
|
||||||
return "120"
|
|
||||||
else:
|
|
||||||
return a["rate_limit_site"]
|
|
||||||
|
|
||||||
def cache_timeout():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
try:
|
|
||||||
return a["cache_timeout"]
|
|
||||||
except:
|
|
||||||
return 120
|
|
||||||
|
|
||||||
def cache_directory():
|
|
||||||
list = []
|
|
||||||
for a in general:
|
|
||||||
if len(a["cache_directory"]) == 0:
|
|
||||||
d = ".flask_cache"
|
|
||||||
return os.path.join(hyperglass_root, d)
|
|
||||||
else:
|
|
||||||
return a["cache_directory"]
|
|
||||||
|
|
||||||
|
|
||||||
class brand:
|
|
||||||
"""Functions to import branding variables and return default values if undefined"""
|
|
||||||
|
|
||||||
def site_title():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["site_title"]) == 0:
|
|
||||||
return "hyperglass"
|
|
||||||
else:
|
|
||||||
return t["site_title"]
|
|
||||||
|
|
||||||
def title():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["title"]) == 0:
|
|
||||||
return "hyperglass"
|
|
||||||
else:
|
|
||||||
return t["title"]
|
|
||||||
|
|
||||||
def subtitle():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["subtitle"]) == 0:
|
|
||||||
return "AS" + gen.primary_asn()
|
|
||||||
else:
|
|
||||||
return t["subtitle"]
|
|
||||||
|
|
||||||
def title_mode():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["title_mode"]) == 0:
|
|
||||||
return "logo_only"
|
|
||||||
else:
|
|
||||||
return t["title_mode"]
|
|
||||||
|
|
||||||
def enable_footer():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
try:
|
|
||||||
return t["enable_footer"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def enable_credit():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
try:
|
|
||||||
return t["enable_credit"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def color_btn_submit():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_btn_submit"]) == 0:
|
|
||||||
return "#40798c"
|
|
||||||
else:
|
|
||||||
return t["color_btn_submit"]
|
|
||||||
|
|
||||||
def color_tag_loctitle():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_tag_loctitle"]) == 0:
|
|
||||||
return "#330036"
|
|
||||||
else:
|
|
||||||
return t["color_tag_loctitle"]
|
|
||||||
|
|
||||||
def color_tag_cmdtitle():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_tag_cmdtitle"]) == 0:
|
|
||||||
return "#330036"
|
|
||||||
else:
|
|
||||||
return t["color_tag_cmdtitle"]
|
|
||||||
|
|
||||||
def color_tag_cmd():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_tag_cmd"]) == 0:
|
|
||||||
return "#ff5e5b"
|
|
||||||
else:
|
|
||||||
return t["color_tag_cmd"]
|
|
||||||
|
|
||||||
def color_tag_loc():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_tag_loc"]) == 0:
|
|
||||||
return "#40798c"
|
|
||||||
else:
|
|
||||||
return t["color_tag_loc"]
|
|
||||||
|
|
||||||
def color_progressbar():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_progressbar"]) == 0:
|
|
||||||
return "#40798c"
|
|
||||||
else:
|
|
||||||
return t["color_progressbar"]
|
|
||||||
|
|
||||||
def color_bg():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_bg"]) == 0:
|
|
||||||
return "#fbfffe"
|
|
||||||
else:
|
|
||||||
return t["color_bg"]
|
|
||||||
|
|
||||||
def color_danger():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["color_danger"]) == 0:
|
|
||||||
return "#ff3860"
|
|
||||||
else:
|
|
||||||
return t["color_danger"]
|
|
||||||
|
|
||||||
def logo_path():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["logo_path"]) == 0:
|
|
||||||
f = "static/images/hyperglass-dark.png"
|
|
||||||
return os.path.join(hyperglass_root, f)
|
|
||||||
else:
|
|
||||||
return t["logo_path"]
|
|
||||||
|
|
||||||
def favicon16_path():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["favicon16_path"]) == 0:
|
|
||||||
f = "static/images/favicon/favicon-16x16.png"
|
|
||||||
return f
|
|
||||||
else:
|
|
||||||
return t["favicon16_path"]
|
|
||||||
|
|
||||||
def favicon32_path():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["favicon32_path"]) == 0:
|
|
||||||
f = "static/images/favicon/favicon-32x32.png"
|
|
||||||
return f
|
|
||||||
else:
|
|
||||||
return t["favicon32_path"]
|
|
||||||
|
|
||||||
def logo_width():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["logo_width"]) == 0:
|
|
||||||
return "384"
|
|
||||||
else:
|
|
||||||
return t["logo_width"]
|
|
||||||
|
|
||||||
def placeholder_prefix():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["placeholder_prefix"]) == 0:
|
|
||||||
return "Prefix, IP, Community, or AS_PATH"
|
|
||||||
else:
|
|
||||||
return t["placeholder_prefix"]
|
|
||||||
|
|
||||||
def show_peeringdb():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
try:
|
|
||||||
return a["show_peeringdb"]
|
|
||||||
except:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def text_results():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["text_results"]) == 0:
|
|
||||||
return "Results"
|
|
||||||
else:
|
|
||||||
return t["text_results"]
|
|
||||||
|
|
||||||
def text_location():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["text_location"]) == 0:
|
|
||||||
return "Location"
|
|
||||||
else:
|
|
||||||
return t["text_location"]
|
|
||||||
|
|
||||||
def text_cache():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["text_cache"]) == 0:
|
|
||||||
cache_timeout_exact = gen.cache_timeout() / 60
|
|
||||||
return "Results will be cached for {cache_timeout} minutes.".format(
|
|
||||||
cache_timeout=math.ceil(cache_timeout_exact)
|
|
||||||
)
|
)
|
||||||
else:
|
]
|
||||||
return t["text_cache"]
|
return networks_dict
|
||||||
|
|
||||||
def primary_font_url():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["primary_font_url"]) == 0:
|
|
||||||
return "https://fonts.googleapis.com/css?family=Nunito:400,600,700"
|
|
||||||
else:
|
|
||||||
return t["primary_font_url"]
|
|
||||||
|
|
||||||
def primary_font_name():
|
class codes:
|
||||||
list = []
|
"""Class for easy calling & recalling of http success/error codes"""
|
||||||
for t in branding:
|
|
||||||
if len(t["primary_font_name"]) == 0:
|
|
||||||
return "Nunito"
|
|
||||||
else:
|
|
||||||
return t["primary_font_name"]
|
|
||||||
|
|
||||||
def mono_font_url():
|
def __init__(self):
|
||||||
list = []
|
# 200 OK: renders standard display text
|
||||||
for t in branding:
|
self.success = 200
|
||||||
if len(t["mono_font_url"]) == 0:
|
# 405 Method Not Allowed: Renders Bulma "warning" class notification message with message text
|
||||||
return "https://fonts.googleapis.com/css?family=Fira+Mono"
|
self.warning = 405
|
||||||
else:
|
# 415 Unsupported Media Type: Renders Bulma "danger" class notification message with message text
|
||||||
return t["mono_font_url"]
|
self.danger = 415
|
||||||
|
|
||||||
def mono_font_name():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["mono_font_name"]) == 0:
|
|
||||||
return "Fira Mono"
|
|
||||||
else:
|
|
||||||
return t["mono_font_name"]
|
|
||||||
|
|
||||||
def text_limiter_title():
|
class command:
|
||||||
list = []
|
def __init__(self, nos):
|
||||||
for t in branding:
|
c = toml.load(os.path.join(dir, "configuration.toml"))
|
||||||
if len(t["text_limiter_title"]) == 0:
|
self.dual = c[nos][0]["dual"]
|
||||||
return "Limit Reached"
|
self.ipv4 = c[nos][0]["ipv4"]
|
||||||
else:
|
self.ipv6 = c[nos][0]["ipv6"]
|
||||||
return t["text_limiter_title"]
|
|
||||||
|
|
||||||
def text_limiter_subtitle():
|
def __call__(self):
|
||||||
list = []
|
return vars(self)
|
||||||
for t in branding:
|
|
||||||
if len(t["text_limiter_subtitle"]) == 0:
|
|
||||||
return "You have accessed this site more than {rate_limit_site} times in the last minute.".format(
|
|
||||||
rate_limit_site=gen.rate_limit_site()
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return t["text_limiter_subtitle"]
|
|
||||||
|
|
||||||
def text_415_title():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["text_415_title"]) == 0:
|
|
||||||
return "Error"
|
|
||||||
else:
|
|
||||||
return t["text_415_title"]
|
|
||||||
|
|
||||||
def text_415_subtitle():
|
class credential:
|
||||||
list = []
|
def __init__(self, cred):
|
||||||
for t in branding:
|
c_list = devices["credential"]
|
||||||
if len(t["text_415_subtitle"]) == 0:
|
self.username = c_list[cred]["username"]
|
||||||
return "Something went wrong."
|
self.password = c_list[cred]["password"]
|
||||||
else:
|
|
||||||
return t["text_415_subtitle"]
|
|
||||||
|
|
||||||
def text_415_button():
|
def __call__(self):
|
||||||
list = []
|
return vars(self)
|
||||||
for t in branding:
|
|
||||||
if len(t["text_415_button"]) == 0:
|
|
||||||
return "Home"
|
|
||||||
else:
|
|
||||||
return t["text_415_button"]
|
|
||||||
|
|
||||||
def text_help_bgp_route():
|
|
||||||
list = []
|
|
||||||
for t in branding:
|
|
||||||
if len(t["text_help_bgp_route"]) == 0:
|
|
||||||
return "Performs BGP table lookup based on IPv4/IPv6 prefix."
|
|
||||||
else:
|
|
||||||
return t["text_help_bgp_route"]
|
|
||||||
|
|
||||||
def text_help_bgp_community():
|
class device:
|
||||||
list = []
|
"""Class to define & export all device variables"""
|
||||||
for t in branding:
|
|
||||||
if len(t["text_help_bgp_community"]) == 0:
|
|
||||||
return 'Performs BGP table lookup based on <a href="https://tools.ietf.org/html/rfc4360">Extended</a> or <a href="https://tools.ietf.org/html/rfc8195">Large</a> community value.'
|
|
||||||
else:
|
|
||||||
return t["text_help_bgp_community"]
|
|
||||||
|
|
||||||
def text_help_bgp_aspath():
|
def __init__(self, device):
|
||||||
list = []
|
d = devices["router"][device]
|
||||||
for t in branding:
|
self.address = d.get("address")
|
||||||
if len(t["text_help_bgp_aspath"]) == 0:
|
self.asn = d.get("asn")
|
||||||
return 'Performs BGP table lookup based on <code>AS_PATH</code> regular expression.<br>For commonly used BGP regular expressions, <a href="https://hyperglass.readthedocs.io/en/latest/Extras/common_as_path_regex/">click here</a>.'
|
self.src_addr_ipv4 = d.get("src_addr_ipv4")
|
||||||
else:
|
self.src_addr_ipv6 = d.get("src_addr_ipv6")
|
||||||
return t["text_help_bgp_aspath"]
|
self.credential = d.get("credential")
|
||||||
|
self.location = d.get("location")
|
||||||
|
self.name = d.get("name")
|
||||||
|
self.display_name = d.get("display_name")
|
||||||
|
self.port = d.get("port")
|
||||||
|
self.type = d.get("type")
|
||||||
|
self.proxy = d.get("proxy")
|
||||||
|
|
||||||
def text_help_ping():
|
def __call__(self):
|
||||||
list = []
|
return vars(self)
|
||||||
for t in branding:
|
|
||||||
if len(t["text_help_ping"]) == 0:
|
|
||||||
return "Sends 5 ICMP echo requests to the target."
|
|
||||||
else:
|
|
||||||
return t["text_help_ping"]
|
|
||||||
|
|
||||||
def text_help_traceroute():
|
|
||||||
list = []
|
class proxy:
|
||||||
for t in branding:
|
def __init__(self, proxy):
|
||||||
if len(t["text_help_traceroute"]) == 0:
|
self.address = proxies_list[proxy]["address"]
|
||||||
return 'Performs UDP Based traceroute to the target.<br>For information about how to interpret traceroute results, <a href="https://www.nanog.org/meetings/nanog45/presentations/Sunday/RAS_traceroute_N45.pdf">click here</a>.'
|
self.username = proxies_list[proxy]["username"]
|
||||||
else:
|
self.password = proxies_list[proxy]["password"]
|
||||||
return t["text_help_traceroute"]
|
self.type = proxies_list[proxy]["type"]
|
||||||
|
self.ssh_command = proxies_list[proxy]["ssh_command"]
|
||||||
|
|
||||||
|
|
||||||
|
class general:
|
||||||
|
"""Class to define and export config variables and export default values if undefined"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
g = configuration["general"][0]
|
||||||
|
self.primary_asn = g.get("primary_asn", "65000")
|
||||||
|
self.org_name = g.get("org_name", "The Company")
|
||||||
|
self.debug = g.get("debug", False)
|
||||||
|
self.google_analytics = g.get("google_analytics", "")
|
||||||
|
self.message_error = g.get("message_error", "{input} is invalid.")
|
||||||
|
self.message_blacklist = g.get("message_blacklist", "{input} is not allowed.")
|
||||||
|
self.message_general_error = g.get(
|
||||||
|
"message_general_error", "Error connecting to device."
|
||||||
|
)
|
||||||
|
self.rate_limit_query = g.get("rate_limit_query", "5")
|
||||||
|
self.message_rate_limit_query = g.get(
|
||||||
|
"message_rate_limit_query",
|
||||||
|
f"Query limit of {self.rate_limit_query} per minute reached. Please wait one minute and try again.",
|
||||||
|
)
|
||||||
|
self.enable_bgp_route = g.get("enable_bgp_route", True)
|
||||||
|
self.enable_bgp_community = g.get("enable_bgp_community", True)
|
||||||
|
self.enable_bgp_aspath = g.get("enable_bgp_aspath", True)
|
||||||
|
self.enable_ping = g.get("enable_ping", True)
|
||||||
|
self.enable_traceroute = g.get("enable_traceroute", True)
|
||||||
|
self.rate_limit_site = g.get("rate_limit_site", "120")
|
||||||
|
self.cache_timeout = g.get("cache_timeout", 120)
|
||||||
|
self.cache_directory = g.get(
|
||||||
|
"cache_directory", os.path.join(hyperglass_root, ".flask_cache")
|
||||||
|
)
|
||||||
|
self.enable_max_prefix = g.get("enable_max_prefix", False)
|
||||||
|
self.max_prefix_length_ipv4 = g.get("max_prefix_length_ipv4", 24)
|
||||||
|
self.max_prefix_length_ipv6 = g.get("max_prefix_length_ipv6", 29)
|
||||||
|
|
||||||
|
|
||||||
|
class branding:
|
||||||
|
"""Class to define and export branding variables and export default values if undefined"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
b = configuration["branding"][0]
|
||||||
|
self.site_title = b.get("site_title", "hyperglass")
|
||||||
|
self.title = b.get("title", "hyperglass")
|
||||||
|
self.subtitle = b.get("subtitle", f"AS{general().primary_asn}")
|
||||||
|
self.title_mode = b.get("title_mode", "logo_only")
|
||||||
|
self.enable_footer = b.get("enable_footer", True)
|
||||||
|
self.enable_credit = b.get("enable_credit", True)
|
||||||
|
self.color_btn_submit = b.get("color_btn_submit", "#40798c")
|
||||||
|
|
||||||
|
self.color_tag_loctitle = b.get("color_tag_loctitle", "#330036")
|
||||||
|
self.color_tag_cmdtitle = b.get("color_tag_cmdtitle", "#330036")
|
||||||
|
self.color_tag_cmd = b.get("color_tag_cmd", "#ff5e5b")
|
||||||
|
self.color_tag_loc = b.get("color_tag_loc", "#40798c")
|
||||||
|
self.color_progressbar = b.get("color_progressbar", "#40798c")
|
||||||
|
self.color_bg = b.get("color_bg", "#fbfffe")
|
||||||
|
self.color_danger = b.get("color_danger", "#ff3860")
|
||||||
|
self.logo_path = b.get(
|
||||||
|
"logo_path",
|
||||||
|
os.path.join(hyperglass_root, "static/images/hyperglass-dark.png"),
|
||||||
|
)
|
||||||
|
self.favicon16_path = b.get(
|
||||||
|
"favicon16_path", "static/images/favicon/favicon-16x16.png"
|
||||||
|
)
|
||||||
|
self.favicon32_path = b.get(
|
||||||
|
"favicon32_path", "static/images/favicon/favicon-32x32.png"
|
||||||
|
)
|
||||||
|
self.logo_width = b.get("logo_width", "384")
|
||||||
|
self.placeholder_prefix = b.get(
|
||||||
|
"placeholder_prefix", "IP, Prefix, Community, or AS_PATH"
|
||||||
|
)
|
||||||
|
self.show_peeringdb = b.get("show_peeringdb", True)
|
||||||
|
self.text_results = b.get("text_results", "Results")
|
||||||
|
self.text_location = b.get("text_location", "Select Location...")
|
||||||
|
self.text_cache = b.get(
|
||||||
|
"text_cache",
|
||||||
|
f"Results will be cached for {math.ceil(general().cache_timeout / 60)} minutes.",
|
||||||
|
)
|
||||||
|
self.primary_font_name = b.get("primary_font_name", "Nunito")
|
||||||
|
self.primary_font_url = b.get(
|
||||||
|
"primary_font_url",
|
||||||
|
"https://fonts.googleapis.com/css?family=Nunito:400,600,700",
|
||||||
|
)
|
||||||
|
self.mono_font_name = b.get("mono_font_name", "Fira Mono")
|
||||||
|
self.mono_font_url = b.get(
|
||||||
|
"mono_font_url", "https://fonts.googleapis.com/css?family=Fira+Mono"
|
||||||
|
)
|
||||||
|
self.text_limiter_title = b.get("text_limiter_title", "Limit Reached")
|
||||||
|
self.text_limiter_subtitle = b.get(
|
||||||
|
"text_limiter_subtitle",
|
||||||
|
f"You have accessed this site more than {general().rate_limit_site} times in the last minute.",
|
||||||
|
)
|
||||||
|
self.text_415_title = b.get("text_415_title", "Error")
|
||||||
|
self.text_415_subtitle = b.get("text_415_subtitle", "Something went wrong.")
|
||||||
|
self.text_415_button = b.get("text_415_button", "Home")
|
||||||
|
self.text_help_bgp_route = b.get(
|
||||||
|
"text_help_bgp_route",
|
||||||
|
"Performs BGP table lookup based on IPv4/IPv6 prefix.",
|
||||||
|
)
|
||||||
|
self.text_help_bgp_community = b.get(
|
||||||
|
"text_help_bgp_community",
|
||||||
|
'Performs BGP table lookup based on <a href="https://tools.ietf.org/html/rfc4360">Extended</a> or <a href="https://tools.ietf.org/html/rfc8195">Large</a> community value.',
|
||||||
|
)
|
||||||
|
self.text_help_bgp_aspath = b.get(
|
||||||
|
"text_help_bgp_aspath",
|
||||||
|
'Performs BGP table lookup based on <code>AS_PATH</code> regular expression.<br>For commonly used BGP regular expressions, <a href="https://hyperglass.readthedocs.io/en/latest/Extras/common_as_path_regex/">click here</a>.',
|
||||||
|
)
|
||||||
|
self.text_help_ping = b.get(
|
||||||
|
"text_help_ping", "Sends 5 ICMP echo requests to the target."
|
||||||
|
)
|
||||||
|
self.text_help_traceroute = b.get(
|
||||||
|
"text_help_traceroute",
|
||||||
|
'Performs UDP Based traceroute to the target.<br>For information about how to interpret traceroute results, <a href="https://www.nanog.org/meetings/nanog45/presentations/Sunday/RAS_traceroute_N45.pdf">click here</a>.',
|
||||||
|
)
|
||||||
|
@@ -16,18 +16,13 @@ import hyperglass.configuration as configuration
|
|||||||
from hyperglass.command import execute
|
from hyperglass.command import execute
|
||||||
from hyperglass import render
|
from hyperglass import render
|
||||||
|
|
||||||
# Load TOML config file
|
|
||||||
devices = configuration.devices()
|
|
||||||
# Filter config file to list of routers & subsequent configurations
|
|
||||||
routers_list = devices["router"]
|
|
||||||
# Filter config file to array of operating systems that require IPv6 BGP lookups in CIDR format
|
|
||||||
ipv6_cidr_list = configuration.requires_ipv6_cidr()
|
|
||||||
# Main Flask definition
|
# Main Flask definition
|
||||||
app = Flask(__name__, static_url_path="/static")
|
app = Flask(__name__, static_url_path="/static")
|
||||||
|
|
||||||
|
general = configuration.general()
|
||||||
# Flask-Limiter Config
|
# Flask-Limiter Config
|
||||||
rate_limit_query = configuration.gen.rate_limit_query() + " per minute"
|
rate_limit_query = f"{general.rate_limit_query} per minute"
|
||||||
rate_limit_site = configuration.gen.rate_limit_site() + "per minute"
|
rate_limit_site = f"{general.rate_limit_site} per minute"
|
||||||
limiter = Limiter(app, key_func=get_remote_address, default_limits=[rate_limit_site])
|
limiter = Limiter(app, key_func=get_remote_address, default_limits=[rate_limit_site])
|
||||||
|
|
||||||
|
|
||||||
@@ -67,8 +62,8 @@ cache = Cache(
|
|||||||
app,
|
app,
|
||||||
config={
|
config={
|
||||||
"CACHE_TYPE": "filesystem",
|
"CACHE_TYPE": "filesystem",
|
||||||
"CACHE_DIR": configuration.gen.cache_directory(),
|
"CACHE_DIR": general.cache_directory,
|
||||||
"CACHE_DEFAULT_TIMEOUT": configuration.gen.cache_timeout(),
|
"CACHE_DEFAULT_TIMEOUT": general.cache_timeout,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,32 +96,9 @@ def testRoute():
|
|||||||
# Flask GET route provides a JSON list of all routers for the selected network/ASN
|
# Flask GET route provides a JSON list of all routers for the selected network/ASN
|
||||||
@app.route("/routers/<asn>", methods=["GET"])
|
@app.route("/routers/<asn>", methods=["GET"])
|
||||||
def get_routers(asn):
|
def get_routers(asn):
|
||||||
results = []
|
nl = configuration.networks_list()
|
||||||
# For any configured router matching the queried ASN, return only the address/hostname, location, and OS type of the matching routers
|
nl_json = json.dumps(nl[asn])
|
||||||
for r in routers_list:
|
return nl_json
|
||||||
if r["asn"] == asn:
|
|
||||||
if r["type"] in ipv6_cidr_list:
|
|
||||||
results.append(
|
|
||||||
dict(
|
|
||||||
location=r["location"],
|
|
||||||
hostname=r["name"],
|
|
||||||
display_name=r["display_name"],
|
|
||||||
type=r["type"],
|
|
||||||
requiresIP6Cidr=True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
results.append(
|
|
||||||
dict(
|
|
||||||
location=r["location"],
|
|
||||||
hostname=r["name"],
|
|
||||||
display_name=r["display_name"],
|
|
||||||
type=r["type"],
|
|
||||||
requiresIP6Cidr=False,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
results_json = json.dumps(results)
|
|
||||||
return results_json
|
|
||||||
|
|
||||||
|
|
||||||
# Flask POST route ingests data from the JS form submit, passes it to the backend looking glass application to perform the filtering/lookups
|
# Flask POST route ingests data from the JS form submit, passes it to the backend looking glass application to perform the filtering/lookups
|
||||||
@@ -140,25 +112,32 @@ def lg():
|
|||||||
cache_key = str(lg_data)
|
cache_key = str(lg_data)
|
||||||
# Check if cached entry exists
|
# Check if cached entry exists
|
||||||
if cache.get(cache_key) is None:
|
if cache.get(cache_key) is None:
|
||||||
cache_value = execute.execute(lg_data)
|
|
||||||
value_output = cache_value[0]
|
|
||||||
value_code = cache_value[1]
|
|
||||||
value_entry = cache_value[0:2]
|
|
||||||
value_params = cache_value[2:]
|
|
||||||
logger.info(f"No cache match for: {cache_key}")
|
|
||||||
# If it doesn't, create a cache entry
|
|
||||||
try:
|
try:
|
||||||
|
cache_value = execute.execute(lg_data)
|
||||||
|
value_output = cache_value[0]
|
||||||
|
value_code = cache_value[1]
|
||||||
|
value_entry = cache_value[0:2]
|
||||||
|
value_params = cache_value[2:]
|
||||||
|
logger.info(f"No cache match for: {cache_key}")
|
||||||
|
# If it doesn't, create a cache entry
|
||||||
cache.set(cache_key, value_entry)
|
cache.set(cache_key, value_entry)
|
||||||
logger.info(f"Added cache entry: {value_params}")
|
logger.info(f"Added cache entry: {value_params}")
|
||||||
except:
|
except:
|
||||||
raise RuntimeError("Unable to add output to cache.", 415, *value_params)
|
logger.error(f"Unable to add output to cache: {cache_key}")
|
||||||
|
raise
|
||||||
# If 200, return output
|
# If 200, return output
|
||||||
response = cache.get(cache_key)
|
response = cache.get(cache_key)
|
||||||
if value_code == 200:
|
if value_code == 200:
|
||||||
return Response(response[0], response[1])
|
try:
|
||||||
|
return Response(response[0], response[1])
|
||||||
|
except:
|
||||||
|
raise
|
||||||
# If 400 error, return error message and code
|
# If 400 error, return error message and code
|
||||||
elif value_code in [405, 415]:
|
elif value_code in [405, 415]:
|
||||||
return Response(response[0], response[1])
|
try:
|
||||||
|
return Response(response[0], response[1])
|
||||||
|
except:
|
||||||
|
raise
|
||||||
# If it does, return the cached entry
|
# If it does, return the cached entry
|
||||||
else:
|
else:
|
||||||
logger.info(f"Cache match for: {cache_key}, returning cached entry...")
|
logger.info(f"Cache match for: {cache_key}, returning cached entry...")
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import sass
|
import sass
|
||||||
import codecs
|
|
||||||
import jinja2
|
import jinja2
|
||||||
import subprocess
|
import subprocess
|
||||||
from logzero import logger
|
from logzero import logger
|
||||||
@@ -16,19 +15,23 @@ hyperglass_root = os.path.dirname(hyperglass.__file__)
|
|||||||
file_loader = jinja2.FileSystemLoader(dir)
|
file_loader = jinja2.FileSystemLoader(dir)
|
||||||
env = jinja2.Environment(loader=file_loader)
|
env = jinja2.Environment(loader=file_loader)
|
||||||
|
|
||||||
# Converts templates/footer.md from Markdown to HTML
|
branding = configuration.branding()
|
||||||
md = Markdown()
|
general = configuration.general()
|
||||||
footer_template = env.get_template("templates/footer.md")
|
networks = configuration.networks()
|
||||||
footer_jinja = footer_template.render(
|
|
||||||
site_title=configuration.brand.site_title(), org_name=configuration.gen.org_name()
|
|
||||||
)
|
|
||||||
footer = footer_jinja
|
|
||||||
|
|
||||||
# Functions for rendering Jinja2 templates & importing variables
|
# Functions for rendering Jinja2 templates & importing variables
|
||||||
|
|
||||||
|
|
||||||
class html:
|
class html:
|
||||||
def renderTemplate(t):
|
def renderTemplate(t):
|
||||||
|
|
||||||
|
# Converts templates/footer.md from Markdown to HTML
|
||||||
|
md = Markdown()
|
||||||
|
footer_template = env.get_template("templates/footer.md")
|
||||||
|
footer_jinja = footer_template.render(
|
||||||
|
site_title=branding.site_title, org_name=general.org_name
|
||||||
|
)
|
||||||
if t == "index":
|
if t == "index":
|
||||||
template = env.get_template("templates/index.html")
|
template = env.get_template("templates/index.html")
|
||||||
elif t == "429":
|
elif t == "429":
|
||||||
@@ -39,56 +42,53 @@ class html:
|
|||||||
template = env.get_template("templates/429.html")
|
template = env.get_template("templates/429.html")
|
||||||
return template.render(
|
return template.render(
|
||||||
# General
|
# General
|
||||||
primary_asn=configuration.gen.primary_asn(),
|
primary_asn=general.primary_asn,
|
||||||
org_name=configuration.gen.org_name(),
|
org_name=general.org_name,
|
||||||
google_analytics=configuration.gen.google_analytics(),
|
google_analytics=general.google_analytics,
|
||||||
enable_recaptcha=configuration.gen.enable_recaptcha(),
|
enable_bgp_route=general.enable_bgp_route,
|
||||||
enable_bgp_route=configuration.gen.enable_bgp_route(),
|
enable_bgp_community=general.enable_bgp_community,
|
||||||
enable_bgp_community=configuration.gen.enable_bgp_community(),
|
enable_bgp_aspath=general.enable_bgp_aspath,
|
||||||
enable_bgp_aspath=configuration.gen.enable_bgp_aspath(),
|
enable_ping=general.enable_ping,
|
||||||
enable_ping=configuration.gen.enable_ping(),
|
enable_traceroute=general.enable_traceroute,
|
||||||
enable_traceroute=configuration.gen.enable_traceroute(),
|
cache_timeout=general.cache_timeout,
|
||||||
cache_timeout=configuration.gen.cache_timeout(),
|
message_rate_limit_query=general.message_rate_limit_query,
|
||||||
message_rate_limit_query=configuration.gen.message_rate_limit_query(),
|
|
||||||
# Branding
|
# Branding
|
||||||
site_title=configuration.brand.site_title(),
|
site_title=branding.site_title,
|
||||||
title=configuration.brand.title(),
|
title=branding.title,
|
||||||
subtitle=configuration.brand.subtitle(),
|
subtitle=branding.subtitle,
|
||||||
title_mode=configuration.brand.title_mode(),
|
title_mode=branding.title_mode,
|
||||||
color_bg=configuration.brand.color_bg(),
|
color_bg=branding.color_bg,
|
||||||
color_danger=configuration.brand.color_danger(),
|
color_danger=branding.color_danger,
|
||||||
color_btn_submit=configuration.brand.color_btn_submit(),
|
color_btn_submit=branding.color_btn_submit,
|
||||||
color_progressbar=configuration.brand.color_progressbar(),
|
color_progressbar=branding.color_progressbar,
|
||||||
color_tag_loctitle=configuration.brand.color_tag_loctitle(),
|
color_tag_loctitle=branding.color_tag_loctitle,
|
||||||
color_tag_cmdtitle=configuration.brand.color_tag_cmdtitle(),
|
color_tag_cmdtitle=branding.color_tag_cmdtitle,
|
||||||
color_tag_cmd=configuration.brand.color_tag_cmd(),
|
color_tag_cmd=branding.color_tag_cmd,
|
||||||
color_tag_loc=configuration.brand.color_tag_loc(),
|
color_tag_loc=branding.color_tag_loc,
|
||||||
enable_credit=configuration.brand.enable_credit(),
|
enable_credit=branding.enable_credit,
|
||||||
enable_footer=configuration.brand.enable_footer(),
|
enable_footer=branding.enable_footer,
|
||||||
footer_content=md.convert(footer),
|
footer_content=md.convert(footer_jinja),
|
||||||
logo_path=configuration.brand.logo_path(),
|
logo_path=branding.logo_path,
|
||||||
logo_width=configuration.brand.logo_width(),
|
logo_width=branding.logo_width,
|
||||||
favicon16_path=configuration.brand.favicon16_path(),
|
favicon16_path=branding.favicon16_path,
|
||||||
favicon32_path=configuration.brand.favicon32_path(),
|
favicon32_path=branding.favicon32_path,
|
||||||
placeholder_prefix=configuration.brand.placeholder_prefix(),
|
placeholder_prefix=branding.placeholder_prefix,
|
||||||
show_peeringdb=configuration.brand.show_peeringdb(),
|
show_peeringdb=branding.show_peeringdb,
|
||||||
text_results=configuration.brand.text_results(),
|
text_results=branding.text_results,
|
||||||
text_location=configuration.brand.text_location(),
|
text_location=branding.text_location,
|
||||||
text_cache=configuration.brand.text_cache(),
|
text_cache=branding.text_cache,
|
||||||
text_415_title=configuration.brand.text_415_title(),
|
text_415_title=branding.text_415_title,
|
||||||
text_415_subtitle=configuration.brand.text_415_subtitle(),
|
text_415_subtitle=branding.text_415_subtitle,
|
||||||
text_415_button=configuration.brand.text_415_button(),
|
text_415_button=branding.text_415_button,
|
||||||
text_help_bgp_route=configuration.brand.text_help_bgp_route(),
|
text_help_bgp_route=branding.text_help_bgp_route,
|
||||||
text_help_bgp_community=configuration.brand.text_help_bgp_community(),
|
text_help_bgp_community=branding.text_help_bgp_community,
|
||||||
text_help_bgp_aspath=configuration.brand.text_help_bgp_aspath(),
|
text_help_bgp_aspath=branding.text_help_bgp_aspath,
|
||||||
text_help_ping=configuration.brand.text_help_ping(),
|
text_help_ping=branding.text_help_ping,
|
||||||
text_help_traceroute=configuration.brand.text_help_traceroute(),
|
text_help_traceroute=branding.text_help_traceroute,
|
||||||
text_limiter_title=configuration.brand.text_limiter_title(),
|
text_limiter_title=branding.text_limiter_title,
|
||||||
text_limiter_subtitle=configuration.brand.text_limiter_subtitle(),
|
text_limiter_subtitle=branding.text_limiter_subtitle,
|
||||||
# Devices
|
# Devices
|
||||||
device_networks=configuration.dev.networks(),
|
device_networks=configuration.networks(),
|
||||||
# device_location=configuration.dev.location(),
|
|
||||||
device_name=configuration.dev.name(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -99,18 +99,18 @@ class css:
|
|||||||
try:
|
try:
|
||||||
template = env.get_template("templates/hyperglass.scss")
|
template = env.get_template("templates/hyperglass.scss")
|
||||||
rendered_output = template.render(
|
rendered_output = template.render(
|
||||||
color_btn_submit=configuration.brand.color_btn_submit(),
|
color_btn_submit=branding.color_btn_submit,
|
||||||
color_progressbar=configuration.brand.color_progressbar(),
|
color_progressbar=branding.color_progressbar,
|
||||||
color_tag_loctitle=configuration.brand.color_tag_loctitle(),
|
color_tag_loctitle=branding.color_tag_loctitle,
|
||||||
color_tag_cmdtitle=configuration.brand.color_tag_cmdtitle(),
|
color_tag_cmdtitle=branding.color_tag_cmdtitle,
|
||||||
color_tag_cmd=configuration.brand.color_tag_cmd(),
|
color_tag_cmd=branding.color_tag_cmd,
|
||||||
color_tag_loc=configuration.brand.color_tag_loc(),
|
color_tag_loc=branding.color_tag_loc,
|
||||||
color_bg=configuration.brand.color_bg(),
|
color_bg=branding.color_bg,
|
||||||
color_danger=configuration.brand.color_danger(),
|
color_danger=branding.color_danger,
|
||||||
primary_font_url=configuration.brand.primary_font_url(),
|
primary_font_url=branding.primary_font_url,
|
||||||
primary_font_name=configuration.brand.primary_font_name(),
|
primary_font_name=branding.primary_font_name,
|
||||||
mono_font_url=configuration.brand.mono_font_url(),
|
mono_font_url=branding.mono_font_url,
|
||||||
mono_font_name=configuration.brand.mono_font_name(),
|
mono_font_name=branding.mono_font_name,
|
||||||
)
|
)
|
||||||
with open(scss_file, "w") as scss_output:
|
with open(scss_file, "w") as scss_output:
|
||||||
scss_output.write(rendered_output)
|
scss_output.write(rendered_output)
|
||||||
|
@@ -66,7 +66,7 @@ $('#network').on('change', () => {
|
|||||||
|
|
||||||
function updateRouters(routers) {
|
function updateRouters(routers) {
|
||||||
routers.forEach(function(r) {
|
routers.forEach(function(r) {
|
||||||
$('#router').append($("<option>").attr('value', r.location).attr('type', r.type).text(r.display_name))
|
$('#router').append($("<option>").attr('value', r.location).text(r.display_name))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +83,13 @@ $('#lgForm').on('submit', function() {
|
|||||||
var ipv6_cidr = new RegExp('^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/((1(1[0-9]|2[0-8]))|([0-9][0-9])|([0-9]))?$');
|
var ipv6_cidr = new RegExp('^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/((1(1[0-9]|2[0-8]))|([0-9][0-9])|([0-9]))?$');
|
||||||
var ipv6_host = new RegExp('^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))?$')
|
var ipv6_host = new RegExp('^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))?$')
|
||||||
var cmd = $('#cmd option:selected').val();
|
var cmd = $('#cmd option:selected').val();
|
||||||
var routerType = $('#router option:selected').attr('type');
|
// var routerType = $('#router option:selected').attr('type');
|
||||||
var ipprefix = $('#ipprefix').val();
|
var ipprefix = $('#ipprefix').val();
|
||||||
var router = $('#router option:selected').val();
|
var router = $('#router option:selected').val();
|
||||||
// Filters selectedRouters JSON object to only the selected router, returns all attributes passed from Flask's `get_routers`
|
// Filters selectedRouters JSON object to only the selected router, returns all attributes passed from Flask's `get_routers`
|
||||||
var routersJson = selectedRouters.filter(r => r.location === router);
|
var routersJson = selectedRouters.filter(r => r.location === router);
|
||||||
// Filters above to value of `requiresIP6Cidr` as passed from Flask's `get_routers`
|
// Filters above to value of `requiresIP6Cidr` as passed from Flask's `get_routers`
|
||||||
var requiresIP6Cidr = routersJson[0].requiresIP6Cidr
|
var requiresIP6Cidr = routersJson[0].requires_ipv6_cidr
|
||||||
|
|
||||||
// If BGP lookup, and lookup is an IPv6 address *without* CIDR prefix (e.g. 2001:db8::1, NOT 2001:db8::/48), and requiresIP6Cidr
|
// If BGP lookup, and lookup is an IPv6 address *without* CIDR prefix (e.g. 2001:db8::1, NOT 2001:db8::/48), and requiresIP6Cidr
|
||||||
// is true, show an error.
|
// is true, show an error.
|
||||||
@@ -185,7 +185,7 @@ var submitForm = function() {
|
|||||||
var router = $('#router option:selected').val();
|
var router = $('#router option:selected').val();
|
||||||
var routername = $('#router option:selected').text();
|
var routername = $('#router option:selected').text();
|
||||||
var ipprefix = $('#ipprefix').val();
|
var ipprefix = $('#ipprefix').val();
|
||||||
var routerType = $('#router option:selected').attr('type');
|
// var routerType = $('#router option:selected').attr('type');
|
||||||
|
|
||||||
$('#output').text("")
|
$('#output').text("")
|
||||||
$('#queryInfo').text("")
|
$('#queryInfo').text("")
|
||||||
@@ -226,14 +226,38 @@ var submitForm = function() {
|
|||||||
$('#output').html(`<br><div class="content"><p class="query-output" id="output">${response}</p></div>`);
|
$('#output').html(`<br><div class="content"><p class="query-output" id="output">${response}</p></div>`);
|
||||||
},
|
},
|
||||||
405: function(response, code) {
|
405: function(response, code) {
|
||||||
|
resultsbox.hide()
|
||||||
progress.hide();
|
progress.hide();
|
||||||
|
$('#ipprefix_error').show()
|
||||||
$('#ipprefix').addClass('is-warning');
|
$('#ipprefix').addClass('is-warning');
|
||||||
$('#output').html(`<br><div class="notification is-warning" id="output">${response.responseText}</div>`);
|
$('#ipprefix_error').html(`
|
||||||
|
<br>
|
||||||
|
<article class="message is-warning is-small" style="display: block;">
|
||||||
|
<div class="message-header" style="display: block;">
|
||||||
|
Input Not Allowed
|
||||||
|
</div>
|
||||||
|
<div id="error" style="display: block;" class="message-body">
|
||||||
|
${response.responseText}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
`);
|
||||||
},
|
},
|
||||||
415: function(response, code) {
|
415: function(response, code) {
|
||||||
|
resultsbox.hide()
|
||||||
progress.hide();
|
progress.hide();
|
||||||
|
$('#ipprefix_error').show()
|
||||||
$('#ipprefix').addClass('is-danger');
|
$('#ipprefix').addClass('is-danger');
|
||||||
$('#output').html(`<br><div class="notification is-danger" id="output">${response.responseText}</div>`);
|
$('#ipprefix_error').html(`
|
||||||
|
<br>
|
||||||
|
<article class="message is-danger is-small" style="display: block;">
|
||||||
|
<div class="message-header" style="display: block;">
|
||||||
|
Invalid Input
|
||||||
|
</div>
|
||||||
|
<div id="error" style="display: block;" class="message-body">
|
||||||
|
${response.responseText}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
`);
|
||||||
},
|
},
|
||||||
429: function(response, code) {
|
429: function(response, code) {
|
||||||
progress.hide();
|
progress.hide();
|
||||||
|
Reference in New Issue
Block a user