From 400b41226220b88a0d34bd004d655fb86ecb3314 Mon Sep 17 00:00:00 2001 From: Benjamin Schubert Date: Tue, 10 Dec 2024 21:48:31 +0000 Subject: [PATCH] Allow managing dashboards in subfolders Since Grafana 11, it is possible to have dashboards in subfolders. This enables users to manage dashboards in those. --- .../fragments/411-dashboard-subfolders.yml | 3 + plugins/modules/grafana_dashboard.py | 34 ++++++++-- roles/grafana/README.md | 1 + .../tasks/dashboard-folder-destination.yml | 66 +++++++++++++++++++ 4 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/411-dashboard-subfolders.yml diff --git a/changelogs/fragments/411-dashboard-subfolders.yml b/changelogs/fragments/411-dashboard-subfolders.yml new file mode 100644 index 00000000..af5c5276 --- /dev/null +++ b/changelogs/fragments/411-dashboard-subfolders.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Allow creating dashboards in subfolders for `grafana_dashboard` diff --git a/plugins/modules/grafana_dashboard.py b/plugins/modules/grafana_dashboard.py index 60cd5b86..335f34c9 100644 --- a/plugins/modules/grafana_dashboard.py +++ b/plugins/modules/grafana_dashboard.py @@ -35,6 +35,13 @@ default: General version_added: "1.0.0" type: str + parent_uid: + description: + - The parent folder UID. + - Available with subfolder feature of Grafana 11. + - Allows creating dashboards in subfolders + version_added: "2.2.0" + type: str state: description: - State of the dashboard. @@ -112,6 +119,15 @@ folder: public dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download +- name: Import Grafana dashboard zabbix in a subfolder + community.grafana.grafana_dashboard: + grafana_url: http://grafana.company.com + grafana_api_key: "{{ grafana_api_key }}" + # Can be retrieved from `community.grafana.grafana_folder` as `folder.uid` + parent_uid: "{{ parent_uid }}" + folder: myteam + dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download + - name: Export dashboard community.grafana.grafana_dashboard: grafana_url: http://grafana.company.com @@ -226,15 +242,17 @@ def get_grafana_version(module, grafana_url, headers): return int(grafana_version) -def grafana_folder_exists(module, grafana_url, folder_name, headers): +def grafana_folder_exists(module, grafana_url, folder_name, parent_uid, 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" - ) + url = "%s/api/folders" % grafana_url + if parent_uid: + url = "%s?parentUid=%s" % (url, parent_uid) + + r, info = fetch_url(module, url, headers=headers, method="GET") if info["status"] != 200: raise GrafanaAPIException( @@ -382,9 +400,14 @@ def grafana_create_dashboard(module, data): # test if the folder exists folder_exists = False + if data["parent_uid"] and grafana_version < 11: + module.fail_json( + failed=True, msg="Subfolder API is available starting Grafana v11" + ) + if grafana_version >= 5: folder_exists, folder_id = grafana_folder_exists( - module, data["url"], data["folder"], headers + module, data["url"], data["folder"], data["parent_uid"], headers ) if folder_exists is False: raise GrafanaAPIException( @@ -612,6 +635,7 @@ def main(): org_id=dict(default=1, type="int"), org_name=dict(type="str"), folder=dict(type="str", default="General"), + parent_uid=dict(type="str"), uid=dict(type="str"), slug=dict(type="str"), path=dict(aliases=["dashboard_url"], type="str"), diff --git a/roles/grafana/README.md b/roles/grafana/README.md index 5c76497c..5ccfa5a6 100644 --- a/roles/grafana/README.md +++ b/roles/grafana/README.md @@ -87,6 +87,7 @@ Configure Grafana organizations, dashboards, folders, datasources, teams and use | org_id | no | | org_name | no | | overwrite | no | +| parent_uid | no | | path | no | | slug | no | | state | no | diff --git a/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml b/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml index 4704002f..d7686bf5 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml @@ -36,3 +36,69 @@ - dfff_result2.failed == true - dfff_result2.changed == false - "dfff_result2.msg == 'error : Dashboard folder \\'inexistent\\' does not exist.'" + +- name: Create a dashboard in a folder + block: + - name: Create the original folder + community.grafana.grafana_folder: + title: parent_folder + state: present + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + register: parent_folder + + - name: Create the dashboard in the folder + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: present + commit_message: Updated by ansible + path: /tmp/dashboard.json + overwrite: true + folder: parent_folder + check_mode: false + register: diff_result3 + ignore_errors: true + - ansible.builtin.assert: + that: + - diff_result3.failed == false + - diff_result3.changed == true + +- name: Create a dashboard in a subfolder + block: + - name: Create the subfolder + community.grafana.grafana_folder: + title: sub_folder + parent_uid: "{{ parent_folder.folder.uid }}" + state: present + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + register: sub_folder + + - name: Create the dashboard in the subfolder + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: present + commit_message: Updated by ansible + path: /tmp/dashboard.json + overwrite: true + folder: sub_folder + parent_uid: "{{ parent_folder.folder.uid }}" + check_mode: false + register: diff_result4 + ignore_errors: true + - ansible.builtin.assert: + that: + - diff_result4.failed == false + - diff_result4.changed == true + rescue: + - name: Skip if we are running an older version of Grafana + ansible.builtin.assert: + that: + - sub_folder.msg | default ('') == 'Subfolder API is available starting Grafana v11' + fail_msg: "Failed to create dashboard in subfolder, for a separate reason than using an older Grafana. See above."