Grafana dashboard : added folder parameter + fix when uid is missing in json file (#63342)

Related: ansible/ansible#61685
This commit is contained in:
Thierry Sallé 2019-10-18 11:35:56 +02:00 committed by John R Barker
parent 7d9a0baa7f
commit 0b9c241822

View file

@ -49,6 +49,11 @@ options:
- The Grafana Organisation ID where the dashboard will be imported / exported.
- Not used when I(grafana_api_key) is set, because the grafana_api_key only belongs to one organisation..
default: 1
folder:
description:
- The Grafana folder where this dashboard will be imported to.
default: General
version_added: '2.10'
state:
description:
- State of the dashboard.
@ -139,6 +144,7 @@ import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url, url_argument_spec
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils._text import to_native
from ansible.module_utils._text import to_text
@ -195,16 +201,40 @@ def get_grafana_version(module, grafana_url, headers):
return int(grafana_version)
def grafana_folder_exists(module, grafana_url, folder_name, headers):
# the 'General' folder is a special case, it's ID is always '0'
if folder_name == 'General':
return True, 0
try:
r, info = fetch_url(module, '%s/api/folders' % grafana_url, headers=headers, method='GET')
if info['status'] != 200:
raise GrafanaAPIException("Unable to query Grafana API for folders (name: %s): %d" % (folder_name, info['status']))
folders = json.loads(r.read())
for folder in folders:
if folder['title'] == folder_name:
return True, folder['id']
except Exception as e:
raise GrafanaAPIException(e)
return False, 0
def grafana_dashboard_exists(module, grafana_url, uid, headers):
dashboard_exists = False
dashboard = {}
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')
uri = '%s/api/dashboards/uid/%s' % (grafana_url, uid)
else:
r, info = fetch_url(module, '%s/api/dashboards/db/%s' % (grafana_url, uid), headers=headers, method='GET')
uri = '%s/api/dashboards/db/%s' % (grafana_url, uid)
r, info = fetch_url(module, uri, headers=headers, method='GET')
if info['status'] == 200:
dashboard_exists = True
try:
@ -219,6 +249,52 @@ def grafana_dashboard_exists(module, grafana_url, uid, headers):
return dashboard_exists, dashboard
def grafana_dashboard_search(module, grafana_url, folder_id, title, headers):
# search by title
uri = '%s/api/search?%s' % (grafana_url, urlencode({
'folderIds': folder_id,
'query': title,
'type': 'dash-db'
}))
r, info = fetch_url(module, uri, headers=headers, method='GET')
if info['status'] == 200:
try:
dashboards = json.loads(r.read())
for d in dashboards:
if d['title'] == title:
return grafana_dashboard_exists(module, grafana_url, d['uid'], headers)
except Exception as e:
raise GrafanaAPIException(e)
else:
raise GrafanaAPIException('Unable to search dashboard %s : %s' % (title, info))
return False, None
# for comparison, we sometimes need to ignore a few keys
def grafana_dashboard_changed(payload, dashboard):
# you don't need to set the version, but '0' is incremented to '1' by Grafana's API
if payload['dashboard']['version'] == 0:
del(payload['dashboard']['version'])
del(dashboard['dashboard']['version'])
# the meta key is not part of the 'payload' ever
if 'meta' in dashboard:
del(dashboard['meta'])
# new dashboards don't require an id attribute (or, it can be 'null'), Grafana's API will generate it
if payload['dashboard']['id'] is None:
del(dashboard['dashboard']['id'])
del(payload['dashboard']['id'])
if payload == dashboard:
return True
return False
def grafana_create_dashboard(module, data):
# define data payload for grafana API
@ -251,24 +327,37 @@ def grafana_create_dashboard(module, data):
else:
uid = None
# test if dashboard already exists
dashboard_exists, dashboard = grafana_dashboard_exists(module, data['grafana_url'], uid, headers=headers)
result = {}
if dashboard_exists is True:
if dashboard == payload:
# unchanged
# test if the folder exists
if grafana_version >= 5:
folder_exists, folder_id = grafana_folder_exists(module, data['grafana_url'], data['folder'], headers)
if folder_exists is False:
result['msg'] = "Dashboard folder '%s' does not exist." % data['folder']
result['uid'] = uid
result['msg'] = "Dashboard %s unchanged." % uid
result['changed'] = False
return result
payload['folderId'] = folder_id
# test if dashboard already exists
if uid:
dashboard_exists, dashboard = grafana_dashboard_exists(
module, data['grafana_url'], uid, headers=headers)
else:
dashboard_exists, dashboard = grafana_dashboard_search(
module, data['grafana_url'], folder_id, payload['dashboard']['title'], headers=headers)
if dashboard_exists is True:
if grafana_dashboard_changed(payload, dashboard):
# update
if 'overwrite' in data and data['overwrite']:
payload['overwrite'] = True
if 'message' in data and 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 grafana_version >= 5:
try:
@ -277,16 +366,26 @@ def grafana_create_dashboard(module, data):
except Exception as e:
raise GrafanaAPIException(e)
result['uid'] = uid
result['msg'] = "Dashboard %s updated" % uid
result['msg'] = "Dashboard %s updated" % payload['dashboard']['title']
result['changed'] = True
else:
body = json.loads(info['body'])
raise GrafanaAPIException('Unable to update the dashboard %s : %s' % (uid, body['message']))
raise GrafanaAPIException('Unable to update the dashboard %s : %s (HTTP: %d)' %
(uid, body['message'], info['status']))
else:
# unchanged
result['uid'] = uid
result['msg'] = "Dashboard %s unchanged." % payload['dashboard']['title']
result['changed'] = False
else:
# create
r, info = fetch_url(module, '%s/api/dashboards/db' % data['grafana_url'], data=json.dumps(payload), headers=headers, method='POST')
if folder_exists is True:
payload['folderId'] = folder_id
r, info = fetch_url(module, '%s/api/dashboards/db' % data['grafana_url'],
data=json.dumps(payload), headers=headers, method='POST')
if info['status'] == 200:
result['msg'] = "Dashboard %s created" % uid
result['msg'] = "Dashboard %s created" % payload['dashboard']['title']
result['changed'] = True
if grafana_version >= 5:
try:
@ -296,7 +395,8 @@ def grafana_create_dashboard(module, data):
raise GrafanaAPIException(e)
result['uid'] = uid
else:
raise GrafanaAPIException('Unable to create the new dashboard %s : %s - %s.' % (uid, info['status'], info))
raise GrafanaAPIException('Unable to create the new dashboard %s : %s - %s.' %
(payload['dashboard']['title'], info['status'], info))
return result
@ -394,6 +494,7 @@ def main():
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'),
folder=dict(type='str', default='General'),
uid=dict(type='str'),
slug=dict(type='str'),
path=dict(type='str'),
@ -423,7 +524,7 @@ def main():
except GrafanaMalformedJson as e:
module.fail_json(
failed=True,
msg="error : json file does not contain a meta section with a slug parameter, or you did not specify the slug parameter"
msg="error : %s" % to_native(e)
)
return
except GrafanaDeleteException as e: