Skip to content
Snippets Groups Projects
dashboard.py 5.79 KiB
Newer Older
Erik Reid's avatar
Erik Reid committed
"""
Grafana Dashhboard API endpoints wrapper functions.
"""
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


Bjarke Madsen's avatar
Bjarke Madsen committed
# 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
Bjarke Madsen's avatar
Bjarke Madsen committed
        logger.exception(f'Error when deleting dashboard with UID #{uid}')
        return False
Bjarke Madsen's avatar
Bjarke Madsen committed
# 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


# lists all dashboards, optionally within a folder
def list_dashboards(request: TokenRequest, folder_id=None):
    params = {
        'query': ''
    }
    if folder_id is not None:
        params['folderIds'] = folder_id

    r = request.get('api/search', params=params)
    return r


# 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

Bjarke Madsen's avatar
Bjarke Madsen committed
# Searches Grafana for a dashboard
# matching the title of the provided dashboard.
def _search_dashboard(request: TokenRequest, dashboard: Dict, folder_id=None):
            'query': dashboard["title"]
        }
        if folder_id is not None:
            params['folderIds'] = folder_id

        r = request.get('api/search', params=params)
        if r and isinstance(r, list):
            if len(r) >= 1:
                for dash in r:
                    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
        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

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