From f9d3952f19bce21d7872d358a9c64bfd86f902a1 Mon Sep 17 00:00:00 2001 From: Bjarke Madsen <bjarke.madsen@geant.org> Date: Wed, 31 Mar 2021 17:08:58 +0200 Subject: [PATCH] Template the home dashboard --- .../grafana/organization.py | 21 ++- brian_dashboard_manager/grafana/provision.py | 15 +- .../templates/homedashboard.json.j2} | 174 +++++++++++++----- test/test_update.py | 26 ++- 4 files changed, 182 insertions(+), 54 deletions(-) rename brian_dashboard_manager/{dashboards/home.json => templating/templates/homedashboard.json.j2} (85%) mode change 100755 => 100644 diff --git a/brian_dashboard_manager/grafana/organization.py b/brian_dashboard_manager/grafana/organization.py index d075660..bf8f2e8 100644 --- a/brian_dashboard_manager/grafana/organization.py +++ b/brian_dashboard_manager/grafana/organization.py @@ -5,11 +5,14 @@ Grafana Organization management helpers. import random import string import logging +import jinja2 +import json +import os from typing import Dict, List, Union from datetime import datetime - from brian_dashboard_manager.grafana.utils.request import AdminRequest, \ TokenRequest +from brian_dashboard_manager.grafana.dashboard import create_dashboard logger = logging.getLogger(__name__) @@ -94,8 +97,20 @@ def delete_expired_api_tokens(request: AdminRequest, org_id: int) -> bool: return True -def set_home_dashboard(request: TokenRequest, dashboard_id: int): +def set_home_dashboard(request: TokenRequest, is_staff): + file = os.path.abspath(os.path.join( + os.path.dirname(__file__), + '..', + 'templating', + 'templates', + 'homedashboard.json.j2')) + + with open(file) as f: + template = jinja2.Template(f.read()) + rendered = template.render({'staff': is_staff}) + rendered = json.loads(rendered) + dashboard = create_dashboard(request, rendered) r = request.put('api/org/preferences', json={ - 'homeDashboardId': dashboard_id + 'homeDashboardId': dashboard.get('id') }) return r and r.get('message') == 'Preferences updated' diff --git a/brian_dashboard_manager/grafana/provision.py b/brian_dashboard_manager/grafana/provision.py index 685be93..345317f 100644 --- a/brian_dashboard_manager/grafana/provision.py +++ b/brian_dashboard_manager/grafana/provision.py @@ -2,6 +2,7 @@ This module is responsible for the entire provisioning lifecycle. """ +import os import logging import time import json @@ -15,8 +16,7 @@ from brian_dashboard_manager.grafana.organization import \ get_organizations, create_organization, create_api_token, \ delete_api_token, delete_expired_api_tokens, set_home_dashboard from brian_dashboard_manager.grafana.dashboard import \ - get_dashboard_definitions, create_dashboard, find_dashboard, \ - delete_dashboard + get_dashboard_definitions, create_dashboard, delete_dashboard from brian_dashboard_manager.grafana.datasource import \ check_provisioned, create_datasource from brian_dashboard_manager.grafana.folder import find_folder, \ @@ -105,8 +105,10 @@ def provision_maybe(config): f.truncate() try: + # don't conditionally provision in dev + val = os.environ.get('FLASK_ENV') != 'development' now = datetime.datetime.now() - write_timestamp(now.timestamp(), True) + write_timestamp(now.timestamp(), val) provision(config) finally: now = datetime.datetime.now() @@ -321,8 +323,6 @@ def provision(config): logger.info('Provisioning static dashboards') for dashboard in get_dashboard_definitions(): if dashboard['title'] not in excluded_dashboards: - if dashboard['title'].lower() == 'home': - dashboard['uid'] = 'home' create_dashboard(token_request, dashboard) else: delete_dashboard(token_request, dashboard) @@ -330,9 +330,8 @@ def provision(config): # Home dashboard is always called "Home" # Make sure it's set for the organization logger.info('Configuring Home dashboard') - home_dashboard = find_dashboard(token_request, 'Home') - if home_dashboard: - set_home_dashboard(token_request, home_dashboard['id']) + is_staff = org['name'] == 'GÉANT Staff' + set_home_dashboard(token_request, is_staff) logger.info(f'Time to complete: {time.time() - start}') for org_id, token in tokens: diff --git a/brian_dashboard_manager/dashboards/home.json b/brian_dashboard_manager/templating/templates/homedashboard.json.j2 old mode 100755 new mode 100644 similarity index 85% rename from brian_dashboard_manager/dashboards/home.json rename to brian_dashboard_manager/templating/templates/homedashboard.json.j2 index 15fa12f..1537116 --- a/brian_dashboard_manager/dashboards/home.json +++ b/brian_dashboard_manager/templating/templates/homedashboard.json.j2 @@ -1,7 +1,7 @@ { "annotations": { "list": [ - { + { "builtIn": 1, "datasource": "-- Grafana --", "enable": true, @@ -16,50 +16,52 @@ "gnetId": null, "graphTooltip": 0, "id": 49, + "uid": "home", "iteration": 1595947519970, "links": [ - { - "asDropdown": true, - "icon": "external link", - "tags": [ - "services" - ], - "targetBlank": true, - "title": "Services", - "type": "dashboards" - }, - { - "asDropdown": true, - "icon": "external link", - "tags": [ - "infrastructure" - ], - "targetBlank": true, - "title": "Infrastructure", - "type": "dashboards" - }, - { - "asDropdown": true, - "icon": "external link", - "tags": [ - "customers" - ], - "targetBlank": true, - "title": "NREN Access", - "type": "dashboards" - }, - { - "asDropdown": true, - "icon": "external link", - "tags": [ - "peers" - ], - "targetBlank": true, - "title": "Peers", - "type": "dashboards" - } + { + "asDropdown": true, + "icon": "external link", + "tags": [ + "services" + ], + "targetBlank": true, + "title": "Services", + "type": "dashboards" + }, + { + "asDropdown": true, + "icon": "external link", + "tags": [ + "infrastructure" + ], + "targetBlank": true, + "title": "Infrastructure", + "type": "dashboards" + }, + { + "asDropdown": true, + "icon": "external link", + "tags": [ + "customers" + ], + "targetBlank": true, + "title": "NREN Access", + "type": "dashboards" + }, + { + "asDropdown": true, + "icon": "external link", + "tags": [ + "peers" + ], + "targetBlank": true, + "title": "Peers", + "type": "dashboards" + } ], "panels": [ + {% if staff %} { "aliasColors": {}, "bars": false, @@ -636,12 +638,99 @@ "alignLevel": null } } + {% else %} + { + "datasource": "PollerInfluxDB", + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "folderId": null, + "gridPos": { + "h": 24, + "w": 24, + "x": 0, + "y": 0 + }, + "headings": true, + "id": 2, + "limit": 100, + "pluginVersion": "7.2.1", + "query": "", + "recent": false, + "repeat": null, + "search": true, + "starred": true, + "tags": [], + "targets": [ + { + "groupBy": [ + { + "params": [ + "$__interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Dashboards", + "type": "dashlist" + } + {% endif %} ], "schemaVersion": 26, "style": "dark", "tags": [], "templating": { "list": [ + {% if staff %} { "allValue": null, "datasource": "PollerInfluxDB", @@ -684,6 +773,7 @@ "type": "query", "useTags": false } + {% endif %} ] }, "time": { @@ -706,4 +796,4 @@ "timezone": "", "title": "Home", "version": 1 -} +} \ No newline at end of file diff --git a/test/test_update.py b/test/test_update.py index 68c3d10..4b63f49 100644 --- a/test/test_update.py +++ b/test/test_update.py @@ -366,6 +366,30 @@ def test_provision(data_config, mocker, client): url=f"http://{data_config['hostname']}/api/datasources", callback=datasources) + def createdashboard(request): + return 200, {}, json.dumps({'id': 666}) + + responses.add_callback( + method=responses.POST, + url=f"http://{data_config['hostname']}/api/dashboards/db", + callback=createdashboard) + + def preferences(request): + return 200, {}, json.dumps({'message': 'Preferences updated'}) + + responses.add_callback( + method=responses.PUT, + url=f"http://{data_config['hostname']}/api/org/preferences", + callback=preferences) + + def homedashboard(request): + return 404, {}, '' + + responses.add_callback( + method=responses.GET, + url=f"http://{data_config['hostname']}/api/dashboards/uid/home", + callback=homedashboard) + PROVISIONED_ORGANIZATION = { 'name': data_config['organizations'][0], 'id': 0 @@ -416,7 +440,7 @@ def test_provision(data_config, mocker, client): 'brian_dashboard_manager.grafana.provision.create_dashboard') # we dont care about this, just mark it created # we dont care about this, tested separately - _mocked_create_dashboard.return_value = None + _mocked_create_dashboard.return_value = {'id': 666} _mocked_delete_api_token = mocker.patch( 'brian_dashboard_manager.grafana.provision.delete_api_token') -- GitLab