"""Renders Jinja2 & Sass templates for use by the front end application.""" # Standard Library Imports from pathlib import Path # Third Party Imports import jinja2 import yaml from markdown2 import Markdown # Project Imports from hyperglass.configuration import networks from hyperglass.configuration import params from hyperglass.exceptions import HyperglassError from hyperglass.util import log # Module Directories working_directory = Path(__file__).resolve().parent hyperglass_root = working_directory.parent file_loader = jinja2.FileSystemLoader(str(working_directory)) env = jinja2.Environment( loader=file_loader, autoescape=True, extensions=["jinja2.ext.autoescape"] ) default_details = { "footer": """ --- template: footer --- By using {{ branding.site_name }}, you agree to be bound by the following terms of \ use: All queries executed on this page are logged for analysis and troubleshooting. \ Users are prohibited from automating queries, or attempting to process queries in \ bulk. This service is provided on a best effort basis, and {{ general.org_name }} \ makes no availability or performance warranties or guarantees whatsoever. """, "bgp_aspath": r""" --- template: bgp_aspath title: Supported AS Path Patterns --- {{ branding.site_name }} accepts the following `AS_PATH` regular expression patterns: | Expression | Match | | :------------------- | :-------------------------------------------- | | `_65000$` | Originated by 65000 | | `^65000_` | Received from 65000 | | `_65000_` | Via 65000 | | `_65000_65001_` | Via 65000 and 65001 | | `_65000(_.+_)65001$` | Anything from 65001 that passed through 65000 | """, "bgp_community": """ --- template: bgp_community title: BGP Communities --- {{ branding.site_name }} makes use of the following BGP communities: | Community | Description | | :-------- | :---------- | | `65000:1` | Example 1 | | `65000:2` | Example 2 | | `65000:3` | Example 3 | """, } default_info = { "bgp_route": """ --- template: bgp_route --- Performs BGP table lookup based on IPv4/IPv6 prefix. """, "bgp_community": """ --- template: bgp_community --- Performs BGP table lookup based on Extended or Large community value. """, "bgp_aspath": """ --- template: bgp_aspath --- Performs BGP table lookup based on `AS_PATH` regular expression. """, "ping": """ --- template: ping --- Sends 5 ICMP echo requests to the target. """, "traceroute": """ --- template: traceroute --- Performs UDP Based traceroute to the target.
For information about how to \ interpret traceroute results, click here. """, } default_help = """ --- template: default_help --- ##### BGP Route Performs BGP table lookup based on IPv4/IPv6 prefix.
##### BGP Community Performs BGP table lookup based on Extended or Large community value.
##### BGP AS Path Performs BGP table lookup based on `AS_PATH` regular expression.
##### Ping Sends 5 ICMP echo requests to the target.
##### Traceroute Performs UDP Based traceroute to the target.
For information about how to \ interpret traceroute results, click here. """ def generate_markdown(section, file_name=None): """Render markdown as HTML. Arguments: section {str} -- Section name Keyword Arguments: file_name {str} -- Markdown file name (default: {None}) Raises: HyperglassError: Raised if YAML front matter is unreadable Returns: {dict} -- Frontmatter dictionary """ if section == "help": file = working_directory.joinpath("templates/info/help.md") if file.exists(): with file.open(mode="r") as file_raw: yaml_raw = file_raw.read() else: yaml_raw = default_help elif section == "details": file = working_directory.joinpath(f"templates/info/details/{file_name}.md") if file.exists(): with file.open(mode="r") as file_raw: yaml_raw = file_raw.read() else: yaml_raw = default_details[file_name] _, frontmatter, content = yaml_raw.split("---", 2) html_classes = {"table": "table"} markdown = Markdown( extras={ "break-on-newline": True, "code-friendly": True, "tables": True, "html-classes": html_classes, } ) frontmatter_rendered = ( jinja2.Environment( loader=jinja2.BaseLoader, autoescape=True, extensions=["jinja2.ext.autoescape"], ) .from_string(frontmatter) .render(params) ) if frontmatter_rendered: frontmatter_loaded = yaml.safe_load(frontmatter_rendered) elif not frontmatter_rendered: frontmatter_loaded = {"frontmatter": None} content_rendered = ( jinja2.Environment( loader=jinja2.BaseLoader, autoescape=True, extensions=["jinja2.ext.autoescape"], ) .from_string(content) .render(params, info=frontmatter_loaded) ) help_dict = dict(content=markdown.convert(content_rendered), **frontmatter_loaded) if not help_dict: raise HyperglassError(f"Error reading YAML frontmatter for {file_name}") return help_dict def render_html(template_name, **kwargs): """Render Jinja2 HTML templates. Arguments: template_name {str} -- Jinja2 template name Raises: HyperglassError: Raised if template is not found Returns: {str} -- Rendered template """ details_name_list = ["footer", "bgp_aspath", "bgp_community"] details_dict = {} for details_name in details_name_list: details_data = generate_markdown("details", details_name) details_dict.update({details_name: details_data}) rendered_help = generate_markdown("help") log.debug(rendered_help) try: template_file = f"templates/{template_name}.html.j2" template = env.get_template(template_file) return template.render( params, rendered_help=rendered_help, details=details_dict, networks=networks, **kwargs, ) except jinja2.TemplateNotFound as template_error: log.error(f"Error rendering Jinja2 template {Path(template_file).resolve()}.") raise HyperglassError(template_error)