grafana-ansible-collection/roles/grafana/tasks/dashboards.yml
gardar 126c45e646
feat: add grafana server role (#48)
Signed-off-by: gardar <gardar@users.noreply.github.com>
2023-05-30 23:01:26 -04:00

134 lines
5.1 KiB
YAML

---
- name: "Download grafana.net dashboards"
become: false
delegate_to: localhost
run_once: true
when: "grafana_dashboards | length > 0"
block:
- name: "Create local grafana dashboard directory"
ansible.builtin.tempfile:
state: directory
register: __tmp_dashboards
changed_when: false
- name: "Download grafana dashboard from grafana.net to local directory"
ansible.builtin.get_url:
url: "https://grafana.com/api/dashboards/{{ item.dashboard_id }}/revisions/{{ item.revision_id }}/download"
dest: "{{ __tmp_dashboards.path }}/{{ item.dashboard_id }}.json"
mode: "0644"
register: __download_dashboards
until: "__download_dashboards is succeeded"
retries: 5
delay: 2
changed_when: false
loop: "{{ grafana_dashboards }}"
# As noted in [1] an exported dashboard replaces the exporter's datasource
# name with a representative name, something like 'DS_GRAPHITE'. The name
# is different for each datasource plugin, but always begins with 'DS_'.
# In the rest of the data, the same name is used, but captured in braces,
# for example: '${DS_GRAPHITE}'.
#
# [1] http://docs.grafana.org/reference/export_import/#import-sharing-with-grafana-2-x-or-3-0
#
# The data structure looks (massively abbreviated) something like:
#
# "name": "DS_GRAPHITE",
# "datasource": "${DS_GRAPHITE}",
#
# If we import the downloaded dashboard verbatim, it will not automatically
# be connected to the data source like we want it. The Grafana UI expects
# us to do the final connection by hand, which we do not want to do.
# So, in the below task we ensure that we replace instances of this string
# with the data source name we want.
# To make sure that we're not being too greedy with the regex replacement
# of the data source to use for each dashboard that's uploaded, we make the
# regex match very specific by using the following:
#
# 1. Literal boundaries for " on either side of the match.
# 2. Non-capturing optional group matches for the ${} bits which may, or
# or may not, be there..
# 3. A case-sensitive literal match for DS .
# 4. A one-or-more case-sensitive match for the part that follows the
# underscore, with only A-Z, 0-9 and - or _ allowed.
#
# This regex can be tested and understood better by looking at the
# matches and non-matches in https://regex101.com/r/f4Gkvg/6
- name: "Set the correct data source name in the dashboard"
ansible.builtin.replace:
dest: "{{ __tmp_dashboards.path }}/{{ item.dashboard_id }}.json"
regexp: '"(?:\${)?DS_[A-Z0-9_-]+(?:})?"'
replace: '"{{ item.datasource }}"'
changed_when: false
loop: "{{ grafana_dashboards }}"
- name: "Import grafana dashboards via api"
community.grafana.grafana_dashboard:
grafana_url: "{{ grafana_api_url }}"
grafana_user: "{{ grafana_security.admin_user }}"
grafana_password: "{{ grafana_security.admin_password }}"
path: "{{ item }}"
message: "Updated by ansible role {{ ansible_role_name }}"
state: present
overwrite: true
no_log: "{{ 'false' if lookup('env', 'CI') else 'true' }}"
with_fileglob:
- "{{ __tmp_dashboards.path }}/*"
- "{{ grafana_dashboards_dir }}/*.json"
when: "not grafana_use_provisioning"
- name: "Import grafana dashboards through provisioning"
when: grafana_use_provisioning
block:
- name: "Create/Update dashboards file (provisioning)"
ansible.builtin.copy:
dest: "/etc/grafana/provisioning/dashboards/ansible.yml"
content: |
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
options:
path: "{{ grafana_data_dir }}/dashboards"
backup: false
owner: root
group: grafana
mode: "0640"
become: true
notify: restart_grafana
- name: "Register previously copied dashboards"
ansible.builtin.find:
paths: "{{ grafana_data_dir }}/dashboards"
hidden: true
patterns:
- "*.json"
register: __dashboards_present
when: grafana_provisioning_synced
- name: "Import grafana dashboards"
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ grafana_data_dir }}/dashboards/{{ item | basename }}"
mode: "0640"
with_fileglob:
- "{{ __tmp_dashboards.path }}/*"
- "{{ grafana_dashboards_dir }}/*.json"
become: true
register: __dashboards_copied
notify: "provisioned dashboards changed"
- name: "Remove dashboards not present on deployer machine (synchronize)"
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop: "{{ __dashboards_present_list | difference(__dashboards_copied_list) }}"
become: true
when: grafana_provisioning_synced
vars:
__dashboards_present_list: "{{ __dashboards_present | json_query('files[*].path') | default([]) }}"
__dashboards_copied_list: "{{ __dashboards_copied | json_query('results[*].dest') | default([]) }}"