| # -*- coding: utf-8 -*- |
| ''' |
| Manage Grafana v3.0 data sources |
| |
| .. versionadded:: 2016.3.0 |
| |
| Token auth setup |
| |
| .. code-block:: yaml |
| |
| grafana_version: 3 |
| grafana: |
| grafana_timeout: 5 |
| grafana_token: qwertyuiop |
| grafana_url: 'https://url.com' |
| |
| Basic auth setup |
| |
| .. code-block:: yaml |
| |
| grafana_version: 3 |
| grafana: |
| grafana_timeout: 5 |
| grafana_user: grafana |
| grafana_password: qwertyuiop |
| grafana_url: 'https://url.com' |
| |
| .. code-block:: yaml |
| |
| Ensure influxdb data source is present: |
| grafana_datasource.present: |
| - name: influxdb |
| - type: influxdb |
| - url: http://localhost:8086 |
| - access: proxy |
| - basic_auth: true |
| - basic_auth_user: myuser |
| - basic_auth_password: mypass |
| - is_default: true |
| ''' |
| from __future__ import absolute_import |
| |
| import json |
| import requests |
| |
| from salt.ext.six import string_types |
| |
| |
| def __virtual__(): |
| '''Only load if grafana v3.0 is configured.''' |
| return __salt__['config.get']('grafana_version', 1) == 3 |
| |
| |
| def present(name, |
| type, |
| url, |
| access='proxy', |
| user='', |
| password='', |
| database='', |
| basic_auth=False, |
| basic_auth_user='', |
| basic_auth_password='', |
| mode=None, |
| domain='default', |
| project=None, |
| is_default=False, |
| profile='grafana'): |
| ''' |
| Ensure that a data source is present. |
| |
| name |
| Name of the data source. |
| |
| type |
| Which type of data source it is ('graphite', 'influxdb' etc.). |
| |
| access |
| Use proxy or direct. Default: proxy |
| |
| url |
| The URL to the data source API. |
| |
| user |
| Optional - user to authenticate with the data source |
| |
| password |
| Optional - password to authenticate with the data source |
| |
| database |
| Optional - database to use with the data source |
| |
| basic_auth |
| Optional - set to True to use HTTP basic auth to authenticate with the |
| data source. |
| |
| basic_auth_user |
| Optional - HTTP basic auth username. |
| |
| basic_auth_password |
| Optional - HTTP basic auth password. |
| |
| mode |
| Optional - Gnocchi authentication mode. |
| |
| domain |
| Optional - Gnocchi domain, defaults to "default". |
| |
| project |
| Optional - Keystone user for Gnocchi. |
| |
| is_default |
| Optional - Set data source as default. Default: False |
| ''' |
| if isinstance(profile, string_types): |
| profile = __salt__['config.option'](profile) |
| |
| ret = {'name': name, 'result': None, 'comment': None, 'changes': None} |
| datasource = _get_datasource(profile, name) |
| data = _get_json_data(name, type, url, |
| access=access, |
| user=user, |
| password=password, |
| database=database, |
| basic_auth=basic_auth, |
| basic_auth_user=basic_auth_user, |
| basic_auth_password=basic_auth_password, |
| mode=mode, |
| domain=domain, |
| project=project, |
| is_default=is_default) |
| |
| if datasource: |
| requests.put( |
| _get_url(profile, datasource['id']), |
| data=json.dumps(data), |
| auth=_get_auth(profile), |
| headers=_get_headers(profile), |
| timeout=profile.get('grafana_timeout', 3), |
| ) |
| ret['result'] = True |
| ret['changes'] = _diff(datasource, data) |
| if ret['changes']['new'] or ret['changes']['old']: |
| ret['comment'] = 'Data source {0} updated'.format(name) |
| else: |
| ret['changes'] = None |
| ret['comment'] = 'Data source {0} already up-to-date'.format(name) |
| else: |
| requests.post( |
| '{0}/api/datasources'.format(profile['grafana_url']), |
| data=json.dumps(data), |
| auth=_get_auth(profile), |
| headers=_get_headers(profile), |
| timeout=profile.get('grafana_timeout', 3), |
| ) |
| ret['result'] = True |
| ret['comment'] = 'New data source {0} added'.format(name) |
| ret['changes'] = data |
| |
| return ret |
| |
| |
| def absent(name, profile='grafana'): |
| ''' |
| Ensure that a data source is present. |
| |
| name |
| Name of the data source to remove. |
| ''' |
| if isinstance(profile, string_types): |
| profile = __salt__['config.option'](profile) |
| |
| ret = {'result': None, 'comment': None, 'changes': None} |
| datasource = _get_datasource(profile, name) |
| |
| if not datasource: |
| ret['result'] = True |
| ret['comment'] = 'Data source {0} already absent'.format(name) |
| return ret |
| |
| requests.delete( |
| _get_url(profile, datasource['id']), |
| auth=_get_auth(profile), |
| headers=_get_headers(profile), |
| timeout=profile.get('grafana_timeout', 3), |
| ) |
| |
| ret['result'] = True |
| ret['comment'] = 'Data source {0} was deleted'.format(name) |
| |
| return ret |
| |
| |
| def _get_url(profile, datasource_id): |
| return '{0}/api/datasources/{1}'.format( |
| profile['grafana_url'], |
| datasource_id |
| ) |
| |
| |
| def _get_datasource(profile, name): |
| response = requests.get( |
| '{0}/api/datasources'.format(profile['grafana_url']), |
| auth=_get_auth(profile), |
| headers=_get_headers(profile), |
| timeout=profile.get('grafana_timeout', 3), |
| ) |
| data = response.json() |
| for datasource in data: |
| if datasource['name'] == name: |
| return datasource |
| |
| |
| def _get_headers(profile): |
| |
| headers = {'Content-type': 'application/json'} |
| |
| if profile.get('grafana_token'): |
| headers['Authorization'] = 'Bearer {0}'.format(profile['grafana_token']) |
| |
| return headers |
| |
| |
| def _get_auth(profile): |
| if profile.get('grafana_token'): |
| return None |
| |
| return requests.auth.HTTPBasicAuth( |
| profile['grafana_user'], |
| profile['grafana_password'] |
| ) |
| |
| |
| def _get_json_data(name, |
| type, |
| url, |
| access='proxy', |
| user='', |
| password='', |
| database='', |
| basic_auth=False, |
| basic_auth_user='', |
| basic_auth_password='', |
| mode=None, |
| domain=None, |
| project=None, |
| is_default=False, |
| type_logo_url='public/app/plugins/datasource/influxdb/img/influxdb_logo.svg', |
| with_credentials=False): |
| data = { |
| 'name': name, |
| 'type': type, |
| 'url': url, |
| 'access': access, |
| 'user': user, |
| 'password': password, |
| 'database': database, |
| 'basicAuth': basic_auth, |
| 'basicAuthUser': basic_auth_user, |
| 'basicAuthPassword': basic_auth_password, |
| 'isDefault': is_default, |
| 'typeLogoUrl': type_logo_url, |
| 'withCredentials': with_credentials, |
| } |
| |
| if data['type'] == 'prometheus': |
| data.update({ |
| 'jsonData': { |
| 'httpMethod': 'GET', |
| 'keepCookies': [] |
| } |
| }) |
| |
| if data['type'] == 'gnocchixyz-gnocchi-datasource': |
| json_data = {} |
| for special in ['mode', 'domain', 'project', 'user', 'password']: |
| value = locals().get(special) |
| if value is not None: |
| if special == 'user': |
| json_data['username'] = value |
| else: |
| json_data[special] = value |
| if json_data: |
| data['jsonData'] = json_data |
| return data |
| |
| |
| def _diff(old, new): |
| old_keys = old.keys() |
| old = old.copy() |
| new = new.copy() |
| for key in old_keys: |
| if key in ['id', 'orgId']: |
| del old[key] |
| # New versions of Grafana can introduce new keys that are not present |
| # in _get_json_data. |
| elif key in new and old[key] == new[key]: |
| del old[key] |
| del new[key] |
| return {'old': old, 'new': new} |