From b22f3d13a8b2b82d78e93d835b9ea7ca5169e1de Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:38:11 +0200 Subject: [PATCH 01/23] refactor: folder finding loop --- plugins/modules/grafana_folder.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 73c437db..a6372fad 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -290,15 +290,16 @@ def create_folder(self, title): return response def get_folder(self, title): - url = "/api/search?type=dash-folder&query={title}".format(title=quote(title)) + url = "/api/search?type=dash-folder&query=%s" % quote(title) response = self._send_request(url, headers=self.headers, method="GET") - for item in response: - if item.get("title") == to_text(title): - return item + folder = next((item for item in response if item["title"] == title)) + if folder: + return folder + return None def delete_folder(self, folder_uid): - url = "/api/folders/{folder_uid}".format(folder_uid=folder_uid) + url = "/api/folders/%s" % folder_uid response = self._send_request(url, headers=self.headers, method="DELETE") return response From d871cd5cf9d0e15470931079691decaeffe9ff3b Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:38:31 +0200 Subject: [PATCH 02/23] chore: add uid and parent_uid arg --- plugins/modules/grafana_folder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index a6372fad..9f34ed8d 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -308,10 +308,12 @@ def main(): argument_spec = base.grafana_argument_spec() argument_spec.update( name=dict(type="str", aliases=["title"], required=True), - state=dict(type="str", default="present", choices=["present", "absent"]), - skip_version_check=dict(type="bool", default=False), org_id=dict(default=1, type="int"), org_name=dict(type="str"), + parent_uid=dict(type="str"), + skip_version_check=dict(type="bool", default=False), + state=dict(type="str", default="present", choices=["present", "absent"]), + uid=dict(type="str"), ) module = AnsibleModule( argument_spec=argument_spec, From 009530dd0bcaadd20311e373e2ec287d6624a6c5 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:16:22 +0200 Subject: [PATCH 03/23] fix: check if multiple folder found by name --- plugins/modules/grafana_folder.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 9f34ed8d..4af28c0e 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -292,9 +292,14 @@ def create_folder(self, title): def get_folder(self, title): url = "/api/search?type=dash-folder&query=%s" % quote(title) response = self._send_request(url, headers=self.headers, method="GET") - folder = next((item for item in response if item["title"] == title)) - if folder: - return folder + folders = [item for item in response if item.get("title") == to_text(title)] + + if len(folders) == 1: + return folders[0] + elif len(folders) > 1: + raise GrafanaError( + f"Multiple folders found for name {title}. Please use uid." + ) return None From 0d19c64714dba9b886d45d5bc2f58bb86b647db5 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:00:04 +0200 Subject: [PATCH 04/23] feat: change get folder api and use parent_uid if defined and check against uid first --- plugins/modules/grafana_folder.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 4af28c0e..978c8bd1 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -289,17 +289,16 @@ def create_folder(self, title): ) return response - def get_folder(self, title): - url = "/api/search?type=dash-folder&query=%s" % quote(title) + def get_folder(self, title, uid=None, parent_uid=None): + url = "/api/folders%s" % ("?parentUid=%s" % parent_uid if parent_uid else "") response = self._send_request(url, headers=self.headers, method="GET") - folders = [item for item in response if item.get("title") == to_text(title)] + if uid: + folders = [item for item in response if item.get("uid") == uid] + else: + folders = [item for item in response if item.get("title") == to_text(title)] - if len(folders) == 1: + if folders: return folders[0] - elif len(folders) > 1: - raise GrafanaError( - f"Multiple folders found for name {title}. Please use uid." - ) return None From 2150a4e91a9f5f96e1fb581692b621f83f027b00 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:00:29 +0200 Subject: [PATCH 05/23] feat: get folders by uid and parent_uid if defined --- plugins/modules/grafana_folder.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 978c8bd1..33ad4fcc 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -331,13 +331,24 @@ def main(): ) state = module.params["state"] title = module.params["name"] + parent_uid = module.params["parent_uid"] + uid = module.params["uid"] module.params["url"] = base.clean_url(module.params["url"]) grafana_iface = GrafanaFolderInterface(module) changed = False - if state == "present": + + if uid and parent_uid: + folder = grafana_iface.get_folder(title, uid, parent_uid) + elif uid: + folder = grafana_iface.get_folder(title, uid) + elif parent_uid: + folder = grafana_iface.get_folder(title, parent_uid=parent_uid) + else: folder = grafana_iface.get_folder(title) + + if state == "present": if folder is None: grafana_iface.create_folder(title) folder = grafana_iface.get_folder(title) @@ -345,7 +356,6 @@ def main(): folder = grafana_iface.get_folder(title) module.exit_json(changed=changed, folder=folder) elif state == "absent": - folder = grafana_iface.get_folder(title) if folder is None: module.exit_json(changed=False, message="No folder found") result = grafana_iface.delete_folder(folder.get("uid")) From a4fc0ef6dac1643874c834212c9243574ac6526a Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:12:16 +0200 Subject: [PATCH 06/23] chore: simplify --- plugins/modules/grafana_folder.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 33ad4fcc..8f190a38 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -339,14 +339,7 @@ def main(): changed = False - if uid and parent_uid: - folder = grafana_iface.get_folder(title, uid, parent_uid) - elif uid: - folder = grafana_iface.get_folder(title, uid) - elif parent_uid: - folder = grafana_iface.get_folder(title, parent_uid=parent_uid) - else: - folder = grafana_iface.get_folder(title) + folder = grafana_iface.get_folder(title, uid, parent_uid) if state == "present": if folder is None: From c1b14cdc72f36a3398cbe3a5e1d9b610f7350655 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:19:31 +0200 Subject: [PATCH 07/23] feat: create folder with uid and parent_uid --- plugins/modules/grafana_folder.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 8f190a38..44142078 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -281,9 +281,9 @@ def get_version(self): return {"major": int(major), "minor": int(minor), "rev": int(rev)} raise GrafanaError("Failed to retrieve version from '%s'" % url) - def create_folder(self, title): + def create_folder(self, title, uid=None, parent_uid=None): url = "/api/folders" - folder = dict(title=title) + folder = dict(title=title, uid=uid, parentUid=parent_uid) response = self._send_request( url, data=folder, headers=self.headers, method="POST" ) @@ -343,10 +343,9 @@ def main(): if state == "present": if folder is None: - grafana_iface.create_folder(title) - folder = grafana_iface.get_folder(title) + grafana_iface.create_folder(title, uid, parent_uid) + folder = grafana_iface.get_folder(title, uid, parent_uid) changed = True - folder = grafana_iface.get_folder(title) module.exit_json(changed=changed, folder=folder) elif state == "absent": if folder is None: From 8181820997ac87aa6b10aee6e0fe8bdac200688d Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:23:06 +0200 Subject: [PATCH 08/23] docs: parent_uid and uid --- plugins/modules/grafana_folder.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 44142078..db5d9e90 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -37,10 +37,18 @@ required: true type: str aliases: [ title ] + uid: + description: + - The folder UID. + type: str + parent_uid: + description: + - The parent folder UID. + type: str state: description: - Delete the members not found in the C(members) parameters from the - - list of members found on the Folder. + - list of members found on the folder. default: present type: str choices: ["present", "absent"] From 258c3a9011b3de0e5d5622c48da52c51bf787a1e Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:45:02 +0200 Subject: [PATCH 09/23] docs: module return values --- plugins/modules/grafana_folder.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index db5d9e90..61f9f4a9 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -116,6 +116,12 @@ type: str sample: - "nErXDvCkzz" + orgId: + description: The organization id + returned: always + type: int + sample: + - 1 title: description: The Folder title returned: always @@ -182,6 +188,12 @@ type: int sample: - 1 + parentUid: + description: The parent folders uid + returned: always as subfolder + type: str + sample: + - "76HjcBH2" """ import json @@ -189,7 +201,6 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import fetch_url, basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base -from ansible.module_utils.six.moves.urllib.parse import quote from ansible.module_utils._text import to_text __metaclass__ = type From 13c3da3be1526b65d789de7071608c50e9a0af6b Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:45:44 +0200 Subject: [PATCH 10/23] test: use module defaults --- .../grafana_folder/tasks/create-delete.yml | 94 +++++++++---------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/tests/integration/targets/grafana_folder/tasks/create-delete.yml b/tests/integration/targets/grafana_folder/tasks/create-delete.yml index c818c2ef..e1ee756c 100644 --- a/tests/integration/targets/grafana_folder/tasks/create-delete.yml +++ b/tests/integration/targets/grafana_folder/tasks/create-delete.yml @@ -1,58 +1,52 @@ --- -- name: Create a Folder - community.grafana.grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: grafana_working_group - state: present - register: result +- module_defaults: + community.grafana.grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + block: + - name: Create a Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result -- ansible.builtin.assert: - that: - - result.changed == true - - result.folder.title == 'grafana_working_group' - when: not ansible_check_mode + - ansible.builtin.assert: + that: + - result.changed == true + - result.folder.title == 'grafana_working_group' + when: not ansible_check_mode -- name: Test folder creation idempotency - community.grafana.grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: grafana_working_group - state: present - register: result + - name: Test folder creation idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result -- ansible.builtin.assert: - that: - - result.changed == false - - result.folder.title == 'grafana_working_group' - when: not ansible_check_mode + - ansible.builtin.assert: + that: + - result.changed == false + - result.folder.title == 'grafana_working_group' + when: not ansible_check_mode -- name: Delete a Folder - community.grafana.grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: grafana_working_group - state: absent - register: result + - name: Delete a Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result -- ansible.builtin.assert: - that: - - result.changed == true - when: not ansible_check_mode + - ansible.builtin.assert: + that: + - result.changed == true + when: not ansible_check_mode -- name: Test folder deletion idempotency - community.grafana.grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: grafana_working_group - state: absent - register: result + - name: Test folder deletion idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result -- ansible.builtin.assert: - that: - - result.changed == false - when: not ansible_check_mode + - ansible.builtin.assert: + that: + - result.changed == false + when: not ansible_check_mode From 1b99cc6e4973f8d8a35953e98ee2829e6184bbcc Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:46:00 +0200 Subject: [PATCH 11/23] test: add sub folder tests --- .../targets/grafana_folder/tasks/sub.yml | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 tests/integration/targets/grafana_folder/tasks/sub.yml diff --git a/tests/integration/targets/grafana_folder/tasks/sub.yml b/tests/integration/targets/grafana_folder/tasks/sub.yml new file mode 100644 index 00000000..d9f95ac5 --- /dev/null +++ b/tests/integration/targets/grafana_folder/tasks/sub.yml @@ -0,0 +1,110 @@ +--- +- module_defaults: + community.grafana.grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + uid: "parent" + block: + - name: Create a parent Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result + + - ansible.builtin.assert: + that: + - result.changed == true + - result.folder.title == 'grafana_working_group' + - result.folder.uid == 'parent' + when: not ansible_check_mode + + - name: Test folder parent creation idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result + + - ansible.builtin.assert: + that: + - result.changed == false + - result.folder.title == 'grafana_working_group' + - result.folder.uid == 'parent' + when: not ansible_check_mode + + - module_defaults: + community.grafana.grafana_folder: + uid: "sub" + parent_uid: "parent" + block: + - name: Create a sub Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result + + - ansible.builtin.assert: + that: + - result.changed == true + - result.folder.title == 'grafana_working_group' + - result.folder.uid == 'sub' + - result.folder.parentUid == 'parent' + when: not ansible_check_mode + + - name: Test sub folder creation idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result + + - ansible.builtin.assert: + that: + - result.changed == false + - result.folder.title == 'grafana_working_group' + - result.folder.uid == 'sub' + - result.folder.parentUid == 'parent' + when: not ansible_check_mode + + - name: Delete sub Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result + + - ansible.builtin.assert: + that: + - result.changed == true + when: not ansible_check_mode + + - name: Test sub folder deletion idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result + + - ansible.builtin.assert: + that: + - result.changed == false + when: not ansible_check_mode + + - name: Delete a Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result + + - ansible.builtin.assert: + that: + - result.changed == true + when: not ansible_check_mode + + - name: Test folder deletion idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result + + - ansible.builtin.assert: + that: + - result.changed == false + when: not ansible_check_mode From aaa9e64cbe0847a3daa6283118e555a9b906a40b Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:54:28 +0200 Subject: [PATCH 12/23] test: add sub folder tests to main --- tests/integration/targets/grafana_folder/tasks/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/targets/grafana_folder/tasks/main.yml b/tests/integration/targets/grafana_folder/tasks/main.yml index 0ac91f37..7880e8dc 100644 --- a/tests/integration/targets/grafana_folder/tasks/main.yml +++ b/tests/integration/targets/grafana_folder/tasks/main.yml @@ -4,3 +4,6 @@ - name: Folder creation and deletion for organization ansible.builtin.include_tasks: org.yml + +- name: Folder creation and deletion for subfolders + ansible.builtin.include_tasks: sub.yml From 2d1780d4118fd369558cf8fb2531cef92b17a4ee Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:54:56 +0200 Subject: [PATCH 13/23] test: fix org name test --- .../targets/grafana_folder/tasks/org.yml | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/tests/integration/targets/grafana_folder/tasks/org.yml b/tests/integration/targets/grafana_folder/tasks/org.yml index f30d48c1..7a628914 100644 --- a/tests/integration/targets/grafana_folder/tasks/org.yml +++ b/tests/integration/targets/grafana_folder/tasks/org.yml @@ -1,7 +1,53 @@ --- - module_defaults: community.grafana.grafana_folder: - org_name: Main Org. + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_name: "Main Org." block: - - name: Folder creation and deletion - ansible.builtin.include_tasks: create-delete.yml + - name: Create a Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result + + - ansible.builtin.assert: + that: + - result.changed == true + - result.folder.title == 'grafana_working_group' + when: not ansible_check_mode + + - name: Test folder creation idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: present + register: result + + - ansible.builtin.assert: + that: + - result.changed == false + - result.folder.title == 'grafana_working_group' + when: not ansible_check_mode + + - name: Delete a Folder + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result + + - ansible.builtin.assert: + that: + - result.changed == true + when: not ansible_check_mode + + - name: Test folder deletion idempotency + community.grafana.grafana_folder: + title: grafana_working_group + state: absent + register: result + + - ansible.builtin.assert: + that: + - result.changed == false + when: not ansible_check_mode From 6a1c04585bf5a719b068cb444d9bbadbaf82e333 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:55:11 +0200 Subject: [PATCH 14/23] test: sub folder tests module defaults --- tests/integration/targets/grafana_folder/tasks/sub.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/targets/grafana_folder/tasks/sub.yml b/tests/integration/targets/grafana_folder/tasks/sub.yml index d9f95ac5..21adb236 100644 --- a/tests/integration/targets/grafana_folder/tasks/sub.yml +++ b/tests/integration/targets/grafana_folder/tasks/sub.yml @@ -34,6 +34,9 @@ - module_defaults: community.grafana.grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" uid: "sub" parent_uid: "parent" block: From 560f1462d25079edb3d932245bf9ac75dd79b229 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 10:00:06 +0200 Subject: [PATCH 15/23] docs: changelog fragment --- changelogs/fragments/381-sub-folders.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/fragments/381-sub-folders.yml diff --git a/changelogs/fragments/381-sub-folders.yml b/changelogs/fragments/381-sub-folders.yml new file mode 100644 index 00000000..36e46667 --- /dev/null +++ b/changelogs/fragments/381-sub-folders.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - `grafana_folder` manage subfolders and specify uid From b5541f83b55af95b2811ed61d6a561d336067001 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 10:04:34 +0200 Subject: [PATCH 16/23] feat: role uid and parent_uid --- roles/grafana/tasks/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/roles/grafana/tasks/main.yml b/roles/grafana/tasks/main.yml index 170d9337..d0ddab40 100644 --- a/roles/grafana/tasks/main.yml +++ b/roles/grafana/tasks/main.yml @@ -277,10 +277,12 @@ - name: Manage folder # noqa: args[module] community.grafana.grafana_folder: name: "{{ folder.name }}" - skip_version_check: "{{ folder.skip_version_check | default(omit) }}" - state: "{{ folder.state | default(omit) }}" org_id: "{{ folder.org_id | default(omit) }}" org_name: "{{ folder.org_name | default(omit) }}" + parent_uid: "{{ folder.parent_uid | default(omit) }}" + skip_version_check: "{{ folder.skip_version_check | default(omit) }}" + state: "{{ folder.state | default(omit) }}" + uid: "{{ folder.uid | default(omit) }}" loop: "{{ grafana_folders }}" loop_control: {loop_var: folder} tags: folder From 01346b1e2de2322084ea2ea77b9dadebbf1b6a39 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Thu, 27 Jun 2024 10:04:50 +0200 Subject: [PATCH 17/23] docs: role new subfolder parameters --- roles/grafana/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/roles/grafana/README.md b/roles/grafana/README.md index a6ce7507..5c76497c 100644 --- a/roles/grafana/README.md +++ b/roles/grafana/README.md @@ -73,10 +73,12 @@ Configure Grafana organizations, dashboards, folders, datasources, teams and use | zabbix_user | no | | [**grafana_folders**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_folder_module.html) | | name | yes | -| skip_version_check | no | -| state | no | | org_id | no | | org_name | no | +| parent_uid | no | +| skip_version_check | no | +| state | no | +| uid | no | | [**grafana_dashboards**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_dashboard_module.html) | | commit_message | no | | dashboard_id | no | From 024730eaf8d9b480ec90bd711253f59eb0a1ab8c Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:00:58 +0200 Subject: [PATCH 18/23] fix: check for api support --- plugins/modules/grafana_folder.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 61f9f4a9..c9fa2364 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -241,6 +241,10 @@ def __init__(self, module): self._module.fail_json( failed=True, msg="Folders API is available starting Grafana v5" ) + if grafana_version["major"] < 11 and module.params["parent_uid"]: + self._module.fail_json( + failed=True, msg="Subfolder API is available starting Grafana v11" + ) def _send_request(self, url, data=None, headers=None, method="GET"): if data is not None: @@ -311,13 +315,16 @@ def create_folder(self, title, uid=None, parent_uid=None): def get_folder(self, title, uid=None, parent_uid=None): url = "/api/folders%s" % ("?parentUid=%s" % parent_uid if parent_uid else "") response = self._send_request(url, headers=self.headers, method="GET") - if uid: - folders = [item for item in response if item.get("uid") == uid] - else: - folders = [item for item in response if item.get("title") == to_text(title)] + if response is not None: + if uid: + folders = [item for item in response if item.get("uid") == uid] + else: + folders = [ + item for item in response if item.get("title") == to_text(title) + ] - if folders: - return folders[0] + if folders: + return folders[0] return None From 6203711a71e340aa70d478b12050403a4bccc18d Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:01:28 +0200 Subject: [PATCH 19/23] test: separate sub folder test file --- .../targets/grafana_folder/tasks/main.yml | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/integration/targets/grafana_folder/tasks/main.yml b/tests/integration/targets/grafana_folder/tasks/main.yml index 7880e8dc..ca3bd168 100644 --- a/tests/integration/targets/grafana_folder/tasks/main.yml +++ b/tests/integration/targets/grafana_folder/tasks/main.yml @@ -1,9 +1,21 @@ --- -- name: Folder creation and deletion - ansible.builtin.include_tasks: create-delete.yml +- name: Include folder task files + ansible.builtin.include_tasks: "{{ item }}.yml" + loop: + - create-delete + - org -- name: Folder creation and deletion for organization - ansible.builtin.include_tasks: org.yml +- name: Check for support of API endpoint + register: result + ignore_errors: true + community.grafana.grafana_folder: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + title: apitest + parent_uid: "parent" + state: absent -- name: Folder creation and deletion for subfolders +- name: Include folder task file for subfolders ansible.builtin.include_tasks: sub.yml + when: "result.msg | default('') != 'Subfolder API is available starting Grafana v11'" From c2b669df69eea1235e2ec61266f3a5e6550af2d3 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:04:21 +0200 Subject: [PATCH 20/23] docs: version notice for parent_uid arg --- plugins/modules/grafana_folder.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index c9fa2364..054386d2 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -44,6 +44,7 @@ parent_uid: description: - The parent folder UID. + - Available with subfolder feature of Grafana 11. type: str state: description: From c13d9f408ce6dc0a10e443cff53ee68094586bca Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:05:27 +0200 Subject: [PATCH 21/23] docs: changelog fragment format fix --- changelogs/fragments/381-sub-folders.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/381-sub-folders.yml b/changelogs/fragments/381-sub-folders.yml index 36e46667..bb6f2506 100644 --- a/changelogs/fragments/381-sub-folders.yml +++ b/changelogs/fragments/381-sub-folders.yml @@ -1,3 +1,3 @@ --- minor_changes: - - `grafana_folder` manage subfolders and specify uid + - Manage subfolders for `grafana_folder` and specify uid From 54e1e4b11f74c6b0de0a7aeb242f5589a8cb8cf6 Mon Sep 17 00:00:00 2001 From: Nemental <15136847+Nemental@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:11:19 +0200 Subject: [PATCH 22/23] chore: lower folder --- plugins/modules/grafana_folder.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 054386d2..dc7e1b6d 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -25,15 +25,15 @@ author: - RĂ©mi REY (@rrey) version_added: "1.0.0" -short_description: Manage Grafana Folders +short_description: Manage Grafana folders description: - - Create/update/delete Grafana Folders through the Folders API. + - Create/update/delete Grafana folders through the folders API. requirements: - - The Folders API is only available starting Grafana 5 and the module will fail if the server version is lower than version 5. + - The folders API is only available starting Grafana 5 and the module will fail if the server version is lower than version 5. options: name: description: - - The title of the Grafana Folder. + - The title of the Grafana folder. required: true type: str aliases: [ title ] @@ -101,18 +101,18 @@ RETURN = """ --- folder: - description: Information about the Folder + description: Information about the folder returned: On success type: complex contains: id: - description: The Folder identifier + description: The folder identifier returned: always type: int sample: - 42 uid: - description: The Folder uid + description: The folder uid returned: always type: str sample: @@ -124,13 +124,13 @@ sample: - 1 title: - description: The Folder title + description: The folder title returned: always type: str sample: - "Department ABC" url: - description: The Folder url + description: The folder url returned: always type: str sample: @@ -240,7 +240,7 @@ def __init__(self, module): self._module.fail_json(failed=True, msg=to_text(e)) if grafana_version["major"] < 5: self._module.fail_json( - failed=True, msg="Folders API is available starting Grafana v5" + failed=True, msg="folders API is available starting Grafana v5" ) if grafana_version["major"] < 11 and module.params["parent_uid"]: self._module.fail_json( @@ -276,7 +276,7 @@ def _send_request(self, url, data=None, headers=None, method="GET"): response = resp.read() or "{}" return self._module.from_json(response) self._module.fail_json( - failed=True, msg="Grafana Folders API answered with HTTP %d" % status_code + failed=True, msg="Grafana folders API answered with HTTP %d" % status_code ) def switch_organization(self, org_id): From 6af16e2ea1b8a9a527d646956e1d090fddbbc099 Mon Sep 17 00:00:00 2001 From: Moritz <15136847+Nemental@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:32:14 +0200 Subject: [PATCH 23/23] chore: simplify if statement Co-authored-by: Sebastian Gumprich --- plugins/modules/grafana_folder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index dc7e1b6d..9e7cea92 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -316,7 +316,7 @@ def create_folder(self, title, uid=None, parent_uid=None): def get_folder(self, title, uid=None, parent_uid=None): url = "/api/folders%s" % ("?parentUid=%s" % parent_uid if parent_uid else "") response = self._send_request(url, headers=self.headers, method="GET") - if response is not None: + if response: if uid: folders = [item for item in response if item.get("uid") == uid] else: