ansible-collections / community.grafana

Grafana Collection for Ansible
http://galaxy.ansible.com/community/grafana
GNU General Public License v3.0
128 stars 81 forks source link

add support for dashboard inputs #329

Open gbolo opened 10 months ago

gbolo commented 10 months ago
SUMMARY

Currently, the web UI in Grafana supports providing inputs when importing dashboards. for example, a dashboard json may have a body containing:

...
  "__inputs": [
    {
      "name": "DS_PROMETHEUS",
      "label": "Prometheus",
      "description": "",
      "type": "datasource",
      "pluginId": "prometheus",
      "pluginName": "Prometheus"
    }
  ]
...

In the same body it can reference those inputs like: "datasource": "${DS_PROMETHEUS}".

When using the web UI in Grafana, it interprets these inputs and allows you to fill them in with a drop down menu. Then when it makes the POST /api/dashboards/import call, it will have the values for those inputs in the body like:

...
  "inputs": [
    {
      "name": "DS_PROMETHEUS",
      "type": "datasource",
      "pluginId": "prometheus",
      "value": "2bc3bb07-e974-5690-9826-c71ae0fdb03a"
    }
  ]
...
ISSUE TYPE
COMPONENT NAME

community.grafana.grafana_dashboard

ADDITIONAL INFORMATION

For example, see this dashboard: https://grafana.com/grafana/dashboards/405-node-exporter-server-metrics/

kosssi commented 2 weeks ago

Dashboard import is not functional without this feature. Did you manage to tie a dashboard to Prometheus otherwise @gbolo ?

pbdname commented 6 days ago

@kosssi, I've come up with an (overly complicated) workaround, but it's the only way I'm able to set up/update the dashboard. Also, I'm really new into Prometheus and Grafana, so I might be missing some concepts, but still, it works for my use case.

The following tasks import Prometheus datasource and then sets up a dashboard for Blackbox Exporter (uid: xtkCtBkiz, see the grafana_dashboard_uid variable), which is defined in template dashboard.json.j2. This template is not directly downloaded from Grafana.com, but I've first imported it manually in one Grafana instance and then copied the JSON Model into the Jinja2 template.

main.yml:

- name: Update Prometheus datasource in Grafana
  community.grafana.grafana_datasource:
    grafana_url: "https://{{ prometheus_grafana_domain }}"
    grafana_user: "admin"
    grafana_password: "{{ vaulted_grafana_user_passwords['admin'] }}"
    name: prometheus
    ds_type: prometheus
    ds_url: http://prometheus:9090/
    access: proxy
  register: grafana_prometheus_datasource
  changed_when: false   # https://github.com/ansible-collections/community.grafana/issues/127
  tags: grafana

- name: Update Grafana dashboards
  ansible.builtin.include_tasks: grafana-dashboard.yml
  loop:
    - {
      uid: blackbox-exporter-j4da,
      # The template has to have 'uid' in all 'datasource' replaced by '{{ datasource_uid }}', for example:
      #   ...
      #   "datasource": {
      #     "type": "prometheus",
      #     "uid": "{{ datasource_uid }}"
      #   }
      #   ...
      template: grafana-dashboards/blackbox-exporter-j4da.json.j2,
      datasource_uid: "{{ grafana_prometheus_datasource.datasource.uid }}"
    }
    - {
      uid: fcb1bdc8-fa9d-4bfd-b725-003b174473ad,
      template: grafana-dashboards/fcb1bdc8-fa9d-4bfd-b725-003b174473ad.json.j2,
      datasource_uid: "{{ grafana_prometheus_datasource.datasource.uid }}"
    }
    - {
      uid: xtkCtBkis,
      template: grafana-dashboards/xtkCtBkis.json.j2,
      datasource_uid: "{{ grafana_prometheus_datasource.datasource.uid }}"
    }
    - {
      uid: xtkCtBkiz,
      template: grafana-dashboards/xtkCtBkiz.json.j2,
      datasource_uid: "{{ grafana_prometheus_datasource.datasource.uid }}"
    }
  loop_control:
    loop_var: dashboard
  tags: grafana

grafana-dashboard.yml:

---

- name: Get current dashboard configuration
  block:
    - name: Create a temporary file for current dashboard JSON
      ansible.builtin.tempfile:
        state: file
        suffix: .dashboard-current.json
      register: temp_dashboard_json_current_file
      changed_when: false
      tags: grafana

    - name: Export current dashboard
      community.grafana.grafana_dashboard:
        grafana_url: "https://{{ prometheus_grafana_domain }}"
        grafana_user: "admin"
        grafana_password: "{{ vaulted_grafana_user_passwords['admin'] }}"
        state: export
        uid: "{{ dashboard.uid }}"
        path: "{{ temp_dashboard_json_current_file.path }}"
      register: grafana_dashboard_current_uid
      changed_when: false
      when: not ansible_check_mode
      tags: grafana

    - name: Read the current dashboard into variable
      ansible.builtin.set_fact:
        dashboard_json_current: "{{ lookup('file', temp_dashboard_json_current_file.path) | from_json }}"
      when:
        - grafana_dashboard_current_uid.msg is defined
        - not (grafana_dashboard_current_uid.msg is search("does not exist."))
      tags: grafana
    - name: Remove the 'id' key from current dashboard
      ansible.builtin.set_fact:
        dashboard_json_current_without_id: "{{ dashboard_json_current.dashboard | combine({'id': omit}, recursive=True) }}"
      when:
        - grafana_dashboard_current_uid.msg is defined
        - not (grafana_dashboard_current_uid.msg is search("does not exist."))
      tags: grafana

- name: Prepare new dashboard configuration
  block:
    - name: Create a temporary file for new dashboard JSON
      ansible.builtin.tempfile:
        state: file
        suffix: .dashboard-new.json
      register: temp_dashboard_json_new_file
      changed_when: false
      tags: grafana

    - name: Generate dashboard JSON with correct datasource UID
      ansible.builtin.template:
        src: "{{ dashboard.template }}"
        dest: "{{ temp_dashboard_json_new_file.path }}"
        owner: root
        group: root
        mode: "600"
      vars:
        datasource_uid: "{{ dashboard.datasource_uid }}"
      changed_when: false
      when: not ansible_check_mode
      tags: grafana

    - name: Read the new dashboard
      ansible.builtin.set_fact:
        dashboard_json_new: "{{ lookup('file', temp_dashboard_json_new_file.path) | from_json }}"
      when: dashboard_json_current_without_id is defined
      tags: grafana

    - name: Remove the 'id' key from new dashboard
      ansible.builtin.set_fact:
        dashboard_json_new_without_id: "{{ dashboard_json_new | combine({'id': omit}, recursive=True) }}"
      when: dashboard_json_current_without_id is defined
      tags: grafana

- name: Remove current dashboard from Grafana
  community.grafana.grafana_dashboard:
    grafana_url: "https://{{ prometheus_grafana_domain }}"
    grafana_user: "admin"
    grafana_password: "{{ vaulted_grafana_user_passwords['admin'] }}"
    uid: "{{ dashboard.uid }}"
    state: absent
  when:
    - dashboard_json_current_without_id is defined
    - dashboard_json_current_without_id != dashboard_json_new_without_id
  tags: grafana

- name: Import Blackbox Exporter dashboard to Grafana
  community.grafana.grafana_dashboard:
    grafana_url: "https://{{ prometheus_grafana_domain }}"
    grafana_user: "admin"
    grafana_password: "{{ vaulted_grafana_user_passwords['admin'] }}"
    state: present
    commit_message: Updated by Ansible
    path: "{{ temp_dashboard_json_new_file.path }}"
  when:
    - dashboard_json_current_without_id is not defined or dashboard_json_current_without_id != dashboard_json_new_without_id
    - temp_dashboard_json_new_file.path is defined
  tags: grafana

- name: Clean up temporary files
  ansible.builtin.file:
    path: "{{ item }}"
    state: absent
  loop:
    - "{{ temp_dashboard_json_new_file.path }}"
    - "{{ temp_dashboard_json_current_file.path }}"
  changed_when: false
  when: not ansible_check_mode
  tags: grafana

...