summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/grafana.py
blob: 2e34187bf3f5db28b409d1097d2765cddf6bb451 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# -*- coding: utf-8 -*-
from __future__ import absolute_import

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