""" 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 # 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 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: params = { '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 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