diff --git a/changelogs/fragments/331-dashboard-by-org-name.yml b/changelogs/fragments/331-dashboard-by-org-name.yml new file mode 100644 index 00000000..576c8c14 --- /dev/null +++ b/changelogs/fragments/331-dashboard-by-org-name.yml @@ -0,0 +1,8 @@ +--- + +minor_changes: + - Add parameter `org_name` to `grafana_dashboard` + +trivial: + - Add tests for new `grafana_dashboard`-parameter `org_name` + - Refactor tests for `grafana_dashboard` diff --git a/plugins/modules/grafana_dashboard.py b/plugins/modules/grafana_dashboard.py index 46d3aeb1..6e8c8ba6 100644 --- a/plugins/modules/grafana_dashboard.py +++ b/plugins/modules/grafana_dashboard.py @@ -18,10 +18,17 @@ options: org_id: description: - - 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.. + - The Grafana organization ID where the dashboard will be imported / exported / deleted. + - Not used when I(grafana_api_key) is set, because the grafana_api_key only belongs to one organization. + - Mutually exclusive with `org_name`. default: 1 type: int + org_name: + description: + - The Grafana organization name where the dashboard will be imported / exported / deleted. + - Not used when I(grafana_api_key) is set, because the grafana_api_key only belongs to one organization. + - Mutually exclusive with `org_id`. + type: str folder: description: - The Grafana folder where this dashboard will be imported to. @@ -157,7 +164,19 @@ class GrafanaDeleteException(Exception): pass -def grafana_switch_organisation(module, grafana_url, org_id, headers): +def grafana_organization_id_by_name(module, grafana_url, org_name, headers): + r, info = fetch_url(module, '%s/api/user/orgs' % grafana_url, headers=headers, method='GET') + if info['status'] != 200: + raise GrafanaAPIException("Unable to retrieve users organizations: %s" % info) + organizations = json.loads(to_text(r.read())) + for org in organizations: + if org['name'] == org_name: + return org['orgId'] + + raise GrafanaAPIException("Current user isn't member of organization: %s" % org_name) + + +def grafana_switch_organization(module, grafana_url, org_id, headers): r, info = fetch_url(module, '%s/api/user/using/%s' % (grafana_url, org_id), headers=headers, method='POST') if info['status'] != 200: raise GrafanaAPIException('Unable to switch to organization %s : %s' % (org_id, info)) @@ -169,7 +188,10 @@ def grafana_headers(module, data): headers['Authorization'] = "Bearer %s" % data['grafana_api_key'] else: module.params['force_basic_auth'] = True - grafana_switch_organisation(module, data['url'], data['org_id'], headers) + if module.params['org_name']: + org_name = module.params['org_name'] + data['org_id'] = grafana_organization_id_by_name(module, data['url'], org_name, headers) + grafana_switch_organization(module, data['url'], data['org_id'], headers) return headers @@ -186,7 +208,7 @@ def get_grafana_version(module, grafana_url, headers): except Exception as e: raise GrafanaAPIException(e) else: - raise GrafanaAPIException('Unable to get grafana version : %s' % info) + raise GrafanaAPIException('Unable to get grafana version: %s' % info) return int(grafana_version) @@ -490,6 +512,7 @@ def main(): argument_spec.update( state=dict(choices=['present', 'absent', 'export'], default='present'), org_id=dict(default=1, type='int'), + org_name=dict(type='str'), folder=dict(type='str', default='General'), uid=dict(type='str'), slug=dict(type='str'), @@ -508,7 +531,7 @@ def main(): ['state', 'export', ['path']], ], required_together=[['url_username', 'url_password', 'org_id']], - mutually_exclusive=[['url_username', 'grafana_api_key'], ['uid', 'slug'], ['path', 'dashboard_id']], + mutually_exclusive=[['url_username', 'grafana_api_key'], ['uid', 'slug'], ['path', 'dashboard_id'], ['org_id', 'org_name']], ) module.params["url"] = clean_url(module.params["url"]) diff --git a/tests/integration/targets/grafana_dashboard/defaults/main.yml b/tests/integration/targets/grafana_dashboard/defaults/main.yml index 8b9c9348..7bb77ea3 100644 --- a/tests/integration/targets/grafana_dashboard/defaults/main.yml +++ b/tests/integration/targets/grafana_dashboard/defaults/main.yml @@ -1,7 +1,4 @@ --- - grafana_url: "http://grafana:3000/" grafana_username: "admin" grafana_password: "admin" - -... diff --git a/tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml b/tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml new file mode 100644 index 00000000..538e7c26 --- /dev/null +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml @@ -0,0 +1,38 @@ +--- +- module_defaults: + community.grafana.grafana_dashboard: + org_name: Main Org. + block: + - name: Check import grafana dashboard from file + 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 + register: result + - ansible.builtin.assert: + that: + - "result.failed == false" + - "result.changed == true" + - "result.msg == 'Dashboard test created'" + + - name: Check import grafana dashboard from file idempotency + 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 + register: result + - ansible.builtin.assert: + that: + - "result.failed == false" + - "result.changed == false" + - "result.msg == 'Dashboard test unchanged.'" + + - ansible.builtin.include_tasks: delete-dashboard.yml diff --git a/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml b/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml index 04b17f72..7a2354ea 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml @@ -1,32 +1,26 @@ --- -- set_fact: - dashboard_uid: "{{ result.uid }}" - - name: Check export grafana dashboard to file - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: export - path: /tmp/dashboard.json + path: /tmp/dashboard_export.json overwrite: true - uid: "{{ dashboard_uid }}" + uid: "{{ result.uid }}" register: result - -- debug: - var: result - -- assert: +- ansible.builtin.assert: that: + - "result.failed == false" - "result.changed == true" - - "result.msg == 'Dashboard {{ dashboard_uid }} exported to /tmp/dashboard.json'" + - "result.msg == 'Dashboard ' ~ result.uid ~ ' exported to /tmp/dashboard_export.json'" -- name: Load /tmp/dashboard.json or fail if missing - set_fact: - exported_dashboard_lines: "{{ lookup('file', '/tmp/dashboard.json').splitlines() }}" +- name: Load /tmp/dashboard_export.json or fail if missing + ansible.builtin.set_fact: + exported_dashboard_lines: "{{ lookup('file', '/tmp/dashboard_export.json').splitlines() }}" - name: Assert that exported dashboard contains formatted JSON - assert: + ansible.builtin.assert: that: - "exported_dashboard_lines | length >= 2" - "exported_dashboard_lines[0] == '{'" 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 f4f3d9f4..4faf28bd 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml @@ -1,33 +1,18 @@ --- -- name: copy dashboard file - copy: - src: "files/dashboard.json" - dest: "/tmp/dashboard.json" - -- block: - - name: Check import grafana dashboard from file to unknown folder fails - 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: inexistent - register: result - ignore_errors: true - -- debug: - var: result - -- set_fact: - # XXX: Too many quotes of different types to do inline. - # I did not manage to find a good way of having it inline. - expected_error: "error : Dashboard folder 'inexistent' does not exist." - -- assert: +- name: Check import grafana dashboard from file to unknown folder fails + 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: inexistent + register: result + ignore_errors: true +- ansible.builtin.assert: that: - - "result.changed == false" - "result.failed == true" - - "result.msg == expected_error" + - "result.changed == false" + - "result.msg == 'error : Dashboard folder \\'inexistent\\' does not exist.'" diff --git a/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml b/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml similarity index 71% rename from tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml rename to tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml index 93df1666..2e54d110 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml @@ -1,12 +1,11 @@ --- -- name: copy dashboard file - copy: +- name: Copy dashboard file + ansible.builtin.copy: src: "files/dashboard.json" dest: "/tmp/dashboard.json" - - name: Check import grafana dashboard from file - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -15,17 +14,14 @@ path: /tmp/dashboard.json overwrite: true register: result - -- debug: - var: result - -- assert: +- ansible.builtin.assert: that: + - "result.failed == false" - "result.changed == true" - "result.msg == 'Dashboard test created'" - name: Check import grafana dashboard from file idempotency - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -34,11 +30,12 @@ path: /tmp/dashboard.json overwrite: true register: result - -- debug: - var: result - -- assert: +- ansible.builtin.assert: that: + - "result.failed == false" - "result.changed == false" - "result.msg == 'Dashboard test unchanged.'" + +- ansible.builtin.include_tasks: dashboard-export.yml + +- ansible.builtin.include_tasks: delete-dashboard.yml diff --git a/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml b/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml index 3b81ebf7..d2d248de 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml @@ -1,6 +1,6 @@ --- - name: Check import grafana dashboard from id - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -10,17 +10,14 @@ dashboard_revision: "1" overwrite: true register: result - -- debug: - var: result - - assert: that: + - "result.failed == false" - "result.changed == true" - "result.msg == 'Dashboard Zabbix Host Status created'" - name: Check import grafana dashboard from id idempotency - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -30,11 +27,10 @@ dashboard_revision: "1" overwrite: true register: result - -- debug: - var: result - -- assert: +- ansible.builtin.assert: that: + - "result.failed == false" - "result.changed == false" - "result.msg == 'Dashboard Zabbix Host Status unchanged.'" + +- ansible.builtin.include_tasks: delete-dashboard.yml diff --git a/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml b/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml index 5146fc9a..13c6555c 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml @@ -1,7 +1,6 @@ --- - - name: Check import grafana dashboard from url - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -10,17 +9,14 @@ dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download overwrite: true register: result - -- debug: - var: result - - assert: that: + - "result.failed == false" - "result.changed == true" - "result.msg == 'Dashboard Zabbix Host Status created'" - name: Check import grafana dashboard from url idempotency - grafana_dashboard: + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -29,11 +25,10 @@ dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download overwrite: true register: result - -- debug: - var: result - -- assert: +- ansible.builtin.assert: that: + - "result.failed == false" - "result.changed == false" - "result.msg == 'Dashboard Zabbix Host Status unchanged.'" + +- ansible.builtin.include_tasks: delete-dashboard.yml diff --git a/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml b/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml index 2013324f..c9f04898 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml @@ -1,16 +1,14 @@ -- name: Check delete dashboard is working - grafana_dashboard: +--- +- name: Delete dashboard + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent uid: "{{ result.uid }}" register: result - -- debug: - var: result - -- assert: +- ansible.builtin.assert: that: + - "result.failed == false" - "result.changed == true" - - "result.msg == 'Dashboard {{ result.uid }} deleted'" + - "result.msg == 'Dashboard ' ~ result.uid ~ ' deleted'" diff --git a/tests/integration/targets/grafana_dashboard/tasks/main.yml b/tests/integration/targets/grafana_dashboard/tasks/main.yml index 570fb4d8..0a082d43 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/main.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/main.yml @@ -1,7 +1,15 @@ -- block: - - include_tasks: dashboard-from-url.yml - - include_tasks: delete-dashboard.yml - - include_tasks: dashboard-from-id.yml - - include_tasks: dashboard-from-file.yml - - include_tasks: dashboard-export.yml - - include_tasks: dashboard-folder-destination.yml +--- +- name: Create dashboard from url + ansible.builtin.include_tasks: dashboard-from-url.yml + +- name: Create dashboard from id + ansible.builtin.include_tasks: dashboard-from-id.yml + +- name: Create dashboard from file and export + ansible.builtin.include_tasks: dashboard-from-file-export.yml + +- name: Create dashboard from file by org_name + ansible.builtin.include_tasks: dashboard-by-org-name.yml + +- name: Create dashboard in non existing folder + ansible.builtin.include_tasks: dashboard-folder-destination.yml