blob: 42599e5a4599a504ad966909a9af57a0ae82e197 [file] [log] [blame]
Ales Komarek3f044b22016-10-30 00:27:24 +02001# -*- coding: utf-8 -*-
2'''
3Manage Grafana v3.0 data sources
4
5.. versionadded:: 2016.3.0
6
7Token auth setup
8
9.. code-block:: yaml
10
11 grafana:
12 grafana_version: 3
13 grafana_timeout: 5
14 grafana_token: qwertyuiop
15 grafana_url: 'https://url.com'
16
17Basic auth setup
18
19.. code-block:: yaml
20
21 grafana:
22 grafana_version: 3
23 grafana_timeout: 5
24 grafana_user: grafana
25 grafana_password: qwertyuiop
26 grafana_url: 'https://url.com'
27
28.. code-block:: yaml
29
30 Ensure influxdb data source is present:
31 grafana_datasource.present:
32 - name: influxdb
33 - type: influxdb
34 - url: http://localhost:8086
35 - access: proxy
36 - basic_auth: true
37 - basic_auth_user: myuser
38 - basic_auth_password: mypass
39 - is_default: true
40'''
41from __future__ import absolute_import
42
43import requests
44
45from salt.ext.six import string_types
46
47
48def __virtual__():
49 '''Only load if grafana v3.0 is configured.'''
50 return __salt__['config.get']('grafana_version', 1) == 3
51
52
53def present(name,
54 type,
55 url,
56 access='proxy',
57 user='',
58 password='',
59 database='',
60 basic_auth=False,
61 basic_auth_user='',
62 basic_auth_password='',
63 is_default=False,
64 type_logo_url='public/app/plugins/datasource/graphite/img/graphite_logo.png',
65 with_credentials=False,
66 json_data=None,
67 profile='grafana'):
68 '''
69 Ensure that a data source is present.
70
71 name
72 Name of the data source.
73
74 type
75 Which type of data source it is ('graphite', 'influxdb' etc.).
76
77 url
78 The URL to the data source API.
79
80 user
81 Optional - user to authenticate with the data source
82
83 password
84 Optional - password to authenticate with the data source
85
86 basic_auth
87 Optional - set to True to use HTTP basic auth to authenticate with the
88 data source.
89
90 basic_auth_user
91 Optional - HTTP basic auth username.
92
93 basic_auth_password
94 Optional - HTTP basic auth password.
95
96 is_default
97 Default: False
98 '''
99 if isinstance(profile, string_types):
100 profile = __salt__['config.option'](profile)
101
102 ret = {'name': name, 'result': None, 'comment': None, 'changes': None}
103 datasource = _get_datasource(profile, name)
104 data = _get_json_data(name, type, url, access, user, password, database,
105 basic_auth, basic_auth_user, basic_auth_password, is_default, json_data)
106
107 if datasource:
108 if profile.get('grafana_token', False):
109 requests.put(
110 _get_url(profile, datasource['id']),
111 data,
112 headers=_get_headers(profile),
113 timeout=profile.get('grafana_timeout', 3),
114 )
115 else:
116 requests.put(
117 _get_url(profile, datasource['id']),
118 data,
119 auth=_get_auth(profile),
120 timeout=profile.get('grafana_timeout', 3),
121 )
122 ret['result'] = True
123 ret['changes'] = _diff(datasource, data)
124 if ret['changes']['new'] or ret['changes']['old']:
125 ret['comment'] = 'Data source {0} updated'.format(name)
126 else:
127 ret['changes'] = None
128 ret['comment'] = 'Data source {0} already up-to-date'.format(name)
129 else:
130 requests.post(
131 '{0}/api/datasources'.format(profile['grafana_url']),
132 data,
133 headers=_get_headers(profile),
134 timeout=profile.get('grafana_timeout', 3),
135 )
136 ret['result'] = True
137 ret['comment'] = 'New data source {0} added'.format(name)
138 ret['changes'] = data
139
140 return ret
141
142
143def absent(name, profile='grafana'):
144 '''
145 Ensure that a data source is present.
146
147 name
148 Name of the data source to remove.
149 '''
150 if isinstance(profile, string_types):
151 profile = __salt__['config.option'](profile)
152
153 ret = {'result': None, 'comment': None, 'changes': None}
154 datasource = _get_datasource(profile, name)
155
156 if not datasource:
157 ret['result'] = True
158 ret['comment'] = 'Data source {0} already absent'.format(name)
159 return ret
160
161 if profile.get('grafana_token', False):
162 requests.delete(
163 _get_url(profile, datasource['id']),
164 headers=_get_headers(profile),
165 timeout=profile.get('grafana_timeout', 3),
166 )
167 else:
168 requests.delete(
169 _get_url(profile, datasource['id']),
170 auth=_get_auth(profile),
171 timeout=profile.get('grafana_timeout', 3),
172 )
173
174 ret['result'] = True
175 ret['comment'] = 'Data source {0} was deleted'.format(name)
176
177 return ret
178
179
180def _get_url(profile, datasource_id):
181 return '{0}/api/datasources/{1}'.format(
182 profile['grafana_url'],
183 datasource_id
184 )
185
186
187def _get_datasource(profile, name):
188 if profile.get('grafana_token', False):
189 response = requests.get(
190 '{0}/api/datasources'.format(profile['grafana_url']),
191 headers=_get_headers(profile),
192 timeout=profile.get('grafana_timeout', 3),
193 )
194 else:
195 response = requests.get(
196 '{0}/api/datasources'.format(profile['grafana_url']),
197 auth=_get_auth(profile),
198 timeout=profile.get('grafana_timeout', 3),
199 )
Adam Tengler0488f932016-11-30 11:59:59 +0100200 if response.status_code >= 400:
201 response.raise_for_status()
Ales Komarek3f044b22016-10-30 00:27:24 +0200202 data = response.json()
203 for datasource in data:
204 if datasource['name'] == name:
205 return datasource
206 return None
207
208
209def _get_headers(profile):
210 return {
211 'Accept': 'application/json',
212 'Authorization': 'Bearer {0}'.format(profile['grafana_token'])
213 }
214
215
216def _get_auth(profile):
217 return requests.auth.HTTPBasicAuth(
218 profile['grafana_user'],
219 profile['grafana_password']
220 )
221
222
223def _get_json_data(name,
224 type,
225 url,
226 access='proxy',
227 user='',
228 password='',
229 database='',
230 basic_auth=False,
231 basic_auth_user='',
232 basic_auth_password='',
233 is_default=False,
234 type_logo_url='public/app/plugins/datasource/graphite/img/graphite_logo.png',
235 with_credentials=False,
236 json_data=None):
237 return {
238 'name': name,
239 'type': type,
240 'url': url,
241 'access': access,
242 'user': user,
243 'password': password,
244 'database': database,
245 'basicAuth': basic_auth,
246 'basicAuthUser': basic_auth_user,
247 'basicAuthPassword': basic_auth_password,
248 'isDefault': is_default,
249 'typeLogoUrl': type_logo_url,
250 'withCredentials': with_credentials,
251 'jsonData': json_data,
252 }
253
254
255def _diff(old, new):
256 old_keys = old.keys()
257 old = old.copy()
258 new = new.copy()
259 for key in old_keys:
260 if key == 'id' or key == 'orgId':
261 del old[key]
262 elif old[key] == new[key]:
263 del old[key]
264 del new[key]
265 return {'old': old, 'new': new}