diff options
-rw-r--r-- | templating/README.md | 70 | ||||
-rwxr-xr-x | templating/templating.py | 37 | ||||
-rw-r--r-- | web/templates/test.conf | 17 |
3 files changed, 113 insertions, 11 deletions
diff --git a/templating/README.md b/templating/README.md new file mode 100644 index 0000000..89f472e --- /dev/null +++ b/templating/README.md @@ -0,0 +1,70 @@ +# Templating Engine + +This engine does the templating for The Gathering. + +The flask server sits behind varnish and waits for incomming GET requests with a template name and optional variables. + +```varnish +backend templating { + .host = "::1"; + .port = "8081"; +} +.... +if (req.url ~ "^/api/templates") { + set req.url = regsub(req.url,"^/api/templates",""); + set req.backend_hint = templating; +} +``` + +## Requirements + +* Python3.6 +* jinja2 +* requests +* flask +* netaddr + +## Settings + +``` +python3 templating.py +usage: templating.py [-t TEMPLATES [TEMPLATES ...]] [-h HOST] [-p PORT] [-d] + [-s SERVER] [-x TIMEOUT] + +Process templates for gondul. + +optional arguments: + -t TEMPLATES [TEMPLATES ...], --templates TEMPLATES [TEMPLATES ...] + location of templates + -h HOST, --host HOST host address + -p PORT, --port PORT host port + -d, --debug enable debug mode + -s SERVER, --server SERVER + gondul server address + -x TIMEOUT, --timeout TIMEOUT + gondul server timeout +``` + +## How to test locally + +You need a directory with all the jinja2 templates. I just assume you git cloned the entire gondul repo. + +An example using [test.conf](../web/templates/test.conf) + +```bash +python3 templating.py --host ::1 --port 8081 --templates ../web/templates --server http://tech:rules@<gondul>:80 +``` + +```bash +curl -s "http://[::1]:8081/test.conf?switch=e1-1" | jq . +{ + "distro_name": "core-dev", + "placement": { + "height": 20, + "width": 250, + "x": "830", + "y": "620" + }, + "tags": [] +} +``` diff --git a/templating/templating.py b/templating/templating.py index 87853b6..1eff4a3 100755 --- a/templating/templating.py +++ b/templating/templating.py @@ -8,20 +8,26 @@ import netaddr import requests from flask import Flask, request -from jinja2 import Environment, FileSystemLoader, TemplateNotFound +from jinja2 import Environment, FileSystemLoader, TemplateNotFound, TemplateError -endpoints = "read/networks read/oplog read/snmp read/switches-management public/distro-tree public/config public/dhcp public/dhcp-summary public/ping public/switches public/switch-state".split() +endpoints = ["read/networks", "read/oplog", "read/snmp", "read/switches-management", "public/distro-tree", + "public/config", "public/dhcp", "public/dhcp-summary", "public/ping", "public/switches", + "public/switch-state"] objects = {} -def getEndpoint(endpoint): - r = requests.get("http://localhost:80/api/{}".format(endpoint)) - if r.status_code != 200: - raise Exception("Bad status code for endpoint {}: {}".format(endpoint, r.status_code)) - return r.json() +def getEndpoint(endpoint: str) -> dict: + """ + Fetches an endpoint and returns the data as a dict. + """ + uri = f"{args.server}/api/{endpoint}" + r = requests.get(uri, timeout=args.timeout) + r.raise_for_status() + return r.json() + def updateData(): for a in endpoints: objects[a] = getEndpoint(a) @@ -52,14 +58,20 @@ def add_header(response): @app.route("/<path>", methods=["GET"]) def root_get(path): - updateData() try: + updateData() template = env.get_template(path) body = template.render(objects=objects, options=request.args) + except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError) as error: + return f'Timeout or connection error from gondul: {err}', 500 except TemplateNotFound: - return 'Template "{}" not found\n'.format(path), 404 + return f'Template "{path}" not found\n', 404 + except TemplateError as err: + return f'Templating of "{path}" failed to render. Most likely due to an error in the template. Error transcript:\n\n{err}\n----\n\n{traceback.format_exc()}\n', 400 + except requests.exceptions.HTTPError as err: + return f'HTTP error from gondul: {err}', 500 except Exception as err: - return 'Templating of "{}" failed to render. Most likely due to an error in the template. Error transcript:\n\n{}\n----\n\n{}\n'.format(path, err, traceback.format_exc()), 400 + return f'Uncaught error: {err}', 500 return body, 200 @@ -71,7 +83,7 @@ def root_post(path): template = env.from_string(content.decode("utf-8")) body = template.render(objects=objects, options=request.args) except Exception as err: - return 'Templating of "{}" failed to render. Most likely due to an error in the template. Error transcript:\n\n{}\n----\n\n{}\n'.format(path, err, traceback.format_exc()), 400 + return 'Templating of "{path}" failed to render. Most likely due to an error in the template. Error transcript:\n\n{err}\n----\n\n{traceback.format_exc()}\n', 400 return body, 200 @@ -80,11 +92,14 @@ parser.add_argument("-t", "--templates", type=str, nargs="+", help="location of parser.add_argument("-h", "--host", type=str, default="127.0.0.1", help="host address") parser.add_argument("-p", "--port", type=int, default=8080, help="host port") parser.add_argument("-d", "--debug", action="store_true", help="enable debug mode") +parser.add_argument("-s", "--server", type=str, default="http://localhost:80", help="gondul server address") +parser.add_argument("-x", "--timeout", type=int, default=2, help="gondul server timeout") args = parser.parse_args() env.loader.searchpath = args.templates if not sys.argv[1:]: parser.print_help() + sys.exit(1) app.run(host=args.host, port=args.port, debug=args.debug) diff --git a/web/templates/test.conf b/web/templates/test.conf new file mode 100644 index 0000000..ce2fe0d --- /dev/null +++ b/web/templates/test.conf @@ -0,0 +1,17 @@ +{# Fetches something simple from gondul #} +{# Query parameters: ?switch=e1-1 #} + +{# Check if ?switch option is given#} +{% if not options["switch"] %} + {# pretty print public/switches endpoint#} + {{ objects["public/switches"] | pprint }} +{% else %} + {# sets sw variable using the query parameter #} + {% set sw = options["switch"] %} +{% endif %} +{% if sw %} + {# find the correct switch from public/switches api endpoint using query paramter#} + {% set switch = objects["public/switches"].switches[sw] %} + {# pretty print info in api #} + {{ switch | tojson }} +{% endif %} |