Skip to content
Snippets Groups Projects
dashboard.py 5.54 KiB
"""
Grafana Dashhboard API endpoints wrapper functions.
"""
import logging
import os
import json
from typing import Dict

from requests.models import HTTPError
from brian_dashboard_manager.grafana.utils.request import TokenRequest

logger = logging.getLogger(__name__)


# Returns dictionary for each dashboard JSON definition in supplied directory
def get_dashboard_definitions(dir=None):  # pragma: no cover
    dashboard_dir = dir or os.path.join(
        os.path.dirname(__file__), '../dashboards/')
    for (dirpath, _, filenames) in os.walk(dashboard_dir):
        for file in filenames:
            if file.endswith('.json'):
                filename = os.path.join(dirpath, file)
                dashboard = json.load(open(filename, 'r'))
                yield dashboard


def delete_dashboard(request: TokenRequest, dashboard, folder_id=None):
    try:
        r = None
        uid = dashboard.get('uid')
        if uid:
            return _delete_dashboard(request, uid)
        elif dashboard.get('title'):
            # if a folder ID is not supplied,
            # dashboard title should be globally unique
            dash = _search_dashboard(request, dashboard, folder_id)
            if dash is None:
                return True
            _delete_dashboard(request, dash.get(
                'dashboard', {}).get('uid', ''))

        logger.info(f'Deleted dashboard: {dashboard.get("title")}')
        return r is not None

    except HTTPError:
        dump = json.dumps(dashboard, indent=2)
        logger.exception(
            f'Error when deleting dashboard:\n{dump}')
        return None


# Deletes a single dashboard for the organization
# the API token is registered to.
def _delete_dashboard(request: TokenRequest, uid: int):
    try:
        r = request.delete(f'api/dashboards/uid/{uid}')
        if r and 'deleted' in r.get('message', ''):
            return True
    except HTTPError as e:
        if e.response is not None and e.response.status_code == 404:
            return True
        logger.exception(f'Error when deleting dashboard with UID #{uid}')
        return False


# Deletes all dashboards for the organization
# the API token is registered to.
def delete_dashboards(request: TokenRequest):
    r = request.get('api/search')
    if r and len(r) > 0:
        for dash in r:
            _delete_dashboard(request, dash['uid'])
    return True


# Searches for a dashboard with given title
def find_dashboard(request: TokenRequest, title=None):
    param = {
        **({'query': title} if title else {}),
        'type': 'dash-db',
        'limit': 5000,
        'page': 1
    }
    r = request.get('api/search', params=param)
    if r and len(r) > 0:
        if title:
            return r[0]
        else:
            while True:
                param['page'] += 1
                page = request.get('api/search', params=param)
                if len(page) > 0:
                    r.extend(page)
                else:
                    break
            return r

    return None


# Searches Grafana for a dashboard
# matching the title of the provided dashboard.
def _search_dashboard(request: TokenRequest, dashboard: Dict, folder_id=None):
    try:
        r = request.get('api/search', params={
            'query': dashboard["title"]
        })
        if r and isinstance(r, list):
            if len(r) >= 1:
                for dash in r:
                    if folder_id:
                        if folder_id != dash.get('folderId'):
                            continue
                    if dash['title'] == dashboard['title']:
                        definition = _get_dashboard(request, dash['uid'])
                        return definition
        return None
    except HTTPError:
        return None


# Fetches dashboard with given UID for the token's organization.
def _get_dashboard(request: TokenRequest, uid: int):

    try:
        r = request.get(f'api/dashboards/uid/{uid}')
    except HTTPError:
        return None
    return r


# Creates or updates (if exists) given dashboard for the token's organization.
# supplied dashboards are JSON blobs exported from GUI with a UID.
def create_dashboard(request: TokenRequest, dashboard: Dict, folder_id=None):

    title = dashboard['title']
    existing_dashboard = None
    has_uid = dashboard.get('uid') is not None
    if has_uid:
        existing_dashboard = _get_dashboard(request, uid=dashboard['uid'])

    # The title might not match the one that's provisioned with that UID.
    # Try to find it by searching for the title instead.
    if existing_dashboard is not None:
        grafana_title = existing_dashboard['dashboard']['title']
        different = grafana_title != title
    else:
        different = False

    if existing_dashboard is None or different:
        existing_dashboard = _search_dashboard(request, dashboard, folder_id)

    if existing_dashboard:
        dashboard['uid'] = existing_dashboard['dashboard']['uid']
        dashboard['id'] = existing_dashboard['dashboard']['id']
        dashboard['version'] = existing_dashboard['dashboard']['version']
    else:
        # We are creating a new dashboard, delete ID if it exists.
        del dashboard['id']

    payload = {
        'dashboard': dashboard,
        'overwrite': False
    }
    if folder_id:
        payload['folderId'] = folder_id

    try:
        # action = "Updating" if existing_dashboard else "Creating"
        # logger.info(f'{action} dashboard: {title}')
        r = request.post('api/dashboards/db', json=payload)
        return r
    except HTTPError:
        logger.exception(f'Error when provisioning dashboard {title}')
    return None