From e3dbcac0a4569fc4d8ee273050881c4d871dd7e5 Mon Sep 17 00:00:00 2001 From: Matt Love Date: Tue, 9 Jul 2019 18:11:46 -0700 Subject: [PATCH] =?UTF-8?q?:wrench:=20TOML=20=E2=86=92=20YAML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hyperglass/render/__init__.py | 231 +++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 100 deletions(-) diff --git a/hyperglass/render/__init__.py b/hyperglass/render/__init__.py index a3f19d8..9aa2b41 100644 --- a/hyperglass/render/__init__.py +++ b/hyperglass/render/__init__.py @@ -6,7 +6,7 @@ from pathlib import Path # Module Imports import sass -import toml +import yaml import jinja2 import logzero from logzero import logger @@ -24,67 +24,88 @@ file_loader = jinja2.FileSystemLoader(str(working_directory)) env = jinja2.Environment(loader=file_loader) default_details = { - "footer": ( - "---\n" - "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": ( - "title: Supported AS Path Patterns\n" - "---\n" - "{{ branding.site_name }} accepts the following `AS_PATH` regular expression " - "patterns:\n" - "| Expression | Match |\n" - "| :------------------- | :-------------------------------------------- |\n" - "| `_65000$` | Originated by 65000 |\n" - "| `^65000_` | Received from 65000 |\n" - "| `_65000_` | Via 65000 |\n" - "| `_65000_65001_` | Via 65000 and 65001 |\n" - "| `_65000(_.+_)65001$` | Anything from 65001 that passed through 65000 |\n" - ), - "bgp_community": ( - "title: BGP Communities\n" - "---\n" - "{{ branding.site_name }} makes use of the following BGP communities:\n" - "| Community | Description |\n" - "| :-------- | :---------- |\n" - "| `65000:1` | Example 1 |\n" - "| `65000:2` | Example 2 |\n" - "| `65000:3` | Example 3 |\n" - ), + "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": [ + "---", + "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": """ ---- -Performs BGP table lookup based on IPv4/IPv6 prefix. -""", - "bgp_community": ( - 'link: {{ general.org_name }} ' - "BGP Communities\n" - "---\n" - "Performs BGP table lookup based on " - "[Extended](https://tools.ietf.org/html/rfc4360) " - "or [Large](https://tools.ietf.org/html/rfc8195) community value." - '
{{ info["bgp_community"]["link"] }}' - ), - "bgp_aspath": ( - 'link: Supported BGP AS Path Expressions\n' - "---\n" - "Performs BGP table lookup based on `AS_PATH` regular expression." - '
{{ info["bgp_aspath"]["link"] }}' - ), - "ping": ("---\n", "Sends 5 ICMP echo requests to the target."), - "traceroute": ( - "---\n" - "Performs UDP Based traceroute to the target.
For information about how to" - "interpret traceroute results, [click here]" - "(https://hyperglass.readthedocs.io/en/latest/assets/traceroute_nanog.pdf)." - ), + "bgp_route": [ + "---", + "template: bgp_route", + "---", + "Performs BGP table lookup based on IPv4/IPv6 prefix.", + ], + "bgp_community": [ + "---", + "template: bgp_community", + ( + 'link: {{ general.org_name }} ' + "BGP Communities" + ), + "---", + "Performs BGP table lookup based on ", + "[Extended](https://tools.ietf.org/html/rfc4360) ", + "or [Large](https://tools.ietf.org/html/rfc8195) community value.", + '
{{ info["link"] }}', + ], + "bgp_aspath": [ + "---", + "template: bgp_aspath", + 'link: Supported BGP AS Path Expressions', + "---", + "Performs BGP table lookup based on `AS_PATH` regular expression.", + '
{{ info["link"] }}', + ], + "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]", + "(https://hyperglass.readthedocs.io/en/latest/assets/traceroute_nanog.pdf).", + ], } @@ -108,26 +129,32 @@ def info(file_name): if file.exists(): with file.open(mode="r") as file_raw: file_read = file_raw.read() - frontmatter, content = file_read.split("---") - frontmatter_dict[file_name] = toml.loads(frontmatter) - md_template_fm = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - frontmatter - ) - md_template_content = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - content - ) + _, frontmatter, content = file_read.split("---") else: - _, frontmatter, content = default_info[file_name].split("+++") - md_template_fm = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - frontmatter - ) - md_template_content = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - content - ) - frontmatter_rendered = md_template_fm.render(params) - frontmatter_dict[file_name] = toml.loads(frontmatter_rendered) - content_rendered = md_template_content.render(params, info=frontmatter_dict) - frontmatter_dict[file_name]["content"] = markdown.convert(content_rendered) + fm_end = default_info[file_name][1:].index("---") + frontmatter = "\n".join(default_info[file_name][1:][:fm_end]) + content = "".join(default_info[file_name][1:][fm_end + 1 :]) + frontmatter_rendered = ( + jinja2.Environment(loader=jinja2.BaseLoader) + .from_string(frontmatter) + .render(params) + ) + if frontmatter_rendered: + frontmatter_loaded = yaml.safe_load(frontmatter_rendered) + if not frontmatter_rendered: + frontmatter_loaded = {"frontmatter": None} + content_rendered = ( + jinja2.Environment(loader=jinja2.BaseLoader) + .from_string(content) + .render(params, info=frontmatter_loaded) + ) + logger.error(frontmatter) + logger.error(frontmatter_loaded) + frontmatter_dict = dict( + content=markdown.convert(content_rendered), **frontmatter_loaded + ) + if not frontmatter_dict: + raise HyperglassError(f"Error reading YAML frontmatter for {file_name}") return frontmatter_dict @@ -137,6 +164,7 @@ def details(file_name): renders TOML frontmatter variables, returns dictionary of variables and HTML content. """ + frontmatter_dict = None html_classes = {"table": "table"} markdown = Markdown( extras={ @@ -147,30 +175,33 @@ def details(file_name): } ) file = working_directory.joinpath(f"templates/info/details/{file_name}.md") - frontmatter_dict = {} if file.exists(): with file.open(mode="r") as file_raw: file_read = file_raw.read() - frontmatter, content = file_read.split("---") - md_template_fm = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - frontmatter - ) - md_template_content = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - content - ) + _, frontmatter, content = file_read.split("---") else: - _, frontmatter, content = default_details[file_name].split("+++") - frontmatter_dict[file_name] = toml.loads(frontmatter) - md_template_fm = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - frontmatter - ) - md_template_content = jinja2.Environment(loader=jinja2.BaseLoader).from_string( - content - ) - frontmatter_rendered = md_template_fm.render(params) - frontmatter_dict[file_name] = toml.loads(frontmatter_rendered) - content_rendered = md_template_content.render(params, details=frontmatter_dict) - frontmatter_dict[file_name]["content"] = markdown.convert(content_rendered) + fm_end = default_details[file_name][1:].index("---") + frontmatter = "\n".join(default_details[file_name][1:][:fm_end]) + content = "".join(default_details[file_name][1:][fm_end + 1 :]) + frontmatter_rendered = ( + jinja2.Environment(loader=jinja2.BaseLoader) + .from_string(frontmatter) + .render(params) + ) + if frontmatter_rendered: + frontmatter_loaded = yaml.safe_load(frontmatter_rendered) + if not frontmatter_rendered: + frontmatter_loaded = {"frontmatter": None} + content_rendered = ( + jinja2.Environment(loader=jinja2.BaseLoader) + .from_string(content) + .render(params, details=frontmatter_loaded) + ) + frontmatter_dict = dict( + content=markdown.convert(content_rendered), **frontmatter_loaded + ) + if not frontmatter_dict: + raise HyperglassError(f"Error reading YAML frontmatter for {file_name}") return frontmatter_dict @@ -180,12 +211,12 @@ def html(template_name): details_dict = {} for details_name in details_name_list: details_data = details(details_name) - details_dict.update(details_data) + details_dict.update({details_name: details_data}) info_list = ["bgp_route", "bgp_aspath", "bgp_community", "ping", "traceroute"] info_dict = {} for info_name in info_list: info_data = info(info_name) - info_dict.update(info_data) + info_dict.update({info_name: info_data}) try: template_file = f"templates/{template_name}.html.j2" template = env.get_template(template_file)