diff --git a/README.md b/README.md index 84a65db87513741d4f9e00fba9afedcdbefc103d..6479408687ac93782ed68328e8e8761772716f24 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,13 @@ # BRIAN Dashboard Manager -## Overview +The BRIAN Dashboard Manager is used +provision Organizations and Dashboards in Grafana for BRIAN. -This module is used to provision Organizations and Dashboards in Grafana for BRIAN. -It implements a Flask-based webservice used only to trigger the provisioning process. - -The dashboards are generated from a list of interfaces obtained from Inventory Provider. - -Jinja templates are populated with data from these interfaces to render Dashboard JSON definitions sent to the Grafana API. - -Grafana API-related code lives in the `grafana` package. - -The `brian_dashboard_manager/grafana/provision.py` file is responsible for the entire provisioning lifecycle. - -Grafana API endpoints have wrapper functions in one file for relevant parts of the API, e.g. `brian_dashboard_manager/grafana/dashboard.py` for the dashboard API functions. - -Another example is `brian_dashboard_manager/grafana/organization.py` for the organization API functions. - -Some of the provisioned dashboards are not generated but are just static JSON files. These are put in the `brian_dashboard_manager/dashboards` directory. The same can be done for JSON datasource definitions in the `datasources` directory. - -The `brian_dashboard_manager/templating` package contains the code and Jinja templates used to render dashboard JSON. Most dashboards reuse the same templates, with the exception of NREN-specific dashboards, which has its own template. - -Of note is the `templating/helpers.py` file, which has all of the predicates and helper functions used to group interfaces together and generate the necessary data for the dashboard templates. -The `templating/render.py` has functions for rendering of the various Jinja templates from the given data. - -## Configuration - -This app allows specification of a few -example configuration parameters. These -parameters should stored in a file formatted -similarly to `config.json.example`, and the name -of this file should be stored in the environment -variable `CONFIG_FILENAME` when running the service. - -## Running this module - -This module has been tested in the following execution environments: - -- As an embedded Flask application. - For example, the application could be launched as follows: +Documentation can be generated by running sphinx: ```bash -$ export FLASK_APP=/path/to/brian_dashboard_manager/app.py -$ export CONFIG_FILENAME=/path/to/config.json -$ flask run +sphinx-build -M html docs/source docs/build ``` -- As a `gunicorn` wsgi service. - - Details of `gunicorn` configuration can be found in the brian_dashboard_manager Puppet repository. - -## Protocol Specification - -The following resources can be requested from the webservice. - -### resources -Any non-empty responses are JSON formatted messages. - -## /update - -This resource is used to trigger the provisioning to Grafana. -It responds to the request immediately after starting the provisioning process. - -```json -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "message": { - "type": "string" - } - } -} -``` +The documents should be viewable in the +workspace of the most recent [Jenkins job](https://jenkins.geant.org/job/brian-dashboard-manager/ws/docs/build/html/index.html). diff --git a/brian_dashboard_manager/grafana/__init__.py b/brian_dashboard_manager/grafana/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..46c928562e86e5c599ef512fd2e529cf74206914 100644 --- a/brian_dashboard_manager/grafana/__init__.py +++ b/brian_dashboard_manager/grafana/__init__.py @@ -0,0 +1,22 @@ +""" +Grafana module +=============== + +Grafana API-related code. + +Provisioning +--------------- +.. automodule:: brian_dashboard_manager.grafana.provision + + +Grafana API +------------- +.. automodule:: brian_dashboard_manager.grafana.dashboard + + +Organizations +---------------- +.. automodule:: brian_dashboard_manager.grafana.organization + + +""" diff --git a/brian_dashboard_manager/grafana/dashboard.py b/brian_dashboard_manager/grafana/dashboard.py index 19c1fee56a16c75aa97aca48bb6bdbec43fc50c7..7763af5941be0281dab11ec00955a7d09fdaa906 100644 --- a/brian_dashboard_manager/grafana/dashboard.py +++ b/brian_dashboard_manager/grafana/dashboard.py @@ -1,3 +1,6 @@ +""" +Grafana Dashhboard API endpoints wrapper functions. +""" import logging import os import json diff --git a/brian_dashboard_manager/grafana/organization.py b/brian_dashboard_manager/grafana/organization.py index 4c01096f279801cbbbe7730cd0281a28c112c746..d075660ea874aec41a559abe3b40786dad8d948d 100644 --- a/brian_dashboard_manager/grafana/organization.py +++ b/brian_dashboard_manager/grafana/organization.py @@ -1,3 +1,7 @@ +""" +Grafana Organization management helpers. + +""" import random import string import logging diff --git a/brian_dashboard_manager/grafana/provision.py b/brian_dashboard_manager/grafana/provision.py index 90eae8885608bf52e9ad553356d0252ec612ce0e..bdb0e29bd093a7f527160fcbe8ab35e709d33e8b 100644 --- a/brian_dashboard_manager/grafana/provision.py +++ b/brian_dashboard_manager/grafana/provision.py @@ -1,3 +1,7 @@ +""" +This module is responsible for the +entire provisioning lifecycle. +""" import logging import time from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor diff --git a/brian_dashboard_manager/routes/update.py b/brian_dashboard_manager/routes/update.py index 681c8679fd366fef2ecd856a695aca24d39a207a..ed538e41ebe6f161dea5235e464537b0a8ac3908 100644 --- a/brian_dashboard_manager/routes/update.py +++ b/brian_dashboard_manager/routes/update.py @@ -6,6 +6,16 @@ from brian_dashboard_manager import CONFIG_KEY routes = Blueprint("update", __name__) +UPDATE_RESPONSE_SCHEMA = { + '$schema': 'http://json-schema.org/draft-07/schema#', + 'type': 'object', + 'properties': { + 'message': { + 'type': 'string' + } + } +} + @routes.after_request def after_request(resp): @@ -14,6 +24,19 @@ def after_request(resp): @routes.route('/', methods=['GET']) def update(): + """ + This resource is used to trigger the provisioning to Grafana. + + It responds to the request immediately after starting + the provisioning process. + + The response will be formatted according to the following schema: + + .. asjson:: + brian_dashboard_manager.routes.update.UPDATE_RESPONSE_SCHEMA + + :return: json + """ executor = ThreadPoolExecutor(max_workers=1) executor.submit(provision, current_app.config[CONFIG_KEY]) return {'data': {'message': 'Provisioning dashboards!'}} diff --git a/brian_dashboard_manager/templating/__init__.py b/brian_dashboard_manager/templating/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..76814c2f1654252d2e9d885b5b3574d91d48259c 100644 --- a/brian_dashboard_manager/templating/__init__.py +++ b/brian_dashboard_manager/templating/__init__.py @@ -0,0 +1,28 @@ +""" +Code and +Jinja templates used to render dashboard JSON. +Most dashboards reuse the +same templates, with the exception of +NREN-specific dashboards, which has +its own template. + + +Templates +----------- +Some of the provisioned dashboards are not generated but are just static +JSON files. These are put in the +`brian_dashboard_manager/dashboards` directory. +The same can be done for JSON datasource definitions in +the `datasources` directory. + + +Helpers +--------- +.. automodule:: brian_dashboard_manager.templating.helpers + + +Rendering +--------- +.. automodule:: brian_dashboard_manager.templating.render + +""" diff --git a/brian_dashboard_manager/templating/helpers.py b/brian_dashboard_manager/templating/helpers.py index 1171c9fc88c0f39837382a46aeb6c7e575698555..fea1a1f194874aefc3968fb662c43020351666fd 100644 --- a/brian_dashboard_manager/templating/helpers.py +++ b/brian_dashboard_manager/templating/helpers.py @@ -1,3 +1,8 @@ +""" +Predicates +and helper functions used to group interfaces together and generate the +necessary data for the dashboard templates. +""" import re import logging import json diff --git a/brian_dashboard_manager/templating/render.py b/brian_dashboard_manager/templating/render.py index dede30c87a9d9869551a1680e183e73a2060d1e4..2aa06bf9258275ffc8dd239e0003588bcd7e1a40 100644 --- a/brian_dashboard_manager/templating/render.py +++ b/brian_dashboard_manager/templating/render.py @@ -1,3 +1,7 @@ +""" +Methods for rendering of the +various Jinja templates from the given data. +""" import os import json import jinja2 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d0c3cbf1020d5c292abdedf27627c6abe25e2293 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..a3a3646fdec3cfa425d49b626b7c0cebb4edfc7b --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,101 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +from importlib import import_module +from docutils.parsers.rst import Directive +from docutils import nodes +from sphinx import addnodes +import json +import os +import sys + +sys.path.insert(0, os.path.abspath( + os.path.join( + os.path.dirname(__file__), + '..', '..', 'inventory_provider'))) + + +class RenderAsJSON(Directive): + # cf. https://stackoverflow.com/a/59883833 + + required_arguments = 1 + + def run(self): + module_path, member_name = self.arguments[0].rsplit('.', 1) + + member_data = getattr(import_module(module_path), member_name) + code = json.dumps(member_data, indent=2) + + literal = nodes.literal_block(code, code) + literal['language'] = 'json' + + return [ + addnodes.desc_name(text=member_name), + addnodes.desc_content('', literal) + ] + + +def setup(app): + app.add_directive('asjson', RenderAsJSON) + + +# -- Project information ----------------------------------------------------- + +project = 'BRIAN Dashboard Manager' +copyright = '2021, swd@geant.org' +author = 'swd@geant.org' + +# The full version, including alpha/beta/rc tags +release = '0.0' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx_rtd_theme', + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' +# html_theme = 'alabaster' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Both the class’ and the __init__ method’s docstring +# are concatenated and inserted. +autoclass_content = "both" +autodoc_typehints = "none" diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst new file mode 100644 index 0000000000000000000000000000000000000000..5f35ebc14879888fe5692152ae72176b34f0f25a --- /dev/null +++ b/docs/source/configuration.rst @@ -0,0 +1,32 @@ + +Configuration and Running +========================= + +Configuration +------------- + +This app allows specification of a few +example configuration parameters. These +parameters should stored in a file formatted +similarly to `config.json.example`, and the name +of this file should be stored in the environment +variable `CONFIG_FILENAME` when running the service. + +Running this module +--------------------- + +This module has been tested in the following execution environments: + +* As an embedded Flask application. + For example, the application could be launched as follows: + +.. code-block:: python + + export FLASK_APP=/path/to/brian_dashboard_manager/app.py + export CONFIG_FILENAME=/path/to/config.json + flask run + +* As a `gunicorn` wsgi service. + + * Details of `gunicorn` configuration can be found in the + brian_dashboard_manager Puppet repository. \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..97444a54aef7962b68d0cc7ec16d4ccf7d46fad0 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,20 @@ +.. BRIAN Dashboard Manager documentation master file, created by + sphinx-quickstart on Tue Mar 16 14:42:57 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +Inventory Provider +================== + +The BRIAN Dashboard Manager is used +provision Organizations and Dashboards in Grafana for BRIAN. + + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + configuration + overview + protocol diff --git a/docs/source/overview.rst b/docs/source/overview.rst new file mode 100644 index 0000000000000000000000000000000000000000..063ba7ee1154fa4fcdb8dd5a5af1140b9961b305 --- /dev/null +++ b/docs/source/overview.rst @@ -0,0 +1,14 @@ + +Overview +========================= + +This module is used to provision Organizations and Dashboards inGrafana for BRIAN. + +The dashboards are generated from a list of interfaces obtained from Inventory Provider. + +Jinja templates are populated with data from these interfaces to render +Dashboard JSON definitions sent to the Grafana API. + +.. automodule:: brian_dashboard_manager.grafana + +.. automodule:: brian_dashboard_manager.templating diff --git a/docs/source/protocol.rst b/docs/source/protocol.rst new file mode 100644 index 0000000000000000000000000000000000000000..bb1ebfdf9b9b4cce9e5c1b8fcc0875325e192086 --- /dev/null +++ b/docs/source/protocol.rst @@ -0,0 +1,19 @@ + +Protocol +========================= + + +This module implements a Flask-based webservice used only to +trigger the provisioning process. + +The following resources can be requested from the webservice. + +resources +----------- +Any non-empty responses are JSON formatted messages. + + +/update +********* + +.. autofunction:: brian_dashboard_manager.routes.update.update diff --git a/requirements.txt b/requirements.txt index f90bc88a6934a897286fbd219cf8157f55ad5f6e..c4cf279340074c24ff4fad9e7766c9f3441f0138 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,11 @@ requests jsonschema flask +jinja2 + pytest pytest-mock responses -jinja2 \ No newline at end of file +sphinx +sphinx-rtd-theme + diff --git a/tox.ini b/tox.ini index 31480e823de253929907310ceab787c6e312ef51..47b349ff0fce3c58ee87abb1c0eafc8ee8330afa 100644 --- a/tox.ini +++ b/tox.ini @@ -16,4 +16,5 @@ commands = coverage xml coverage html coverage report --fail-under 75 - flake8 \ No newline at end of file + flake8 + sphinx-build -M html docs/source docs/build