mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
added a bunch of docstrings and comments for things
This commit is contained in:
@@ -1 +1 @@
|
|||||||
#!/usr/bin/env python3
|
name = "hyperglass"
|
||||||
|
@@ -9,7 +9,7 @@ from netaddr import IPNetwork, IPAddress, IPSet
|
|||||||
# Project Imports
|
# Project Imports
|
||||||
from hyperglass import configuration
|
from hyperglass import configuration
|
||||||
|
|
||||||
|
# Configuration Imports
|
||||||
code = configuration.codes()
|
code = configuration.codes()
|
||||||
g = configuration.general()
|
g = configuration.general()
|
||||||
|
|
||||||
|
@@ -16,6 +16,10 @@ from hyperglass.command import construct
|
|||||||
|
|
||||||
|
|
||||||
class ipcheck:
|
class ipcheck:
|
||||||
|
"""Checks input IPv4 or IPv6 address against host & CIDR regex patters,
|
||||||
|
returns dictionary of discovered attributes. Used for input validation in
|
||||||
|
command.execute module."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ipv4_host = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)?$"
|
self.ipv4_host = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)?$"
|
||||||
self.ipv4_cidr = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(3[0-2]|2[0-9]|1[0-9]|[0-9])?$"
|
self.ipv4_cidr = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(3[0-2]|2[0-9]|1[0-9]|[0-9])?$"
|
||||||
@@ -37,7 +41,7 @@ class ipcheck:
|
|||||||
|
|
||||||
|
|
||||||
class params:
|
class params:
|
||||||
"""Sends input parameters to construct module for use by execution functions"""
|
"""Sends input parameters to command.construct module for use by execution functions"""
|
||||||
|
|
||||||
class http:
|
class http:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -58,6 +62,7 @@ class params:
|
|||||||
return vars(self)
|
return vars(self)
|
||||||
|
|
||||||
def nm_host(self):
|
def nm_host(self):
|
||||||
|
"""Defines netmiko end-host dictionary"""
|
||||||
c = configuration.credential(d.credential)
|
c = configuration.credential(d.credential)
|
||||||
attr = {
|
attr = {
|
||||||
"host": self.router,
|
"host": self.router,
|
||||||
@@ -69,6 +74,7 @@ class params:
|
|||||||
return attr
|
return attr
|
||||||
|
|
||||||
def nm_proxy(self):
|
def nm_proxy(self):
|
||||||
|
"""Defines netmiko SSH proxy dictionary"""
|
||||||
p = configuration.proxy(d.proxy)
|
p = configuration.proxy(d.proxy)
|
||||||
attr = {
|
attr = {
|
||||||
"host": p.address,
|
"host": p.address,
|
||||||
@@ -81,6 +87,8 @@ class params:
|
|||||||
|
|
||||||
|
|
||||||
class connect:
|
class connect:
|
||||||
|
"""Performs the actual connection to the end device"""
|
||||||
|
|
||||||
class restapi:
|
class restapi:
|
||||||
def frr():
|
def frr():
|
||||||
"""Sends HTTP POST to router running the hyperglass-frr API"""
|
"""Sends HTTP POST to router running the hyperglass-frr API"""
|
||||||
@@ -200,16 +208,18 @@ def execute(lg_data):
|
|||||||
# Checks if device type is on the requires_ipv6_cidr list
|
# Checks if device type is on the requires_ipv6_cidr list
|
||||||
requires_ipv6_cidr = configuration.requires_ipv6_cidr(d.type)
|
requires_ipv6_cidr = configuration.requires_ipv6_cidr(d.type)
|
||||||
|
|
||||||
# Check blacklist list 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"]:
|
||||||
blacklist = IPSet(configuration.blacklist())
|
blacklist = IPSet(configuration.blacklist())
|
||||||
msg = general.msg_error_notallowed.format(i=lg_ipprefix)
|
msg = general.msg_error_notallowed.format(i=lg_ipprefix)
|
||||||
|
# Check blacklist list for prefixes/IPs, return error upon a match
|
||||||
if IPNetwork(lg_ipprefix).ip in blacklist:
|
if IPNetwork(lg_ipprefix).ip in blacklist:
|
||||||
return (msg, code.warning, lg_data)
|
return (msg, code.warning, lg_data)
|
||||||
|
# Check if device requires IPv6 queries to be in CIDR format, return error if True
|
||||||
if lg_cmd == "bgp_route" and IPNetwork(lg_ipprefix).version == 6:
|
if lg_cmd == "bgp_route" and IPNetwork(lg_ipprefix).version == 6:
|
||||||
if requires_ipv6_cidr == True and ipc["type"] == "host":
|
if requires_ipv6_cidr == True and ipc["type"] == "host":
|
||||||
msg = general.msg_error_ipv6cidr.format(d=d.display_name)
|
msg = general.msg_error_ipv6cidr.format(d=d.display_name)
|
||||||
return (msg, code.warning, lg_data)
|
return (msg, code.warning, lg_data)
|
||||||
|
# Check if input prefix is in CIDR format, and if command is ping/traceroute, return error if True
|
||||||
if lg_cmd in ["ping", "traceroute"] and ipc["type"] == "cidr":
|
if lg_cmd in ["ping", "traceroute"] and ipc["type"] == "cidr":
|
||||||
return (msg, code.warning, lg_data)
|
return (msg, code.warning, lg_data)
|
||||||
|
|
||||||
@@ -231,7 +241,7 @@ def execute(lg_data):
|
|||||||
m=general.max_prefix_length_ipv6, i=IPNetwork(lg_ipprefix)
|
m=general.max_prefix_length_ipv6, i=IPNetwork(lg_ipprefix)
|
||||||
)
|
)
|
||||||
return (msg, code.warning, lg_data)
|
return (msg, code.warning, lg_data)
|
||||||
|
# Sends validated data to target execution library and returns output
|
||||||
if d.type == "frr":
|
if d.type == "frr":
|
||||||
http = params().http()
|
http = params().http()
|
||||||
try:
|
try:
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
def parse(output, type, cmd):
|
def parse(output, type, cmd):
|
||||||
"""Splits Cisco IOS BGP output by AFI, returns only IPv4 & IPv6 output for protocol-agnostic commands (Community & AS_PATH Lookups)"""
|
"""Splits Cisco IOS BGP output by AFI, returns only IPv4 & IPv6 output for protocol-agnostic commands (Community & AS_PATH Lookups)"""
|
||||||
try:
|
try:
|
||||||
|
@@ -9,17 +9,21 @@ import hyperglass
|
|||||||
# Project Directories
|
# 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
|
# TOML Imports
|
||||||
configuration = toml.load(os.path.join(dir, "configuration.toml"))
|
configuration = toml.load(os.path.join(dir, "configuration.toml"))
|
||||||
devices = toml.load(os.path.join(dir, "devices.toml"))
|
devices = toml.load(os.path.join(dir, "devices.toml"))
|
||||||
|
|
||||||
|
|
||||||
def blacklist():
|
def blacklist():
|
||||||
|
"""Returns list of subnets/IPs defined in blacklist.toml"""
|
||||||
b = toml.load(os.path.join(dir, "blacklist.toml"))
|
b = toml.load(os.path.join(dir, "blacklist.toml"))
|
||||||
return b["blacklist"]
|
return b["blacklist"]
|
||||||
|
|
||||||
|
|
||||||
def requires_ipv6_cidr(nos):
|
def requires_ipv6_cidr(nos):
|
||||||
|
"""Returns boolean for input NOS association with the NOS list defined
|
||||||
|
in requires_ipv6_cidr.toml"""
|
||||||
r = toml.load(os.path.join(dir, "requires_ipv6_cidr.toml"))
|
r = toml.load(os.path.join(dir, "requires_ipv6_cidr.toml"))
|
||||||
nos_list = r["requires_ipv6_cidr"]
|
nos_list = r["requires_ipv6_cidr"]
|
||||||
if nos in nos_list:
|
if nos in nos_list:
|
||||||
@@ -30,7 +34,8 @@ def requires_ipv6_cidr(nos):
|
|||||||
|
|
||||||
def networks():
|
def networks():
|
||||||
"""Returns dictionary of ASNs as keys, list of associated locations as values.
|
"""Returns dictionary of ASNs as keys, list of associated locations as values.
|
||||||
Used for populating the /routers/<asn> Flask route."""
|
Imported as a Jinja2 variable on the main page that populates the network/ASN
|
||||||
|
select class."""
|
||||||
asn_dict = {}
|
asn_dict = {}
|
||||||
rl = devices["router"]
|
rl = devices["router"]
|
||||||
for r in rl.values():
|
for r in rl.values():
|
||||||
@@ -43,6 +48,11 @@ def networks():
|
|||||||
|
|
||||||
|
|
||||||
def networks_list():
|
def networks_list():
|
||||||
|
"""Returns a dictionary of ASNs as keys, list of associated locations,
|
||||||
|
router hostnames, and router display names as keys. Used by Flask to
|
||||||
|
populate the /routers/<asn> route, which is ingested by a JS Ajax call
|
||||||
|
to populate the list of locations associated with the selected network/ASN
|
||||||
|
on the main page."""
|
||||||
networks_dict = {}
|
networks_dict = {}
|
||||||
rl = devices["router"]
|
rl = devices["router"]
|
||||||
for r in rl.values():
|
for r in rl.values():
|
||||||
@@ -53,7 +63,6 @@ def networks_list():
|
|||||||
location=r["location"],
|
location=r["location"],
|
||||||
hostname=r["name"],
|
hostname=r["name"],
|
||||||
display_name=r["display_name"],
|
display_name=r["display_name"],
|
||||||
requires_ipv6_cidr=requires_ipv6_cidr(r["type"]),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -62,14 +71,13 @@ def networks_list():
|
|||||||
location=r["location"],
|
location=r["location"],
|
||||||
hostname=r["name"],
|
hostname=r["name"],
|
||||||
display_name=r["display_name"],
|
display_name=r["display_name"],
|
||||||
requires_ipv6_cidr=requires_ipv6_cidr(r["type"]),
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
return networks_dict
|
return networks_dict
|
||||||
|
|
||||||
|
|
||||||
class codes:
|
class codes:
|
||||||
"""Class for easy calling & recalling of http success/error codes"""
|
"""Reusable status code attributes"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# 200 OK: renders standard display text
|
# 200 OK: renders standard display text
|
||||||
@@ -81,6 +89,8 @@ class codes:
|
|||||||
|
|
||||||
|
|
||||||
class command:
|
class command:
|
||||||
|
"""Associates input NOS with matched commands defined in commands.toml"""
|
||||||
|
|
||||||
def __init__(self, nos):
|
def __init__(self, nos):
|
||||||
c = toml.load(os.path.join(dir, "commands.toml"))
|
c = toml.load(os.path.join(dir, "commands.toml"))
|
||||||
self.dual = c[nos][0]["dual"]
|
self.dual = c[nos][0]["dual"]
|
||||||
@@ -92,6 +102,9 @@ class command:
|
|||||||
|
|
||||||
|
|
||||||
class credential:
|
class credential:
|
||||||
|
"""Associates input credential key name with configured credential username &
|
||||||
|
password in devices.toml."""
|
||||||
|
|
||||||
def __init__(self, cred):
|
def __init__(self, cred):
|
||||||
c_list = devices["credential"]
|
c_list = devices["credential"]
|
||||||
self.username = c_list[cred]["username"]
|
self.username = c_list[cred]["username"]
|
||||||
@@ -102,7 +115,7 @@ class credential:
|
|||||||
|
|
||||||
|
|
||||||
class device:
|
class device:
|
||||||
"""Class to define & export all device variables"""
|
"""Associates input device key name with configured device attributes in devices.toml"""
|
||||||
|
|
||||||
def __init__(self, device):
|
def __init__(self, device):
|
||||||
d = devices["router"][device]
|
d = devices["router"][device]
|
||||||
@@ -123,6 +136,8 @@ class device:
|
|||||||
|
|
||||||
|
|
||||||
class proxy:
|
class proxy:
|
||||||
|
"""Associates input proxy key name with configured proxy attributes in devices.toml"""
|
||||||
|
|
||||||
def __init__(self, proxy):
|
def __init__(self, proxy):
|
||||||
p = devices["proxy"][proxy]
|
p = devices["proxy"][proxy]
|
||||||
self.address = p["address"]
|
self.address = p["address"]
|
||||||
@@ -133,7 +148,7 @@ class proxy:
|
|||||||
|
|
||||||
|
|
||||||
class general:
|
class general:
|
||||||
"""Class to define and export config variables and export default values if undefined"""
|
"""Exports general config variables and sets default values if undefined"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
g = configuration["general"][0]
|
g = configuration["general"][0]
|
||||||
@@ -183,7 +198,7 @@ class general:
|
|||||||
|
|
||||||
|
|
||||||
class branding:
|
class branding:
|
||||||
"""Class to define and export branding variables and export default values if undefined"""
|
"""Exports branding config variables and sets default values if undefined"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
b = configuration["branding"][0]
|
b = configuration["branding"][0]
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Module Imports
|
# Module Imports
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
# Module Imports
|
||||||
import os
|
import os
|
||||||
import sass
|
import sass
|
||||||
import jinja2
|
import jinja2
|
||||||
@@ -7,31 +7,35 @@ from logzero import logger
|
|||||||
from markdown2 import Markdown
|
from markdown2 import Markdown
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
|
||||||
|
# Project Imports
|
||||||
import hyperglass
|
import hyperglass
|
||||||
from hyperglass import configuration
|
from hyperglass import configuration
|
||||||
|
|
||||||
|
# Module 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__)
|
||||||
file_loader = jinja2.FileSystemLoader(dir)
|
file_loader = jinja2.FileSystemLoader(dir)
|
||||||
env = jinja2.Environment(loader=file_loader)
|
env = jinja2.Environment(loader=file_loader)
|
||||||
|
|
||||||
|
# Configuration Imports
|
||||||
branding = configuration.branding()
|
branding = configuration.branding()
|
||||||
general = configuration.general()
|
general = configuration.general()
|
||||||
networks = configuration.networks()
|
networks = configuration.networks()
|
||||||
|
|
||||||
|
|
||||||
# Functions for rendering Jinja2 templates & importing variables
|
|
||||||
|
|
||||||
|
|
||||||
class html:
|
class html:
|
||||||
def renderTemplate(t):
|
"""Performs HTML rendering actions"""
|
||||||
|
|
||||||
# Converts templates/footer.md from Markdown to HTML
|
def renderTemplate(t):
|
||||||
|
"""Renders Jinja2 HTML templates"""
|
||||||
|
|
||||||
|
# Convert templates/footer.md from Markdown to HTML
|
||||||
md = Markdown()
|
md = Markdown()
|
||||||
footer_template = env.get_template("templates/footer.md")
|
footer_template = env.get_template("templates/footer.md")
|
||||||
footer_jinja = footer_template.render(
|
footer_jinja = footer_template.render(
|
||||||
site_title=branding.site_title, org_name=general.org_name
|
site_title=branding.site_title, org_name=general.org_name
|
||||||
)
|
)
|
||||||
|
# Render template based on input template 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":
|
||||||
@@ -90,9 +94,14 @@ class html:
|
|||||||
|
|
||||||
|
|
||||||
class css:
|
class css:
|
||||||
|
"""Performs CSS/Sass rendering actions"""
|
||||||
|
|
||||||
def renderTemplate():
|
def renderTemplate():
|
||||||
|
"""Renders Jinja2 template to Sass file, then compiles Sass as CSS"""
|
||||||
|
|
||||||
scss_file = os.path.join(hyperglass_root, "static/sass/hyperglass.scss")
|
scss_file = os.path.join(hyperglass_root, "static/sass/hyperglass.scss")
|
||||||
css_file = os.path.join(hyperglass_root, "static/css/hyperglass.css")
|
css_file = os.path.join(hyperglass_root, "static/css/hyperglass.css")
|
||||||
|
# Renders Jinja2 template as Sass file
|
||||||
try:
|
try:
|
||||||
template = env.get_template("templates/hyperglass.scss")
|
template = env.get_template("templates/hyperglass.scss")
|
||||||
rendered_output = template.render(
|
rendered_output = template.render(
|
||||||
@@ -114,6 +123,7 @@ class css:
|
|||||||
except:
|
except:
|
||||||
logger.error("Error rendering Jinja2 template.")
|
logger.error("Error rendering Jinja2 template.")
|
||||||
raise TypeError("Error rendering Jinja2 template.")
|
raise TypeError("Error rendering Jinja2 template.")
|
||||||
|
# Compiles Sass to CSS
|
||||||
try:
|
try:
|
||||||
generated_sass = sass.compile(filename=scss_file)
|
generated_sass = sass.compile(filename=scss_file)
|
||||||
with open(css_file, "w") as css_output:
|
with open(css_file, "w") as css_output:
|
||||||
|
Reference in New Issue
Block a user