|
|
@ -16,24 +16,30 @@ DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
---
|
|
|
|
module: grafana_dashboard
|
|
|
|
module: grafana_dashboard
|
|
|
|
author:
|
|
|
|
author:
|
|
|
|
- Thierry Sallé (@tsalle)
|
|
|
|
- Thierry Sallé (@seuf)
|
|
|
|
version_added: "2.5"
|
|
|
|
version_added: "2.5"
|
|
|
|
short_description: Manage Grafana dashboards
|
|
|
|
short_description: Manage Grafana dashboards
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- Create, update, delete, export Grafana dashboards via API.
|
|
|
|
- Create, update, delete, export Grafana dashboards via API.
|
|
|
|
options:
|
|
|
|
options:
|
|
|
|
grafana_url:
|
|
|
|
url:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The Grafana URL.
|
|
|
|
- The Grafana URL.
|
|
|
|
required: true
|
|
|
|
required: true
|
|
|
|
grafana_user:
|
|
|
|
aliases: [ grafana_url ]
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
|
|
|
|
url_username:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The Grafana API user.
|
|
|
|
- The Grafana API user.
|
|
|
|
default: admin
|
|
|
|
default: admin
|
|
|
|
grafana_password:
|
|
|
|
aliases: [ grafana_user ]
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
|
|
|
|
url_password:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The Grafana API password.
|
|
|
|
- The Grafana API password.
|
|
|
|
default: admin
|
|
|
|
default: admin
|
|
|
|
|
|
|
|
aliases: [ grafana_password ]
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
grafana_api_key:
|
|
|
|
grafana_api_key:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The Grafana API key.
|
|
|
|
- The Grafana API key.
|
|
|
@ -51,10 +57,15 @@ options:
|
|
|
|
default: present
|
|
|
|
default: present
|
|
|
|
slug:
|
|
|
|
slug:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
|
|
|
|
- Deprecated since Grafana 5. Use grafana dashboard uid instead.
|
|
|
|
- slug of the dashboard. It's the friendly url name of the dashboard.
|
|
|
|
- slug of the dashboard. It's the friendly url name of the dashboard.
|
|
|
|
- When C(state) is C(present), this parameter can override the slug in the meta section of the json file.
|
|
|
|
- When C(state) is C(present), this parameter can override the slug in the meta section of the json file.
|
|
|
|
- If you want to import a json dashboard exported directly from the interface (not from the api),
|
|
|
|
- If you want to import a json dashboard exported directly from the interface (not from the api),
|
|
|
|
you have to specify the slug parameter because there is no meta section in the exported json.
|
|
|
|
you have to specify the slug parameter because there is no meta section in the exported json.
|
|
|
|
|
|
|
|
uid:
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
|
|
|
|
description:
|
|
|
|
|
|
|
|
- uid of the dasboard to export when C(state) is C(export) or C(absent).
|
|
|
|
path:
|
|
|
|
path:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The path to the json file containing the Grafana dashboard to import or export.
|
|
|
|
- The path to the json file containing the Grafana dashboard to import or export.
|
|
|
@ -73,42 +84,63 @@ options:
|
|
|
|
- This should only be used on personally controlled sites using self-signed certificates.
|
|
|
|
- This should only be used on personally controlled sites using self-signed certificates.
|
|
|
|
type: bool
|
|
|
|
type: bool
|
|
|
|
default: 'yes'
|
|
|
|
default: 'yes'
|
|
|
|
|
|
|
|
client_cert:
|
|
|
|
|
|
|
|
description:
|
|
|
|
|
|
|
|
- PEM formatted certificate chain file to be used for SSL client authentication.
|
|
|
|
|
|
|
|
- This file can also include the key as well, and if the key is included, client_key is not required
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
|
|
|
|
client_key:
|
|
|
|
|
|
|
|
description:
|
|
|
|
|
|
|
|
- PEM formatted file that contains your private key to be used for SSL client
|
|
|
|
|
|
|
|
- authentication. If client_cert contains both the certificate and key, this option is not required
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
|
|
|
|
use_proxy:
|
|
|
|
|
|
|
|
description:
|
|
|
|
|
|
|
|
- Boolean of whether or not to use proxy.
|
|
|
|
|
|
|
|
default: 'yes'
|
|
|
|
|
|
|
|
type: bool
|
|
|
|
|
|
|
|
version_added: 2.7
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
EXAMPLES = '''
|
|
|
|
- name: Import Grafana dashboard foo
|
|
|
|
- hosts: localhost
|
|
|
|
grafana_dashboard:
|
|
|
|
connection: local
|
|
|
|
grafana_url: http://grafana.company.com
|
|
|
|
tasks:
|
|
|
|
grafana_api_key: XXXXXXXXXXXX
|
|
|
|
- name: Import Grafana dashboard foo
|
|
|
|
state: present
|
|
|
|
grafana_dashboard:
|
|
|
|
message: Updated by ansible
|
|
|
|
grafana_url: http://grafana.company.com
|
|
|
|
overwrite: yes
|
|
|
|
grafana_api_key: "{{ grafana_api_key }}"
|
|
|
|
path: /path/to/dashboards/foo.json
|
|
|
|
state: present
|
|
|
|
|
|
|
|
message: Updated by ansible
|
|
|
|
- name: Export dashboard
|
|
|
|
overwrite: yes
|
|
|
|
grafana_dashboard:
|
|
|
|
path: /path/to/dashboards/foo.json
|
|
|
|
grafana_url: http://grafana.company.com
|
|
|
|
|
|
|
|
grafana_api_key: XXXXXXXXXXXX
|
|
|
|
- name: Export dashboard
|
|
|
|
state: export
|
|
|
|
grafana_dashboard:
|
|
|
|
slug: foo
|
|
|
|
grafana_url: http://grafana.company.com
|
|
|
|
path: /path/to/dashboards/foo.json
|
|
|
|
grafana_user: "admin"
|
|
|
|
|
|
|
|
grafana_password: "{{ grafana_password }}"
|
|
|
|
|
|
|
|
org_id: 1
|
|
|
|
|
|
|
|
state: export
|
|
|
|
|
|
|
|
uid: "000000653"
|
|
|
|
|
|
|
|
path: "/path/to/dashboards/000000653.json"
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
RETURN = '''
|
|
|
|
RETURN = '''
|
|
|
|
---
|
|
|
|
---
|
|
|
|
slug:
|
|
|
|
uid:
|
|
|
|
description: slug of the created / deleted / exported dashboard.
|
|
|
|
description: uid or slug of the created / deleted / exported dashboard.
|
|
|
|
returned: success
|
|
|
|
returned: success
|
|
|
|
type: string
|
|
|
|
type: string
|
|
|
|
sample: foo
|
|
|
|
sample: 000000063
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
import json
|
|
|
|
import base64
|
|
|
|
import string
|
|
|
|
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils.urls import fetch_url
|
|
|
|
from ansible.module_utils.urls import fetch_url, url_argument_spec
|
|
|
|
from ansible.module_utils._text import to_bytes
|
|
|
|
from ansible.module_utils._text import to_native
|
|
|
|
|
|
|
|
|
|
|
|
__metaclass__ = type
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
|
|
|
@ -125,26 +157,62 @@ class GrafanaExportException(Exception):
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GrafanaDeleteException(Exception):
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def grafana_switch_organisation(module, grafana_url, org_id, headers):
|
|
|
|
def grafana_switch_organisation(module, grafana_url, org_id, headers):
|
|
|
|
r, info = fetch_url(module, '%s/api/user/using/%s' % (grafana_url, org_id), headers=headers, method='POST')
|
|
|
|
r, info = fetch_url(module, '%s/api/user/using/%s' % (grafana_url, org_id), headers=headers, method='POST')
|
|
|
|
if info['status'] != 200:
|
|
|
|
if info['status'] != 200:
|
|
|
|
raise GrafanaAPIException('Unable to switch to organization %s : %s' % (org_id, info))
|
|
|
|
raise GrafanaAPIException('Unable to switch to organization %s : %s' % (org_id, info))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def grafana_dashboard_exists(module, grafana_url, slug, headers):
|
|
|
|
def grafana_headers(module, data):
|
|
|
|
|
|
|
|
headers = {'content-type': 'application/json; charset=utf8'}
|
|
|
|
|
|
|
|
if 'grafana_api_key' in data and data['grafana_api_key']:
|
|
|
|
|
|
|
|
headers['Authorization'] = "Bearer %s" % data['grafana_api_key']
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
module.params['force_basic_auth'] = True
|
|
|
|
|
|
|
|
grafana_switch_organisation(module, data['grafana_url'], data['org_id'], headers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return headers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_grafana_version(module, grafana_url, headers):
|
|
|
|
|
|
|
|
grafana_version = None
|
|
|
|
|
|
|
|
r, info = fetch_url(module, '%s/api/frontend/settings' % grafana_url, headers=headers, method='GET')
|
|
|
|
|
|
|
|
if info['status'] == 200:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
settings = json.loads(r.read())
|
|
|
|
|
|
|
|
grafana_version = string.split(settings['buildInfo']['version'], '.')[0]
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
raise GrafanaAPIException(e)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
raise GrafanaAPIException('Unable to get grafana version : %s' % info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return int(grafana_version)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def grafana_dashboard_exists(module, grafana_url, uid, headers):
|
|
|
|
dashboard_exists = False
|
|
|
|
dashboard_exists = False
|
|
|
|
dashboard = {}
|
|
|
|
dashboard = {}
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db/%s' % (grafana_url, slug), headers=headers, method='GET')
|
|
|
|
|
|
|
|
|
|
|
|
grafana_version = get_grafana_version(module, grafana_url, headers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if grafana_version >= 5:
|
|
|
|
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/uid/%s' % (grafana_url, uid), headers=headers, method='GET')
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db/%s' % (grafana_url, uid), headers=headers, method='GET')
|
|
|
|
if info['status'] == 200:
|
|
|
|
if info['status'] == 200:
|
|
|
|
dashboard_exists = True
|
|
|
|
dashboard_exists = True
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
dashboard = json.loads(r.read())
|
|
|
|
dashboard = json.loads(r.read())
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
raise GrafanaMalformedJson(e)
|
|
|
|
raise GrafanaAPIException(e)
|
|
|
|
elif info['status'] == 404:
|
|
|
|
elif info['status'] == 404:
|
|
|
|
dashboard_exists = False
|
|
|
|
dashboard_exists = False
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise GrafanaAPIException('Unable to get dashboard %s : %s' % (slug, info))
|
|
|
|
raise GrafanaAPIException('Unable to get dashboard %s : %s' % (uid, info))
|
|
|
|
|
|
|
|
|
|
|
|
return dashboard_exists, dashboard
|
|
|
|
return dashboard_exists, dashboard
|
|
|
|
|
|
|
|
|
|
|
@ -156,60 +224,75 @@ def grafana_create_dashboard(module, data):
|
|
|
|
with open(data['path'], 'r') as json_file:
|
|
|
|
with open(data['path'], 'r') as json_file:
|
|
|
|
payload = json.load(json_file)
|
|
|
|
payload = json.load(json_file)
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
raise GrafanaMalformedJson("Can't load json file %s" % str(e))
|
|
|
|
raise GrafanaAPIException("Can't load json file %s" % to_native(e))
|
|
|
|
|
|
|
|
|
|
|
|
# define http header
|
|
|
|
# define http header
|
|
|
|
headers = {'content-type': 'application/json; charset=utf8'}
|
|
|
|
headers = grafana_headers(module, data)
|
|
|
|
if 'grafana_api_key' in data and data['grafana_api_key']:
|
|
|
|
|
|
|
|
headers['Authorization'] = "Bearer %s" % data['grafana_api_key']
|
|
|
|
grafana_version = get_grafana_version(module, data['grafana_url'], headers)
|
|
|
|
else:
|
|
|
|
if grafana_version < 5:
|
|
|
|
auth = base64.b64encode(to_bytes('%s:%s' % (data['grafana_user'], data['grafana_password'])).replace('\n', ''))
|
|
|
|
if data.get('slug'):
|
|
|
|
headers['Authorization'] = 'Basic %s' % auth
|
|
|
|
uid = data['slug']
|
|
|
|
grafana_switch_organisation(module, data['grafana_url'], data['org_id'], headers)
|
|
|
|
elif 'meta' in payload and 'slug' in payload['meta']:
|
|
|
|
|
|
|
|
uid = payload['meta']['slug']
|
|
|
|
if data.get('slug'):
|
|
|
|
else:
|
|
|
|
slug = data['slug']
|
|
|
|
raise GrafanaMalformedJson('No slug found in json. Needed with grafana < 5')
|
|
|
|
elif 'meta' in payload and 'slug' in payload['meta']:
|
|
|
|
|
|
|
|
slug = payload['meta']['slug']
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise GrafanaMalformedJson('No slug found in json')
|
|
|
|
if data.get('uid'):
|
|
|
|
|
|
|
|
uid = data['uid']
|
|
|
|
|
|
|
|
elif 'uid' in payload['dashboard']:
|
|
|
|
|
|
|
|
uid = payload['dashboard']['uid']
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
uid = None
|
|
|
|
|
|
|
|
|
|
|
|
# test if dashboard already exists
|
|
|
|
# test if dashboard already exists
|
|
|
|
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], slug, headers=headers)
|
|
|
|
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], uid, headers=headers)
|
|
|
|
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
result = {}
|
|
|
|
if dashboard_exists is True:
|
|
|
|
if dashboard_exists is True:
|
|
|
|
if dashboard == payload:
|
|
|
|
if dashboard == payload:
|
|
|
|
# unchanged
|
|
|
|
# unchanged
|
|
|
|
result['slug'] = data['slug']
|
|
|
|
result['uid'] = uid
|
|
|
|
result['msg'] = "Dashboard %s unchanged." % data['slug']
|
|
|
|
result['msg'] = "Dashboard %s unchanged." % uid
|
|
|
|
result['changed'] = False
|
|
|
|
result['changed'] = False
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# update
|
|
|
|
# update
|
|
|
|
if 'overwrite' in data and data['overwrite']:
|
|
|
|
if 'overwrite' in data and data['overwrite'] == 'yes':
|
|
|
|
payload['overwrite'] = True
|
|
|
|
payload['overwrite'] = True
|
|
|
|
if 'message' in data and data['message']:
|
|
|
|
if 'message' in data and data['message']:
|
|
|
|
payload['message'] = data['message']
|
|
|
|
payload['message'] = data['message']
|
|
|
|
|
|
|
|
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db' % data['grafana_url'], data=json.dumps(payload), headers=headers, method='POST')
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db' % data['grafana_url'], data=json.dumps(payload), headers=headers, method='POST')
|
|
|
|
if info['status'] == 200:
|
|
|
|
if info['status'] == 200:
|
|
|
|
result['slug'] = slug
|
|
|
|
if grafana_version >= 5:
|
|
|
|
result['msg'] = "Dashboard %s updated" % slug
|
|
|
|
try:
|
|
|
|
|
|
|
|
dashboard = json.loads(r.read())
|
|
|
|
|
|
|
|
uid = dashboard['uid']
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
raise GrafanaAPIException(e)
|
|
|
|
|
|
|
|
result['uid'] = uid
|
|
|
|
|
|
|
|
result['msg'] = "Dashboard %s updated" % uid
|
|
|
|
result['changed'] = True
|
|
|
|
result['changed'] = True
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
body = json.loads(info['body'])
|
|
|
|
body = json.loads(info['body'])
|
|
|
|
raise GrafanaAPIException('Unable to update the dashboard %s : %s' % (slug, body['message']))
|
|
|
|
raise GrafanaAPIException('Unable to update the dashboard %s : %s' % (uid, body['message']))
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# create
|
|
|
|
# create
|
|
|
|
if 'dashboard' not in payload:
|
|
|
|
if 'dashboard' not in payload:
|
|
|
|
payload = {'dashboard': payload}
|
|
|
|
payload = {'dashboard': payload}
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db' % data['grafana_url'], data=json.dumps(payload), headers=headers, method='POST')
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db' % data['grafana_url'], data=json.dumps(payload), headers=headers, method='POST')
|
|
|
|
if info['status'] == 200:
|
|
|
|
if info['status'] == 200:
|
|
|
|
result['msg'] = "Dashboard %s created" % slug
|
|
|
|
result['msg'] = "Dashboard %s created" % uid
|
|
|
|
result['changed'] = True
|
|
|
|
result['changed'] = True
|
|
|
|
result['slug'] = slug
|
|
|
|
if grafana_version >= 5:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
dashboard = json.loads(r.read())
|
|
|
|
|
|
|
|
uid = dashboard['uid']
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
raise GrafanaAPIException(e)
|
|
|
|
|
|
|
|
result['uid'] = uid
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise GrafanaAPIException('Unable to create the new dashboard %s : %s - %s.' % (slug, info['status'], info))
|
|
|
|
raise GrafanaAPIException('Unable to create the new dashboard %s : %s - %s.' % (uid, info['status'], info))
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
@ -217,32 +300,41 @@ def grafana_create_dashboard(module, data):
|
|
|
|
def grafana_delete_dashboard(module, data):
|
|
|
|
def grafana_delete_dashboard(module, data):
|
|
|
|
|
|
|
|
|
|
|
|
# define http headers
|
|
|
|
# define http headers
|
|
|
|
headers = {'content-type': 'application/json'}
|
|
|
|
headers = grafana_headers(module, data)
|
|
|
|
if 'grafana_api_key' in data and data['grafana_api_key']:
|
|
|
|
|
|
|
|
headers['Authorization'] = "Bearer %s" % data['grafana_api_key']
|
|
|
|
grafana_version = get_grafana_version(module, data['grafana_url'], headers)
|
|
|
|
|
|
|
|
if grafana_version < 5:
|
|
|
|
|
|
|
|
if data.get('slug'):
|
|
|
|
|
|
|
|
uid = data['slug']
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
raise GrafanaMalformedJson('No slug parameter. Needed with grafana < 5')
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
auth = base64.b64encode(to_bytes('%s:%s' % (data['grafana_user'], data['grafana_password'])).replace('\n', ''))
|
|
|
|
if data.get('uid'):
|
|
|
|
headers['Authorization'] = 'Basic %s' % auth
|
|
|
|
uid = data['uid']
|
|
|
|
grafana_switch_organisation(module, data['grafana_url'], data['org_id'], headers)
|
|
|
|
else:
|
|
|
|
|
|
|
|
raise GrafanaDeleteException('No uid specified %s')
|
|
|
|
|
|
|
|
|
|
|
|
# test if dashboard already exists
|
|
|
|
# test if dashboard already exists
|
|
|
|
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], data['slug'], headers=headers)
|
|
|
|
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], uid, headers=headers)
|
|
|
|
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
result = {}
|
|
|
|
if dashboard_exists is True:
|
|
|
|
if dashboard_exists is True:
|
|
|
|
# delete
|
|
|
|
# delete
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db/%s' % (data['grafana_url'], data['slug']), headers=headers, method='DELETE')
|
|
|
|
if grafana_version < 5:
|
|
|
|
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/db/%s' % (data['grafana_url'], uid), headers=headers, method='DELETE')
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
r, info = fetch_url(module, '%s/api/dashboards/uid/%s' % (data['grafana_url'], uid), headers=headers, method='DELETE')
|
|
|
|
if info['status'] == 200:
|
|
|
|
if info['status'] == 200:
|
|
|
|
result['msg'] = "Dashboard %s deleted" % data['slug']
|
|
|
|
result['msg'] = "Dashboard %s deleted" % uid
|
|
|
|
result['changed'] = True
|
|
|
|
result['changed'] = True
|
|
|
|
result['slug'] = data['slug']
|
|
|
|
result['uid'] = uid
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise GrafanaAPIException('Unable to update the dashboard %s : %s' % (data['slug'], info))
|
|
|
|
raise GrafanaAPIException('Unable to update the dashboard %s : %s' % (uid, info))
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# dashboard does not exist, do nothing
|
|
|
|
# dashboard does not exist, do nothing
|
|
|
|
result = {'msg': "Dashboard %s does not exist." % data['slug'],
|
|
|
|
result = {'msg': "Dashboard %s does not exist." % uid,
|
|
|
|
'changed': False,
|
|
|
|
'changed': False,
|
|
|
|
'slug': data['slug']}
|
|
|
|
'uid': uid}
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
@ -250,53 +342,65 @@ def grafana_delete_dashboard(module, data):
|
|
|
|
def grafana_export_dashboard(module, data):
|
|
|
|
def grafana_export_dashboard(module, data):
|
|
|
|
|
|
|
|
|
|
|
|
# define http headers
|
|
|
|
# define http headers
|
|
|
|
headers = {'content-type': 'application/json'}
|
|
|
|
headers = grafana_headers(module, data)
|
|
|
|
if 'grafana_api_key' in data and data['grafana_api_key']:
|
|
|
|
|
|
|
|
headers['Authorization'] = "Bearer %s" % data['grafana_api_key']
|
|
|
|
grafana_version = get_grafana_version(module, data['grafana_url'], headers)
|
|
|
|
|
|
|
|
if grafana_version < 5:
|
|
|
|
|
|
|
|
if data.get('slug'):
|
|
|
|
|
|
|
|
uid = data['slug']
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
raise GrafanaMalformedJson('No slug parameter. Needed with grafana < 5')
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
auth = base64.b64encode(to_bytes('%s:%s' % (data['grafana_user'], data['grafana_password'])).replace('\n', ''))
|
|
|
|
if data.get('uid'):
|
|
|
|
headers['Authorization'] = 'Basic %s' % auth
|
|
|
|
uid = data['uid']
|
|
|
|
grafana_switch_organisation(module, data['grafana_url'], data['org_id'], headers)
|
|
|
|
else:
|
|
|
|
|
|
|
|
raise GrafanaExportException('No uid specified')
|
|
|
|
|
|
|
|
|
|
|
|
# test if dashboard already exists
|
|
|
|
# test if dashboard already exists
|
|
|
|
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], data['slug'], headers=headers)
|
|
|
|
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], uid, headers=headers)
|
|
|
|
|
|
|
|
|
|
|
|
if dashboard_exists is True:
|
|
|
|
if dashboard_exists is True:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
with open(data['path'], 'w') as f:
|
|
|
|
with open(data['path'], 'w') as f:
|
|
|
|
f.write(json.dumps(dashboard))
|
|
|
|
f.write(json.dumps(dashboard))
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
raise GrafanaExportException("Can't write json file : %s" % str(e))
|
|
|
|
raise GrafanaExportException("Can't write json file : %s" % to_native(e))
|
|
|
|
result = {'msg': "Dashboard %s exported to %s" % (data['slug'], data['path']),
|
|
|
|
result = {'msg': "Dashboard %s exported to %s" % (uid, data['path']),
|
|
|
|
'slug': data['slug'],
|
|
|
|
'uid': uid,
|
|
|
|
'changed': True}
|
|
|
|
'changed': True}
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
result = {'msg': "Dashboard %s does not exist." % data['slug'],
|
|
|
|
result = {'msg': "Dashboard %s does not exist." % uid,
|
|
|
|
'slug': data['slug'],
|
|
|
|
'uid': uid,
|
|
|
|
'changed': False}
|
|
|
|
'changed': False}
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def main():
|
|
|
|
|
|
|
|
# use the predefined argument spec for url
|
|
|
|
|
|
|
|
argument_spec = url_argument_spec()
|
|
|
|
|
|
|
|
# remove unnecessary arguments
|
|
|
|
|
|
|
|
del argument_spec['force']
|
|
|
|
|
|
|
|
del argument_spec['force_basic_auth']
|
|
|
|
|
|
|
|
del argument_spec['http_agent']
|
|
|
|
|
|
|
|
argument_spec.update(
|
|
|
|
|
|
|
|
state=dict(choices=['present', 'absent', 'export'], default='present'),
|
|
|
|
|
|
|
|
url=dict(aliases=['grafana_url'], required=True),
|
|
|
|
|
|
|
|
url_username=dict(aliases=['grafana_user'], default='admin'),
|
|
|
|
|
|
|
|
url_password=dict(aliases=['grafana_password'], default='admin', no_log=True),
|
|
|
|
|
|
|
|
grafana_api_key=dict(type='str', no_log=True),
|
|
|
|
|
|
|
|
org_id=dict(default=1, type='int'),
|
|
|
|
|
|
|
|
uid=dict(type='str'),
|
|
|
|
|
|
|
|
slug=dict(type='str'),
|
|
|
|
|
|
|
|
path=dict(type='str'),
|
|
|
|
|
|
|
|
overwrite=dict(type='bool', default=False),
|
|
|
|
|
|
|
|
message=dict(type='str'),
|
|
|
|
|
|
|
|
)
|
|
|
|
module = AnsibleModule(
|
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=dict(
|
|
|
|
argument_spec=argument_spec,
|
|
|
|
state=dict(choices=['present', 'absent', 'export'],
|
|
|
|
|
|
|
|
default='present'),
|
|
|
|
|
|
|
|
grafana_url=dict(required=True),
|
|
|
|
|
|
|
|
grafana_user=dict(default='admin'),
|
|
|
|
|
|
|
|
grafana_password=dict(default='admin', no_log=True),
|
|
|
|
|
|
|
|
grafana_api_key=dict(type='str', no_log=True),
|
|
|
|
|
|
|
|
org_id=dict(default=1, type='int'),
|
|
|
|
|
|
|
|
slug=dict(type='str'),
|
|
|
|
|
|
|
|
path=dict(type='str'),
|
|
|
|
|
|
|
|
overwrite=dict(type='bool', default=False),
|
|
|
|
|
|
|
|
message=dict(type='str'),
|
|
|
|
|
|
|
|
validate_certs=dict(type='bool', default=True)
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
supports_check_mode=False,
|
|
|
|
supports_check_mode=False,
|
|
|
|
required_together=[['grafana_user', 'grafana_password', 'org_id']],
|
|
|
|
required_together=[['url_username', 'url_password', 'org_id']],
|
|
|
|
mutually_exclusive=[['grafana_user', 'grafana_api_key']],
|
|
|
|
mutually_exclusive=[['grafana_user', 'grafana_api_key'], ['uid', 'slug']],
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
@ -309,7 +413,7 @@ def main():
|
|
|
|
except GrafanaAPIException as e:
|
|
|
|
except GrafanaAPIException as e:
|
|
|
|
module.fail_json(
|
|
|
|
module.fail_json(
|
|
|
|
failed=True,
|
|
|
|
failed=True,
|
|
|
|
msg="error : %s" % e
|
|
|
|
msg="error : %s" % to_native(e)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
except GrafanaMalformedJson as e:
|
|
|
|
except GrafanaMalformedJson as e:
|
|
|
@ -318,10 +422,16 @@ def main():
|
|
|
|
msg="error : json file does not contain a meta section with a slug parameter, or you did'nt specify the slug parameter"
|
|
|
|
msg="error : json file does not contain a meta section with a slug parameter, or you did'nt specify the slug parameter"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
except GrafanaDeleteException as e:
|
|
|
|
|
|
|
|
module.fail_json(
|
|
|
|
|
|
|
|
failed=True,
|
|
|
|
|
|
|
|
msg="error : Can't delete dashboard : %s" % to_native(e)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return
|
|
|
|
except GrafanaExportException as e:
|
|
|
|
except GrafanaExportException as e:
|
|
|
|
module.fail_json(
|
|
|
|
module.fail_json(
|
|
|
|
failed=True,
|
|
|
|
failed=True,
|
|
|
|
msg="error : json file cannot be written : %s" % str(e)
|
|
|
|
msg="error : Can't export dashboard : %s" % to_native(e)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|