diff options
Diffstat (limited to '')
-rw-r--r-- | src/pybind/mgr/dashboard/grafana.py | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/grafana.py b/src/pybind/mgr/dashboard/grafana.py new file mode 100644 index 000000000..8edf9c57d --- /dev/null +++ b/src/pybind/mgr/dashboard/grafana.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- + +import json +import logging +import os +import time + +import requests + +from .exceptions import GrafanaError +from .settings import Settings + +logger = logging.getLogger('grafana') + + +class GrafanaRestClient(object): + + @staticmethod + def url_validation(method, path): + response = requests.request( + method, + path, + verify=Settings.GRAFANA_API_SSL_VERIFY) + return response.status_code + + @staticmethod + def push_dashboard(dashboard_obj): + if not Settings.GRAFANA_API_URL: + raise GrafanaError("The Grafana API URL is not set") + if not Settings.GRAFANA_API_URL.startswith('http'): + raise GrafanaError("The Grafana API URL is invalid") + if not Settings.GRAFANA_API_USERNAME: + raise GrafanaError("The Grafana API username is not set") + if not Settings.GRAFANA_API_PASSWORD: + raise GrafanaError("The Grafana API password is not set") + url = Settings.GRAFANA_API_URL.rstrip('/') + \ + '/api/dashboards/db' + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + } + payload = { + 'dashboard': dashboard_obj, + 'overwrite': True, + } + try: + response = requests.post( + url, + headers=headers, + data=json.dumps(payload), + auth=(Settings.GRAFANA_API_USERNAME, + Settings.GRAFANA_API_PASSWORD), + verify=Settings.GRAFANA_API_SSL_VERIFY + ) + except requests.ConnectionError: + raise GrafanaError("Could not connect to Grafana server") + response.raise_for_status() + return response.status_code, response.json() + + +class Retrier(object): + def __init__(self, tries, sleep, func, *args, **kwargs): + """ + Wraps a function. An instance of this class may be called to call that + function, retrying if it raises an exception. Sleeps between retries, + eventually reraising the original exception when retries are exhausted. + Once the function returns a value, that value is returned. + + :param tries: How many times to try, before reraising the exception + :type tries: int + :param sleep: How many seconds to wait between tries + :type sleep: int|float + :param func: The function to execute + :type func: function + :param args: Any arguments to pass to the function + :type args: list + :param kwargs: Any keyword arguments to pass to the function + :type kwargs: dict + """ + assert tries >= 1 + self.tries = int(tries) + self.tried = 0 + self.sleep = sleep + self.func = func + self.args = args + self.kwargs = kwargs + + def __call__(self): + result = None + while self.tried < self.tries: + try: + result = self.func(*self.args, **self.kwargs) + except Exception: # pylint: disable=broad-except + if self.tried == self.tries - 1: + raise + else: + self.tried += 1 + time.sleep(self.sleep) + else: + return result + + +def load_local_dashboards(): + if os.environ.get('CEPH_DEV') == '1' or 'UNITTEST' in os.environ: + path = os.path.abspath(os.path.join( + os.path.dirname(__file__), + '../../../../monitoring/ceph-mixin/dashboards_out/' + )) + else: + path = '/etc/grafana/dashboards/ceph-dashboard' + dashboards = dict() + for item in [p for p in os.listdir(path) if p.endswith('.json')]: + db_path = os.path.join(path, item) + with open(db_path) as f: + dashboards[item] = json.loads(f.read()) + return dashboards + + +def push_local_dashboards(tries=1, sleep=0): + try: + dashboards = load_local_dashboards() + except (EnvironmentError, ValueError): + logger.exception("Failed to load local dashboard files") + raise + + def push(): + try: + grafana = GrafanaRestClient() + for body in dashboards.values(): + grafana.push_dashboard(body) + except Exception: + logger.exception("Failed to push dashboards to Grafana") + raise + retry = Retrier(tries, sleep, push) + retry() + return True |