]>
Commit | Line | Data |
---|---|---|
81eedcae | 1 | # -*- coding: utf-8 -*- |
81eedcae TL |
2 | |
3 | import json | |
9f95a23c | 4 | import logging |
81eedcae TL |
5 | import os |
6 | import time | |
f67539c2 | 7 | |
81eedcae TL |
8 | import requests |
9 | ||
81eedcae TL |
10 | from .exceptions import GrafanaError |
11 | from .settings import Settings | |
12 | ||
9f95a23c TL |
13 | logger = logging.getLogger('grafana') |
14 | ||
15 | ||
81eedcae TL |
16 | class GrafanaRestClient(object): |
17 | ||
18 | @staticmethod | |
19 | def url_validation(method, path): | |
20 | response = requests.request( | |
21 | method, | |
92f5a8d4 TL |
22 | path, |
23 | verify=Settings.GRAFANA_API_SSL_VERIFY) | |
81eedcae TL |
24 | return response.status_code |
25 | ||
26 | @staticmethod | |
27 | def push_dashboard(dashboard_obj): | |
28 | if not Settings.GRAFANA_API_URL: | |
29 | raise GrafanaError("The Grafana API URL is not set") | |
30 | if not Settings.GRAFANA_API_URL.startswith('http'): | |
31 | raise GrafanaError("The Grafana API URL is invalid") | |
32 | if not Settings.GRAFANA_API_USERNAME: | |
33 | raise GrafanaError("The Grafana API username is not set") | |
34 | if not Settings.GRAFANA_API_PASSWORD: | |
35 | raise GrafanaError("The Grafana API password is not set") | |
36 | url = Settings.GRAFANA_API_URL.rstrip('/') + \ | |
37 | '/api/dashboards/db' | |
38 | headers = { | |
39 | 'Accept': 'application/json', | |
40 | 'Content-Type': 'application/json', | |
41 | } | |
42 | payload = { | |
43 | 'dashboard': dashboard_obj, | |
44 | 'overwrite': True, | |
45 | } | |
46 | try: | |
47 | response = requests.post( | |
48 | url, | |
49 | headers=headers, | |
50 | data=json.dumps(payload), | |
51 | auth=(Settings.GRAFANA_API_USERNAME, | |
52 | Settings.GRAFANA_API_PASSWORD), | |
92f5a8d4 | 53 | verify=Settings.GRAFANA_API_SSL_VERIFY |
81eedcae TL |
54 | ) |
55 | except requests.ConnectionError: | |
56 | raise GrafanaError("Could not connect to Grafana server") | |
57 | response.raise_for_status() | |
58 | return response.status_code, response.json() | |
59 | ||
60 | ||
61 | class Retrier(object): | |
62 | def __init__(self, tries, sleep, func, *args, **kwargs): | |
63 | """ | |
64 | Wraps a function. An instance of this class may be called to call that | |
65 | function, retrying if it raises an exception. Sleeps between retries, | |
66 | eventually reraising the original exception when retries are exhausted. | |
67 | Once the function returns a value, that value is returned. | |
68 | ||
69 | :param tries: How many times to try, before reraising the exception | |
70 | :type tries: int | |
71 | :param sleep: How many seconds to wait between tries | |
72 | :type sleep: int|float | |
73 | :param func: The function to execute | |
74 | :type func: function | |
75 | :param args: Any arguments to pass to the function | |
76 | :type args: list | |
77 | :param kwargs: Any keyword arguments to pass to the function | |
78 | :type kwargs: dict | |
79 | """ | |
80 | assert tries >= 1 | |
81 | self.tries = int(tries) | |
82 | self.tried = 0 | |
83 | self.sleep = sleep | |
84 | self.func = func | |
85 | self.args = args | |
86 | self.kwargs = kwargs | |
87 | ||
88 | def __call__(self): | |
89 | result = None | |
90 | while self.tried < self.tries: | |
91 | try: | |
92 | result = self.func(*self.args, **self.kwargs) | |
93 | except Exception: # pylint: disable=broad-except | |
94 | if self.tried == self.tries - 1: | |
95 | raise | |
96 | else: | |
97 | self.tried += 1 | |
98 | time.sleep(self.sleep) | |
99 | else: | |
100 | return result | |
101 | ||
102 | ||
103 | def load_local_dashboards(): | |
104 | if os.environ.get('CEPH_DEV') == '1' or 'UNITTEST' in os.environ: | |
105 | path = os.path.abspath(os.path.join( | |
106 | os.path.dirname(__file__), | |
20effc67 | 107 | '../../../../monitoring/ceph-mixin/dashboards_out/' |
81eedcae TL |
108 | )) |
109 | else: | |
110 | path = '/etc/grafana/dashboards/ceph-dashboard' | |
111 | dashboards = dict() | |
112 | for item in [p for p in os.listdir(path) if p.endswith('.json')]: | |
113 | db_path = os.path.join(path, item) | |
114 | with open(db_path) as f: | |
115 | dashboards[item] = json.loads(f.read()) | |
116 | return dashboards | |
117 | ||
118 | ||
119 | def push_local_dashboards(tries=1, sleep=0): | |
120 | try: | |
121 | dashboards = load_local_dashboards() | |
122 | except (EnvironmentError, ValueError): | |
123 | logger.exception("Failed to load local dashboard files") | |
124 | raise | |
125 | ||
126 | def push(): | |
127 | try: | |
128 | grafana = GrafanaRestClient() | |
129 | for body in dashboards.values(): | |
130 | grafana.push_dashboard(body) | |
131 | except Exception: | |
132 | logger.exception("Failed to push dashboards to Grafana") | |
133 | raise | |
134 | retry = Retrier(tries, sleep, push) | |
135 | retry() | |
136 | return True |