1
0
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:
checktheroads
2019-05-28 12:19:40 -07:00
parent 0174e58692
commit f3427ba9a8
7 changed files with 53 additions and 21 deletions

View File

@@ -1 +1 @@
#!/usr/bin/env python3 name = "hyperglass"

View File

@@ -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()

View File

@@ -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:

View File

@@ -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:

View File

@@ -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]

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# Module Imports # Module Imports
import os import os
import sys import sys

View File

@@ -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: