massive overhaul

This commit is contained in:
Matt Love 2019-07-29 22:13:11 -07:00
parent 4bc56895a9
commit 3f7988910d
31 changed files with 860 additions and 2817 deletions

View file

@ -198,7 +198,7 @@ class Execute:
def __init__(self, lg_data):
self.input_data = lg_data
self.input_location = self.input_data["location"]
self.input_type = self.input_data["type"]
self.input_type = self.input_data["query_type"]
self.input_target = self.input_data["target"]
def parse(self, raw_output, nos):

View file

@ -65,7 +65,7 @@ try:
devices = models.Routers.import_params(user_devices["router"])
credentials = models.Credentials.import_params(user_devices["credential"])
proxies = models.Proxies.import_params(user_devices["proxy"])
networks = models.Networks.import_params(user_devices["network"])
_networks = models.Networks.import_params(user_devices["network"])
except ValidationError as validation_errors:
errors = validation_errors.errors()
for error in errors:
@ -86,3 +86,105 @@ logzero_formatter = logzero.LogFormatter(fmt=log_format, datefmt=date_format)
logzero_config = logzero.setup_default_logger(
formatter=logzero_formatter, level=log_level
)
class Networks:
def __init__(self):
self.routers = devices.routers
self.networks = _networks.networks
def networks_verbose(self):
locations_dict = {}
for (router, router_params) in self.routers.items():
for (netname, net_params) in self.networks.items():
if router_params["network"] == netname:
net_display = net_params["display_name"]
if net_display in locations_dict:
locations_dict[net_display].append(
{
"location": router_params["location"],
"hostname": router,
"display_name": router_params["display_name"],
}
)
elif net_display not in locations_dict:
locations_dict[net_display] = [
{
"location": router_params["location"],
"hostname": router,
"display_name": router_params["display_name"],
}
]
if not locations_dict:
raise ConfigError("Unable to build network to device mapping")
return locations_dict
def networks_display(self):
locations_dict = {}
for (router, router_params) in devices.routers.items():
for (netname, net_params) in _networks.networks.items():
if router_params["network"] == netname:
net_display = net_params["display_name"]
if net_display in locations_dict:
locations_dict[net_display].append(
router_params["display_name"]
)
elif net_display not in locations_dict:
locations_dict[net_display] = [router_params["display_name"]]
if not locations_dict:
raise ConfigError("Unable to build network to device mapping")
return [
{"network_name": netname, "location_names": display_name}
for (netname, display_name) in locations_dict.items()
]
net = Networks()
networks = net.networks_verbose()
logger.debug(networks)
display_networks = net.networks_display()
# def networks():
# locations_dict = {}
# for (router, router_params) in devices.routers.items():
# for (netname, net_params) in _networks.networks.items():
# if router_params["network"] == netname:
# net_display = net_params["display_name"]
# if net_display in locations_dict:
# locations_dict[net_display].append(
# {
# "location": router_params["location"],
# "hostname": router,
# "display_name": router_params["display_name"],
# }
# )
# elif net_display not in locations_dict:
# locations_dict[net_display] = [
# {
# "location": router_params["location"],
# "hostname": router,
# "display_name": router_params["display_name"],
# }
# ]
# if not locations_dict:
# raise ConfigError("Unable to build network to device mapping")
# return locations_dict
# def display_networks():
# locations_dict = {}
# for (router, router_params) in devices.routers.items():
# for (netname, net_params) in _networks.networks.items():
# if router_params["network"] == netname:
# net_display = net_params["display_name"]
# if net_display in locations_dict:
# locations_dict[net_display].append(router_params["display_name"])
# elif net_display not in locations_dict:
# locations_dict[net_display] = [router_params["display_name"]]
# if not locations_dict:
# raise ConfigError("Unable to build network to device mapping")
# return locations_dict
# networks = networks()
# display_networks = display_networks()

View file

@ -44,7 +44,8 @@ class Router(BaseSettings):
"""Model for per-router config in devices.yaml."""
address: Union[IPvAnyAddress, str]
asn: int
# asn: int
network: str
src_addr_ipv4: IPv4Address
src_addr_ipv6: IPv6Address
credential: str
@ -70,6 +71,42 @@ class Router(BaseSettings):
class Routers(BaseSettings):
"""Base model for devices class."""
# @staticmethod
# def build_network_lists(valid_devices):
# """
# Builds locations dict, which is converted to JSON and passed to
# JavaScript to associate locations with the selected network/ASN.
# Builds networks dict, which is used to render the network/ASN
# select element contents.
# """
# locations_dict = {}
# networks_dict = {}
# for (dev, params) in valid_devices.items():
# asn = str(params["asn"])
# if asn in locations_dict:
# locations_dict[asn].append(
# {
# "location": params["location"],
# "hostname": dev,
# "display_name": params["display_name"],
# }
# )
# networks_dict[asn].append(params["location"])
# elif asn not in locations_dict:
# locations_dict[asn] = [
# {
# "location": params["location"],
# "hostname": dev,
# "display_name": params["display_name"],
# }
# ]
# networks_dict[asn] = [params["location"]]
# if not locations_dict:
# raise ConfigError('Unable to build locations list from "devices.yaml"')
# if not networks_dict:
# raise ConfigError('Unable to build networks list from "devices.yaml"')
# return (locations_dict, networks_dict)
@staticmethod
def build_network_lists(valid_devices):
"""
@ -79,33 +116,6 @@ class Routers(BaseSettings):
Builds networks dict, which is used to render the network/ASN
select element contents.
"""
locations_dict = {}
networks_dict = {}
for (dev, params) in valid_devices.items():
asn = str(params["asn"])
if asn in locations_dict:
locations_dict[asn].append(
{
"location": params["location"],
"hostname": dev,
"display_name": params["display_name"],
}
)
networks_dict[asn].append(params["location"])
elif asn not in locations_dict:
locations_dict[asn] = [
{
"location": params["location"],
"hostname": dev,
"display_name": params["display_name"],
}
]
networks_dict[asn] = [params["location"]]
if not locations_dict:
raise ConfigError('Unable to build locations list from "devices.yaml"')
if not networks_dict:
raise ConfigError('Unable to build networks list from "devices.yaml"')
return (locations_dict, networks_dict)
@classmethod
def import_params(cls, input_params):
@ -122,11 +132,11 @@ class Routers(BaseSettings):
setattr(Routers, dev, router_params)
routers.update({dev: router_params.dict()})
hostnames.append(dev)
locations_dict, networks_dict = Routers.build_network_lists(routers)
# locations_dict, networks_dict = Routers.build_network_lists(routers)
Routers.routers = routers
Routers.hostnames = hostnames
Routers.locations = locations_dict
Routers.networks = networks_dict
# Routers.locations = locations_dict
# Routers.networks = networks_dict
return Routers()
class Config:
@ -139,7 +149,6 @@ class Routers(BaseSettings):
class Network(BaseSettings):
"""Model for per-network/asn config in devices.yaml"""
asn: int
display_name: str
@ -154,9 +163,12 @@ class Networks(BaseSettings):
the credentials class.
"""
obj = Networks()
networks = {}
for (netname, params) in input_params.items():
netname = clean_name(netname)
setattr(Networks, netname, Network(**params))
networks.update({netname: Network(**params).dict()})
Networks.networks = networks
return obj
class Config:
@ -339,7 +351,8 @@ class Branding(BaseSettings):
"""Class model for 404 Error Page"""
title: str = "Error"
subtitle: str = "Page Not Found"
subtitle: str = "{uri} isn't a thing"
button: str = "Home"
class Error500(BaseSettings):
"""Class model for 500 Error Page"""

View file

@ -1,6 +1,7 @@
"""Hyperglass Front End"""
# Standard Library Imports
import os
import time
from ast import literal_eval
from pathlib import Path
@ -12,6 +13,7 @@ from prometheus_client import CollectorRegistry
from prometheus_client import Counter
from prometheus_client import generate_latest
from prometheus_client import multiprocess
from prometheus_client import CONTENT_TYPE_LATEST
from sanic import Sanic
from sanic import response
from sanic.exceptions import NotFound
@ -26,6 +28,7 @@ from hyperglass.command.execute import Execute
from hyperglass.configuration import devices
from hyperglass.configuration import logzero_config # noqa: F401
from hyperglass.configuration import params
from hyperglass.configuration import display_networks
from hyperglass.constants import Supported
from hyperglass.constants import code
from hyperglass.exceptions import HyperglassError
@ -76,13 +79,13 @@ limiter = Limiter(app, key_func=get_remote_address, global_limits=[rate_limit_si
# Prometheus Config
count_data = Counter(
"count_data", "Query Counter", ["source", "type", "loc_id", "target"]
"count_data", "Query Counter", ["source", "query_type", "loc_id", "target"]
)
count_errors = Counter(
"count_errors",
"Error Counter",
["code", "reason", "source", "type", "loc_id", "target"],
["code", "reason", "source", "query_type", "loc_id", "target"],
)
count_ratelimit = Counter(
@ -101,14 +104,20 @@ async def metrics(request):
registry = CollectorRegistry()
multiprocess.MultiProcessCollector(registry)
latest = generate_latest(registry)
return response.text(latest)
return response.text(
latest,
headers={
"Content-Type": CONTENT_TYPE_LATEST,
"Content-Length": str(len(latest)),
},
)
@app.exception(NotFound)
async def handle_404(request, exception):
"""Renders full error page for invalid URI"""
html = render.html("404")
path = request.path
html = render.html("404", uri=path)
client_addr = get_remote_address(request)
count_notfound.labels(exception, path, client_addr).inc()
logger.error(f"Error: {exception}, Path: {path}, Source: {client_addr}")
@ -118,7 +127,7 @@ async def handle_404(request, exception):
@app.exception(RateLimitExceeded)
async def handle_429(request, exception):
"""Renders full error page for too many site queries"""
html = render.html("429")
html = render.html("ratelimit-site")
client_addr = get_remote_address(request)
count_ratelimit.labels(exception, client_addr).inc()
logger.error(f"Error: {exception}, Source: {client_addr}")
@ -148,14 +157,14 @@ async def clear_cache():
@limiter.limit(rate_limit_site, error_message="Site")
async def site(request):
"""Main front-end web application"""
return response.html(render.html("index"))
return response.html(render.html("index", primary_asn=params.general.primary_asn))
@app.route("/test", methods=["GET"])
async def test_route(request):
"""Test route for various tests"""
html = render.html("500")
return response.html(html, status=500), 500
return response.html(html, status=500)
@app.route("/locations/<asn>", methods=["GET"])
@ -164,7 +173,17 @@ async def get_locations(request, asn):
GET route provides a JSON list of all locations for the selected
network/ASN.
"""
return response.json(devices.locations[asn])
# return response.json(devices.locations[asn])
return response.json([])
@app.route("/networks", methods=["GET"])
async def get_networks(request):
"""
GET route provides a JSON list of all locations for the selected
network/ASN.
"""
return response.json(display_networks)
@app.route("/lg", methods=["POST"])
@ -187,14 +206,14 @@ async def hyperglass_main(request):
logger.debug("No selection specified")
return response.html(params.messages.no_location, status=code.invalid)
# Return error if no query type is selected
if not Supported.is_supported_query(lg_data["type"]):
if not Supported.is_supported_query(lg_data["query_type"]):
logger.debug("No query specified")
return response.html(params.messages.no_query_type, status=code.invalid)
# Get client IP address for Prometheus logging & rate limiting
client_addr = get_remote_address(request)
# Increment Prometheus counter
count_data.labels(
client_addr, lg_data["type"], lg_data["location"], lg_data["target"]
client_addr, lg_data["query_type"], lg_data["location"], lg_data["target"]
).inc()
logger.debug(f"Client Address: {client_addr}")
@ -237,7 +256,7 @@ async def hyperglass_main(request):
response_status,
code.get_reason(response_status),
client_addr,
lg_data["type"],
lg_data["query_type"],
lg_data["location"],
lg_data["target"],
).inc()

View file

@ -14,7 +14,7 @@ from markdown2 import Markdown
# Project Imports
from hyperglass.configuration import devices
from hyperglass.configuration import logzero_config # noqa: F401
from hyperglass.configuration import params
from hyperglass.configuration import params, networks
from hyperglass.exceptions import HyperglassError
# Module Directories
@ -78,10 +78,11 @@ Performs BGP table lookup based on IPv4/IPv6 prefix.
template: bgp_community
link: <a href="#" id="helplink_bgpc">{{ general.org_name }} BGP Communities</a>
---
Performs BGP table lookup based on [Extended](https://tools.ietf.org/html/rfc4360) \
or [Large](https://tools.ietf.org/html/rfc8195) community value.
Performs BGP table lookup based on <a href="https://tools.ietf.org/html/rfc4360" target\
="_blank">Extended</a> or <a href="https://tools.ietf.org/html/rfc8195" target=\
"_blank">Large</a> community value.
{{ info["link"] | safe }}
<!-- {{ info["link"] | safe }} -->
""",
"bgp_aspath": """
---
@ -90,7 +91,7 @@ link: <a href="#" id="helplink_bgpa">Supported BGP AS Path Expressions</a>
---
Performs BGP table lookup based on `AS_PATH` regular expression.
{{ info["link"] | safe }}
<!-- {{ info["link"] | safe }} -->
""",
"ping": """
---
@ -103,8 +104,8 @@ Sends 5 ICMP echo requests to the target.
template: traceroute
---
Performs UDP Based traceroute to the target.<br>For information about how to \
interpret traceroute results, [click here]\
(https://hyperglass.readthedocs.io/en/latest/assets/traceroute_nanog.pdf).
interpret traceroute results, <a href="https://hyperglass.readthedocs.io/en/latest/ass\
ets/traceroute_nanog.pdf" target="_blank">click here</a>.
""",
}
@ -128,7 +129,7 @@ def generate_markdown(section, file_name):
else:
yaml_raw = defaults[file_name]
_, frontmatter, content = yaml_raw.split("---", 2)
html_classes = {"table": "table"}
html_classes = {"table": "ui compact table"}
markdown = Markdown(
extras={
"break-on-newline": True,
@ -165,7 +166,7 @@ def generate_markdown(section, file_name):
return help_dict
def html(template_name):
def html(template_name, **kwargs):
"""Renders Jinja2 HTML templates"""
details_name_list = ["footer", "bgp_aspath", "bgp_community"]
details_dict = {}
@ -181,7 +182,7 @@ def html(template_name):
template_file = f"templates/{template_name}.html.j2"
template = env.get_template(template_file)
return template.render(
params, info=info_dict, details=details_dict, networks=devices.networks
params, info=info_dict, details=details_dict, networks=networks, **kwargs
)
except jinja2.TemplateNotFound as template_error:
logger.error(

View file

@ -0,0 +1,2 @@
.DS_Store
old/

View file

@ -1,42 +1,18 @@
{% extends "templates/base.html.j2" %}
<!DOCTYPE html>
<html>
<head>
{% block head %}
{% include "templates/inlinestyle.html.j2" %}
{% endblock %}
</head>
{% block content %}
<body class="has-background-danger">
<section class="section">
<nav class="navbar has-background-danger">
<div class="navbar-brand has-background-danger">
</div>
</nav>
<br>
<br>
<br>
<br>
<br>
<br>
</section>
<section>
<div class="container has-text-centered">
<h1 class="title is-size-1">
{{ branding.text.error404.title }}
</h1>
<h2 class="subtitle is-size-3">
{{ branding.text.error404.subtitle }}
</h2>
<br>
</div>
</section>
{% if branding.footer.enable == true %}
{% include "templates/footer.html.j2" %}
{% endif %}
{% if branding.credit.enable == true %}
{% include "templates/credit.html.j2" %}
{% endif %}
<div class="ui vertical center aligned segment" id="lg-maincontainer">
{% import "templates/errortext.html.j2" as errortext %}
{{ errortext.errortext(branding.text.error404.title, branding.text.error404.subtitle.format(uri=uri), branding.text.error404.button, "h1", "h3") }}
</div>
{% endblock %}
</body>
</html>
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -1,45 +0,0 @@
{% extends "templates/base.html.j2" %}
<!DOCTYPE html>
<html>
<head>
</head>
{% block content %}
<body class="has-background-danger">
<section class="section">
<nav class="navbar has-background-danger">
<div class="container">
<div class="navbar-brand">
</div>
</div>
</nav>
<br>
<br>
<br>
<br>
<br>
<br>
</section>
<section>
<div class="container has-text-centered">
<h1 class="title is-size-1">
{{ features.rate_limit.site.title }}
</h1>
<h2 class="subtitle is-size-3">
{{ features.rate_limit.site.subtitle }}
</h2>
<br>
<a href="/" class="button is-medium is-rounded is-inverted is-danger is-outlined">{{ features.rate_limit.site.button }}</a>
</div>
</section>
{% if branding.footer.enable == true %}
{% include "templates/footer.html.j2" %}
{% endif %}
{% if branding.credit.enable == true %}
{% include "templates/credit.html.j2" %}
{% endif %}
{% endblock %}
</body>
</html>

View file

@ -1,45 +1,18 @@
{% extends "templates/base.html.j2" %}
<!DOCTYPE html>
<html>
<head>
{% block head %}
{% include "templates/inlinestyle.html.j2" %}
{% endblock %}
</head>
{% block content %}
<body class="has-background-danger">
<section class="section">
<nav class="navbar has-background-danger">
<div class="container">
<div class="navbar-brand">
</div>
</div>
</nav>
<br>
<br>
<br>
<br>
<br>
<br>
</section>
<section>
<div class="container has-text-centered">
<h1 class="title is-size-1">
{{ branding.text.error500.title }}
</h1>
<h2 class="subtitle is-size-3">
{{ branding.text.error500.subtitle }}
</h2>
<br>
<a href="/" class="button is-medium is-rounded is-inverted is-danger is-outlined">{{ branding.text.error500.button }}</a>
</div>
</section>
{% if branding.footer.enable == true %}
{% include "templates/footer.html.j2" %}
{% endif %}
{% if branding.credit.enable == true %}
{% include "templates/credit.html.j2" %}
{% endif %}
<div class="ui vertical center aligned segment" id="lg-maincontainer">
{% import "templates/errortext.html.j2" as errortext %}
{{ errortext.errortext(branding.text.error500.title, branding.text.error500.subtitle, branding.text.error500.button, "h1", "h3") }}
</div>
{% endblock %}
</body>
</html>
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -1,44 +1,54 @@
<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>{{ branding.site_name }}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="apple-touch-icon" sizes="180x180" href="{{ branding.logo.favicons }}apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ branding.logo.favicons }}favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ branding.logo.favicons }}favicon-32x32.png">
<link rel="manifest" href="{{ branding.logo.favicons }}site.webmanifest">
<link rel="mask-icon" href="{{ branding.logo.favicons }}safari-pinned-tab.svg" color="{{ branding.colors.tag.command }}">
<link rel="shortcut icon" href="{{ branding.logo.favicons }}favicon.ico">
<meta name="msapplication-TileColor" content="{{ branding.colors.tag.location_title }}">
<meta name="msapplication-config" content="{{ branding.logo.favicons }}browserconfig.xml">
<meta name="theme-color" content="{{ branding.colors.button_submit }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ branding.logo.favicons }}apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="16x16" href="{{ branding.logo.favicons }}favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="{{ branding.logo.favicons }}favicon-32x32.png" />
<link rel="manifest" href="{{ branding.logo.favicons }}site.webmanifest" />
<link rel="mask-icon" href="{{ branding.logo.favicons }}safari-pinned-tab.svg"
color="{{ branding.colors.tag.command }}" />
<link rel="shortcut icon" href="{{ branding.logo.favicons }}favicon.ico" />
<meta name="msapplication-TileColor" content="{{ branding.colors.tag.location_title }}" />
<meta name="msapplication-config" content="{{ branding.logo.favicons }}browserconfig.xml" />
<meta name="theme-color" content="{{ branding.colors.button_submit }}" />
<link href="static/css/icofont/icofont.min.css" rel="stylesheet" />
<link href="static/css/hyperglass.css" rel="stylesheet" />
{% endblock %}
<link href="static/semantic/dist/semantic.min.css" rel="stylesheet" type="text/css" />
<link href="static/css/animsition.min.css" rel="stylesheet" />
<!-- <link href="static/css/hyperglass.css" rel="stylesheet" /> -->
{% block head %}
{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
<body id="root">
{% block content %}
{% endblock %}
{% block footer %}
{% endblock %}
<script src="static/js/jquery-3.4.0.min.js"></script>
<script src="static/semantic/dist/semantic.min.js"></script>
<script src="static/js/clipboard.min.js"></script>
<script src="static/js/animsition.min.js"></script>
<script src="static/js/hyperglass.js"></script>
{% if general.google_analytics %}
<!--Google Analytics-->
<script async src="https://www.googletagmanager.com/gtag/js?id={{general.google_analytics}}">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "{{ general.google_analytics }}");
</script>
{% endif %}
{% block scripts %}
{% endblock %}
</body>
{% block scripts %}
<script src="static/js/jquery-3.4.0.min.js"></script>
<script src="static/js/clipboard.min.js"></script>
<script src="static/js/hyperglass.js"></script>
{% if general.google_analytics %}
<!--Google Analytics-->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ general.google_analytics }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', '{{ general.google_analytics }}');
</script>
{% endif %}
{% endblock %}
</html>
</html>

View file

@ -1,3 +1,5 @@
<div class="content is-small has-text-centered" id="credit">
<p>Powered by <a href="https://github.com/checktheroads/hyperglass">hyperglass</a>. Source code licensed <a href="https://github.com/checktheroads/hyperglass/blob/master/LICENSE">BSD 3-Clause Clear.</a></p>
</div>
<p class="ui ">Powered by <a href="https://github.com/checktheroads/hyperglass" target="_blank">hyperglass</a>.
Source code
licensed <a href="https://github.com/checktheroads/hyperglass/blob/master/LICENSE" target="_blank">BSD
3-Clause
Clear.</a></p>

View file

@ -0,0 +1,12 @@
{% macro errortext(title, subtitle, button, size_title="h1", size_subtitle="h3") -%}
<div class="ui content container">
<{{ size_title }} class="ui header">{{ title }}
</{{ size_title }}>
<{{ size_subtitle }} class="ui header">{{ subtitle }}</{{ size_subtitle }}>
<br>
<a href="/"><button class="ui basic small button">
<i class="angle left icon"></i>
{{ button }}
</button></a>
</div>
{%- endmacro %}

View file

@ -1,7 +1,39 @@
<footer class="footer">
<div class="container">
<div class="content is-small has-text-centered">
{{ details["footer"]["content"] | safe }}
{% if branding.footer.enable and branding.credit.enable %}
<div class="ui vertical footer segment">
<div class="ui center aligned fluid container">
<div class="ui vertical basic segments">
<div class="ui padded segment" id="lg-footer-text">
{{ details.footer.content | safe }}
</div>
<div class="ui padded segment" id="lg-credit-text">
{% include "templates/credit.html.j2" %}
</div>
</div>
</div>
</footer>
</div>
{% elif branding.footer.enable and not branding.credit.enable %}
<div class="ui vertical footer segment">
<div class="ui center aligned fluid container">
<div class="ui vertical basic segments">
<div class="ui padded segment" id="lg-footer-text">
{{ details.footer.content | safe }}
</div>
</div>
</div>
</div>
{% elif branding.credit.enable and not branding.footer.enable %}
<div class="ui vertical footer segment">
<div class="ui center aligned fluid container">
<div class="ui vertical basic segments">
<div class="ui padded segment" id="lg-credit-text">
{% include "templates/credit.html.j2" %}
</div>
</div>
</div>
</div>
{% elif not branding.footer.enable and not branding.footer.enable %}
<div class="ui vertical footer segment">
<div class="ui center aligned fluid container">
</div>
</div>
{% endif %}

View file

@ -1,264 +1,106 @@
{% extends "templates/base.html.j2" %}
<!DOCTYPE html>
<head>
{% block head %}
{% include "templates/inlinestyle.html.j2" %}
{% endblock %}
</head>
<body>
{% block content %}
<div class="modal" id="ratelimit">
<div class="modal-background"></div>
<div class="modal-content">
<article class="message is-danger">
<div class="message-header">
<p>{{ features.rate_limit.query.title }}</p>
</div>
<div class="message-body">
<p>{{ features.rate_limit.query.message }}</p>
<br>
<div class="buttons is-right">
<a href="/" class="button is-danger is-rounded is-outlined">{{ features.rate_limit.query.button }}</a>
</div>
</div>
</article>
</div>
{% block content %}
{% include "templates/ratelimit-query.html.j2" %}
{% if branding.peering_db.enable %}
<div class="ui secondary menu">
<div class="right menu">
<a href="https://as{{ general.primary_asn }}.peeringdb.com/" target="_blank" class="item">
PeeringDB&nbsp;
<i class="sign out alternate icon"></i>
</a>
</div>
{% if features.bgp_aspath.enable == true %}
<div class="modal" id="help_bgp_aspath">
<div class="modal-background"></div>
<div class="modal-content is-clipped">
<div class="box">
<p class="title">{{ details["bgp_aspath"]["title"] | safe }}</p>
{{ details["bgp_aspath"]["content"] | safe }}
</div>
</div>
<button class="modal-close is-large" aria-label="close"></button>
</div>
{% endif %}
{% if features.bgp_community.enable == true %}
<div class="modal" id="help_bgp_community">
<div class="modal-background"></div>
<div class="modal-content">
<div class="box">
<p class="title">{{ details["bgp_community"]["title"] | safe }}</p>
{{ details["bgp_community"]["content"] | safe }}
</div>
</div>
<button class="modal-close is-large" aria-label="close"></button>
</div>
{% endif %}
<nav class="navbar">
<div class="container is-fluid">
<div class="navbar-brand">
</div>
{% if branding.peering_db.enable == true %}
<div class="navbar-menu">
<div class="navbar-end">
<a class="navbar-item" href="https://as{{ general.primary_asn }}.peeringdb.com" target="_blank">
<span>PeeringDB</span>
<span class="icon">
<i class="icofont-external"></i>
</span>
</a>
</span>
</div>
</div>
{% endif %}
</div>
</nav>
<section class="section">
<div class="container has-text-centered is-fluid">
{% if branding.text.title_mode == 'all' %}
<img src="{{ branding.logo.path }}" style="width: {{ branding.logo.width }}px;">
<h1 class="title is-3" id="lg-title">
{{ branding.text.title }}
</h1>
<h2 class="subtitle is-5" id="lg-subtitle">
{{ branding.text.subtitle }}
</h2>
<br>
{% elif branding.text.title_mode == 'text_only' %}
<h1 class="title is-1" id="lg-title">
{{ branding.text.title }}
</h1>
<h2 class="subtitle is-3" id="lg-subtitle">
{{ branding.text.subtitle }}
</h2>
<br>
{% elif branding.text.title_mode == 'logo_title' %}
<img src="{{ branding.logo.path }}" style="width: {{ branding.logo.width }}px;">
<h1 class="title is-3" id="lg-title">
{{ branding.text.title }}
</h1>
{% elif branding.text.title_mode == 'logo_only' %}
<br>
<br>
<br>
<img src="{{ branding.logo.path }}" style="width: {{ branding.logo.width }}px;">
<br>
<br>
{% endif %}
<br>
<form onsubmit="return false" name="queryform" id="lgForm" action="?" method="POST">
<div class="container is-fluid">
<div class="field has-addons has-addons-centered">
<div class="control has-icons-left is-expanded">
<input type="text" class="input is-medium is-rounded is-family-monospace" id="target" placeholder="{{ branding.text.query_placeholder }}">
<span class="icon is-small is-left"><i class="icofont-at"></i></span>
</div>
</div>
<br>
<div class="field has-addons has-addons-centered" id="lg-netlocdropdown">
<div class="control has-icons-left" id="network-control">
<div class="select is-medium is-rounded">
<select id="network" name="network" style="width: 256px">
{% for net in networks %}
<option value="{{ net }}">AS{{ net }}</option>
{% endfor %}
</select>
</div>
{% endif %}
<div class="ui vertical center aligned segment" id="lg-maincontainer">
<div class="ui content container animsition" data-animsition-out-class="fade-out-right"
data-animsition-in-class="fade-in-left" id="lg-form">
{% import "templates/title.html.j2" as title %}
{{ title.title(branding, primary_asn, size_title="h1", size_subtitle="h3") }}
<form class="ui huge form" onsubmit="return false" name="queryform" id="lgForm" action="?" method="POST">
<div class="two fields">
<div class="field">
<div class="ui fluid search selection dropdown" id="location">
<input type="hidden" name="location">
<div class="default text">{{ branding.text.location }}</div>
<i class="dropdown icon"></i>
<div class="menu">
{% for (netname, loc_params) in networks.items() %}
<div class="divider"></div>
<div class="header">
<i class="globe icon"></i>
{{ netname }}
</div>
<span class="icon is-left"><i class="icofont-cloudapp"></i></span>
</div>
<div class="control has-icons-left" id="location-control">
<div class="select is-medium is-rounded">
<select id="location" style="width: 256px">
<option id="text_location" selected disabled>{{ branding.text.location }}</option>
</select>
{% for param in loc_params %}
<div class="item" data-value='{{param["hostname"]}}'>
{{param["display_name"]}}
</div>
<span class="icon is-left"><i class="icofont-location-arrow"></i></span>
</div>
</div>
<br>
</div>
<div class="container is-fluid">
<div class="field has-addons has-addons-centered is-grouped-centered">
<div class="control">
<div class="dropdown is-right" id="help-dropdown">
<div class="dropdown-trigger">
<button type="button" class="button is-rounded is-medium" aria-haspopup="true" aria-controls="dropdown-menu2" id="help-dropdown-button">
<span class="icon is-size-6 lg-icon-help">
<i class="icofont-info-circle" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu is-expanded" id="dropdown-menu2" role="menu">
<div class="dropdown-content lg-help">
{% if features.bgp_route.enable == true %}
<div class="dropdown-item">
<strong>{{ branding.text.bgp_route | safe }}</strong>
<p>{{ info["bgp_route"]["content"] | safe }}</p>
</div>
{% endif %}
{% if features.bgp_community.enable == true %}
<div class="dropdown-item">
<strong>{{ branding.text.bgp_community | safe }}</strong>
<p>{{ info["bgp_community"]["content"] | safe }}</p>
</div>
{% endif %}
{% if features.bgp_aspath.enable == true %}
<div class="dropdown-item">
<strong>{{ branding.text.bgp_aspath | safe }}</strong>
<p>{{ info["bgp_aspath"]["content"] | safe }}</p>
</div>
{% endif %}
{% if features.ping.enable == true %}
<div class="dropdown-item">
<strong>{{ branding.text.ping | safe }}</strong>
<p>{{ info["ping"]["content"] | safe }}</p>
</div>
{% endif %}
{% if features.traceroute.enable == true %}
<div class="dropdown-item">
<strong>{{ branding.text.traceroute | safe }}</strong>
<p>{{ info["traceroute"]["content"] | safe }}</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="control">
<div class="select is-medium is-rounded">
<select id="type">
<option selected disabled>
{{ branding.text.query_type }}
</option>
{% if features.bgp_route.enable == true %}
<option name="type" id="type_bgp_route" value="bgp_route">
{{ branding.text.bgp_route }}
</option>
{% endif %}
{% if features.bgp_community.enable == true %}
<option name="type" id="type_bgp_community" value="bgp_community">
{{ branding.text.bgp_community }}
</option>
{% endif %}
{% if features.bgp_aspath.enable == true %}
<option name="type" id="type_bgp_aspath" value="bgp_aspath">
{{ branding.text.bgp_aspath }}
</option>
{% endif %}
{% if features.ping.enable == true %}
<option name="type" id="type_ping" value="ping">
{{ branding.text.ping }}
</option>
{% endif %}
{% if features.traceroute.enable == true %}
<option name="type" id="type_traceroute" value="traceroute">
{{ branding.text.traceroute }}
</option>
{% endif %}
</select>
</div>
</div>
<div class="control">
<button class="button lg-btn-submit is-medium is-rounded" type="submit" name="type">
<span class="icon">
<i class="icofont-search-1"></i>
</span>
</button>
<input type="hidden" id='{{param["hostname"]}}' name='{{param["display_name"]}}' value="{{ netname }}">
{% endfor %}
{% endfor %}
</div>
</div>
</div>
<div class="columns is-centered">
<div class="column is-one-third" id="target_error">
<div class="field">
<div class="ui selection dropdown left icon" id="query_type">
<input type="hidden" name="query_type">
<i class="dropdown icon"></i>
<div class="default text">{{ branding.text.query_type }}</div>
<div class="ui menu">
{% if features.bgp_route.enable %}
<div class="item feature-selection" id="type_bgp_route" data-value="bgp_route">
{{ branding.text.bgp_route }}
</div>
{% endif %}
{% if features.bgp_community.enable == true %}
<div class="item feature-selection" id="type_bgp_community" data-value="bgp_community">
{{ branding.text.bgp_community }}
</div>
{% endif %}
{% if features.bgp_aspath.enable == true %}
<div class="item feature-selection" id="type_bgp_aspath" data-value="bgp_aspath">
{{ branding.text.bgp_aspath }}
</div>
{% endif %}
{% if features.ping.enable == true %}
<div class="item feature-selection" id="type_ping" data-value="ping">
{{ branding.text.ping }}
</div>
{% endif %}
{% if features.traceroute.enable == true %}
<div class="item feature-selection" id="type_traceroute" data-value="traceroute">
{{ branding.text.traceroute }}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="field" id="field-target">
<div class="ui fluid icon input">
<input type="text" placeholder="{{ branding.text.query_placeholder }}" id="target">
<i class="search link icon" id="submit_button"></i>
</div>
</div>
<div class="ui hidden message"></div>
</form>
</section>
<section class="section">
<div class="container is-fluid">
<div class="box" id="resultsbox">
<a class="button is-rounded is-pulled-right" id="btn-copy" data-clipboard-target="#output">
<span class="icon is-small">
<i id="copy-icon" class="icofont-ui-copy"></i>
</span>
</a>
<p class="title" id="results">{{ branding.text.results }}</p>
<p id="queryInfo">
</p>
<p id="progress">
<br>
<progress class="progress is-medium lg-progressbar" max="100">50%</progress>
</p>
<br>
<p class="query-output" id="output">
</p>
{% if features.cache.show_text == true %}
<hr>
<p class="is-size-7">{{ features.cache.text }}</p>
{% endif %}
</div>
</div>
</section>
{% if branding.footer.enable == true %}
{% include "templates/footer.html.j2" %}
{% endif %}
{% if branding.credit.enable == true %}
{% include "templates/credit.html.j2" %}
{% endif %}
{% endblock %}
</body>
</html>
<div class="ui popup" id="bgpr_help_content">{{ info["bgp_route"]["content"] | safe }}</div>
<div class="ui popup" id="bgpc_help_content">{{ info["bgp_community"]["content"] | safe }}<div class="ui divider">
</div>{{ details["bgp_community"]["content"] | safe }}</div>
<div class="ui popup" id="bgpa_help_content">{{ info["bgp_aspath"]["content"] | safe }}<div class="ui divider">
</div>{{ details["bgp_aspath"]["content"] | safe }}</div>
<div class="ui popup" id="ping_help_content">{{ info["ping"]["content"] | safe }}</div>
<div class="ui popup" id="traceroute_help_content">{{ info["traceroute"]["content"] | safe }}</div>
</div>
{% include "templates/results.html.j2" %}
</div>
{% endblock %}
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -0,0 +1,74 @@
<style type="text/css">
body {
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: grayscale;
}
@media only screen and (min-width: 768px) {
.ui.container {
width: 700px;
max-width: 700px;
}
}
#lg-results a {
color: rgba(0, 0, 0, .87);
}
#lg-maincontainer {
padding-top: 36px;
min-height: 100vh;
}
#lg-results-output {
-webkit-overflow-scrolling: touch;
overflow-x: auto;
}
.ui.content.container {
height: 300px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.ui.vertical.segment footer.ui.segment {
width: 100%;
position: absolute;
bottom: 0;
padding-left: 2em;
padding-right: 2em;
}
.ui.huge.header {
font-size: 3rem;
}
.ui.fluid.container {
height: 75%;
width: 75%;
}
#lg-footer-text,
#lg-credit-text {
font-size: 75%;
margin-left: 12.5%;
margin-right: 12.5%;
}
#lg-credit-text a,
#lg-footer-text a {
color: inherit;
}
#clip-button {
float: right;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
</style>

View file

@ -0,0 +1,15 @@
<div class="ui modal" id="ratelimit">
<div class="header">
{{ features.rate_limit.query.title }}
</div>
<div class="content">
<p>{{ features.rate_limit.query.message }}</p>
</div>
<div class="actions">
<a href="/">
<div class="ui button">
{{ features.rate_limit.query.button }}
</div>
</a>
</div>
</div>

View file

@ -0,0 +1,18 @@
{% extends "templates/base.html.j2" %}
<head>
{% block head %}
{% include "templates/inlinestyle.html.j2" %}
{% endblock %}
</head>
{% block content %}
<div class="ui vertical center aligned segment" id="lg-maincontainer">
{% import "templates/errortext.html.j2" as errortext %}
{{ errortext.errortext(features.rate_limit.site.title, features.rate_limit.site.subtitle, features.rate_limit.site.button, "h1", "h3") }}
</div>
{% endblock %}
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -0,0 +1,29 @@
<div class="ui content fluid left aligned container animsition" data-animsition-out-class="fade-out-left"
data-animsition-in-class="fade-in-right" id="lg-results">
{% import "templates/title.html.j2" as title %}
{{ title.title(branding, primary_asn, size_title="h1", size_subtitle="h3") }}
<div class="ui segments">
<div class="ui padded fluid segment">
<h2 class="ui header">
<a href="#" id="results_back"><i class="icon angle left"></i></a>
<div class="content">
{{ branding.text.results }}
</div>
</h2>
<div id="results_detail">
</div>
</div>
<div class="ui padded fluid segment">
<!-- <button class="ui basic right floated icon button" id="clip-button" data-clipboard-target="#lg-results-segment"><i
class="copy outline icon" id="clip-icon"></i></button> -->
<i id="clip-button" data-clipboard-target="#lg-results-segment" data-content="Copy Output"
class="copy link icon"></i>
<div id="lg-results-segment"></div>
</div>
{% if features.cache.show_text %}
<div class="ui padded fluid segment">
<span class="ui small text">{{ features.cache.text }}</span>
</div>
{% endif %}
</div>
</div>

View file

@ -0,0 +1,19 @@
{% macro title(branding, primary_asn, size_title="h1", size_subtitle="h3") -%}
{% if branding.text.title_mode == 'text_only' %}
<{{ size_title }} class="ui header">{{ branding.text.title }}</{{ size_title }}>
<{{ size_subtitle }} class="ui header">{{ branding.text.subtitle.format(primary_asn=primary_asn) }}</{{ size_subtitle }}>
<br>
{% elif branding.text.title_mode == 'all' %}
<img src="{{ branding.logo.path }}" style="width: {{ branding.logo.width }}px;">
<{{ size_title }} class="ui header">{{ branding.text.title }}</{{ size_title }}>
<{{ size_subtitle }} class="ui header">{{ branding.text.subtitle.format(primary_asn=primary_asn) }}</{{ size_subtitle }}>
<br>
{% elif branding.text.title_mode == 'logo_title' %}
<img src="{{ branding.logo.path }}" style="width: {{ branding.logo.width }}px;">
<{{ size_title }} class="ui header">{{ branding.text.title }}</{{ size_title }}>
<br>
{% elif branding.text.title_mode == 'logo_only' %}
<img src="{{ branding.logo.path }}" style="width: {{ branding.logo.width }}px;">
<br>
{% endif %}
{%- endmacro %}

7
hyperglass/static/css/animsition.min.css vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.3 MiB

File diff suppressed because one or more lines are too long

8
hyperglass/static/js/animsition.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View file

@ -1,259 +1,219 @@
// Get the list of locations for the selected Network
var progress = ($('#progress'));
var resultsbox = ($('#resultsbox'));
var target_error = ($('#target_error'));
var target_input = ($('#target'));
adjustDropdowns();
var progress = $("#progress");
var resultsbox = $("#resultsbox");
var target_error = $("#target_error");
var target_input = $("#target");
// adjustDropdowns();
clearPage();
// Bulma Toggable Dropdown - help text
$('#help-dropdown').click(
function (event) {
event.stopPropagation();
$(this).toggleClass('is-active');
}
);
$(".selection.dropdown").dropdown({
fullTextSearch: true,
match: "both",
allowCategorySelection: true,
ignoreCare: true
});
$("#type_bgp_route").popup({
hoverable: true,
variation: "wide",
position: "right center",
html: $("#bgpr_help_content").html()
});
$("#type_bgp_community").popup({
hoverable: true,
variation: "wide",
position: "right center",
html: $("#bgpc_help_content").html()
});
$("#type_bgp_aspath").popup({
hoverable: true,
variation: "wide",
position: "right center",
html: $("#bgpa_help_content").html()
});
$("#type_ping").popup({
hoverable: true,
variation: "wide",
position: "right center",
html: $("#ping_help_content").html()
});
$("#type_traceroute").popup({
hoverable: true,
variation: "wide",
position: "right center",
html: $("#traceroute_help_content").html()
});
// ClipboardJS Elements
var btn_copy = document.getElementById('btn-copy');
var clipboard = new ClipboardJS(btn_copy);
clipboard.on('success', function (e) {
console.log(e);
$('#btn-copy').addClass('is-success').addClass('is-outlined');
$('#copy-icon').removeClass('icofont-ui-copy').addClass('icofont-check');
setTimeout(function () {
$('#btn-copy').removeClass('is-success').removeClass('is-outlined');
$('#copy-icon').removeClass('icofont-check').addClass('icofont-ui-copy');
}, 1000);
var clip_button = document.getElementById("clip-button");
var clipboard = new ClipboardJS(clip_button);
clipboard.on("success", function (e) {
$("#clip-button")
.removeClass("copy link icon")
.addClass("green check icon");
e.clearSelection();
setTimeout(function () {
$("#clip-button")
.removeClass("green check icon")
.addClass("copy link icon");
}, 800);
});
clipboard.on('error', function (e) {
console.log(e);
clipboard.on("error", function (e) {
console.log(e);
});
$('.modal-background, .modal-close').click(
function (event) {
event.stopPropagation();
$('.modal').removeClass("is-active");
}
);
// Adjust behavior of help text dropdown based on device screen size
$('#help-dropdown-button').click(
function (event) {
if (window.innerWidth < 1024) {
$('#help-dropdown').removeClass('is-right');
$('.lg-help').addClass('lg-help-mobile').removeClass('lg-help');
}
}
);
function adjustDropdowns() {
var actual_width = window.innerWidth;
if (actual_width < 1024) {
$('#lg-netlocdropdown').removeClass('has-addons').removeClass('has-addons-centered').addClass('is-grouped').addClass('is-grouped-centered').addClass('is-grouped-multiline');
$('#network').css('width', actual_width * 0.85);
$('#location').css('width', actual_width * 0.85);
}
}
function clearErrors() {
progress.hide();
target_error.hide();
if (target_input.hasClass("is-warning")) {
target_input.removeClass("is-warning");
}
if (target_input.hasClass("is-danger")) {
target_input.removeClass("is-danger");
}
$("#lgForm").removeClass("error");
$("#lgForm").removeClass("warning");
$("#lgForm > div.ui.message").html("").removeClass("error").addClass("hidden");
$("#field-target").removeClass("error");
$(".ui.fluid.icon.input").removeClass("loading");
}
function clearPage() {
progress.hide();
resultsbox.hide();
target_error.hide();
if (target_input.hasClass("is-warning")) {
target_input.removeClass("is-warning");
}
if (target_input.hasClass("is-danger")) {
target_input.removeClass("is-danger");
}
}
function prepResults() {
progress.show();
resultsbox.show();
$(".ui.fluid.icon.input").removeClass("loading");
progress.hide();
resultsbox.hide();
target_error.hide();
if (target_input.hasClass("is-warning")) {
target_input.removeClass("is-warning");
}
if (target_input.hasClass("is-danger")) {
target_input.removeClass("is-danger");
}
}
$(document).ready(function () {
var defaultasn = $("#network").val();
$.ajax({
url: '/locations/' + defaultasn,
context: document.body,
type: 'get',
success: function (data) {
selectedRouters = data;
console.log(selectedRouters);
updateRouters(selectedRouters);
},
error: function (err) {
console.log(err);
}
});
$('#lg-results').hide();
$(".animsition").animsition({
inClass: 'fade-in',
outClass: 'fade-out',
inDuration: 800,
outDuration: 800,
transition: function (url) { window.location.href = url; }
});
$('#lg-form').animsition('in');
});
$('#network').on('change', (function (event) {
var asn = $("select[id=network").val();
$('#location').children(":not(#text_location)").remove();
$.ajax({
url: '/locations/' + asn,
type: 'get',
success: function (data) {
clearPage();
updateRouters(JSON.parse(data));
},
error: function (err) {
console.log(err);
}
});
}));
function updateRouters(locations) {
locations.forEach(function (r) {
$('#location').append($("<option>").attr('value', r.hostname).text(r.display_name));
});
}
$('#helplink_bgpc').click(function (event) {
$('#help_bgp_community').addClass("is-active");
});
$('#helplink_bgpa').click(function (event) {
$('#help_bgp_aspath').addClass("is-active");
});
$("#results_back").on("click", function () {
$('#lg-results').animsition('out', $('#lg-form'), '#');
$('#lg-results').hide();
$('#lg-form').show();
$('#lg-form').animsition('in');
})
// Submit Form Action
$('#lgForm').on('submit', function () {
submitForm();
$("#lgForm").form().submit(function (event) {
event.preventDefault();
clearErrors();
submitForm();
});
function submitForm() {
clearErrors();
var type = $('#type option:selected').val();
var type_title = $('#type option:selected').text();
var network = $('#network option:selected').val();
var location = $('#location option:selected').val();
var location_name = $('#location option:selected').text();
var target = $('#target').val();
$("#submit_button").on("click", function () {
clearErrors();
submitForm();
})
var tags = [
'<div class="field is-grouped is-grouped-multiline">',
'<div class="control">',
'<div class="tags has-addons">',
'<span class="tag lg-tag-loc-title">AS',
network,
'</span>',
'<span class="tag lg-tag-loc">',
location_name,
'</span>',
'</div>',
'</div>',
'<div class="control">',
'<div class="tags has-addons">',
'<span class="tag lg-tag-type-title">',
type_title,
'</span>',
'<span class="tag lg-tag-type">',
target,
'</span>',
'</div>',
'</div>',
'</div>'
].join('');
$('#output').text("");
$('#queryInfo').text("");
$('#queryInfo').html(tags);
$.ajax(
{
url: '/lg',
type: 'POST',
data: JSON.stringify(
{
location: location,
type: type,
target: target
}
),
contentType: "application/json; charset=utf-8",
context: document.body,
readyState: prepResults(),
statusCode: {
200: function (response, code) {
response_html = [
'<br>',
'<div class="content">',
'<p class="query-output" id="output">',
response,
'</p>',
'</div>',
];
progress.hide();
$('#output').html(response_html);
},
401: function (response, code) {
response_html = [
'<br>',
'<div class="notification is-danger">',
response.responseText,
'</div>',
].join('');
clearPage();
target_error.show();
target_input.addClass('is-danger');
target_error.html(response_html);
},
405: function (response, code) {
response_html = [
'<br>',
'<div class="notification is-warning">',
response.responseText,
'</div>',
].join('');
clearPage();
target_error.show();
target_input.addClass('is-warning');
target_error.html(response_html);
},
415: function (response, code) {
response_html = [
'<br>',
'<div class="notification is-danger">',
response.responseText,
'</div>',
].join('');
clearPage();
target_error.show();
target_input.addClass('is-danger');
target_error.html(response_html);
},
429: function (response, code) {
clearPage();
$("#ratelimit").addClass("is-active");
},
504: function (response, code) {
response_html = [
'<br>',
'<div class="notification is-danger">',
response.responseText,
'</div>',
].join('');
clearPage();
target_error.show();
target_error.html(response_html);
}
}
}
);
function buildError(msgClass, msg) {
var msgHtml = [
'<div class="ui ',
msgClass,
' message transition hidden>',
'<i class="close icon"></i>',
'<p>',
msg,
'</p>',
'</div>'
].join("");
return msgHtml;
}
function submitForm() {
clearErrors();
var query_type = $("#query_type").dropdown("get value");
var query_type_title = $("#query_type").dropdown("get text");
var location = $("#location").dropdown("get value");
var location_name = $("#location").dropdown("get text");
var target = $("#target").val();
console.log(query_type);
console.log(location);
console.log(target);
network = $("#" + location).val();
var tags = [
'<div class="ui label">',
network,
'<div class="detail">',
location_name,
"</div>",
"</div>",
'<div class="ui label">',
query_type_title,
'<div class="detail">',
target,
"</div>",
"</div>"
].join("");
$("#results_detail").html(tags);
$(".ui.fluid.icon.input").addClass("loading");
$.ajax({
url: "/lg",
type: "POST",
data: JSON.stringify({
location: location,
query_type: query_type,
target: target
}),
contentType: "application/json; charset=utf-8",
context: document.body,
statusCode: {
200: function (response, code) {
$('#lg-form').animsition('out', $('#lg-results'), '#');
$('#lg-results').show();
$('#lg-results').animsition('in');
response_html = [
'<pre>',
response,
"</pre>"
].join("");
$(".ui.fluid.icon.input").removeClass("loading");
$("#lg-results-segment").html(response_html);
},
401: function (response, code) {
$("#lgForm").addClass("error");
$("#lgForm > div.ui.hidden.message").html(response.responseText).addClass("error").removeClass("hidden");
$("#field-target").addClass("error");
$(".ui.fluid.icon.input").removeClass("loading");
},
405: function (response, code) {
$("#lgForm").addClass("error");
$("#lgForm > div.ui.hidden.message").html(response.responseText).addClass("error").removeClass("hidden");
$("#field-target").addClass("error");
$(".ui.fluid.icon.input").removeClass("loading");
},
415: function (response, code) {
$("#lgForm").addClass("warning");
$("#lgForm > div.ui.hidden.message").html(response.responseText).addClass("warning").removeClass("hidden");
$(".ui.fluid.icon.input").removeClass("loading");
},
429: function (response, code) {
$("#ratelimit").modal("show");
},
504: function (response, code) {
$("#lgForm").addClass("error");
$("#lgForm > div.ui.hidden.message").html(response.responseText).addClass("error").removeClass("hidden");
$("#field-target").addClass("error");
$(".ui.fluid.icon.input").removeClass("loading");
}
}
});
};

View file

@ -0,0 +1,22 @@
{
"base": "semantic/",
"paths": {
"source": {
"config": "src/theme.config",
"definitions": "src/definitions/",
"site": "src/site/",
"themes": "src/themes/"
},
"output": {
"packaged": "dist/",
"uncompressed": "dist/components/",
"compressed": "dist/components/",
"themes": "dist/themes/"
},
"clean": "dist/"
},
"permission": false,
"autoInstall": false,
"rtl": false,
"version": "2.7.7"
}

View file

@ -9,18 +9,20 @@ port = 8001
try:
import multiprocessing
import os
import tempfile
from hyperglass import render
from hyperglass import hyperglass
from hyperglass.configuration import params
except ImportError as import_error:
raise RuntimeError(import_error)
debug = False
access_log = True
if params.general.debug:
debug = True
access_log = False
elif not params.general.debug:
debug = False
access_log = True
# Override the number of web server workers if necessary:
workers = multiprocessing.cpu_count()
@ -35,6 +37,10 @@ def start():
render.css()
except Exception as render_error:
raise RuntimeError(render_error)
tempdir = tempfile.TemporaryDirectory(prefix="hyperglass_")
os.environ["prometheus_multiproc_dir"] = tempdir.name
try:
hyperglass.app.run(
host=host,

58
setup.py Normal file
View file

@ -0,0 +1,58 @@
from distutils.core import setup
import sys
if sys.version_info < (3, 6):
sys.exit("Python 3.6+ is required.")
import shutil
from pathlib import Path
with open("README.md", "r") as ld:
long_description = ld.read()
package_json = {
"dependencies": {
"animsition": "^4.0.2",
"clipboard": "^2.0.4",
"fomantic-ui": "^2.7.7",
"jquery": "^3.4.1",
}
}
setup(
name="hyperglass",
version="1.0.0",
author="Matt Love",
author_email="matt@allroads.io",
description="hyperglass is a modern, customizable network looking glass written in Python 3.",
url="https://github.com/checktheroads/hyperglass",
python_requires=">=3.6",
packages=["hyperglass"],
install_requires=[
"aredis==1.1.5",
"click==6.7",
"hiredis==1.0.0",
"http3==0.6.7",
"jinja2==2.10.1",
"libsass==0.18.0",
"logzero==1.5.0",
"markdown2==2.3.7",
"netmiko==2.3.3",
"passlib==1.7.1",
"prometheus_client==0.7.0",
"pydantic==0.29",
"pyyaml==5.1.1",
"redis==3.2.1",
"sanic_limiter==0.1.3",
"sanic==19.6.2",
"sshtunnel==0.1.5",
],
setup_requires=[
"calmjs==3.4.1",
]
package_json=package_json,
license="BSD 3-Clause Clear License",
long_description=long_description,
long_description_content_type="text/markdown",
)